def recursive_eval_unquote(s_expression, _environment): """Return a copy of s_expression, with all occurrences of unquoted s-expressions replaced by their evaluated values. Note that we can only have unquote-splicing in a sublist, since we can only return one value, e.g `,@(1 2 3). """ if isinstance(s_expression, Atom): return (s_expression, _environment) elif isinstance(s_expression, Nil): return (s_expression, _environment) elif s_expression[0] == Symbol("unquote"): check_argument_number('unquote', arguments, 1, 1) return eval_s_expression(s_expression[1], _environment) else: # return a list of s_expressions that have been # recursively checked for unquote list_elements = [] for element in s_expression: if isinstance(element, Cons) and \ element[0] == Symbol('unquote-splicing'): check_argument_number('unquote-splicing', element.tail, 1, 1) (result, _environment) = eval_s_expression(element[1], _environment) if not isinstance(result, Cons) and not isinstance( result, Nil): raise SchemeArityError( "unquote-splicing requires a list.") for item in result: list_elements.append(item) else: (result, _environment) = recursive_eval_unquote( element, _environment) list_elements.append(result) return (Cons.from_list(list_elements), _environment)
def __init__(self, outer, binds=None, exprs=None): self.outer = outer self.data = {} if binds: # print('binds', binds, 'exprs', exprs) for i, bind in enumerate(binds): if bind == Symbol("&"): self.set(binds[i + 1].name, list(exprs[i:])) break else: self.set(bind.name, exprs[i])
def define_variadic_function(arguments, environment): function_name_with_parameters = arguments[0] function_name = arguments[0][0] function_parameters = function_name_with_parameters.tail function_body = arguments.tail dot_position = function_parameters.index(Symbol('.')) if dot_position < len(function_parameters) - 2: raise SchemeSyntaxError("You can only have one improper list " "(you have %d parameters after the '.')." % (len(function_parameters) - 1 - dot_position)) if dot_position == len(function_parameters) - 1: raise SchemeSyntaxError( "Must name an improper list parameter after '.'.") def named_variadic_function(_arguments, _environment): # a function that takes a variable number of arguments if dot_position == 0: explicit_parameters = Nil() else: explicit_parameters = deepcopy(function_parameters) # create a linked list holding all the parameters before the dot current_head = explicit_parameters # find the position in the list just before the dot for i in range(dot_position - 2): current_head = current_head.tail # then remove the rest of the list current_head.tail = Nil() improper_list_parameter = function_parameters[dot_position + 1] # check we have been given sufficient arguments for our explicit parameters check_argument_number(function_name.value, _arguments, len(explicit_parameters)) local_environment = {} # evaluate arguments _arguments = deepcopy(_arguments) for i in range(len(_arguments)): (_arguments[i], _environment) = eval_s_expression(_arguments[i], _environment) # assign parameters for (parameter, parameter_value) in zip(explicit_parameters, _arguments): local_environment[parameter] = parameter_value # put the remaining arguments in our improper parameter remaining_arguments = _arguments for i in range(len(explicit_parameters)): remaining_arguments = remaining_arguments.tail local_environment[improper_list_parameter.value] = remaining_arguments new_environment = dict(_environment, **local_environment) # evaluate our function_body in this environment for s_exp in function_body: result, new_environment = eval_s_expression(s_exp, new_environment) # update global variables that weren't masked by locals for variable_name in _environment: if variable_name not in local_environment: _environment[variable_name] = new_environment[variable_name] return (result, _environment) # assign this function to this name environment[function_name.value] = UserFunction(named_variadic_function, function_name.value) return (None, environment)
def p_atom_symbol(p): "atom : SYMBOL" p[0] = Symbol(p[1])
def p_list_unquotesplicingsugar(p): "list : UNQUOTESPLICINGSUGAR sexpression" # convert ,foo to (unquote foo) p[0] = Cons(Symbol("unquote-splicing"), Cons(p[2]))
def p_list_unquotesugar(p): "list : UNQUOTESUGAR sexpression" # convert ,foo to (unquote foo) p[0] = Cons(Symbol("unquote"), Cons(p[2]))
def p_list_quasiquotesugar(p): "list : QUASIQUOTESUGAR sexpression" # convert `foo to (quasiquote foo) p[0] = Cons(Symbol("quasiquote"), Cons(p[2]))