def assert_exp_length(ast, length): if len(ast) > length: msg = "Malformed %s, too many arguments: %s" % (ast[0], unparse(ast)) raise LispError(msg) elif len(ast) < length: msg = "Malformed %s, too few arguments: %s" % (ast[0], unparse(ast)) raise LispError(msg)
def parse_text(source): """Recursive method to parse the Slow Loris text into an AST""" # if type is list if type(source) == list: for i in range(len(source)): source[i] = parse_text(source[i]) return source # type is string if len(source) > 0 and source[0] == "'": return ['quote', parse_text(source[1:])] elif '(' in source: start = source.find('(') end = find_matching_paren(source, start) if end == -1: raise LispError("Incomplete expression: %s" % source[start:]) last = parse_text(source[end + 1:]) if last: return parse_text(split_exps(source[start + 1:end])) + [last] else: return parse_text(split_exps(source[start + 1:end])) elif ')' in source: raise LispError("Expected EOF") else: return source.strip()
def assert_valid_definition(d): if len(d) != 2: msg = "Wrong number of arguments for variable definition: %s" % d raise LispError(msg) elif not isinstance(d[0], str): msg = "Attempted to define non-symbol as variable: %s" % d raise LispError(msg)
def eval_rhead(ast, env): assert_exp_length(ast, 2) ls = evaluate(ast[1], env) if not is_list(ls): raise LispError('The argument of rhead should be a list.') elif len(ls) == 0: raise LispError('And empty list has no reverse head.') return ls[-1]
def eval_lambda(ast, env): if len(ast) != 3: raise LispError('Wrong number of arguments to lambda form') if not is_list(ast[1]): raise LispError('The parameters of lambda should be a list.') return Closure(env, ast[1], ast[2])
def eval_env_vars(ast, env): if len(env.variables[ast[0]].params) != len(ast) - 1: e = "wrong number of arguments, expected %d got %d" % \ (len(env.variables[ast[0]].params), len(ast) - 1) raise LispError("wrong number of arguments, expected 2 got 3") vals = [evaluate(t, env) for t in ast[1:]] return evaluate([env.variables[ast[0]]] + vals, env)
def apply(ast, env): closure = ast[0] args = ast[1:] if len(args) != len(closure.params): msg = "wrong number of arguments, expected %d got %d" \ % (len(closure.params), len(args)) raise LispError(msg) args = [evaluate(a, env) for a in args] bindings = dict(zip(closure.params, args)) new_env = closure.env.extend(bindings) return evaluate(closure.body, new_env)
def evaluate(ast, env): """Evaluate an Abstract Syntax Tree in the specified environment.""" if is_symbol(ast): if ast[0] == '"': return ast return env.lookup(ast) elif is_atom(ast): return ast elif is_list(ast): return eval_list(ast, env) else: raise LispError('Syntax error: %s' % unparse(ast))
def eval_list(ast, env): """evaluate all the built-in functions""" if ast[0] == 'quote': return eval_quote(ast, env) elif ast[0] == 'exit': return eval_exit(ast) elif ast[0] == 'print': return eval_print(ast, env) elif ast[0] == 'atom': return eval_atom(ast, env) elif ast[0] == 'let': return eval_let(ast, env) elif ast[0] == 'def': return eval_def(ast, env) elif ast[0] == 'lambda': return eval_lambda(ast, env) elif ast[0] == 'eq': return eval_eq(ast, env) elif ast[0] == 'set': return eval_set(ast, env) elif ast[0] == 'if': return eval_if(ast, env) elif ast[0] == 'cons': return eval_cons(ast, env) elif ast[0] == 'head': return eval_head(ast, env) elif ast[0] == 'tail': return eval_tail(ast, env) elif ast[0] == 'rhead': return eval_rhead(ast, env) elif ast[0] == 'rtail': return eval_rtail(ast, env) elif ast[0] == 'empty': return eval_empty(ast, env) elif ast[0] in [ '+', '-', '*', '**', '/', 'mod', '<', '<=', '=', '!=', '>=', '>' ]: return eval_math(ast, env) elif ast[0] == 'random': return eval_random() elif ast[0] in ['int', 'float', 'str', 'type']: return eval_types(ast, env) elif ast[0] in ['str_append', 'str_split']: return eval_string(ast, env) elif is_closure(ast[0]): return apply(ast, env) elif is_symbol(ast[0]) or is_list(ast[0]): return evaluate([evaluate(ast[0], env)] + ast[1:], env) else: raise LispError('%s is not a function' % unparse(ast[0]))
def find_matching_paren(source, start=0): """Given a string and the index of an opening parenthesis, determines the index of the matching closing paren.""" assert source[start] == '(' pos = start open_brackets = 1 while open_brackets > 0: pos += 1 if len(source) == pos: raise LispError("Incomplete expression: %s" % source[start:]) if source[pos] == '(': open_brackets += 1 if source[pos] == ')': open_brackets -= 1 return pos
def eval_cons(ast, env): assert_exp_length(ast, 3) ls = evaluate(ast[2], env) if not is_list(ls): raise LispError('The second argument of cons should be a list.') return [evaluate(ast[1], env)] + ls
def eval_empty(ast, env): assert_exp_length(ast, 2) ls = evaluate(ast[1], env) if not is_list(ls): raise LispError('The argument of empty should be a list.') return len(ls) == 0