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)
Exemple #2
0
 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]))