Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write scrapscript->JS compiler in scrapscript #100

Draft
wants to merge 12 commits into
base: trunk
Choose a base branch
from
82 changes: 79 additions & 3 deletions scrapscript.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
}
Expand Down Expand Up @@ -1183,6 +1183,19 @@ def improve_closure(closure: Closure) -> Closure:
return Closure(env, closure.func)


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))


# 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)):
Expand Down Expand Up @@ -3469,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
Expand Down Expand Up @@ -4380,18 +4393,81 @@ 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))


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),
"$$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())),
"$$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),
}


PRELUDE = """
id = x -> x

. 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) ++ ")"
| {type="Function", arg=arg, body=body} ->
"(" ++ (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
. compile_case =
| {type="MatchCase", pattern={type="Int", value=value}, body=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) ++ "; }"
| {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}) ++ ");"
| {type="MatchCase", pattern={type="Record", data={}}, body=body} ->
"if (__arg === {}) { return " ++ (compile body) ++ "; }"
)

. join = sep ->
| [] -> ""
| [x] -> x
| [x, ...xs] -> x ++ sep ++ (join sep xs)

. quicksort =
| [] -> []
| [p, ...xs] -> (concat ((quicksort (ltp xs p)) +< p) (quicksort (gtp xs p))
Expand Down Expand Up @@ -4424,7 +4500,7 @@ def listlength(obj: Object) -> Object:
| n ->
| [] -> []
| [x, ...xs] -> x >+ take (n - 1) xs

. all = f ->
| [] -> #true
| [x, ...xs] -> f x && all f xs
Expand Down
Loading