Commit 1586a3a8 authored by Anya Helene Bagge's avatar Anya Helene Bagge 🦆
Browse files

initial

parents
# Lark example
* [Lark on GitHub](https://github.com/lark-parser/lark)
* Install: `pip install lark --upgrade`
* [Lark documentation](https://lark-parser.readthedocs.io/)
* [Lark Cheatsheet](https://github.com/lark-parser/lark/blob/master/docs/_static/lark_cheatsheet.pdf)
* [Lark Online IDE](https://lark-parser.org/ide)
* [Lark tutorial](https://github.com/lark-parser/lark/blob/master/docs/json_tutorial.md)
from lark import Lark
from lark.visitors import Transformer, Visitor, Interpreter, v_args
import sys
operators = {
'+': 'BINARY_ADD',
'-': 'BINARY_SUBTRACT',
'*': 'BINARY_MULTIPLY',
'/': 'BINARY_TRUE_DIVIDE'
}
def error(*msg):
print(*msg, file=sys.stderr)
class Env:
def __init__(self, next = None):
self.code = ''
self.variables = {}
self.consts = {}
#if next != None:
# self.variables = next.variables.copy()
self.next = next
self.return_type = None
def const(self, value, type):
c = self.consts.get(value, None)
if c == None:
c = (len(self.consts), type)
self.consts[value] = c
return c
def declare_name(self, name, type):
name = str(name)
type = str(type)
var = self.variables.get(name, None)
if var == None:
var = (len(self.variables), type)
self.variables[name] = var
return var[0]
def lookup_name(self, name):
name = str(name)
return self.variables[name]
def lookup(self, table, nest):
try:
return table.index(value)
except ValueError:
table.append(value)
return len(table)-1
def lookup_or_add(self, table, value):
try:
return table.index(value)
except ValueError:
table.append(value)
return len(table)-1
def compile(self):
codeobj = compile('0', 'filename', 'exec')
codeobj = codeobj.replace(co_name='name', co_names=tuple(self.variables.keys()), co_consts=tuple(self.consts), co_code=bytes(self.code))
return codeobj
def __str__(self):
s = 'Constants:\n'
for i, c in enumerate(self.consts):
s = f'{s} {i:3d}: {c}\n'
s = s + 'Names:\n'
for i, v in enumerate(self.variables):
s = f'{s} {i:3d}: {v}\n'
s = s + str(self.code)
return s
def compile_instr(instr, dest):
if isinstance(instr, tuple):
(iname, iarg) = instr
else:
iname = instr
iarg = 0
#print(iname)
dest.append(dis.opmap[iname])
dest.append(iarg)
class Compiler(Transformer):
def __init__(self):
self.code = Code()
def number(self, args): # args are NUM token
val = int(args[0])
return [('LOAD_CONST', self.code.const(val))]
def var(self, args):
varname = str(args[0])
return [('LOAD_NAME', self.code.name(varname))]
def addexpr(self, args): # args are [lhs,op,rhs]
lhs = args[0]
op = args[1]
rhs = args[2]
return lhs + rhs + [(operators[op], 0)]
def mulexpr(self, args): # args are [lhs,op,rhs]
lhs = args[0]
op = args[1]
rhs = args[2]
return lhs + rhs + [(operators[op], 0)]
def ifexpr(self, args): # args are expressions [cond,then,else]
cond = args[0]
ifTrue = args[1]
ifFalse = args[2]
return [*cond, ('POP_JUMP_IF_FALSE', len(ifTrue)), *ifTrue, ('JUMP', len(ifFalse)), *ifFalse]
def var_decl(self, args): # args[0] is varname ID, args[1] is initializer expr
varname = str(args[0])
return [*args[1], ('STORE_NAME', self.code.name(varname))]
def asgn_stat(self, args): # args[0] is varname ID, args[1] is initializer expr
varname = str(args[0])
return [*args[1], ('STORE_NAME', self.code.name(varname))]
def ret_stat(self, args):
return [*args[0], ('RETURN_VALUE', 0)]
def program(self, args): # args is program
bytelist = []
prog = sum(args, [])
print("Program", prog)
for a in prog:
compile_instr(a, bytelist)
self.code.code = bytes(bytelist)
return self.code
def compile_program(prog):
codes = []
for decl in prog:
c = Compiler().transform(decl)
codes.append(c)
return codes
class BetterCompiler(Interpreter):
def __init__(self, env = None):
if env == None:
self.env = Env()
else:
self.env = env
def NUM(self, tok):
print(tok)
return int(tok)
def ID(self, tok):
print(tok)
return str(tok)
def lookup_name(self, name):
return self.env.lookup_name(name)
def declare_name(self, name, type):
return self.env.declare_name(name, type)
def program(self, tree):
stats_code = sum(self.visit_children(tree), [])
print("Program: ", stats_code)
bytelist = []
for instr in stats_code:
compile_instr(instr, bytelist)
self.env.code = bytes(bytelist)
return self.env
def class_decl(self, tree):
name, decls = tree.children
name = str(name)
env_c = Env(self.env)
env_c.is_class = True
env_c.class_name = name
sub = BetterCompiler(env_c)
byte_code = sub.visit(decls)
def fun_decl(self, tree):
print("Function: ", tree)
name, arg_name, arg_type, ret_type, body = tree.children
name = str(name)
arg_type = self.visit(arg_type)
ret_type = self.visit(ret_type)
fun_type = f'{arg_type}{ret_type}'
env_f = Env(self.env)
env_f.declare_name(arg_name, arg_type)
env_f.declare_name(name, fun_type)
env_f.return_type = ret_type
#print("Function: ", name, arg_name, arg_type, ret_type)
print(env_f.variables)
sub = BetterCompiler(env_f)
body_code = sub.visit(body)
print("Function: ", body_code)
bytelist = []
for instr in body_code:
compile_instr(instr, bytelist)
env_f.code = bytes(bytelist)
code = env_f.compile()
(code_i, _) = self.env.const(code, fun_type)
(name_str_i, _) = self.env.const(name, 'str')
fun_i = self.declare_name(name, fun_type)
return [('LOAD_CONST', code_i),
('LOAD_CONST', name_str_i),
('MAKE_FUNCTION', 0),
('STORE_NAME', fun_i)]
def var_decl(self, tree):
name, type, (val_code, init_type) = self.visit_children(tree)
i = self.declare_name(name, type)
#if type != init_type:
# raise TypeError(f'Got {init_type}, expected {type}')
return [*val_code, ('STORE_NAME', i)]
def asgn_stat(self, tree):
name, val_code = self.visit_children(tree)
i, type = self.lookup_name(name)
return [*val_code, ('STORE_NAME', i)]
def ret_stat(self, tree):
[(val_code,val_type)] = self.visit_children(tree)
if val_type != self.env.return_type:
error(TypeError(f"Expected return type {self.env.return_type}, got {val_type}"))
print(f"Return: {val_code}:{val_type}")
return [*val_code, ('RETURN_VALUE', 0)]
def apply_expr(self, tree):
(fun_val, fun_type), (arg_val, arg_type) = self.visit_children(tree)
param_type, ret_type = fun_type.split('→')
if param_type != arg_type:
error(TypeError(f'Wrong argument type'))
return ([*fun_val, *arg_val, ('CALL_FUNCTION', 1)], ret_type)
def number(self, tree):
[val] = self.visit_children(tree)
(i, _) = self.env.const(int(val), "int")
return ([('LOAD_CONST', i)], "int") # iconst i
def var(self, tree):
print('var', tree)
name = self.visit_children(tree)
if self.env.is_class:
try:
(i, type) = self.lookup_name(name)
return ([('LOAD_FAST', 0),
('LOAD_ATTR', i)], type)
except KeyError:
pass
try:
(i, type) = self.lookup_name(name)
return ([('LOAD_NAME', i)], type) # iload i – MOV eax, (esp+i)
except KeyError:
if self.env.next != None:
(i, type) = self.env.next.lookup_name(name)
return ([('LOAD_GLOBAL', i)], type)
else:
raise KeyError(name)
def addexpr(self, tree): # args are [lhs,op,rhs]
[(lhs, ltype), op, (rhs, rtype)] = self.visit_children(tree)
if ltype == 'int' and rtype == 'int':
return ([*lhs, *rhs, (operators[op], 0)], 'int')
else:
raise TypeError(f"Got {ltype}+{rtype}, expected ints")
def class_decl(self, tree):
name, decls = self.visit_children(tree)
def int_type(self, tree):
print("int_type: ", tree)
return "int"
def named_type(self, tree):
[name] = self.visit_children(tree)
return name
parser = Lark(
'''
?expr: "fn" ID ":" type "→" expr -> funexpr
| "if" expr "then" expr "else" expr -> ifexpr
| addexpr
?addexpr: mulexpr
| addexpr ADDOP mulexpr
?mulexpr: apply
| mulexpr MULOP apply
?apply: atom
| expr "(" expr ")" -> apply_expr
| expr "." ID -> dot_expr
?atom: ID -> var
| NUM -> number
| "-" atom -> neg
| "(" expr ")"
?decl: "var" ID ":" type "=" expr ";" -> var_decl
| "fun" ID "(" ID ":" type ")" ":" type " {" stat* "}" -> fun_decl
| "class" ID "{" decl* "}" -> class_decl
?stat: decl
| ID "=" expr ";" -> asgn_stat
| "return" expr ";" -> ret_stat
?type: "int" -> int_type
| "(" type "→" type ")" -> fun_type
| ID -> named_type
program: stat* -> program
IF: "if"
ID: /[_a-zA-Z][_a-zA-Z0-9]*/
MULOP: "*" | "/"
ADDOP: "+" | "-"
NUM: /[0-9]+/
%import common.ESCAPED_STRING
%import common.NUMBER
%import common.WS
%ignore WS
''', start='program')
def parseExpr(src):
return parser.parse(src, start='expr')
def parseStat(src):
return parser.parse(src, start='stat')
def parseProgram(src):
return parser.parse(src, start='program')
#t = parser.parse('3')
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment