From 17287c5edc82bc64e8d91d6e89bc62d95a627bb8 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Sat, 20 Jan 2024 14:04:36 -0500 Subject: [PATCH 01/12] wip --- scrapscript.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/scrapscript.py b/scrapscript.py index 8cceeb14..8ff8051c 100755 --- a/scrapscript.py +++ b/scrapscript.py @@ -1183,6 +1183,20 @@ def improve_closure(closure: Closure) -> Closure: return Closure(env, closure.func) +def lift_simple(x: object) -> Object: + if isinstance(x, int): + return Int(x) + if isinstance(x, str): + return String(x) + raise NotImplementedError(type(x)) + + +def as_record(obj: dict[str, object]) -> Record: + conv: Callable[[object], Object] = lambda x: as_record(x) if isinstance(x, dict) else lift_simple(x) + return Record({key: conv(value) for key, value in obj.items()}) + + +# pylint: disable=redefined-builtin def eval_exp(env: Env, exp: Object) -> Object: logger.debug(exp) if isinstance(exp, (Int, Float, String, Bytes, Hole, Closure, NativeFunction, Symbol)): @@ -4386,6 +4400,7 @@ def listlength(obj: Object) -> Object: "$$jsondecode": NativeFunction("$$jsondecode", jsondecode), "$$serialize": NativeFunction("$$serialize", lambda obj: Bytes(serialize(obj))), "$$listlength": NativeFunction("$$listlength", listlength), + "$$asrecord": NativeFunction("$$asrecord", lambda exp: as_record(exp.serialize())), } From f3186a808870975470517553603d07b640a500e1 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Sat, 20 Jan 2024 14:35:41 -0500 Subject: [PATCH 02/12] Write compiler in scrapscript --- scrapscript.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/scrapscript.py b/scrapscript.py index 8ff8051c..600b0724 100755 --- a/scrapscript.py +++ b/scrapscript.py @@ -743,7 +743,7 @@ class Binop(Object): def serialize(self) -> Dict[str, object]: return { "type": "Binop", - "op": self.op.name, + "op": BinopKind.to_str(self.op), "left": self.left.serialize(), "right": self.right.serialize(), } @@ -4394,6 +4394,12 @@ def listlength(obj: Object) -> Object: return Int(len(obj.items)) +def int_as_str(obj: Object) -> String: + if not isinstance(obj, Int): + raise TypeError(f"int_as_str expected Int, but got {type(obj).__name__}") + return String(str(obj.value)) + + STDLIB = { "$$add": Closure({}, Function(Var("x"), Function(Var("y"), Binop(BinopKind.ADD, Var("x"), Var("y"))))), "$$fetch": NativeFunction("$$fetch", fetch), @@ -4401,6 +4407,7 @@ def listlength(obj: Object) -> Object: "$$serialize": NativeFunction("$$serialize", lambda obj: Bytes(serialize(obj))), "$$listlength": NativeFunction("$$listlength", listlength), "$$asrecord": NativeFunction("$$asrecord", lambda exp: as_record(exp.serialize())), + "$$int_as_str": NativeFunction("$$int_as_str", int_as_str), } @@ -4447,6 +4454,11 @@ def listlength(obj: Object) -> Object: . any = f -> | [] -> #false | [x, ...xs] -> f x || any f xs + +. compile = + | {type = "Int", value = value} -> $$int_as_str value + | {type = "Var", name = name} -> name + | {type = "Binop", op = op, left = left, right = right } -> (compile left) ++ op ++ (compile right) """ From b7f36e850f53a121a72ac66aa1ca55356970ae01 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Sat, 20 Jan 2024 14:49:51 -0500 Subject: [PATCH 03/12] Compile more --- scrapscript.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/scrapscript.py b/scrapscript.py index 600b0724..31e1e813 100755 --- a/scrapscript.py +++ b/scrapscript.py @@ -1183,19 +1183,18 @@ def improve_closure(closure: Closure) -> Closure: return Closure(env, closure.func) -def lift_simple(x: object) -> Object: +def as_record(x: object) -> Object: if isinstance(x, int): return Int(x) if isinstance(x, str): return String(x) + if isinstance(x, list): + return List([as_record(item) for item in x]) + if isinstance(x, dict): + return Record({key: as_record(value) for key, value in x.items()}) raise NotImplementedError(type(x)) -def as_record(obj: dict[str, object]) -> Record: - conv: Callable[[object], Object] = lambda x: as_record(x) if isinstance(x, dict) else lift_simple(x) - return Record({key: conv(value) for key, value in obj.items()}) - - # pylint: disable=redefined-builtin def eval_exp(env: Env, exp: Object) -> Object: logger.debug(exp) @@ -4414,6 +4413,20 @@ def int_as_str(obj: Object) -> String: PRELUDE = """ id = x -> x +. compile = + | {type = "Int", value = value} -> $$int_as_str value + | {type = "Var", name = name} -> name + | {type = "Binop", op = op, left = left, right = right} -> (compile left) ++ op ++ (compile right) + | {type = "List", items = items} -> "[" ++ (join ", " (map compile items)) ++ "]" + | {type = "Assign", name = name, value = value} -> "((" ++ name ++ ") =>" ++ (compile value) ++ ")(" + | {type = "Where", binding={type="Assign", name=name, value=value}, body=body} -> + "(" ++ (compile name) ++ " => " ++ (compile body) ++ ")(" ++ (compile value) ++ ")" + +. join = sep -> + | [] -> "" + | [x] -> x + | [x, ...xs] -> x ++ sep ++ (join sep xs) + . quicksort = | [] -> [] | [p, ...xs] -> (concat ((quicksort (ltp xs p)) +< p) (quicksort (gtp xs p)) @@ -4454,11 +4467,6 @@ def int_as_str(obj: Object) -> String: . any = f -> | [] -> #false | [x, ...xs] -> f x || any f xs - -. compile = - | {type = "Int", value = value} -> $$int_as_str value - | {type = "Var", name = name} -> name - | {type = "Binop", op = op, left = left, right = right } -> (compile left) ++ op ++ (compile right) """ From 603b5ac1c09326efd2fdb449c471c5531e355e7c Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Sat, 20 Jan 2024 14:50:26 -0500 Subject: [PATCH 04/12] Remove ws errors --- scrapscript.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scrapscript.py b/scrapscript.py index 31e1e813..3686222d 100755 --- a/scrapscript.py +++ b/scrapscript.py @@ -3482,7 +3482,7 @@ def test_match_record_spread(self) -> None: self.assertEqual( self._run( """ - f {x = 4, y = 5} + f {x = 4, y = 5} . f = | {} -> 0 | {x = a, ...} -> a @@ -4418,7 +4418,7 @@ def int_as_str(obj: Object) -> String: | {type = "Var", name = name} -> name | {type = "Binop", op = op, left = left, right = right} -> (compile left) ++ op ++ (compile right) | {type = "List", items = items} -> "[" ++ (join ", " (map compile items)) ++ "]" - | {type = "Assign", name = name, value = value} -> "((" ++ name ++ ") =>" ++ (compile value) ++ ")(" + | {type = "Assign", name = name, value = value} -> "((" ++ name ++ ") =>" ++ (compile value) ++ ")(" | {type = "Where", binding={type="Assign", name=name, value=value}, body=body} -> "(" ++ (compile name) ++ " => " ++ (compile body) ++ ")(" ++ (compile value) ++ ")" @@ -4459,7 +4459,7 @@ def int_as_str(obj: Object) -> String: | n -> | [] -> [] | [x, ...xs] -> x >+ take (n - 1) xs - + . all = f -> | [] -> #true | [x, ...xs] -> f x && all f xs From 4c368cbb6d196b53020f2c9d5e467941ca4a4f8c Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Sat, 20 Jan 2024 14:52:26 -0500 Subject: [PATCH 05/12] less ws --- scrapscript.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scrapscript.py b/scrapscript.py index 3686222d..d9479a06 100755 --- a/scrapscript.py +++ b/scrapscript.py @@ -4414,12 +4414,12 @@ def int_as_str(obj: Object) -> String: id = x -> x . compile = - | {type = "Int", value = value} -> $$int_as_str value - | {type = "Var", name = name} -> name - | {type = "Binop", op = op, left = left, right = right} -> (compile left) ++ op ++ (compile right) - | {type = "List", items = items} -> "[" ++ (join ", " (map compile items)) ++ "]" - | {type = "Assign", name = name, value = value} -> "((" ++ name ++ ") =>" ++ (compile value) ++ ")(" - | {type = "Where", binding={type="Assign", name=name, value=value}, body=body} -> + | {type="Int", value=value} -> $$int_as_str value + | {type="Var", name=name} -> name + | {type="Binop", op=op, left=left, right=right} -> (compile left) ++ op ++ (compile right) + | {type="List", items=items} -> "[" ++ (join ", " (map compile items)) ++ "]" + | {type="Assign", name=name, value=value} -> "((" ++ name ++ ") =>" ++ (compile value) ++ ")(" + | {type="Where", binding={type="Assign", name=name, value=value}, body=body} -> "(" ++ (compile name) ++ " => " ++ (compile body) ++ ")(" ++ (compile value) ++ ")" . join = sep -> From 0b297f4690843d9f6deaf7604da27c22a939091d Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Sat, 20 Jan 2024 14:55:23 -0500 Subject: [PATCH 06/12] Add fun/apply --- scrapscript.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scrapscript.py b/scrapscript.py index d9479a06..b3f5a91c 100755 --- a/scrapscript.py +++ b/scrapscript.py @@ -4421,6 +4421,9 @@ def int_as_str(obj: Object) -> String: | {type="Assign", name=name, value=value} -> "((" ++ name ++ ") =>" ++ (compile value) ++ ")(" | {type="Where", binding={type="Assign", name=name, value=value}, body=body} -> "(" ++ (compile name) ++ " => " ++ (compile body) ++ ")(" ++ (compile value) ++ ")" + | {type="Function", arg=arg, body=body} -> + "(" ++ (compile arg) ++ " => " ++ (compile body) ++ ")" + | {type="Apply", func=func, arg=arg} -> "(" ++ (compile func) ++ ")(" ++ (compile arg) ++ ")" . join = sep -> | [] -> "" From 4e4bcf53a2d0facfa09669c2cd578967bdffb87b Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Sun, 21 Jan 2024 12:28:21 -0500 Subject: [PATCH 07/12] Special case string concat --- scrapscript.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scrapscript.py b/scrapscript.py index b3f5a91c..d6c7dbba 100755 --- a/scrapscript.py +++ b/scrapscript.py @@ -4416,6 +4416,7 @@ def int_as_str(obj: Object) -> String: . compile = | {type="Int", value=value} -> $$int_as_str value | {type="Var", name=name} -> name + | {type="Binop", op="++", left=left, right=right} -> (compile left) ++ "+" ++ (compile right) | {type="Binop", op=op, left=left, right=right} -> (compile left) ++ op ++ (compile right) | {type="List", items=items} -> "[" ++ (join ", " (map compile items)) ++ "]" | {type="Assign", name=name, value=value} -> "((" ++ name ++ ") =>" ++ (compile value) ++ ")(" From 3bc09d15dea72ed21de7ae692de8852818483d3d Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Sun, 21 Jan 2024 12:44:55 -0500 Subject: [PATCH 08/12] rec and str --- scrapscript.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/scrapscript.py b/scrapscript.py index d6c7dbba..4c8748b6 100755 --- a/scrapscript.py +++ b/scrapscript.py @@ -4399,6 +4399,18 @@ def int_as_str(obj: Object) -> String: return String(str(obj.value)) +def str_as_str(obj: Object) -> String: + if not isinstance(obj, String): + raise TypeError(f"str_as_str expected String, but got {type(obj).__name__}") + return String(repr(obj.value)) + + +def record_each(obj: Object) -> Object: + if not isinstance(obj, Record): + raise TypeError(f"record_each expected Record, but got {type(obj).__name__}") + return List([Record({"key": String(key), "value": value}) for key, value in obj.data.items()]) + + STDLIB = { "$$add": Closure({}, Function(Var("x"), Function(Var("y"), Binop(BinopKind.ADD, Var("x"), Var("y"))))), "$$fetch": NativeFunction("$$fetch", fetch), @@ -4407,6 +4419,8 @@ def int_as_str(obj: Object) -> String: "$$listlength": NativeFunction("$$listlength", listlength), "$$asrecord": NativeFunction("$$asrecord", lambda exp: as_record(exp.serialize())), "$$int_as_str": NativeFunction("$$int_as_str", int_as_str), + "$$str_as_str": NativeFunction("$$str_as_str", str_as_str), + "$$record_each": NativeFunction("$$record_each", record_each), } @@ -4416,9 +4430,13 @@ def int_as_str(obj: Object) -> String: . compile = | {type="Int", value=value} -> $$int_as_str value | {type="Var", name=name} -> name + | {type="String", value=value} -> $$str_as_str value | {type="Binop", op="++", left=left, right=right} -> (compile left) ++ "+" ++ (compile right) | {type="Binop", op=op, left=left, right=right} -> (compile left) ++ op ++ (compile right) | {type="List", items=items} -> "[" ++ (join ", " (map compile items)) ++ "]" + | {type="Record", data=data} -> ("{" ++ (join ", " (map compile_pair ($$record_each data))) ++ "}" + . compile_pair = | {key=key, value=value} -> key ++ ":" ++ (compile value) + ) | {type="Assign", name=name, value=value} -> "((" ++ name ++ ") =>" ++ (compile value) ++ ")(" | {type="Where", binding={type="Assign", name=name, value=value}, body=body} -> "(" ++ (compile name) ++ " => " ++ (compile body) ++ ")(" ++ (compile value) ++ ")" From 8471aa2d017e7ce756db65fc87730a962e9976b5 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Sun, 21 Jan 2024 13:03:04 -0500 Subject: [PATCH 09/12] Compile match/case int literals --- scrapscript.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scrapscript.py b/scrapscript.py index 4c8748b6..ddf4a0d8 100755 --- a/scrapscript.py +++ b/scrapscript.py @@ -4443,6 +4443,14 @@ def record_each(obj: Object) -> Object: | {type="Function", arg=arg, body=body} -> "(" ++ (compile arg) ++ " => " ++ (compile body) ++ ")" | {type="Apply", func=func, arg=arg} -> "(" ++ (compile func) ++ ")(" ++ (compile arg) ++ ")" + | {type="MatchFunction", cases=cases} -> + (foldr (case -> acc -> (compile_case case) ++ "\n" ++ acc) + "raise 'no matching cases';" + cases + . compile_case = + | {type="MatchCase", pattern={type="Int", value=value}, body=body} -> + "if (__arg == " ++ ($$int_as_str value) ++ ") { return " ++ (compile body) ++ "; }" + ) . join = sep -> | [] -> "" From 85f14cb5ee475ac85ea455f74b07d39ee73f6bef Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Sun, 21 Jan 2024 13:04:08 -0500 Subject: [PATCH 10/12] Compile match/case string literals --- scrapscript.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scrapscript.py b/scrapscript.py index ddf4a0d8..f387b236 100755 --- a/scrapscript.py +++ b/scrapscript.py @@ -4449,7 +4449,9 @@ def record_each(obj: Object) -> Object: cases . compile_case = | {type="MatchCase", pattern={type="Int", value=value}, body=body} -> - "if (__arg == " ++ ($$int_as_str value) ++ ") { return " ++ (compile body) ++ "; }" + "if (__arg === " ++ ($$int_as_str value) ++ ") { return " ++ (compile body) ++ "; }" + | {type="MatchCase", pattern={type="String", value=value}, body=body} -> + "if (__arg === " ++ ($$str_as_str value) ++ ") { return " ++ (compile body) ++ "; }" ) . join = sep -> From 97871bd32bec426cd3a383857ee4cc53ffa63221 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Sun, 21 Jan 2024 13:19:12 -0500 Subject: [PATCH 11/12] Compile match/case var --- scrapscript.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scrapscript.py b/scrapscript.py index f387b236..4ac976af 100755 --- a/scrapscript.py +++ b/scrapscript.py @@ -4444,6 +4444,7 @@ def record_each(obj: Object) -> Object: "(" ++ (compile arg) ++ " => " ++ (compile body) ++ ")" | {type="Apply", func=func, arg=arg} -> "(" ++ (compile func) ++ ")(" ++ (compile arg) ++ ")" | {type="MatchFunction", cases=cases} -> + -- TODO(max): Figure out what to do about __arg. Gensym? (foldr (case -> acc -> (compile_case case) ++ "\n" ++ acc) "raise 'no matching cases';" cases @@ -4452,6 +4453,12 @@ def record_each(obj: Object) -> Object: "if (__arg === " ++ ($$int_as_str value) ++ ") { return " ++ (compile body) ++ "; }" | {type="MatchCase", pattern={type="String", value=value}, body=body} -> "if (__arg === " ++ ($$str_as_str value) ++ ") { return " ++ (compile body) ++ "; }" + | {type="MatchCase", pattern={type="Var", name=name}, body=body} -> + "return (" ++ compile ({type="Where", + binding={type="Assign", + name={type="Var", name=name}, + value={type="Var", name="__arg"}}, + body=body}) ++ ");" ) . join = sep -> From 8d0907fa52965136411d9c626dec0434133cb782 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Sun, 21 Jan 2024 13:22:52 -0500 Subject: [PATCH 12/12] Compile match/case empty record --- scrapscript.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scrapscript.py b/scrapscript.py index 4ac976af..3e139d01 100755 --- a/scrapscript.py +++ b/scrapscript.py @@ -4459,6 +4459,8 @@ def record_each(obj: Object) -> Object: name={type="Var", name=name}, value={type="Var", name="__arg"}}, body=body}) ++ ");" + | {type="MatchCase", pattern={type="Record", data={}}, body=body} -> + "if (__arg === {}) { return " ++ (compile body) ++ "; }" ) . join = sep ->