mirror of
https://git.planet-casio.com/Lephenixnoir/JustUI.git
synced 2025-06-06 21:35:05 +02:00
juic: small improvements to record ctors, not working yet
This commit is contained in:
parent
ce39929bb4
commit
5f198ff6b0
6 changed files with 56 additions and 41 deletions
|
@ -11,6 +11,9 @@ def juiLen(x):
|
||||||
|
|
||||||
R_record = RecordType("record", None)
|
R_record = RecordType("record", None)
|
||||||
R_subrecord = RecordType("subrecord", R_record)
|
R_subrecord = RecordType("subrecord", R_record)
|
||||||
|
R_jwidget = RecordType("jwidget", None)
|
||||||
|
R_jlabel = RecordType("jlabel", R_jwidget)
|
||||||
|
R_jfkeys = RecordType("jfkeys", R_jwidget)
|
||||||
|
|
||||||
builtinClosure = Closure(parent=None, scope=MutableScopeData(defs={
|
builtinClosure = Closure(parent=None, scope=MutableScopeData(defs={
|
||||||
"print": (0, BuiltinFunction(juiPrint)),
|
"print": (0, BuiltinFunction(juiPrint)),
|
||||||
|
@ -19,5 +22,8 @@ builtinClosure = Closure(parent=None, scope=MutableScopeData(defs={
|
||||||
# TODO: Remove the built-in record type "record" used for testing
|
# TODO: Remove the built-in record type "record" used for testing
|
||||||
"record": (0, R_record),
|
"record": (0, R_record),
|
||||||
"subrecord": (0, R_subrecord),
|
"subrecord": (0, R_subrecord),
|
||||||
|
"jwidget": (0, R_jwidget),
|
||||||
|
"jlabel": (0, R_jlabel),
|
||||||
|
"jfkeys": (0, R_jfkeys),
|
||||||
|
|
||||||
}, timestamp=1))
|
}, timestamp=1))
|
||||||
|
|
|
@ -46,29 +46,18 @@ class RecordType:
|
||||||
|
|
||||||
# TODO: Record prototypes?
|
# TODO: Record prototypes?
|
||||||
|
|
||||||
# @dataclass
|
@dataclass
|
||||||
# class RecordCtor:
|
class RecordCtor:
|
||||||
# # Context in which the record body is evaluated
|
func: Function
|
||||||
# closure: "juic.eval.Closure"
|
|
||||||
# # List of entries to produce, of type `REC_ATTR` or `REC_VALUE`
|
|
||||||
# entries: list["juic.parser.Node"]
|
|
||||||
# # Parameter names, must all be unique. May be empty
|
|
||||||
# params: list[str] = field(default_factory=[])
|
|
||||||
# # Name of variadic argument if one; must also be unique
|
|
||||||
# variadic: str | None = None
|
|
||||||
|
|
||||||
# @staticmethod
|
|
||||||
# def makePlainRecordCtor():
|
|
||||||
# return RecordCtor(closure=None, entries=[], params=[], variadic=None)
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Record:
|
class Record:
|
||||||
# A record type if it's pure, or a thunk with the base object otherwise.
|
# A record type if it's pure, or a thunk with the base object otherwise.
|
||||||
base: Union[RecordType, "juic.eval.Thunk"]
|
base: Union[RecordType, "juic.eval.Thunk"]
|
||||||
# Standard key-value attribute pairs
|
# Standard key-value attribute pairs
|
||||||
attr: dict[str, "juic.eval.Thunk"]
|
attr: dict[str, Union["JuiValue", "juic.eval.Thunk"]]
|
||||||
# Children elements
|
# Children elements
|
||||||
children: list["juic.eval.Thunk"]
|
children: list[Union["JuiValue", "juic.eval.Thunk"]]
|
||||||
# TODO: Keep track of variables that are not fields, i.e. "methods"?
|
# TODO: Keep track of variables that are not fields, i.e. "methods"?
|
||||||
# scope: dict[str, "JuiValue"]
|
# scope: dict[str, "JuiValue"]
|
||||||
# TODO: Labels
|
# TODO: Labels
|
||||||
|
@ -100,7 +89,7 @@ def juiIsProjectable(value):
|
||||||
return type(value) in [Record, juic.eval.PartialRecordSnapshot]
|
return type(value) in [Record, juic.eval.PartialRecordSnapshot]
|
||||||
|
|
||||||
def juiIsConstructible(value):
|
def juiIsConstructible(value):
|
||||||
return type(value) in [RecordType, Function]
|
return type(value) in [RecordType, RecordCtor]
|
||||||
|
|
||||||
def juiIsRecordUpdatable(value):
|
def juiIsRecordUpdatable(value):
|
||||||
return type(value) == Record
|
return type(value) == Record
|
||||||
|
@ -134,6 +123,8 @@ def juiValueString(v):
|
||||||
case Record() as r:
|
case Record() as r:
|
||||||
s = r.base.name + " {"
|
s = r.base.name + " {"
|
||||||
s += "; ".join(x + ": " + juiValueString(y) for x, y in r.attr.items())
|
s += "; ".join(x + ": " + juiValueString(y) for x, y in r.attr.items())
|
||||||
|
if len(r.attr) and len(r.children):
|
||||||
|
s += "; "
|
||||||
s += "; ".join(juiValueString(x) for x in r.children)
|
s += "; ".join(juiValueString(x) for x in r.children)
|
||||||
return s + "}"
|
return s + "}"
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
36
juic/eval.py
36
juic/eval.py
|
@ -258,8 +258,9 @@ class Context:
|
||||||
variadic = params[variadics[0]][0] if variadics else None
|
variadic = params[variadics[0]][0] if variadics else None
|
||||||
|
|
||||||
# Build function object
|
# Build function object
|
||||||
|
# TODO: Make a full scope not just an expr
|
||||||
return Function(closure = self.currentStateClosure(),
|
return Function(closure = self.currentStateClosure(),
|
||||||
body = body,
|
body = Node(Node.T.SCOPE_EXPR, [body]),
|
||||||
params = [name for (name, v) in params if not v],
|
params = [name for (name, v) in params if not v],
|
||||||
variadic = variadic)
|
variadic = variadic)
|
||||||
|
|
||||||
|
@ -394,7 +395,7 @@ class Context:
|
||||||
case Record() as r:
|
case Record() as r:
|
||||||
if field not in r.attr:
|
if field not in r.attr:
|
||||||
raise Exception(f"access to undefined field {field}")
|
raise Exception(f"access to undefined field {field}")
|
||||||
return self.evalThunk(r.attr[field])
|
return self.evalValueOrThunk(r.attr[field])
|
||||||
case PartialRecordSnapshot() as prs:
|
case PartialRecordSnapshot() as prs:
|
||||||
if field in prs.fieldThunks:
|
if field in prs.fieldThunks:
|
||||||
return self.evalThunk(prs.fieldThunks[field])
|
return self.evalThunk(prs.fieldThunks[field])
|
||||||
|
@ -433,7 +434,10 @@ class Context:
|
||||||
print(" " + juiValueString(ve))
|
print(" " + juiValueString(ve))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return self.evalExpr(node)
|
case Node.T.SCOPE_EXPR, [e]:
|
||||||
|
return self.evalExpr(e)
|
||||||
|
|
||||||
|
raise Exception(f"execStmt: unrecognized node {node.ctor.name}")
|
||||||
|
|
||||||
# TODO: Context.eval*: continue failed computations to find other errors?
|
# TODO: Context.eval*: continue failed computations to find other errors?
|
||||||
def evalThunk(self, th: Thunk) -> JuiValue:
|
def evalThunk(self, th: Thunk) -> JuiValue:
|
||||||
|
@ -457,6 +461,9 @@ class Context:
|
||||||
th.result = result
|
th.result = result
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def evalValueOrThunk(self, v: JuiValue | Thunk) -> JuiValue:
|
||||||
|
return self.evalThunk(v) if isinstance(v, Thunk) else v
|
||||||
|
|
||||||
def evalCall(self, f: JuiValue, args: list[Node]) -> JuiValue:
|
def evalCall(self, f: JuiValue, args: list[Node]) -> JuiValue:
|
||||||
# Built-in functions: just evaluate arguments and go
|
# Built-in functions: just evaluate arguments and go
|
||||||
# TODO: Check types of built-in function calls
|
# TODO: Check types of built-in function calls
|
||||||
|
@ -483,6 +490,7 @@ class Context:
|
||||||
self.addDefinition(name, th)
|
self.addDefinition(name, th)
|
||||||
# self.currentScope.dump()
|
# self.currentScope.dump()
|
||||||
# self.currentClosure.dump()
|
# self.currentClosure.dump()
|
||||||
|
assert f.body.ctor == Node.T.SCOPE_EXPR
|
||||||
return self.execStmt(f.body)
|
return self.execStmt(f.body)
|
||||||
|
|
||||||
def evalRecordConstructor(self, ctor: JuiValue, entries: list[Node]) \
|
def evalRecordConstructor(self, ctor: JuiValue, entries: list[Node]) \
|
||||||
|
@ -504,20 +512,24 @@ class Context:
|
||||||
if type(ctor) == RecordType:
|
if type(ctor) == RecordType:
|
||||||
r = Record(base=ctor, attr=dict(), children=[])
|
r = Record(base=ctor, attr=dict(), children=[])
|
||||||
|
|
||||||
if len(args) > 0:
|
|
||||||
raise JuiRuntimeError(f"arguments given to type rec ctor")
|
|
||||||
|
|
||||||
# Create thunks for all entries while providing them with
|
# Create thunks for all entries while providing them with
|
||||||
# progressively more complete PRS of r.
|
# progressively more complete PRS of r.
|
||||||
prs = PartialRecordSnapshot()
|
prs = PartialRecordSnapshot()
|
||||||
|
|
||||||
for i in attr:
|
for i, e in enumerate(entries):
|
||||||
name, label, node = entries[i].args
|
if e.ctor == Node.T.REC_ATTR:
|
||||||
|
name, label, node = e.args
|
||||||
|
elif e.ctor == Node.T.REC_VALUE:
|
||||||
|
name, label, node = None, None, *e.args
|
||||||
|
|
||||||
th = Thunk(ast=node, closure=self.currentStateClosure())
|
th = Thunk(ast=node, closure=self.currentStateClosure())
|
||||||
th.thisReference = prs.copy()
|
th.thisReference = prs.copy()
|
||||||
|
|
||||||
r.attr[name] = th
|
if name is not None:
|
||||||
prs.fieldThunks[name] = th
|
r.attr[name] = th
|
||||||
|
prs.fieldThunks[name] = th
|
||||||
|
else:
|
||||||
|
r.children.append(th)
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
@ -613,7 +625,9 @@ class Context:
|
||||||
match v:
|
match v:
|
||||||
case Record() as r:
|
case Record() as r:
|
||||||
for a in r.attr:
|
for a in r.attr:
|
||||||
self.force(r.attr[a])
|
r.attr[a] = self.force(r.attr[a])
|
||||||
|
for i, e in enumerate(r.children):
|
||||||
|
r.children[i] = self.force(e)
|
||||||
return r
|
return r
|
||||||
case Thunk() as th:
|
case Thunk() as th:
|
||||||
self.evalThunk(th)
|
self.evalThunk(th)
|
||||||
|
|
|
@ -31,18 +31,22 @@ record { attr: 2 + 3; };
|
||||||
|
|
||||||
record { x: 1; y: 2; z: subrecord { u: "42"; }; };
|
record { x: 1; y: 2; z: subrecord { u: "42"; }; };
|
||||||
|
|
||||||
/*
|
fun stack(x) = x;
|
||||||
record {
|
fun stretch(x) = x;
|
||||||
|
|
||||||
|
jwidget {
|
||||||
fullscreen: true;
|
fullscreen: true;
|
||||||
@title jlabel { title; };
|
@title jlabel { "title"; };
|
||||||
@stack jwidget {} | stack | stretch;
|
@stack jwidget {} | stack | stretch;
|
||||||
|
|
||||||
let x = 4;
|
// let x = 4;
|
||||||
jfkeys { x };
|
// jfkeys { x };
|
||||||
|
jfkeys { 4 };
|
||||||
|
|
||||||
fun test(x, y, ...all) = x + y + sum(all);
|
// fun test(x, y, ...all) = x + y + sum(all);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
fun _(fx, cg) = if(param("FX")) fx else cg;
|
fun _(fx, cg) = if(param("FX")) fx else cg;
|
||||||
fun stack(elem) = elem <{ layout: stack };
|
fun stack(elem) = elem <{ layout: stack };
|
||||||
fun stretch(elem) = elem <{ stretch_x: 1; stretch_y: 1; strech_beyond_limits: false };
|
fun stretch(elem) = elem <{ stretch_x: 1; stretch_y: 1; strech_beyond_limits: false };
|
||||||
|
|
|
@ -74,10 +74,10 @@ def main(argv):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
for node in ast.args:
|
for node in ast.args:
|
||||||
node.dump()
|
|
||||||
v = ctx.execStmt(node)
|
v = ctx.execStmt(node)
|
||||||
v = ctx.force(v)
|
if node.ctor == juic.parser.Node.T.SCOPE_EXPR:
|
||||||
print(">>>>>>>", juic.eval.juiValueString(v))
|
v = ctx.force(v)
|
||||||
|
print(">>>>>>>", juic.eval.juiValueString(v))
|
||||||
|
|
||||||
ctx.currentScope.dump()
|
ctx.currentScope.dump()
|
||||||
ctx.currentClosure.dump()
|
ctx.currentClosure.dump()
|
||||||
|
|
|
@ -279,7 +279,7 @@ class Node:
|
||||||
"LIT", "IDENT", "OP", "THIS", "PROJ", "CALL", "IF", "SCOPE",
|
"LIT", "IDENT", "OP", "THIS", "PROJ", "CALL", "IF", "SCOPE",
|
||||||
"RECORD", "REC_ATTR", "REC_VALUE",
|
"RECORD", "REC_ATTR", "REC_VALUE",
|
||||||
"LET_DECL", "FUN_DECL", "REC_DECL", "SET_STMT",
|
"LET_DECL", "FUN_DECL", "REC_DECL", "SET_STMT",
|
||||||
"UNIT_TEST"])
|
"SCOPE_EXPR", "UNIT_TEST"])
|
||||||
ctor: T
|
ctor: T
|
||||||
args: list[Any]
|
args: list[Any]
|
||||||
|
|
||||||
|
@ -360,7 +360,7 @@ class JuiParser(LL1Parser):
|
||||||
case T.KW if t.value == "null":
|
case T.KW if t.value == "null":
|
||||||
node = Node(Node.T.LIT, [None])
|
node = Node(Node.T.LIT, [None])
|
||||||
case T.KW if t.value in ["true", "false"]:
|
case T.KW if t.value in ["true", "false"]:
|
||||||
node = Node(Node.T.LIT, t.value == "true")
|
node = Node(Node.T.LIT, [t.value == "true"])
|
||||||
case "(":
|
case "(":
|
||||||
node = self.expr()
|
node = self.expr()
|
||||||
self.expect(")")
|
self.expect(")")
|
||||||
|
@ -373,7 +373,7 @@ class JuiParser(LL1Parser):
|
||||||
# | expr0 "<{" record_entry,* "}" (record update)
|
# | expr0 "<{" record_entry,* "}" (record update)
|
||||||
# | expr0 "(" expr,* ")" (function call)
|
# | expr0 "(" expr,* ")" (function call)
|
||||||
# | expr0 "." ident (projection, same prec as call)
|
# | expr0 "." ident (projection, same prec as call)
|
||||||
@LL1Parser.binaryOpsRight(mkOpNode, ["|"])
|
@LL1Parser.binaryOpsLeft(mkOpNode, ["|"])
|
||||||
@LL1Parser.binaryOpsLeft(mkOpNode, ["<|"])
|
@LL1Parser.binaryOpsLeft(mkOpNode, ["<|"])
|
||||||
@LL1Parser.binaryOpsLeft(mkOpNode, ["||"])
|
@LL1Parser.binaryOpsLeft(mkOpNode, ["||"])
|
||||||
@LL1Parser.binaryOpsLeft(mkOpNode, ["&&"])
|
@LL1Parser.binaryOpsLeft(mkOpNode, ["&&"])
|
||||||
|
@ -528,4 +528,4 @@ class JuiParser(LL1Parser):
|
||||||
self.expect(JuiLexer.T.UNIT_TEST_MARKER)
|
self.expect(JuiLexer.T.UNIT_TEST_MARKER)
|
||||||
return Node(Node.T.UNIT_TEST, [None, self.expr()])
|
return Node(Node.T.UNIT_TEST, [None, self.expr()])
|
||||||
case _:
|
case _:
|
||||||
return self.expr()
|
return Node(Node.T.SCOPE_EXPR, [self.expr()])
|
||||||
|
|
Loading…
Add table
Reference in a new issue