Пример #1
0
def full_evaluate(expression, environment):
    """
    Fully evaluate an expression until its basic representation
    """

    while True:
        if is_thunk(expression):
            if not expression.is_evaluated:
                expression.is_evaluated = True
                expression.expression = full_evaluate(expression.expression,
                                                      expression.environment)
            return expression.expression
        elif is_symbol(expression):
            expression = environment[expression]
            continue
        elif (is_atom(expression) or is_nil(expression) or
              is_procedure(expression) or is_macro(expression) or
              callable(expression)):
            return expression
        elif not is_pair(expression):
            raise ValueError("Cannot evaluate: %s" % expression)
        elif car(expression) == 'delay':
            if len(expression) != 2:
                raise SyntaxError("Unexpected delay form: %s. Should be (delay <expression>)" %
                                  expression)
            return Thunk(cadr(expression), environment)
        elif car(expression) == 'defined?':
            if len(expression) != 2:
                raise SyntaxError("Unexpected defined? form: %s. Should be (defined? <symbol>)" %
                                  expression)
            name = cadr(expression)
            if not is_symbol(name):
                raise SyntaxError("Argument of defined? form should be a symbol. Evaluating: %s" %
                                  expression)
            return environment.exists(name)
        elif car(expression) == 'define':
            if len(expression) != 3:
                raise SyntaxError("Unexpected define form: %s. Should be (define <symbol> <expression>)" %
                                  expression)
            name = cadr(expression)
            if not is_symbol(name):
                raise SyntaxError("First argument of define form should be a symbol. Evaluating: %s" %
                                  expression)
            environment[name] = Thunk(caddr(expression), environment)
            return name
        elif car(expression) == 'quote':
            if len(expression) != 2:
                raise SyntaxError("Unexpected quote form: %s. Should be (quote <expression>)" %
                                  expression)
            return cadr(expression)
        elif car(expression) == 'eval':
            if len(expression) != 2:
                raise SyntaxError("Unexpected eval form: %s. Should be (eval <expression>)" %
                                  expression)
            expression = full_evaluate(cadr(expression), environment)
            continue
        elif car(expression) == 'if':
            if len(expression) != 4:
                raise SyntaxError("Unexpected if form: %s. Should be (if <condition> <consequent> <alternative>)" %
                                  expression)
            condition = full_evaluate(cadr(expression), environment)
            expression = caddr(expression) if condition else cadddr(expression)
            continue
        elif car(expression) == 'lambda':
            if len(expression) < 3:
                raise SyntaxError("Unexpected lambda form: %s. Should be (lambda (<param> ...) <expression> ...)" %
                                  expression)
            parameters = cadr(expression)
            if is_pair(parameters):
                current = parameters
                while is_pair(current):
                    if not is_symbol(car(current)):
                        raise SyntaxError("Lambda parameters should be symbols. In %s" %
                                          expression)
                    current = cdr(current)
                if not is_nil(current) and not is_symbol(current):
                    raise SyntaxError("Lambda optional parameter should be a symbol or nil. In %s" %
                                      expression)
            elif not is_symbol(parameters) and not is_nil(parameters):
                raise SyntaxError("Lambda parameters should be a symbol or a list of zero or more. In %s" %
                                  expression)

            return Procedure(parameters, # parameters
                             cddr(expression), # body (list of expressions)
                             environment)
        elif car(expression) == 'macro':
            if len(expression) < 3:
                raise SyntaxError("Unexpected define macro: %s. Should be (macro (<resword> ...) (<pattern> <transformation> ...) ...)" %
                                  expression)
            res_words = cadr(expression)
            rules = cddr(expression)
            if not is_nil(res_words) and not is_pair(res_words):
                raise SyntaxError("Macro reserved words should be a list of symbols or nil. In %s" %
                                  expression)
            if is_pair(res_words):
                for word in res_words:
                    if not is_symbol(word):
                        raise SyntaxError("Macro reserved words should all be symbols. In %s" %
                                          expression)
            for rule in rules:
                if len(rule) < 2:
                    raise SyntaxError("Macro rule should be in the form (<pattern> <expression> ...). In %s" %
                                      expression)
            return Macro( [(car(e), cdr(e)) for e in rules], # rules
                          [] if not res_words else set(iter(res_words)) ) # reserved words
        else:
            # evaluate head
            operator = full_evaluate(car(expression), environment)

            if is_macro(operator):
                # evaluate recursively only the inner expressions (not the last)
                current = operator.transform(expression)
                while cdr(current) is not None:
                    full_evaluate(car(current), environment)
                    current = cdr(current)
                expression = car(current)
                continue
            else:
                # The unevaluated operands
                unev_operands = cdr(expression)

                if callable(operator):
                    # evaluate each operand recursively
                    operands = [full_evaluate(e, environment) for e in unev_operands] if unev_operands else []
                    # return the application of the built-in procedure
                    return operator(make_list(operands))
                elif is_procedure(operator):
                    # create Thunks (promise to evaluate) for each operand
                    unev_op_list = list(iter(unev_operands)) if unev_operands else []
                    proc_environment = Environment(parent=operator.environment)
                    # if the lambda parameters is not in the format ( () [. <symbol>] )
                    # for taking zero or more arguments
                    if len(operator.parameters) != 1 or not is_nil(operator.parameters[0]):
                        for name in operator.parameters:
                            try:
                                # take next argument
                                proc_environment[name] = Thunk(unev_op_list.pop(0),
                                                               environment)
                            except IndexError:
                                raise ValueError("Insufficient parameters for procedure %s. It should be at least %d" %
                                                 (operator, len(operator.parameters)))
                    if not is_nil(operator.optional):
                        # the optional argument is something, that when
                        # evaluated, yields the list of rest of the operands
                        # evaluated
                        proc_environment[operator.optional] = Thunk(cons(lambda x: x,
                                                                         make_list(unev_op_list)),
                                                                    environment)
                    elif unev_op_list:
                        raise ValueError("Too much parameters for procedure %s. It should be %d." %
                                         (operator, len(operator.parameters)))

                    # evaluate recursively only the inner procedure expressions
                    # (not the last)
                    current = operator.body
                    while cdr(current) is not None:
                        full_evaluate(car(current), proc_environment)
                        current = cdr(current)

                    environment = proc_environment
                    expression = car(current)
                    # continue not-recursively to evaluate the procedure's body
                    # in the extended environment
                    continue
                else:
                    raise ValueError("Not an operator: %s, in expression: %s" %
                                     (operator, expression))
Пример #2
0
def make_global_environment():
    env = NumericEnvironment()

    # utf-8 stdin and out
    stdin = codecs.getreader('utf-8')(sys.stdin)
    stdout = codecs.getreader('utf-8')(sys.stdout)

    env.update({
        # built-in symbols
        'nil':
        None,
        '#t':
        True,
        '#f':
        False,

        # symbolic tests
        'procedure?':
        BuiltinProcedure(lambda args: is_procedure(car(args)), 'procedure?', 1,
                         1),
        'macro?':
        BuiltinProcedure(lambda args: is_macro(car(args)), 'macro?', 1, 1),
        'thunk?':
        BuiltinProcedure(lambda args: is_thunk(car(args)), 'thunk?', 1, 1),
        'symbol?':
        BuiltinProcedure(lambda args: is_symbol(car(args)), 'symbol?', 1, 1),
        'atom?':
        BuiltinProcedure(lambda args: is_atom(car(args)), 'atom?', 1, 1),
        'pair?':
        BuiltinProcedure(lambda args: is_pair(car(args)), 'pair?', 1, 1),
        'nil?':
        BuiltinProcedure(lambda args: is_nil(car(args)), 'nil?', 1, 1),
        'eq?':
        BuiltinProcedure(lambda args: car(args) is cadr(args), 'eq?', 2, 2),
        '=':
        BuiltinProcedure(lambda args: car(args) == cadr(args), '=', 2, 2),

        # symbolic manipulation
        'explode':
        BuiltinProcedure(lambda args: make_list(list(car(args))), 'explode', 1,
                         1),
        'implode':
        BuiltinProcedure(lambda args: ''.join(iter(args)), 'implode', 1),

        # basic data manipulation
        'car':
        BuiltinProcedure(lambda args: caar(args), 'car', 1, 1),
        "cdr":
        BuiltinProcedure(lambda args: cdar(args), "cdr", 1, 1),
        "cons":
        BuiltinProcedure(lambda args: cons(car(args), cadr(args)), "cons", 2,
                         2),

        # I/O operations
        'write':
        BuiltinProcedure(
            lambda args: stdout.write(
                unicode(car(args)).encode('utf-8').decode('string_escape')),
            'write', 1, 1),
        'read':
        BuiltinProcedure(lambda args: stdin.read(1), 'read', 0, 0),
        'file-open':
        BuiltinProcedure(
            lambda args: codecs.open(car(args), cadr(args), 'utf-8'),
            'file-open', 2, 2),
        'file-close':
        BuiltinProcedure(lambda args: car(args).close(), 'file-close', 1, 1),
        'file-write':
        BuiltinProcedure(
            lambda args: car(args).write(
                unicode(cadr(args)).encode('utf-8').decode('string_escape').
                decode('utf-8')), 'file-write', 2, 2),
        'file-read':
        BuiltinProcedure(lambda args: car(args).read(1), 'file-read', 1, 1),

        # dependency inclusion
        'include':
        IncludeMacro(),

        # arithmetic operations
        '+':
        BuiltinProcedure(lambda args: reduce(operator.add, args), '+', 2),
        '-':
        BuiltinProcedure(lambda args: reduce(operator.sub, args), '-', 2),
        '*':
        BuiltinProcedure(lambda args: reduce(operator.mul, args), '*', 2),
        '/':
        BuiltinProcedure(lambda args: reduce(operator.div, args), '/', 2),
        'mod':
        BuiltinProcedure(lambda args: reduce(operator.mod, args), 'mod', 2),
        '<':
        BuiltinProcedure(lambda args: car(args) < cadr(args), '<', 2),
        '>':
        BuiltinProcedure(lambda args: car(args) > cadr(args), '>', 2),
        '<=':
        BuiltinProcedure(lambda args: car(args) <= cadr(args), '<=', 2),
        '>=':
        BuiltinProcedure(lambda args: car(args) >= cadr(args), '>=', 2),
    })
    return env
Пример #3
0
def make_global_environment():
    env = NumericEnvironment()

    # utf-8 stdin and out
    stdin = codecs.getreader("utf-8")(sys.stdin)
    stdout = codecs.getreader("utf-8")(sys.stdout)

    env.update(
        {
            # built-in symbols
            "nil": None,
            "#t": True,
            "#f": False,
            # symbolic tests
            "procedure?": BuiltinProcedure(lambda args: is_procedure(car(args)), "procedure?", 1, 1),
            "macro?": BuiltinProcedure(lambda args: is_macro(car(args)), "macro?", 1, 1),
            "thunk?": BuiltinProcedure(lambda args: is_thunk(car(args)), "thunk?", 1, 1),
            "symbol?": BuiltinProcedure(lambda args: is_symbol(car(args)), "symbol?", 1, 1),
            "atom?": BuiltinProcedure(lambda args: is_atom(car(args)), "atom?", 1, 1),
            "pair?": BuiltinProcedure(lambda args: is_pair(car(args)), "pair?", 1, 1),
            "nil?": BuiltinProcedure(lambda args: is_nil(car(args)), "nil?", 1, 1),
            "eq?": BuiltinProcedure(lambda args: car(args) is cadr(args), "eq?", 2, 2),
            "=": BuiltinProcedure(lambda args: car(args) == cadr(args), "=", 2, 2),
            # symbolic manipulation
            "explode": BuiltinProcedure(lambda args: make_list(list(car(args))), "explode", 1, 1),
            "implode": BuiltinProcedure(lambda args: "".join(iter(args)), "implode", 1),
            # basic data manipulation
            "car": BuiltinProcedure(lambda args: caar(args), "car", 1, 1),
            "cdr": BuiltinProcedure(lambda args: cdar(args), "cdr", 1, 1),
            "cons": BuiltinProcedure(lambda args: cons(car(args), cadr(args)), "cons", 2, 2),
            # I/O operations
            "write": BuiltinProcedure(
                lambda args: stdout.write(unicode(car(args)).encode("utf-8").decode("string_escape")), "write", 1, 1
            ),
            "read": BuiltinProcedure(lambda args: stdin.read(1), "read", 0, 0),
            "file-open": BuiltinProcedure(lambda args: codecs.open(car(args), cadr(args), "utf-8"), "file-open", 2, 2),
            "file-close": BuiltinProcedure(lambda args: car(args).close(), "file-close", 1, 1),
            "file-write": BuiltinProcedure(
                lambda args: car(args).write(
                    unicode(cadr(args)).encode("utf-8").decode("string_escape").decode("utf-8")
                ),
                "file-write",
                2,
                2,
            ),
            "file-read": BuiltinProcedure(lambda args: car(args).read(1), "file-read", 1, 1),
            # dependency inclusion
            "include": IncludeMacro(),
            # arithmetic operations
            "+": BuiltinProcedure(lambda args: reduce(operator.add, args), "+", 2),
            "-": BuiltinProcedure(lambda args: reduce(operator.sub, args), "-", 2),
            "*": BuiltinProcedure(lambda args: reduce(operator.mul, args), "*", 2),
            "/": BuiltinProcedure(lambda args: reduce(operator.div, args), "/", 2),
            "mod": BuiltinProcedure(lambda args: reduce(operator.mod, args), "mod", 2),
            "<": BuiltinProcedure(lambda args: car(args) < cadr(args), "<", 2),
            ">": BuiltinProcedure(lambda args: car(args) > cadr(args), ">", 2),
            "<=": BuiltinProcedure(lambda args: car(args) <= cadr(args), "<=", 2),
            ">=": BuiltinProcedure(lambda args: car(args) >= cadr(args), ">=", 2),
        }
    )
    return env
Пример #4
0
def full_evaluate(expression, environment):
    """
    Fully evaluate an expression until its basic representation
    """

    while True:
        if is_thunk(expression):
            if not expression.is_evaluated:
                expression.is_evaluated = True
                expression.expression = full_evaluate(expression.expression,
                                                      expression.environment)
            return expression.expression
        elif is_symbol(expression):
            expression = environment[expression]
            continue
        elif (is_atom(expression) or is_nil(expression)
              or is_procedure(expression) or is_macro(expression)
              or callable(expression)):
            return expression
        elif not is_pair(expression):
            raise ValueError("Cannot evaluate: %s" % expression)
        elif car(expression) == 'delay':
            if len(expression) != 2:
                raise SyntaxError(
                    "Unexpected delay form: %s. Should be (delay <expression>)"
                    % expression)
            return Thunk(cadr(expression), environment)
        elif car(expression) == 'defined?':
            if len(expression) != 2:
                raise SyntaxError(
                    "Unexpected defined? form: %s. Should be (defined? <symbol>)"
                    % expression)
            name = cadr(expression)
            if not is_symbol(name):
                raise SyntaxError(
                    "Argument of defined? form should be a symbol. Evaluating: %s"
                    % expression)
            return environment.exists(name)
        elif car(expression) == 'define':
            if len(expression) != 3:
                raise SyntaxError(
                    "Unexpected define form: %s. Should be (define <symbol> <expression>)"
                    % expression)
            name = cadr(expression)
            if not is_symbol(name):
                raise SyntaxError(
                    "First argument of define form should be a symbol. Evaluating: %s"
                    % expression)
            environment[name] = Thunk(caddr(expression), environment)
            return name
        elif car(expression) == 'quote':
            if len(expression) != 2:
                raise SyntaxError(
                    "Unexpected quote form: %s. Should be (quote <expression>)"
                    % expression)
            return cadr(expression)
        elif car(expression) == 'eval':
            if len(expression) != 2:
                raise SyntaxError(
                    "Unexpected eval form: %s. Should be (eval <expression>)" %
                    expression)
            expression = full_evaluate(cadr(expression), environment)
            continue
        elif car(expression) == 'if':
            if len(expression) != 4:
                raise SyntaxError(
                    "Unexpected if form: %s. Should be (if <condition> <consequent> <alternative>)"
                    % expression)
            condition = full_evaluate(cadr(expression), environment)
            expression = caddr(expression) if condition else cadddr(expression)
            continue
        elif car(expression) == 'lambda':
            if len(expression) < 3:
                raise SyntaxError(
                    "Unexpected lambda form: %s. Should be (lambda (<param> ...) <expression> ...)"
                    % expression)
            parameters = cadr(expression)
            if is_pair(parameters):
                current = parameters
                while is_pair(current):
                    if not is_symbol(car(current)):
                        raise SyntaxError(
                            "Lambda parameters should be symbols. In %s" %
                            expression)
                    current = cdr(current)
                if not is_nil(current) and not is_symbol(current):
                    raise SyntaxError(
                        "Lambda optional parameter should be a symbol or nil. In %s"
                        % expression)
            elif not is_symbol(parameters) and not is_nil(parameters):
                raise SyntaxError(
                    "Lambda parameters should be a symbol or a list of zero or more. In %s"
                    % expression)

            return Procedure(
                parameters,  # parameters
                cddr(expression),  # body (list of expressions)
                environment)
        elif car(expression) == 'macro':
            if len(expression) < 3:
                raise SyntaxError(
                    "Unexpected define macro: %s. Should be (macro (<resword> ...) (<pattern> <transformation> ...) ...)"
                    % expression)
            res_words = cadr(expression)
            rules = cddr(expression)
            if not is_nil(res_words) and not is_pair(res_words):
                raise SyntaxError(
                    "Macro reserved words should be a list of symbols or nil. In %s"
                    % expression)
            if is_pair(res_words):
                for word in res_words:
                    if not is_symbol(word):
                        raise SyntaxError(
                            "Macro reserved words should all be symbols. In %s"
                            % expression)
            for rule in rules:
                if len(rule) < 2:
                    raise SyntaxError(
                        "Macro rule should be in the form (<pattern> <expression> ...). In %s"
                        % expression)
            return Macro(
                [(car(e), cdr(e)) for e in rules],  # rules
                []
                if not res_words else set(iter(res_words)))  # reserved words
        else:
            # evaluate head
            operator = full_evaluate(car(expression), environment)

            if is_macro(operator):
                # evaluate recursively only the inner expressions (not the last)
                current = operator.transform(expression)
                while cdr(current) is not None:
                    full_evaluate(car(current), environment)
                    current = cdr(current)
                expression = car(current)
                continue
            else:
                # The unevaluated operands
                unev_operands = cdr(expression)

                if callable(operator):
                    # evaluate each operand recursively
                    operands = [
                        full_evaluate(e, environment) for e in unev_operands
                    ] if unev_operands else []
                    # return the application of the built-in procedure
                    return operator(make_list(operands))
                elif is_procedure(operator):
                    # create Thunks (promise to evaluate) for each operand
                    unev_op_list = list(
                        iter(unev_operands)) if unev_operands else []
                    proc_environment = Environment(parent=operator.environment)
                    # if the lambda parameters is not in the format ( () [. <symbol>] )
                    # for taking zero or more arguments
                    if len(operator.parameters) != 1 or not is_nil(
                            operator.parameters[0]):
                        for name in operator.parameters:
                            try:
                                # take next argument
                                proc_environment[name] = Thunk(
                                    unev_op_list.pop(0), environment)
                            except IndexError:
                                raise ValueError(
                                    "Insufficient parameters for procedure %s. It should be at least %d"
                                    % (operator, len(operator.parameters)))
                    if not is_nil(operator.optional):
                        # the optional argument is something, that when
                        # evaluated, yields the list of rest of the operands
                        # evaluated
                        proc_environment[operator.optional] = Thunk(
                            cons(lambda x: x, make_list(unev_op_list)),
                            environment)
                    elif unev_op_list:
                        raise ValueError(
                            "Too much parameters for procedure %s. It should be %d."
                            % (operator, len(operator.parameters)))

                    # evaluate recursively only the inner procedure expressions
                    # (not the last)
                    current = operator.body
                    while cdr(current) is not None:
                        full_evaluate(car(current), proc_environment)
                        current = cdr(current)

                    environment = proc_environment
                    expression = car(current)
                    # continue not-recursively to evaluate the procedure's body
                    # in the extended environment
                    continue
                else:
                    raise ValueError("Not an operator: %s, in expression: %s" %
                                     (operator, expression))