From 323c7a62828b18ccd2652bcfdc6de4824bdedde5 Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Tue, 8 Jun 2021 19:56:30 -0500 Subject: [PATCH] initial --- .gitignore | 2 ++ Justfile | 2 ++ arith.ag | 13 +++++++++++++ gen.py | 22 ++++++++++++++++++++++ grammar.lark | 44 ++++++++++++++++++++++++++++++++++++++++++++ main.py | 35 +++++++++++++++++++++++++++++++++++ mypy.ini | 2 ++ 7 files changed, 120 insertions(+) create mode 100644 .gitignore create mode 100644 Justfile create mode 100644 arith.ag create mode 100644 gen.py create mode 100644 grammar.lark create mode 100644 main.py create mode 100644 mypy.ini diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..791e803 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.mypy_cache +gen/ diff --git a/Justfile b/Justfile new file mode 100644 index 0000000..029ae70 --- /dev/null +++ b/Justfile @@ -0,0 +1,2 @@ +watch: + watchexec -ce py -i gen 'mypy *.py && python gen.py && mypy gen/*.py' diff --git a/arith.ag b/arith.ag new file mode 100644 index 0000000..6d4adf7 --- /dev/null +++ b/arith.ag @@ -0,0 +1,13 @@ +iface Node { + val: int, +} + +node Expr : Node { + "+" => { + self.val = l.val + r.val; + } + "*" => { + self.val = l.val * r.val; + } + => { self.val = parseInt(n); } +} diff --git a/gen.py b/gen.py new file mode 100644 index 0000000..ac6c0d8 --- /dev/null +++ b/gen.py @@ -0,0 +1,22 @@ +import textwrap +import os +from lark import Lark + +p = Lark(open("grammar.lark").read()) + +if __name__ == "__main__": + with open("arith.ag") as f: + data = f.read() + + t = p.parse(data) + print(t) + + if not os.path.exists("gen"): + os.makedirs("gen") + with open("gen/arith.py", "w") as f: + f.write(textwrap.dedent(""" + from typing import Generic, TypeVar + T = TypeVar('T') + class Thunk(Generic[T]): + pass + """)) diff --git a/grammar.lark b/grammar.lark new file mode 100644 index 0000000..9e2a3bf --- /dev/null +++ b/grammar.lark @@ -0,0 +1,44 @@ +start: decl* + +decl: iface + | node + +iface: IFACE IDENT "{" iface_field (COMMA iface_field)? COMMA? "}" +iface_field: IDENT COLON IDENT + +node: NODE IDENT COLON IDENT "{" node_prod* "}" +node_prod: prod THICCARROW "{" (equation SEMI)* "}" +prod: sym* +sym: IDENT + | LANG IDENT COLON node_ref RANG + | STRING +node_ref: IDENT + | STRING +equation: expr EQ expr + +expr: expr DOT expr + | expr ADD expr + | expr MUL expr + | expr LPAR args RPAR + | IDENT +args: expr (COMMA expr)? COMMA? + +IDENT: /([a-zA-Z][a-zA-Z0-9_]*)|(_[a-zA-Z0-9_]+)/ +IFACE: "iface" +NODE: "node" +LANG: "<" +RANG: ">" +ADD: "+" +MUL: "*" +COMMA: "," +COLON: ":" +LPAR: "(" +RPAR: ")" +EQ: "=" +DOT: "." +SEMI: ";" +THICCARROW: "=>" + +%import python.STRING +%import common.WS +%ignore WS \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..82e7ce0 --- /dev/null +++ b/main.py @@ -0,0 +1,35 @@ +from typing import Generic, TypeVar, Optional, Callable + +T = TypeVar("T") + +class Thunk(Generic[T]): + value: Optional[T] + + def __init__(self, func: Callable[[], T]): + self.func = func + self.value = None + + def get(self) -> T: + if self.value is None: + self.value = self.func() + return self.value + +class Node: + value: Thunk[int] + +class Add(Node): + def __init__(self, left: Node, right: Node): + self.value = Thunk(lambda: left.value.get() + right.value.get()) + +class Mul(Node): + def __init__(self, left: Node, right: Node): + self.value = Thunk(lambda: left.value.get() * right.value.get()) + +class Lit(Node): + def __init__(self, num: int): + self.num = num + self.value = Thunk(lambda: num) + +if __name__ == "__main__": + tree = Add(Mul(Lit(3), Lit(4)), Lit(5)) + print(tree.value.get()) diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..70a2ffb --- /dev/null +++ b/mypy.ini @@ -0,0 +1,2 @@ +[mypy] +strict = True \ No newline at end of file