more shit

This commit is contained in:
Michael Zhang 2021-06-09 02:44:52 -05:00
parent d91eb65d4d
commit 5dd92c99fe
9 changed files with 148 additions and 25 deletions

3
.gitignore vendored
View file

@ -1,4 +1,5 @@
.mypy_cache .mypy_cache
__pycache__ __pycache__
.vim .vim
gen/ gen/*
!gen/__init__.py

View file

@ -1,2 +1,2 @@
watch: 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'

View file

@ -11,9 +11,7 @@ class Iface(Decl):
class IfaceField: pass class IfaceField: pass
class IfaceRef: pass class IfaceRef: pass
class Expr: pass
class Node(Decl):
ifaces: List[IfaceRef]
class NodeRef: pass class NodeRef: pass
class NodeRefByName(NodeRef): class NodeRefByName(NodeRef):
@ -21,14 +19,16 @@ class NodeRefByName(NodeRef):
self.name = name self.name = name
def __repr__(self) -> str: return f"NodeRefByName({self.name})" def __repr__(self) -> str: return f"NodeRefByName({self.name})"
class Sym: pass class Sym: pass
class SymRename(Sym): class SymRename(Sym):
def __init__(self, name: str, node_ref: NodeRef): def __init__(self, name: str, node_ref: NodeRef):
self.name = name self.name = name
self.node_ref = node_ref self.node_ref = node_ref
def __repr__(self) -> str: return f"SymRename({self.name} : {self.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: class Variant:
def __init__(self, prod: List[Sym], equations: List[Equation]): def __init__(self, prod: List[Sym], equations: List[Equation]):
@ -36,7 +36,12 @@ class Variant:
self.equations = equations self.equations = equations
def __repr__(self) -> str: return f"Variant({self.prod}, {self.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): class ExprDot(Expr):
def __init__(self, left: Expr, right: Expr): def __init__(self, left: Expr, right: Expr):
self.left = left self.left = left
@ -65,16 +70,24 @@ class ExprName(Expr):
class Parser(Transformer[List[Decl]]): class Parser(Transformer[List[Decl]]):
def program(self, items: List[Decl]) -> List[Decl]: return items def program(self, items: List[Decl]) -> List[Decl]: return items
# interfaces
def iface(self, items: List[Any]) -> Iface: def iface(self, items: List[Any]) -> Iface:
[name, fields] = items [name, fields] = items
return Iface(name) return Iface(name)
def iface_field(self, items: List[str]) -> IfaceField: def iface_field(self, items: List[str]) -> IfaceField:
[name, ty] = items [name, ty] = items
return IfaceField() 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: 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: def variant(self, items: List[Any]) -> Variant:
[prod, equations] = items [prod, equations] = items
return Variant(prod, equations) 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 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: def expr_dot(self, items: List[Expr]) -> Expr:
[left, right] = items [left, right] = items
return ExprDot(left, right) return ExprDot(left, right)
@ -99,4 +116,5 @@ class Parser(Transformer[List[Decl]]):
def sep_trail(self, items: List[Tree]) -> List[T]: def sep_trail(self, items: List[Tree]) -> List[T]:
return list(map(lambda it: cast(T, it), items)) return list(map(lambda it: cast(T, it), items))
def ident(self, items: List[Token]) -> str: return cast(str, items[0].value) def ident(self, items: List[Token]) -> str: return cast(str, items[0].value)

View file

@ -1,15 +1,26 @@
from typing import * from typing import *
import textwrap
import re
from agast import * from agast import *
class TypecheckResult: global i
def __init__(self, pd: str): i = 0
self.parser_data = pd
class TypecheckResult:
def __init__(self, pd: str = "", ex: str = ""):
self.parser_data = pd
self.extra = ex
def typecheck(program: List[Decl]) -> TypecheckResult: def typecheck(program: List[Decl]) -> TypecheckResult:
i = 0 res = TypecheckResult()
def gen(name: str = "") -> str: def gen(prefix: str = "", suffix: str = "") -> str:
return f"__ag{i:03}{name}" 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 # collect a list of name -> iface declarations
ifaces: Dict[str, Decl] = dict( ifaces: Dict[str, Decl] = dict(
@ -24,6 +35,51 @@ def typecheck(program: List[Decl]) -> TypecheckResult:
productions_hi: Dict[str, Union[str, List[str]]] = dict() productions_hi: Dict[str, Union[str, List[str]]] = dict()
for node in filter(lambda c: isinstance(c, Node), program): 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("") 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

16
gen.py
View file

@ -1,5 +1,6 @@
import textwrap import textwrap
import os import os
import importlib
from lark import Lark from lark import Lark
from agast import * from agast import *
@ -23,7 +24,8 @@ if __name__ == "__main__":
if not os.path.exists("gen"): if not os.path.exists("gen"):
os.makedirs("gen") os.makedirs("gen")
with open("gen/arith.py", "w") as f: 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 typing import Generic, TypeVar, Optional, Callable
from lark import Lark, Transformer from lark import Lark, Transformer
T = TypeVar('T') T = TypeVar('T')
@ -37,7 +39,15 @@ if __name__ == "__main__":
if self.value is None: if self.value is None:
self.value = self.func() self.value = self.func()
return self.value return self.value
parser = Lark('''{res.parser_data}''') parser = Lark('''start:
{pd}''')
class Trans(Transformer[None]): class Trans(Transformer[None]):
pass 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

0
gen/__init__.py Normal file
View file

View file

@ -2,14 +2,20 @@ program: decl*
?decl: iface ?decl: iface
| node | node
| func
sep_trail{item, punc}: item (punc item)? punc? sep_trail{item, punc}: item (punc item)? punc?
func: "fn" ident "(" ")" ("->" ty) "{" "}"
iface: "iface" ident "{" sep_trail{iface_field, ","} "}" iface: "iface" ident "{" sep_trail{iface_field, ","} "}"
iface_field: ident ":" ident iface_field: ident ":" ident
node: "node" ident ":" ident "{" variant* "}" iface_ref: ident
variant: prod "=>" "{" equation_* "}" iface_refs: iface_ref*
node: "node" ident ":" iface_refs "{" variants "}"
variants: variant*
variant: prod "=>" "{" equations "}"
prod: sym* prod: sym*
sym: sym_rename sym: sym_rename
| STRING | STRING
@ -17,7 +23,10 @@ sym_rename: "<" ident ":" node_ref ">"
node_ref: node_ref_name node_ref: node_ref_name
| STRING | STRING
node_ref_name: ident node_ref_name: ident
equations: equation_*
equation_: equation ";" equation_: equation ";"
// TODO: the left side should really be a separate type
// called lvalue, and should NOT include literals
equation: expr "=" expr equation: expr "=" expr
expr: expr_dot expr: expr_dot
@ -32,9 +41,13 @@ expr_call: expr "(" args ")"
expr_name: ident expr_name: ident
args: sep_trail{expr, ","} args: sep_trail{expr, ","}
ty: ident
ident: IDENT ident: IDENT
COMMENT: /\/\/[^\n]*/
IDENT: /([a-zA-Z][a-zA-Z0-9_]*)|(_[a-zA-Z0-9_]+)/ IDENT: /([a-zA-Z][a-zA-Z0-9_]*)|(_[a-zA-Z0-9_]+)/
%import python.STRING %import python.STRING
%import common.WS %import common.WS
%ignore WS %ignore WS
%ignore COMMENT

25
let.ag Normal file
View file

@ -0,0 +1,25 @@
iface HasEnv {
env: Map<str, str>,
}
iface HasVal {
val: str,
}
alias Ident = /([a-zA-Z][a-zA-Z0-9_]*)|(_[a-zA-Z0-9_]+)/
node Expr : HasEnv + HasVal {
"let" <name:Ident> "=" <val:Expr> "in" <body:Expr> => {
body.env = self.env.with(name, val);
self.val = body.val;
}
<name:Ident> => {
// 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);
}
<string:StringLit> => {
self.val = string;
}
}

View file

@ -1 +1 @@
watchexec --shell=powershell -ce py,lark -i gen './run.ps1' watchexec --shell=powershell -ce py,lark,ag -i gen './run.ps1'