diff --git a/agast.py b/agast.py index 376ae3a..6ff1e03 100644 --- a/agast.py +++ b/agast.py @@ -1,11 +1,26 @@ from typing import * -from lark import Transformer, Tree +from lark import Transformer, Tree, Token -class Interface: pass +T = TypeVar("T") -class Node: pass +class Decl: + name: str +class Iface(Decl): + def __init__(self, name: str): + self.name = name +class IfaceField: pass + +class IfaceRef: pass + +class Node(Decl): + ifaces: List[IfaceRef] class NodeRef: pass +class NodeRefByName(NodeRef): + def __init__(self, name: str): + self.name = name + def __repr__(self) -> str: return f"NodeRefByName({self.name})" + class Sym: pass class SymRename(Sym): @@ -42,14 +57,33 @@ class ExprCall(Expr): self.func = func self.args = args def __repr__(self) -> str: return f"{self.func}({self.args})" +class ExprName(Expr): + def __init__(self, name: str): + self.name = name + def __repr__(self) -> str: return f"{self.name}" + +class Parser(Transformer[List[Decl]]): + def program(self, items: List[Decl]) -> List[Decl]: return items + + 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 node(self, items: List[Any]) -> Node: + return Node() -class Parser(Transformer[None]): def variant(self, items: List[Any]) -> Variant: [prod, equations] = items return Variant(prod, equations) def prod(self, items: List[Sym]) -> List[Sym]: return items + 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]) + def expr_dot(self, items: List[Expr]) -> Expr: [left, _, right] = items return ExprDot(left, right) @@ -62,3 +96,7 @@ class Parser(Transformer[None]): def expr_call(self, items: List[Expr]) -> Expr: [func, _, args, _] = items return ExprMul(func, args) + + 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 new file mode 100644 index 0000000..24f2cc1 --- /dev/null +++ b/agtypeck.py @@ -0,0 +1,17 @@ +from typing import * +from agast import * + +def typecheck(program: List[Decl]) -> None: + i = 0 + def gen(name: str = "") -> str: + return f"__ag{i:03}{name}" + + # collect a list of name -> iface declarations + ifaces: Dict[str, Decl] = dict( + map(lambda c: (c.name, c), + filter(lambda c: isinstance(c, Iface), + program))) + print(ifaces) + + for node in filter(lambda c: isinstance(c, Node), program): + print(node) diff --git a/gen.py b/gen.py index b773ea2..ed33aa9 100644 --- a/gen.py +++ b/gen.py @@ -2,23 +2,34 @@ import textwrap import os from lark import Lark -from agast import Parser, Interface +from agast import * +from agtypeck import * -p = Lark(open("grammar.lark").read(), parser="lalr", transformer=Parser()) +p = Lark(open("grammar.lark").read(), start="program", parser="lalr") if __name__ == "__main__": with open("arith.ag") as f: data = f.read() - t = p.parse(data) - print(t) + cst = p.parse(data) + # print("cst", cst) + + trans = Parser() + ast = trans.transform(cst) + print("ast", ast) + + typecheck(ast) + + parser_data = "" if not os.path.exists("gen"): os.makedirs("gen") with open("gen/arith.py", "w") as f: - f.write(textwrap.dedent(""" + f.write(textwrap.dedent(f""" from typing import Generic, TypeVar + from lark import Lark T = TypeVar('T') class Thunk(Generic[T]): pass + parser = Lark('''{parser_data}''') """)) diff --git a/grammar.lark b/grammar.lark index 338fb44..4d02bbc 100644 --- a/grammar.lark +++ b/grammar.lark @@ -1,19 +1,22 @@ -start: decl* +program: decl* -decl: iface +?decl: iface | node -iface: IFACE IDENT "{" iface_field (COMMA iface_field)? COMMA? "}" -iface_field: IDENT COLON IDENT +sep_trail{item, punc}: item (punc item)? punc? -node: NODE IDENT COLON IDENT "{" variant* "}" +iface: "iface" ident "{" sep_trail{iface_field, ","} "}" +iface_field: ident ":" ident + +node: NODE ident ":" ident "{" variant* "}" variant: prod "=>" "{" equation_* "}" prod: sym* sym: sym_rename | STRING -sym_rename: "<" IDENT ":" node_ref ">" -node_ref: IDENT +sym_rename: "<" ident ":" node_ref ">" +node_ref: node_ref_name | STRING +node_ref_name: ident equation_: equation SEMI equation: expr EQ expr @@ -21,13 +24,15 @@ expr: expr_dot | expr_add | expr_mul | expr_call - | IDENT + | expr_name expr_dot: expr DOT expr expr_add: expr ADD expr expr_mul: expr MUL expr expr_call: expr LPAR args RPAR +expr_name: ident args: expr (COMMA expr)? COMMA? +ident: IDENT IDENT: /([a-zA-Z][a-zA-Z0-9_]*)|(_[a-zA-Z0-9_]+)/ IFACE: "iface" NODE: "node" diff --git a/run.ps1 b/run.ps1 new file mode 100644 index 0000000..896b876 --- /dev/null +++ b/run.ps1 @@ -0,0 +1,16 @@ +#blessed https://stackoverflow.com/a/52784160 + +function Invoke-Call { + param ( + [scriptblock]$ScriptBlock, + [string]$ErrorAction = $ErrorActionPreference + ) + & @ScriptBlock + if (($lastexitcode -ne 0) -and $ErrorAction -eq "Stop") { + exit $lastexitcode + } +} + +Invoke-Call -ScriptBlock {mypy (get-item *.py) } -ErrorAction Stop +Invoke-Call -ScriptBlock {python gen.py } -ErrorAction Stop +Invoke-Call -ScriptBlock {mypy (get-item gen/*.py) } -ErrorAction Stop \ No newline at end of file diff --git a/watch.ps1 b/watch.ps1 new file mode 100644 index 0000000..2bcc73f --- /dev/null +++ b/watch.ps1 @@ -0,0 +1 @@ +watchexec --shell=powershell -ce py -i gen './run.ps1'