From 5dd92c99fef6fac679e0c28171c61b0741d3b9ee Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Wed, 9 Jun 2021 02:44:52 -0500 Subject: [PATCH] more shit --- .gitignore | 3 ++- Justfile | 2 +- agast.py | 34 +++++++++++++++++------ agtypeck.py | 72 +++++++++++++++++++++++++++++++++++++++++++------ gen.py | 16 ++++++++--- gen/__init__.py | 0 grammar.lark | 19 ++++++++++--- let.ag | 25 +++++++++++++++++ watch.ps1 | 2 +- 9 files changed, 148 insertions(+), 25 deletions(-) create mode 100644 gen/__init__.py create mode 100644 let.ag diff --git a/.gitignore b/.gitignore index 6b88e1b..727c9cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .mypy_cache __pycache__ .vim -gen/ +gen/* +!gen/__init__.py diff --git a/Justfile b/Justfile index f7cb75d..b676118 100644 --- a/Justfile +++ b/Justfile @@ -1,2 +1,2 @@ watch: - watchexec -ce py,lark -i gen 'mypy *.py && python gen.py && mypy gen/*.py' + watchexec -ce py,lark,ag -i gen 'mypy *.py && python gen.py && mypy gen/*.py' diff --git a/agast.py b/agast.py index 117dcc7..3e910e1 100644 --- a/agast.py +++ b/agast.py @@ -11,9 +11,7 @@ class Iface(Decl): class IfaceField: pass class IfaceRef: pass - -class Node(Decl): - ifaces: List[IfaceRef] +class Expr: pass class NodeRef: pass class NodeRefByName(NodeRef): @@ -21,14 +19,16 @@ class NodeRefByName(NodeRef): self.name = name def __repr__(self) -> str: return f"NodeRefByName({self.name})" - class Sym: pass class SymRename(Sym): def __init__(self, name: str, node_ref: NodeRef): self.name = name self.node_ref = node_ref def __repr__(self) -> str: return f"SymRename({self.name} : {self.node_ref})" -class Equation: pass +class Equation: + def __init__(self, lhs: Expr, rhs: Expr): + self.lhs = lhs + self.rhs = rhs class Variant: def __init__(self, prod: List[Sym], equations: List[Equation]): @@ -36,7 +36,12 @@ class Variant: self.equations = equations def __repr__(self) -> str: return f"Variant({self.prod}, {self.equations})" -class Expr: pass +class Node(Decl): + def __init__(self, name: str, ifaces: List[IfaceRef], variants: List[Variant]): + self.name = name + self.ifaces = ifaces + self.variants = variants + class ExprDot(Expr): def __init__(self, left: Expr, right: Expr): self.left = left @@ -65,16 +70,24 @@ class ExprName(Expr): class Parser(Transformer[List[Decl]]): def program(self, items: List[Decl]) -> List[Decl]: return items + # interfaces def iface(self, items: List[Any]) -> Iface: [name, fields] = items return Iface(name) def iface_field(self, items: List[str]) -> IfaceField: [name, ty] = items return IfaceField() + def iface_ref(self, items: List[str]) -> str: return items[0] + def iface_refs(self, items: List[IfaceRef]) -> List[IfaceRef]: return items + # nodes def node(self, items: List[Any]) -> Node: - return Node() + [name, ifaces, variants] = items + return Node(name, ifaces, variants) + def node_ref_name(self, items: List[str]) -> NodeRefByName: return NodeRefByName(items[0]) + # variants + def variants(self, items: List[Variant]) -> List[Variant]: return items def variant(self, items: List[Any]) -> Variant: [prod, equations] = items return Variant(prod, equations) @@ -82,8 +95,12 @@ class Parser(Transformer[List[Decl]]): def sym_rename(self, items: List[Any]) -> Sym: return SymRename(items[0], items[1]) - def node_ref_name(self, items: List[str]) -> NodeRefByName: return NodeRefByName(items[0]) + # equations + def equations(self, items: List[Equation]) -> List[Equation]: return items + def equation_(self, items: List[Equation]) -> Equation: return items[0] + def equation(self, items: List[Expr]) -> Equation: return Equation(items[0], items[1]) + # expr def expr_dot(self, items: List[Expr]) -> Expr: [left, right] = items return ExprDot(left, right) @@ -99,4 +116,5 @@ class Parser(Transformer[List[Decl]]): def sep_trail(self, items: List[Tree]) -> List[T]: return list(map(lambda it: cast(T, it), items)) + def ident(self, items: List[Token]) -> str: return cast(str, items[0].value) diff --git a/agtypeck.py b/agtypeck.py index 638027d..70b9e2c 100644 --- a/agtypeck.py +++ b/agtypeck.py @@ -1,15 +1,26 @@ from typing import * +import textwrap +import re from agast import * -class TypecheckResult: - def __init__(self, pd: str): - self.parser_data = pd +global i +i = 0 +class TypecheckResult: + def __init__(self, pd: str = "", ex: str = ""): + self.parser_data = pd + self.extra = ex def typecheck(program: List[Decl]) -> TypecheckResult: - i = 0 - def gen(name: str = "") -> str: - return f"__ag{i:03}{name}" + res = TypecheckResult() + def gen(prefix: str = "", suffix: str = "") -> str: + global i + presan = re.sub("[^0-9a-zA-Z]+", "_", prefix) + sufsan = re.sub("[^0-9a-zA-Z]+", "_", suffix) + i += 1 + return f"{presan}{i}{sufsan}" + def v(name: str) -> str: + return f"__ag_{name}" # collect a list of name -> iface declarations ifaces: Dict[str, Decl] = dict( @@ -24,6 +35,51 @@ def typecheck(program: List[Decl]) -> TypecheckResult: productions_hi: Dict[str, Union[str, List[str]]] = dict() for node in filter(lambda c: isinstance(c, Node), program): - print(node) + node = cast(Node, node) + n_class_name = gen(node.name) + class_decl = textwrap.dedent(f""" + class {v(n_class_name)}: pass + """) + res.extra += class_decl - return TypecheckResult("") \ No newline at end of file + print(node.name, node.ifaces) + + for variant in node.variants: + v_class_name = gen(f"{n_class_name}_var") + class_decl = textwrap.dedent(f""" + class {v(v_class_name)}({v(n_class_name)}): + ''' ''' + pass + """) + res.extra += class_decl + + prod_name = gen(node.name) + print(prod_name) + + for sym in variant.prod: + print("sym", sym) + + # for each of the equations, find out what the equation is + # trying to compute, and generate a thunk corresponding to + # that value. + for eq in variant.equations: + print("--eq", eq) + print(eq.lhs) + eq_name = gen(f"eq_{node.name}") + thunk_name = gen(f"thunk_{node.name}") + + func_impl = textwrap.dedent(f""" + def {eq_name}() -> None: + ''' {repr(eq)} ''' + pass + def {thunk_name}() -> Thunk[None]: + return Thunk({eq_name}) + """) + print(f"```py\n{func_impl}\n```") + res.extra += func_impl + + # this is a "type alias" that connects it to one of the generated + # names above + res.extra += f"{node.name} = {v(n_class_name)}" + + return res \ No newline at end of file diff --git a/gen.py b/gen.py index cdbcc8a..abb860a 100644 --- a/gen.py +++ b/gen.py @@ -1,5 +1,6 @@ import textwrap import os +import importlib from lark import Lark from agast import * @@ -23,7 +24,8 @@ if __name__ == "__main__": if not os.path.exists("gen"): os.makedirs("gen") with open("gen/arith.py", "w") as f: - f.write(textwrap.dedent(f""" + fmt_str = textwrap.dedent(""" + __all__ = ["parse"] from typing import Generic, TypeVar, Optional, Callable from lark import Lark, Transformer T = TypeVar('T') @@ -37,7 +39,15 @@ if __name__ == "__main__": if self.value is None: self.value = self.func() return self.value - parser = Lark('''{res.parser_data}''') + parser = Lark('''start: + {pd}''') class Trans(Transformer[None]): pass - """)) + {ex} + def parse(input: str) -> None: + print(input) + """) + f.write(fmt_str.format(pd=res.parser_data, ex=res.extra)) + + mod = importlib.import_module("gen.arith") + mod.parse("1 + 2 * 3") # type: ignore diff --git a/gen/__init__.py b/gen/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/grammar.lark b/grammar.lark index 95fe0a3..377cbe6 100644 --- a/grammar.lark +++ b/grammar.lark @@ -2,14 +2,20 @@ program: decl* ?decl: iface | node + | func sep_trail{item, punc}: item (punc item)? punc? +func: "fn" ident "(" ")" ("->" ty) "{" "}" + iface: "iface" ident "{" sep_trail{iface_field, ","} "}" iface_field: ident ":" ident -node: "node" ident ":" ident "{" variant* "}" -variant: prod "=>" "{" equation_* "}" +iface_ref: ident +iface_refs: iface_ref* +node: "node" ident ":" iface_refs "{" variants "}" +variants: variant* +variant: prod "=>" "{" equations "}" prod: sym* sym: sym_rename | STRING @@ -17,7 +23,10 @@ sym_rename: "<" ident ":" node_ref ">" node_ref: node_ref_name | STRING node_ref_name: ident +equations: equation_* equation_: equation ";" +// TODO: the left side should really be a separate type +// called lvalue, and should NOT include literals equation: expr "=" expr expr: expr_dot @@ -32,9 +41,13 @@ expr_call: expr "(" args ")" expr_name: ident args: sep_trail{expr, ","} +ty: ident + ident: IDENT +COMMENT: /\/\/[^\n]*/ IDENT: /([a-zA-Z][a-zA-Z0-9_]*)|(_[a-zA-Z0-9_]+)/ %import python.STRING %import common.WS -%ignore WS \ No newline at end of file +%ignore WS +%ignore COMMENT \ No newline at end of file diff --git a/let.ag b/let.ag new file mode 100644 index 0000000..2139de7 --- /dev/null +++ b/let.ag @@ -0,0 +1,25 @@ +iface HasEnv { + env: Map, +} + +iface HasVal { + val: str, +} + +alias Ident = /([a-zA-Z][a-zA-Z0-9_]*)|(_[a-zA-Z0-9_]+)/ + +node Expr : HasEnv + HasVal { + "let" "=" "in" => { + body.env = self.env.with(name, val); + self.val = body.val; + } + => { + // TODO: does env need to be referenced here? + // TODO: how to check for unbound names ahead of time + // (for self-implementation) + self.val = self.env.lookup(name); + } + => { + self.val = string; + } +} diff --git a/watch.ps1 b/watch.ps1 index 5d2498c..60aac37 100644 --- a/watch.ps1 +++ b/watch.ps1 @@ -1 +1 @@ -watchexec --shell=powershell -ce py,lark -i gen './run.ps1' +watchexec --shell=powershell -ce py,lark,ag -i gen './run.ps1'