Пример #1
0
def quasiquote(ast):
    if not isinstance(ast, (list, Vector)) or len(ast) == 0:
        return [Symbol("quote"), ast]
    elif ast[0] == Symbol("unquote"):
        return ast[1]
    elif isinstance(ast[0],
                    (list, Vector)) and ast[0][0] == Symbol("splice-unquote"):
        return [Symbol("concat"), ast[0][1], quasiquote(ast[1:])]
    else:
        return [Symbol("cons"), quasiquote(ast[0]), quasiquote(ast[1:])]
Пример #2
0
def main():
    status = True
    repl_env = Env(outer=None)
    repl_env[Symbol("+")] = lambda a, b: Number(a.value + b.value)
    repl_env[Symbol('-')] = lambda a, b: Number(a.value - b.value)
    repl_env[Symbol('*')] = lambda a, b: Number(a.value * b.value)
    repl_env[Symbol('/')] = lambda a, b: Number(a.value // b.value)
    while status:
        try:
            rep(repl_env)
        except EOFError:
            print("Bye Gitesh :)")
            status = False
Пример #3
0
def eval_(ast, env):
    if isinstance(ast, list):
        if ast:
            if ast[0] == Symbol("def!"):
                val = eval_(ast[2], env)
                env[ast[1]] = val
                return val
            elif ast[0] == Symbol("let*"):
                new_env = Env(env)
                # for loop b/c each def can depend on prev defs
                for key, value in zip(ast[1][::2], ast[1][1::2]):
                    new_env[key] = eval_(value, new_env)
                return eval_(ast[2], new_env)
            elif ast[0] == Symbol("if"):
                result = eval_(ast[1], env)
                if result is not None and result is not False:  # then
                    return eval_(ast[2], env)
                elif len(ast) >= 4:  # else
                    return eval_(ast[3], env)
                else:  # no else clause
                    return None
            elif ast[0] == Symbol("fn*"):

                def func(*args):
                    binds = itertools.zip_longest(ast[1], args)
                    data = {}
                    for bind, expr in binds:
                        if bind == Symbol("&"):  # varargs
                            key, val = next(binds)
                            if val:
                                data[key] = [expr, val
                                             ] + [arg for _, arg in binds]
                            elif expr:  # 1 arg special case
                                data[key] = [expr]
                            else:  # 0 args special case
                                data[key] = []
                            break
                        else:
                            data[bind] = expr
                    new_env = Env(env, data)
                    return eval_(ast[2], new_env)

                return func
            elif ast[0] == Symbol("do"):
                return [eval_(el, env) for el in ast[1:]][-1]
            else:
                return eval_(ast[0], env)(*eval_ast(ast[1:], env))
        else:
            return ast
    else:
        return eval_ast(ast, env)
Пример #4
0
 def read_form(self):
     if not self.tokens:
         return None
     x = self.peek()
     if x == "(":
         return self.read_list(")", list)
     elif x == "[":
         return self.read_list("]", Vector)
     elif x == "{":
         return self.read_list("}", hash_map)
     elif x == "@":  #atom deref
         return self.macro("deref")
     elif x == "'":
         return self.macro("quote")
     elif x == "`":
         return self.macro("quasiquote")
     elif x == "~":
         return self.macro("unquote")
     elif x == "~@":
         return self.macro("splice-unquote")
     elif x == "^":
         self.pos += 1
         val = self.read_form()
         fn = self.read_form()
         return [Symbol("with-meta"), fn, val]
     else:
         return self.read_atom()
Пример #5
0
 def read_atom(self):
     x = self.next()
     try:
         return int(x)
     except ValueError:
         pass
     if x[0] == '"':  # strings
         string = ""
         escaped = False
         for c in x[1:-1]:
             if escaped:
                 if c == "n":
                     string += "\n"
                 else:
                     string += c
                 escaped = False
             elif c == "\\":
                 escaped = True
             else:
                 string += c
         return string
     if x[0] == ":":  #keywords
         return Keyword(x[1:])
     if x == "true":
         return True
     if x == "false":
         return False
     if x == "nil":
         return None
     return Symbol(x)
Пример #6
0
def eval_(ast, env):
    if isinstance(ast, list):
        if ast:
            if ast[0] == Symbol("def!"):
                val = eval_(ast[2], env)
                env[ast[1]] = val
                return val
            elif ast[0] == Symbol("let*"):
                env = Env(env)
                for key, value in zip(ast[1][::2], ast[1][1::2]):
                    env[key] = eval_(value, env)
                return eval_(ast[2], env)
            else:
                return env[ast[0]](*eval_ast(ast[1:], env))
        else:
            return ast
    else:
        return eval_ast(ast, env)
Пример #7
0
def read_atom(reader):
    int_re = re.compile(r"-?[0-9]+$")
    token = reader.next()
    if re.match(int_re, token): return int(token)
    elif token[0] == '"': return unescape(token[1:-1])
    elif token == "nil": return None
    elif token == "true": return True
    elif token == "false": return False
    else: return Symbol(token)
Пример #8
0
def eval_list(alist, env):
    if isinstance(alist[0], Symbol) and alist[0].is_same_as(Symbol("def!")):
        result = eval_ast(alist[2], env)
        env[alist[1]] = result
    elif isinstance(alist[0], Symbol) and alist[0].is_same_as(Symbol("do")):
        for elem in alist[1:-1]:
            eval_ast(elem, env)
        result = eval_ast(alist[-1], env)
    elif isinstance(alist[0], Symbol) and alist[0].is_same_as(Symbol("if")):
        condition = eval_ast(alist[1], env)
        if isinstance(condition, NoneType) or (isinstance(condition, Boolean)
                                               and not condition.value):
            result = eval_ast(alist[3], env) if len(alist) == 4 else NoneType()
        else:
            result = eval_ast(alist[2], env)
    elif isinstance(alist[0], Symbol) and alist[0].is_same_as(Symbol("let*")):
        let_env = Env(outer=env)
        param_bindings = alist[1]
        if not isinstance(param_bindings,
                          List) or len(param_bindings) % 2 != 0:
            raise SyntaxError("Invalid parameter bindings in the `let*` form.")

        i = 0
        while i < len(param_bindings):
            let_env[param_bindings[i]] = eval_ast(param_bindings[i + 1],
                                                  let_env)
            i += 2

        result = eval_ast(alist[2], let_env)
    elif isinstance(alist[0], Symbol) and alist[0].is_same_as(Symbol("fn*")):
        result = FunctionType(params=alist[1], body=alist[2], env=env)
    else:
        func = eval_ast(alist[0], env)
        args = [eval_ast(elem, env) for elem in alist[1:]]
        if isinstance(func, FunctionType):
            func_env = Env(outer=func.closed_env)
            for k, v in zip(func.parameters, args):
                func_env[k] = v
            result = eval_ast(func.body, env=func_env)
        else:
            result = func(*args)

    return result
Пример #9
0
def read_atom(parser: Reader):
    current_token = parser.next()
    if is_integer(current_token):
        result = Number(int(current_token))
    elif current_token in ["true", "false"]:
        result = Boolean(True if current_token == "true" else False)
    elif current_token == "nil":
        result = NoneType()
    else:
        result = Symbol(current_token)
    return result
Пример #10
0
def eval_(ast, env):
    while True:
        if isinstance(ast, list):
            if ast:
                if ast[0] == Symbol("def!"):
                    val = eval_(ast[2], env)
                    env[ast[1]] = val
                    return val
                elif ast[0] == Symbol("let*"):
                    env = Env(env)
                    # for loop b/c each def can depend on prev defs
                    for key, value in zip(ast[1][::2], ast[1][1::2]):
                        env[key] = eval_(value, env)
                    ast = ast[2]
                    continue
                elif ast[0] == Symbol("if"):
                    result = eval_(ast[1], env)
                    if result is not None and result is not False:  # then
                        ast = ast[2]
                    elif len(ast) >= 4:  # else
                        ast = ast[3]
                        continue
                    else:  # no else clause
                        return None
                elif ast[0] == Symbol("fn*"):
                    return Function(ast[2], ast[1], env, eval_)
                elif ast[0] == Symbol("do"):
                    eval_ast(ast[1:-1], env)
                    ast = ast[-1]
                    continue
                else:
                    fn = eval_(ast[0], env)
                    if isinstance(fn, Function):
                        env = fn.bind_args(eval_ast(ast[1:], env))
                        ast = fn.body
                        continue
                    return fn(*eval_ast(ast[1:], env))
            else:
                return ast
        else:
            return eval_ast(ast, env)
Пример #11
0
def read_form(reader):
    token = reader.peek()
    if token[0] == ";":
        reader.next()
        return Comment(token)
    if token == "'":
        reader.next()
        return List((Symbol("quote"), read_form(reader)))
    if token == "`":
        reader.next()
        return List((Symbol("quasiquote"), read_form(reader)))
    if token == "~@":
        reader.next()
        return List((Symbol("splice-unquote"), read_form(reader)))
    if token == "~":
        reader.next()
        return List((Symbol("unquote"), read_form(reader)))
    if token == "@":
        reader.next()
        return List((Symbol("deref"), read_form(reader)))
    if token == "^":
        reader.next()
        metadata = read_form(reader)
        data = read_form(reader)
        return List((Symbol("with-meta"), data, metadata))
    if token == "(":
        return read_list(reader)
    if token == "[":
        return read_array(reader)
    if token == "{":
        return read_hash_map(reader)
    else:
        return read_atom(reader)
Пример #12
0
def read_atom(tokens: Reader):
    token = tokens.peek()
    int_re = re.compile(r"-?[0-9]+$")
    float_re = re.compile(r"-?[0-9][0-9.]*$")
    if token[0] == "\"":
        if token[-1] == "\"":
            return String(token[1:-1])
        else:
            raise MalException("missing closing \"")
    if re.match(int_re, token): return Integer(int(token))
    if re.match(float_re, token): return float(token)
    if token == "true": return Bolean(True)
    if token == "false": return Bolean(False)
    if token == "nill": return Nill()

    return Symbol(token)
Пример #13
0
 def func(*args):
     binds = itertools.zip_longest(ast[1], args)
     data = {}
     for bind, expr in binds:
         if bind == Symbol("&"):  # varargs
             key, val = next(binds)
             if val:
                 data[key] = [expr, val
                              ] + [arg for _, arg in binds]
             elif expr:  # 1 arg special case
                 data[key] = [expr]
             else:  # 0 args special case
                 data[key] = []
             break
         else:
             data[bind] = expr
     new_env = Env(env, data)
     return eval_(ast[2], new_env)
Пример #14
0
def read_atom(reader):
    token = reader.next()
    if re.match(INT_RE, token):
        return int(token)

    if re.match(STRING_RE, token):
        return String(_unescape(token[1:-1]))
    if token[0] == '"':
        raise Exception('Expected \'"\', found EOF')
    if token[0] == ':':
        return Keyword(token)
    if token == "true":
        return True
    if token == "false":
        return False
    if token == "nil":
        return None
    else:
        return Symbol(token)
Пример #15
0
def read_atom(reader: Reader) -> MalType:
    token = reader.next()

    parse_keyword_scalars = {
        'nil': Nil(),
        'true': BoolTrue(),
        'false': BoolFalse(),
    }

    if token in parse_keyword_scalars:
        return parse_keyword_scalars[token]
    elif token[0] == '"':
        if token[-1] == '"':
            return String(token)
        else:
            raise MalParseError(f"Expected '\"', got EOF")
    else:
        try:
            parsed_integer = int(token)
            return Number(parsed_integer)
        except ValueError:
            return Symbol(token)
Пример #16
0

def core_is_list_empty(arg: List):
    return Boolean(len(arg.list) == 0)


def core_list_count(arg: List):
    return Number(len(arg.list))


def core_equal(first, second):
    return first == second


namespace = (
    (Symbol("+"), lambda a, b: Number(a.value + b.value)),
    (Symbol("-"), lambda a, b: Number(a.value - b.value)),
    (Symbol("*"), lambda a, b: Number(a.value * b.value)),
    (Symbol("/"), lambda a, b: Number(a.value // b.value)),
    (Symbol("<"), lambda a, b: Boolean(a.value < b.value)),
    (Symbol("<="), lambda a, b: Boolean(a.value <= b.value)),
    (Symbol(">"), lambda a, b: Boolean(a.value > b.value)),
    (Symbol(">="), lambda a, b: Boolean(a.value >= b.value)),
    (Symbol("="), core_equal),
    (Symbol("prn"), core_prn),
    (Symbol("list"), core_list),
    (Symbol("list?"), core_is_list),
    (Symbol("empty?"), core_is_list_empty),
    (Symbol("count"), core_list_count),
)
Пример #17
0
from mal_types import Symbol, MalType


class Env:
    def __init__(self, outer=None):
        self.outer = outer
        self.data = dict()

    def set(self, key: Symbol, value):
        self.data[key] = value

    def find(self, key: Symbol):
        if key in self.data:
            return self.data
        elif self.outer is not None:
            return self.outer.find(key)
        return None

    def get(self, key: Symbol):
        env = self.find(key)
        if env is None:
            raise SymbolNotFound(f"'{key}' not found.")
        return env[key]


repl_env = Env()
repl_env.set(Symbol('+'), lambda a, b: a + b)
repl_env.set(Symbol('-'), lambda a, b: a - b)
repl_env.set(Symbol('*'), lambda a, b: a * b)
repl_env.set(Symbol('/'), lambda a, b: int(a / b))
Пример #18
0
def read_metadata(reader: Reader) -> MalType:
    reader.next()
    metadata = read_from(reader)
    expr = read_from(reader)
    return Sexpr([Symbol('with-meta'), expr,
                  metadata])  # Todo: replace Symbol by Function
Пример #19
0
def eval_(ast, env):
    while True:
        if isinstance(ast, list):
            if ast:
                ast = macro_expand(ast, env)
                if isinstance(ast, list):
                    if ast[0] == Symbol("def!"):
                        val = eval_(ast[2], env)
                        env[ast[1]] = val
                        return val
                    elif ast[0] == Symbol("let*"):
                        env = Env(env)
                        # for loop b/c each def can depend on prev defs
                        for key, value in zip(ast[1][::2], ast[1][1::2]):
                            env[key] = eval_(value, env)
                        ast = ast[2]
                        continue
                    elif ast[0] == Symbol("if"):
                        result = eval_(ast[1], env)
                        if result is not None and result is not False:  # then
                            ast = ast[2]
                        elif len(ast) >= 4:  # else
                            ast = ast[3]
                            continue
                        else:  # no else clause
                            return None
                    elif ast[0] == Symbol("fn*"):
                        return Function(ast[2], ast[1], env, eval_)
                    elif ast[0] == Symbol("do"):
                        eval_ast(ast[1:-1], env)
                        ast = ast[-1]
                        continue
                    elif ast[0] == Symbol("quote"):
                        return ast[1]
                    elif ast[0] == Symbol("quasiquote"):
                        ast = quasiquote(ast[1])
                        continue
                    elif ast[0] == Symbol("defmacro!"):
                        val = eval_(ast[2], env)
                        val.is_macro = True
                        env[ast[1]] = val
                        return val
                    elif ast[0] == Symbol("macroexpand"):
                        return macro_expand(ast[1], env)
                    elif ast[0] == Symbol("try*"):
                        try:
                            return eval_(ast[1], env)
                        except Exception as e:
                            if ast[2][0] != Symbol("catch*"):
                                print("Warning: catch* not found",
                                      file=sys.stderr)
                            env = Env(env)
                            env[ast[2][1]] = str(e)
                            return eval_(ast[2][2], env)

                    else:
                        fn = eval_(ast[0], env)
                        if isinstance(fn, Function):
                            env = fn.bind_args(eval_ast(ast[1:], env))
                            ast = fn.body
                            continue
                        return fn(*eval_ast(ast[1:], env))
                else:
                    return eval_ast(ast, env)
            else:
                return ast
        else:
            return eval_ast(ast, env)
Пример #20
0
    while is_macro_call(ast, env):
        macro = env[ast[0]]
        ast = macro(*ast[1:])
    return ast


def print_(inp: str) -> str:
    return pretty_print(inp, True)


def rep(inp: str):
    return print_(eval_(read(inp), global_env))


#setup global env
global_env = Env(None, {Symbol(k): v for k, v in core.items()})
global_env[Symbol("eval")] = lambda ast: eval_(ast, global_env)
global_env[Symbol("*ARGV*")] = sys.argv[2:]
rep('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) "\\nnil)")))))'
    )
rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"
    )


def main():

    if len(sys.argv) > 1:
        rep(f"(load-file \"{sys.argv[1]}\")")
        return
    while True:
        try:
Пример #21
0
def test_env_simple_w_symbols():
    env = Env()
    env.set(Symbol("a"), 3)

    assert (3 == env.get(Symbol("a")))
Пример #22
0
def eval_(ast, env):
    while True:
        #if Symbol("el") in env and env[Symbol("el")][1] is None:
        #	print(ast, env.data[Symbol("el")])

        if isinstance(ast, list):
            if ast:
                ast = macro_expand(ast, env)
                if isinstance(ast, list):
                    if ast[0] == Symbol("def!"):
                        val = eval_(ast[2], env)
                        env[ast[1]] = val
                        return val
                    elif ast[0] == Symbol("let*"):
                        env = Env(env)
                        # for loop b/c each def can depend on prev defs
                        for key, value in zip(ast[1][::2], ast[1][1::2]):
                            env[key] = eval_(value, env)
                        ast = ast[2]
                        continue
                    elif ast[0] == Symbol("if"):
                        result = eval_(ast[1], env)
                        if result is not None and result is not False:  # then
                            ast = ast[2]
                            continue
                        elif len(ast) >= 4:  # else
                            ast = ast[3]
                            continue
                        else:  # no else clause
                            return None
                    elif ast[0] == Symbol("fn*"):
                        return Function(ast[2], ast[1], env, eval_)
                    elif ast[0] == Symbol("do"):
                        eval_ast(ast[1:-1], env)
                        ast = ast[-1]
                        continue
                    elif ast[0] == Symbol("quote"):
                        return ast[1]
                    elif ast[0] == Symbol("quasiquote"):
                        ast = quasiquote(ast[1])
                        continue
                    elif ast[0] == Symbol("defmacro!"):
                        val = eval_(
                            ast[2],
                            env).copy()  # avoid mutating existing function
                        val.is_macro = True
                        env[ast[1]] = val
                        return val
                    elif ast[0] == Symbol("macroexpand"):
                        return macro_expand(ast[1], env)
                    elif ast[0] == Symbol("try*"):
                        try:
                            return eval_(ast[1], env)
                        except Exception as e:  # pylint: disable=broad-except
                            try:
                                if ast[2][0] != Symbol("catch*"):
                                    raise ValueError("catch* not found")
                                env = Env(env)
                                if isinstance(e, MalException):
                                    env[ast[2][1]] = e.val  # pylint: disable=no-member
                                else:
                                    env[ast[2][1]] = str(e)
                                return eval_(ast[2][2], env)
                            except IndexError:  # no catch clause
                                raise e
                    else:
                        fn = eval_(ast[0], env)
                        if isinstance(fn, Function):
                            env = fn.bind_args(eval_ast(ast[1:], env))
                            ast = fn.body
                            continue
                        else:
                            return fn(*eval_ast(ast[1:], env))
                else:
                    return eval_ast(ast, env)
            else:
                return ast
        else:
            return eval_ast(ast, env)
Пример #23
0
    while is_macro_call(ast, env):
        macro = env[ast[0]]
        ast = macro(*ast[1:])
    return ast


def print_(inp: str) -> str:
    return pretty_print(inp, True)


def rep(inp: str):
    return print_(eval_(read(inp), global_env))


#setup global env
global_env = Env(None, {Symbol(k): v for k, v in core.items()})
global_env[Symbol("eval")] = lambda ast: eval_(ast, global_env)
global_env[Symbol("*ARGV*")] = sys.argv[2:]
global_env[Symbol("*host-language*")] = "python.3"
rep("(def! not (fn* [a] (if a false true)))")
rep('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) "\\nnil)")))))'
    )
rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"
    )


def main():

    if len(sys.argv) > 1:
        rep(f"(load-file \"{sys.argv[1]}\")")
        return
Пример #24
0
#!/usr/bin/env python3
import readline  # pylint: disable=unused-import
import sys

from core import core
from env import Env
from mal_types import Function, Symbol, Vector
from pretty_print import pretty_print
from reader import read_str

global_env = Env(None, {Symbol(k): v for k, v in core.items()})


def read(inp: str):
    return read_str(inp)


def eval_(ast, env):
    while True:
        if isinstance(ast, list):
            if ast:
                if ast[0] == Symbol("def!"):
                    val = eval_(ast[2], env)
                    env[ast[1]] = val
                    return val
                elif ast[0] == Symbol("let*"):
                    env = Env(env)
                    # for loop b/c each def can depend on prev defs
                    for key, value in zip(ast[1][::2], ast[1][1::2]):
                        env[key] = eval_(value, env)
                    ast = ast[2]
Пример #25
0
 def macro(self, func: str):
     self.pos += 1
     return [Symbol(func), self.read_form()]