updates
This commit is contained in:
parent
ac37c8d491
commit
8c687c4d1c
9 changed files with 213 additions and 122 deletions
2
Justfile
2
Justfile
|
@ -1,2 +1,2 @@
|
|||
watch:
|
||||
watchexec -ce py,lark,ag -i gen 'mypy *.py && python gen.py && mypy gen/*.py'
|
||||
watchexec -ce py,lark,ag -i gen 'mypy *.py && python agmain.py && mypy gen/*.py'
|
||||
|
|
53
agast.py
53
agast.py
|
@ -3,28 +3,50 @@ from lark import Transformer, Tree, Token
|
|||
|
||||
T = TypeVar("T")
|
||||
|
||||
class Ast:
|
||||
# def __new__(cls: Type[Ast], name: str, bases: Tuple[type], namespace: Dict[str, Any]) -> Ast:
|
||||
# x = super().__new__(cls, name, bases, namespace)
|
||||
# x.id = cls.__gen()
|
||||
# return x
|
||||
id: str
|
||||
n = 0
|
||||
@classmethod
|
||||
def __gen(cls, name: str = "") -> str:
|
||||
newid = cls.n
|
||||
cls.n += 1
|
||||
return f"_a{newid}{name}"
|
||||
def __init__(self) -> None:
|
||||
self.id = self.__gen()
|
||||
|
||||
class Decl:
|
||||
name: str
|
||||
class Iface(Decl):
|
||||
def __init__(self, name: str):
|
||||
self.name = name
|
||||
class IfaceField: pass
|
||||
|
||||
class IfaceRef: pass
|
||||
class Expr: pass
|
||||
class IfaceRef(str): pass
|
||||
class IfaceField:
|
||||
def __init__(self, name: str, ty: str):
|
||||
self.name = name
|
||||
self.ty = ty
|
||||
class Iface(Decl):
|
||||
def __init__(self, name: str, fields: List[IfaceField]):
|
||||
self.name = name
|
||||
self.fields = fields
|
||||
|
||||
class Expr(Ast):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
class NodeRef: pass
|
||||
class NodeRefByName(NodeRef):
|
||||
class NodeRefByName(NodeRef, str):
|
||||
def __init__(self, name: str):
|
||||
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):
|
||||
def __init__(self, name: str, ty: NodeRef):
|
||||
self.name = name
|
||||
self.node_ref = node_ref
|
||||
def __repr__(self) -> str: return f"SymRename({self.name} : {self.node_ref})"
|
||||
self.ty = ty
|
||||
def __repr__(self) -> str: return f"SymRename({self.name} : {self.ty})"
|
||||
class Equation:
|
||||
def __init__(self, lhs: Expr, rhs: Expr):
|
||||
self.lhs = lhs
|
||||
|
@ -45,26 +67,31 @@ class Node(Decl):
|
|||
|
||||
class ExprDot(Expr):
|
||||
def __init__(self, left: Expr, right: str):
|
||||
super().__init__()
|
||||
self.left = left
|
||||
self.right = right
|
||||
def __repr__(self) -> str: return f"{self.left}.{self.right}"
|
||||
class ExprAdd(Expr):
|
||||
def __init__(self, left: Expr, right: Expr):
|
||||
super().__init__()
|
||||
self.left = left
|
||||
self.right = right
|
||||
def __repr__(self) -> str: return f"{self.left} + {self.right}"
|
||||
class ExprMul(Expr):
|
||||
def __init__(self, left: Expr, right: Expr):
|
||||
super().__init__()
|
||||
self.left = left
|
||||
self.right = right
|
||||
def __repr__(self) -> str: return f"{self.left} * {self.right}"
|
||||
class ExprCall(Expr):
|
||||
def __init__(self, func: Expr, args: List[Expr]):
|
||||
super().__init__()
|
||||
self.func = func
|
||||
self.args = args
|
||||
def __repr__(self) -> str: return f"{self.func}({self.args})"
|
||||
class ExprName(Expr):
|
||||
def __init__(self, name: str):
|
||||
super().__init__()
|
||||
self.name = name
|
||||
def __repr__(self) -> str: return f"{self.name}"
|
||||
|
||||
|
@ -74,10 +101,10 @@ class Parser(Transformer[List[Decl]]):
|
|||
# interfaces
|
||||
def iface(self, items: List[Any]) -> Iface:
|
||||
[name, fields] = items
|
||||
return Iface(name)
|
||||
return Iface(name, fields)
|
||||
def iface_field(self, items: List[str]) -> IfaceField:
|
||||
[name, ty] = items
|
||||
return IfaceField()
|
||||
return IfaceField(name, ty)
|
||||
def iface_ref(self, items: List[str]) -> str: return items[0]
|
||||
def iface_refs(self, items: List[IfaceRef]) -> List[IfaceRef]: return items
|
||||
|
||||
|
@ -98,7 +125,7 @@ class Parser(Transformer[List[Decl]]):
|
|||
|
||||
# equations
|
||||
def equations(self, items: List[Equation]) -> List[Equation]: return items
|
||||
def equation_(self, items: List[Equation]) -> Equation: return items[0]
|
||||
def equation_semi(self, items: List[Equation]) -> Equation: return items[0]
|
||||
def equation(self, items: List[Expr]) -> Equation: return Equation(items[0], items[1])
|
||||
|
||||
# expr
|
||||
|
|
140
aggen.py
Normal file
140
aggen.py
Normal file
|
@ -0,0 +1,140 @@
|
|||
from typing import *
|
||||
import textwrap
|
||||
import re
|
||||
import copy
|
||||
|
||||
from agast import *
|
||||
|
||||
global i
|
||||
i = 0
|
||||
|
||||
class GenResult:
|
||||
def __init__(self, pd: str = "", ex: str = ""):
|
||||
self.parser_data = pd
|
||||
self.extra = ex
|
||||
|
||||
def gen(program: List[Decl]) -> GenResult:
|
||||
res = GenResult()
|
||||
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}"
|
||||
|
||||
# builtins
|
||||
builtins: Dict[str, str] = {
|
||||
"parseInt": "",
|
||||
}
|
||||
|
||||
# collect a list of name -> iface declarations
|
||||
ifaces: Dict[str, Iface] = dict(
|
||||
map(lambda c: (c.name, cast(Iface, c)),
|
||||
filter(lambda c: isinstance(c, Iface),
|
||||
program)))
|
||||
|
||||
# list of node -> iface mappings
|
||||
what_ifaces: Dict[str, Set[str]] = dict()
|
||||
what_fields: Dict[str, Dict[str, str]] = dict()
|
||||
for node in filter(lambda c: isinstance(c, Node), program):
|
||||
node = cast(Node, node)
|
||||
# all_fields = dict()
|
||||
what_ifaces[node.name] = set(node.ifaces)
|
||||
this_fields = dict()
|
||||
for iface in node.ifaces:
|
||||
fields = ifaces[iface].fields
|
||||
for field in fields:
|
||||
if field.name in this_fields:
|
||||
raise Exception("duplicate field name")
|
||||
this_fields[field.name] = field.ty
|
||||
what_fields[node.name] = this_fields
|
||||
print("what_ifaces:", what_ifaces)
|
||||
print("what_fields:", what_fields)
|
||||
|
||||
# a high-level dictionary of productions; this has sub-productions
|
||||
# that should be further expanded at a later step before converting
|
||||
# into lark code
|
||||
productions_hi: Dict[str, Union[str, List[str]]] = dict()
|
||||
|
||||
# TODO: this should probably not be inlined here, but i'll move it
|
||||
# out once i get more info into the 'env'
|
||||
def collect_required_thunks(env: List[Tuple[str, NodeRef]], expr: Expr) -> Dict[str, str]:
|
||||
names = dict(env)
|
||||
print(f"collect_required_thunks({expr})", expr.__class__)
|
||||
if isinstance(expr, ExprDot):
|
||||
return collect_required_thunks(env, expr.left)
|
||||
elif isinstance(expr, ExprMul):
|
||||
a = collect_required_thunks(env, expr.left)
|
||||
b = collect_required_thunks(env, expr.right)
|
||||
a.update(b)
|
||||
return a
|
||||
elif isinstance(expr, ExprAdd):
|
||||
a = collect_required_thunks(env, expr.left)
|
||||
b = collect_required_thunks(env, expr.right)
|
||||
a.update(b)
|
||||
return a
|
||||
elif isinstance(expr, ExprCall):
|
||||
return collect_required_thunks(env, expr.func)
|
||||
elif isinstance(expr, ExprName):
|
||||
if expr.name not in names and expr.name not in builtins:
|
||||
raise Exception(f"unbound name '{expr.name}'")
|
||||
return dict()
|
||||
raise Exception(f"unhandled {expr.__class__}")
|
||||
|
||||
for node in filter(lambda c: isinstance(c, Node), program):
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
# create an environment for checking the equations based on
|
||||
# the production
|
||||
env: List[Tuple[str, NodeRef]] = list()
|
||||
for sym in variant.prod:
|
||||
if isinstance(sym, SymRename):
|
||||
env.append((sym.name, sym.ty))
|
||||
print(env)
|
||||
|
||||
# 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:
|
||||
eq_name = gen(f"eq_{node.name}")
|
||||
thunk_name = gen(f"thunk_{node.name}")
|
||||
|
||||
print("RHS", eq.rhs, eq.rhs.id)
|
||||
collect_required_thunks(copy.deepcopy(env), eq.rhs)
|
||||
|
||||
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
|
|
@ -4,7 +4,7 @@ import importlib
|
|||
from lark import Lark
|
||||
|
||||
from agast import *
|
||||
from agtypeck import *
|
||||
from aggen import *
|
||||
|
||||
p = Lark(open("grammar.lark").read(), start="program", parser="lalr")
|
||||
|
||||
|
@ -13,22 +13,24 @@ if __name__ == "__main__":
|
|||
data = f.read()
|
||||
|
||||
cst = p.parse(data)
|
||||
# print("cst", cst)
|
||||
|
||||
trans = Parser()
|
||||
ast = trans.transform(cst)
|
||||
print("ast", ast)
|
||||
|
||||
res = typecheck(ast)
|
||||
res = gen(ast)
|
||||
|
||||
if not os.path.exists("gen"):
|
||||
os.makedirs("gen")
|
||||
with open("gen/arith.py", "w") as f:
|
||||
fmt_str = textwrap.dedent("""
|
||||
__all__ = ["parse"]
|
||||
from typing import Generic, TypeVar, Optional, Callable
|
||||
from typing import Generic, TypeVar, Optional, Callable, Dict, Any
|
||||
from lark import Lark, Transformer
|
||||
T = TypeVar('T')
|
||||
builtins: Dict[str, Any] = {{
|
||||
"parseInt": lambda s: int(s)
|
||||
}}
|
||||
class Thunk(Generic[T]):
|
||||
''' A thunk represents a value that may be computed lazily. '''
|
||||
value: Optional[T]
|
85
agtypeck.py
85
agtypeck.py
|
@ -1,85 +0,0 @@
|
|||
from typing import *
|
||||
import textwrap
|
||||
import re
|
||||
from agast import *
|
||||
|
||||
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:
|
||||
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(
|
||||
map(lambda c: (c.name, c),
|
||||
filter(lambda c: isinstance(c, Iface),
|
||||
program)))
|
||||
print(ifaces)
|
||||
|
||||
# a high-level dictionary of productions; this has sub-productions
|
||||
# that should be further expanded at a later step before converting
|
||||
# into lark code
|
||||
productions_hi: Dict[str, Union[str, List[str]]] = dict()
|
||||
|
||||
for node in filter(lambda c: isinstance(c, Node), program):
|
||||
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
|
||||
|
||||
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
|
2
arith.ag
2
arith.ag
|
@ -4,7 +4,7 @@ iface HasValue {
|
|||
|
||||
node Expr : HasValue {
|
||||
<l:Expr> "+" <r:Expr> => {
|
||||
self.val = l.val + r.val;
|
||||
self.val = l.val + r.val * l.val;
|
||||
}
|
||||
<l:Expr> "*" <r:Expr> => {
|
||||
self.val = l.val * r.val;
|
||||
|
|
41
grammar.lark
41
grammar.lark
|
@ -1,8 +1,8 @@
|
|||
program: decl*
|
||||
|
||||
?decl: iface
|
||||
| node
|
||||
| func
|
||||
| node
|
||||
| func
|
||||
|
||||
sep_trail{item, punc}: item (punc item)? punc?
|
||||
|
||||
|
@ -17,28 +17,35 @@ node: "node" ident ":" iface_refs "{" variants "}"
|
|||
variants: variant*
|
||||
variant: prod "=>" "{" equations "}"
|
||||
prod: sym*
|
||||
sym: sym_rename
|
||||
| STRING
|
||||
?sym: sym_rename
|
||||
| STRING
|
||||
sym_rename: "<" ident ":" node_ref ">"
|
||||
node_ref: node_ref_name
|
||||
| STRING
|
||||
?node_ref: node_ref_name
|
||||
| STRING
|
||||
node_ref_name: ident
|
||||
equations: equation_*
|
||||
equation_: equation ";"
|
||||
equations: equation_semi*
|
||||
equation_semi: equation ";"
|
||||
// TODO: the left side should really be a separate type
|
||||
// called lvalue, and should NOT include literals
|
||||
equation: expr "=" expr
|
||||
|
||||
?expr: expr_dot
|
||||
| expr_add
|
||||
| expr_mul
|
||||
| expr_call
|
||||
| expr_name
|
||||
expr_dot: expr "." expr
|
||||
expr_add: expr "+" expr
|
||||
expr_mul: expr "*" expr
|
||||
expr_call: expr "(" args ")"
|
||||
// Expressions
|
||||
?expr: expr2
|
||||
| expr_add
|
||||
expr_add: expr "+" expr2
|
||||
|
||||
?expr2: expr3
|
||||
| expr_mul
|
||||
| expr_call
|
||||
expr_mul: expr2 "*" expr3
|
||||
expr_call: expr2 "(" args ")"
|
||||
|
||||
?expr3: "(" expr ")"
|
||||
| expr_dot
|
||||
| expr_name
|
||||
expr_dot: expr3 "." ident
|
||||
expr_name: ident
|
||||
|
||||
args: sep_trail{expr, ","}
|
||||
|
||||
ty: ident
|
||||
|
|
2
run.ps1
2
run.ps1
|
@ -12,5 +12,5 @@ function Invoke-Call {
|
|||
}
|
||||
|
||||
Invoke-Call -ScriptBlock {mypy (get-item *.py) } -ErrorAction Stop
|
||||
Invoke-Call -ScriptBlock {python gen.py } -ErrorAction Stop
|
||||
Invoke-Call -ScriptBlock {python agmain.py } -ErrorAction Stop
|
||||
Invoke-Call -ScriptBlock {mypy (get-item gen/*.py) } -ErrorAction Stop
|
Loading…
Reference in a new issue