1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
|
import re
from collections import ChainMap
from functools import reduce
from mistletoe.base_renderer import BaseRenderer
from mistletoe import span_token, block_token
from mistletoe.core_tokens import MatchObj
class Program(block_token.BlockToken):
def __init__(self, lines):
self.children = span_token.tokenize_inner(''.join([line.strip() for line in lines]))
class Expr(span_token.SpanToken):
@classmethod
def find(cls, string):
matches = []
start = []
for i, c in enumerate(string):
if c == '(':
start.append(i)
elif c == ')':
pos = start.pop()
end_pos = i + 1
content = string[pos + 1:i]
matches.append(MatchObj(pos, end_pos, (pos + 1, i, content)))
return matches
def __repr__(self):
return '<Expr {}>'.format(self.children)
class Number(span_token.SpanToken):
pattern = re.compile(r"(\d+)")
parse_inner = False
def __init__(self, match):
self.number = eval(match.group(0))
def __repr__(self):
return '<Number {}>'.format(self.number)
class Variable(span_token.SpanToken):
pattern = re.compile(r"([^\s()]+)")
parse_inner = False
def __init__(self, match):
self.name = match.group(0)
def __repr__(self):
return '<Variable {!r}>'.format(self.name)
class Whitespace(span_token.SpanToken):
parse_inner = False
def __new__(self, _):
return None
class Procedure:
def __init__(self, expr_token, body, env):
self.params = [child.name for child in expr_token.children]
self.body = body
self.env = env
class Scheme(BaseRenderer):
def __init__(self):
self.render_map = {
"Program": self.render_program,
"Expr": self.render_expr,
"Number": self.render_number,
"Variable": self.render_variable,
}
block_token._token_types = []
span_token._token_types = [Expr, Number, Variable, Whitespace]
self.env = ChainMap({
"define": self.define,
"lambda": lambda expr_token, *body: Procedure(expr_token, body, self.env),
"+": lambda x, y: self.render(x) + self.render(y),
"-": lambda x, y: self.render(x) - self.render(y),
"*": lambda x, y: self.render(x) * self.render(y),
"/": lambda x, y: self.render(x) / self.render(y),
"<": lambda x, y: self.render(x) < self.render(y),
">": lambda x, y: self.render(x) > self.render(y),
"<=": lambda x, y: self.render(x) <= self.render(y),
">=": lambda x, y: self.render(x) >= self.render(y),
"=": lambda x, y: self.render(x) == self.render(y),
"true": True,
"false": False,
"cons": lambda x, y: (self.render(x), self.render(y)),
"car": lambda pair: self.render(pair)[0],
"cdr": lambda pair: self.render(pair)[1],
"and": lambda *args: all(map(self.render, args)),
"or": lambda *args: any(map(self.render, args)),
"not": lambda x: not self.render(x),
"if": lambda cond, true, false: self.render(true) if self.render(cond) else self.render(false),
"cond": self.cond,
"null": None,
"null?": lambda x: self.render(x) is None,
"list": lambda *args: reduce(lambda x, y: (y, x), map(self.render, reversed(args)), None),
"display": lambda *args: print(*map(self.render, args)),
})
def render_program(self, token):
return self.render_inner(token)
def render_inner(self, token):
result = None
for child in token.children:
result = self.render(child)
return result
def render_expr(self, token):
proc, *args = token.children
proc = self.render(proc)
return self.apply(proc, args) if isinstance(proc, Procedure) else proc(*args)
def render_number(self, token):
return token.number
def render_variable(self, token):
return self.env[token.name]
def define(self, *args):
if len(args) == 2:
name_token, val_token = args
self.env[name_token.name] = self.render(val_token)
else:
name_token, expr_token, *body = args
self.env[name_token.name] = Procedure(expr_token, body, self.env)
def cond(self, *exprs):
for expr in exprs:
test, value = expr.children
if test == 'else' and 'else' not in self.env:
return self.render(value)
if self.render(test):
return self.render(value)
def apply(self, proc, args):
old_env = self.env
self.env = proc.env.new_child()
try:
for param, arg in zip(proc.params, args):
self.env[param] = self.render(arg)
result = None
for expr in proc.body:
result = self.render(expr)
finally:
self.env = old_env
return result
|