aboutsummaryrefslogtreecommitdiff
path: root/core/operators.nom
diff options
context:
space:
mode:
authorBruce Hill <bitbucket@bruce-hill.com>2018-02-02 15:48:28 -0800
committerBruce Hill <bitbucket@bruce-hill.com>2018-02-02 15:49:42 -0800
commit505fec2a61d2571317cc4bbc36ec0f4822a63f9d (patch)
treec2b37e9db8e2f958fbca0caa0a9c4924912a37a9 /core/operators.nom
parent513c721198b2256235a95c98d161ab1bb51e6671 (diff)
Restructured the nomsu files to group all the essentials into core/ and
all the optionals into lib/. lib/core.nom and tests/all.nom are no longer needed now.
Diffstat (limited to 'core/operators.nom')
-rw-r--r--core/operators.nom209
1 files changed, 209 insertions, 0 deletions
diff --git a/core/operators.nom b/core/operators.nom
new file mode 100644
index 0000000..d2929bf
--- /dev/null
+++ b/core/operators.nom
@@ -0,0 +1,209 @@
+#..
+ This file contains definitions of operators like "+" and "and".
+
+use "core/metaprogramming.nom"
+
+# Indexing:
+immediately:
+ #.. NOTE!!! It's critical that there are spaces around %key if it's a string,
+ otherwise, Lua will get confused and interpret %obj[[[foo]]] as %obj("[foo]")
+ instead of %obj[ "foo" ].
+ It's also critical to have parens around %obj, otherwise Lua is too dumb to
+ realize that {x=1}["x"] is the same as ({x=1})["x"] or that
+ {x=1}.x is the same as ({x=1}).x
+ compile [..]
+ %obj' %key, %obj's %key, %key in %obj, %key'th in %obj, %key of %obj,
+ %key st in %obj, %key nd in %obj, %key rd in %obj, %key th in %obj,
+ ..to:
+ lua> ".."
+ local obj_lua = \(%obj as lua expr);
+ if not obj_lua:sub(-1,-1):match("[a-zA-Z)]") then
+ obj_lua = "("..obj_lua..")";
+ end
+ local key_lua = \(%key as lua expr);
+ local key_attr = (key_lua:match("'([a-zA-Z][a-zA-Z0-9]*)'")
+ or key_lua:match('"([a-zA-Z][a-zA-Z0-9]*)"'));
+ if key_attr then
+ return {expr=obj_lua.."."..key_attr};
+ elseif key_lua:sub(1,1) == "[" then
+ key_lua = " "..key_lua.." ";
+ end
+ return {expr=obj_lua.."["..key_lua.."]"};
+
+# Comparison Operators
+immediately:
+ compile [%x < %y] to {expr:"(\(%x as lua expr) < \(%y as lua expr))"}
+ compile [%x > %y] to {expr:"(\(%x as lua expr) > \(%y as lua expr))"}
+ compile [%x <= %y] to {expr:"(\(%x as lua expr) <= \(%y as lua expr))"}
+ compile [%x >= %y] to {expr:"(\(%x as lua expr) >= \(%y as lua expr))"}
+ # TODO: optimize case of [%x,%y] = [1,2]
+ compile [%a is %b, %a = %b, %a == %b] to:
+ lua> ".."
+ local safe = {Text=true, Number=true};
+ local a_lua, b_lua = nomsu:tree_to_lua(\%a).expr, nomsu:tree_to_lua(\%b).expr;
+ if safe[\%a.type] or safe[\%b.type] then
+ return {expr="("..a_lua.." == "..b_lua..")"};
+ else
+ return {expr="utils.equivalent("..a_lua..", "..b_lua..")"};
+ end
+ compile [%a isn't %b, %a is not %b, %a not= %b, %a != %b] to:
+ lua> ".."
+ local safe = {Text=true, Number=true};
+ local a_lua, b_lua = nomsu:tree_to_lua(\%a).expr, nomsu:tree_to_lua(\%b).expr;
+ if safe[\%a.type] or safe[\%b.type] then
+ return {expr="("..a_lua.." ~= "..b_lua..")"};
+ else
+ return {expr="(not utils.equivalent("..a_lua..", "..b_lua.."))"};
+ end
+ # For strict identity checking, use (%x's id) is (%y's id)
+ compile [%'s id, id of %] to {expr:"nomsu.ids[\(% as lua expr)]"}
+
+# Variable assignment operator
+immediately:
+ compile [%var <- %value] to:
+ lua> "local \%var_lua = nomsu:tree_to_lua(\%var);"
+ assume (%var_lua's "expr") or barf "Invalid target for assignment: \(%var's source code)"
+ lua> "local \%value_lua = nomsu:tree_to_lua(\%value);"
+ assume (%value_lua's "expr") or barf "Invalid value for assignment: \(%value's source code)"
+ return {..}
+ statements:"\(%var_lua's "expr") = \(%value_lua's "expr");"
+ locals: =lua "(\%var.type == 'Var' and {\%var_lua.expr} or nil)"
+
+immediately:
+ # Simultaneous mutli-assignments like: x,y,z = 1,x,3;
+ compile [<- %assignments] to:
+ %locals <- []
+ %targets <- []
+ %values <- []
+ assume ((%assignments' "type") is "Dict") or barf ".."
+ Expected a Dict for the assignments part of '<- %' statement, not \(%assignments' source code)
+ lua> ".."
+ for i, item in ipairs(\%assignments.value) do
+ local target, value = item.dict_key, item.dict_value;
+ local target_lua = nomsu:tree_to_lua(target);
+ if not target_lua.expr then error("Invalid target for assignment: "..target:get_src()); end
+ local value_lua = nomsu:tree_to_lua(value);
+ if not value_lua.expr then error("Invalid value for assignment: "..value:get_src()); end
+ if target.type == "Var" then
+ table.insert(\%locals, target_lua.expr);
+ end
+ table.insert(\%targets, target_lua.expr);
+ table.insert(\%values, value_lua.expr);
+ end
+ utils.deduplicate(\%locals);
+ return {locals=\%locals, statements=(table.concat(\%targets, ", ").." = "..table.concat(\%values, ", ")..";")};
+
+immediately:
+ compile [export %var <- %value] to:
+ %var_lua <- (%var as lua)
+ assume (%var_lua's "expr") or barf "Invalid target for assignment: \(%var's source code)"
+ %value_lua <- (%value as lua)
+ assume (%value_lua's "expr") or barf "Invalid value for assignment: \(%value's source code)"
+ return {statements:"\(%var_lua's "expr") = \(%value_lua's "expr");"}
+
+ compile [exporting %exported %body] to:
+ %body_lua <- (%body as lua)
+ %leftover_locals <- (=lua "{unpack(\%body_lua.locals or {})}")
+ assume ((%exported's "type") = "List") or barf ".."
+ Expected a List for the export part of 'exporting' statement, not \(%exported's source code)
+ lua> ".."
+ for i, item in ipairs(\%exported.value) do
+ if item.type ~= "Var" then
+ error("'exporting' statement expects Vars, not: "..item:get_src());
+ end
+ local var = nomsu:tree_to_lua(item).expr;
+ utils.remove_from_list(\%leftover_locals, var);
+ end
+ return {locals:%leftover_locals, statements:=lua "\%body_lua.statements or (\%body_lua.expr..';')"}
+
+ compile [with %assignments %body] to:
+ %body_lua <- (%body as lua)
+ %locals <- []
+ %declarations <- []
+ %leftover_locals <- (=lua "{unpack(\%body_lua.locals or {})}")
+ assume ((%assignments' "type") is "List") or barf ".."
+ Expected a List for the assignments part of 'with' statement, not \(%assignments' source code)
+ lua> ".."
+ for i, item in ipairs(\%assignments.value) do
+ if item.type == "Var" then
+ local var = nomsu:tree_to_lua(item).expr;
+ utils.remove_from_list(\%leftover_locals, var);
+ table.insert(\%locals, var);
+ else
+ if not (item.type == "FunctionCall" and #item.value == 3 and item.value[2].value == "<-") then
+ error("'with' statement expects entries of the form: '%var <- %value', not: "..item:get_src());
+ end
+ local target, value = item.value[1], item.value[3];
+ local target_lua = nomsu:tree_to_lua(target);
+ if not target_lua.expr then error("Invalid target for assignment: "..target:get_src()); end
+ local value_lua = nomsu:tree_to_lua(value);
+ if not value_lua.expr then error("Invalid value for assignment: "..value:get_src()); end
+ if target.type == "Var" then
+ utils.remove_from_list(\%leftover_locals, target_lua.expr);
+ end
+ table.insert(\%declarations, (("local %s = %s;\\n "):format(
+ target_lua.expr, value_lua.expr)));
+ end
+ end
+ local locals_code = "";
+ if #\%locals > 0 then
+ locals_code = "\\nlocal "..table.concat(\%locals, ", ")..";";
+ end
+ local declaration_code = "";
+ if #\%declarations > 0 then
+ declaration_code = "\\n"..table.concat(\%declarations, "\\n");
+ end
+ return {locals=\%leftover_locals, statements=([[
+ do%s%s
+ %s
+ end]]):format(locals_code, declaration_code, \%body_lua.statements or (\%body_lua.expr..";"))};
+
+immediately:
+ # Math Operators
+ compile [%x + %y] to {expr:"(\(%x as lua expr) + \(%y as lua expr))"}
+ compile [%x - %y] to {expr:"(\(%x as lua expr) - \(%y as lua expr))"}
+ compile [%x * %y] to {expr:"(\(%x as lua expr) * \(%y as lua expr))"}
+ compile [%x / %y] to {expr:"(\(%x as lua expr) / \(%y as lua expr))"}
+ compile [%x ^ %y] to {expr:"(\(%x as lua expr) ^ \(%y as lua expr))"}
+ compile [%x wrapped around %y, %x mod %y] to {expr:"(\(%x as lua expr) % \(%y as lua expr))"}
+
+ # 3-part chained comparisons
+ # (uses a lambda to avoid re-evaluating middle value, while still being an expression)
+ parse [%x < %y < %z] as: =lua "(function(x,y,z) return x < y and y < z; end)(\%x,\%y,\%z)"
+ parse [%x <= %y < %z] as: =lua "(function(x,y,z) return x <= y and y < z; end)(\%x,\%y,\%z)"
+ parse [%x < %y <= %z] as: =lua "(function(x,y,z) return x < y and y <= z; end)(\%x,\%y,\%z)"
+ parse [%x <= %y <= %z] as: =lua "(function(x,y,z) return x <= y and y <= z; end)(\%x,\%y,\%z)"
+ parse [%x > %y > %z] as: =lua "(function(x,y,z) return x > y and y > z; end)(\%x,\%y,\%z)"
+ parse [%x >= %y > %z] as: =lua "(function(x,y,z) return x >= y and y > z; end)(\%x,\%y,\%z)"
+ parse [%x > %y >= %z] as: =lua "(function(x,y,z) return x > y and y >= z; end)(\%x,\%y,\%z)"
+ parse [%x >= %y >= %z] as: =lua "(function(x,y,z) return x >= y and y >= z; end)(\%x,\%y,\%z)"
+ # TODO: optimize for common case where x,y,z are all either variables or number literals
+
+ # Boolean Operators
+ compile [%x and %y] to {expr:"(\(%x as lua expr) and \(%y as lua expr))"}
+ compile [%x or %y] to {expr:"(\(%x as lua expr) or \(%y as lua expr))"}
+
+ # Bitwise Operators
+ compile [%a OR %b, %a | %b] to {expr:"bit32.bor(\(%a as lua expr), \(%b as lua expr))"}
+ compile [%a XOR %b] to {expr:"bit32.bxor(\(%a as lua expr), \(%b as lua expr))"}
+ compile [%a AND %b, %a & %b] to {expr:"bit32.band(\(%a as lua expr), \(%b as lua expr))"}
+ compile [NOT %, ~ %] to {expr:"bit32.bnot(\(% as lua expr))"}
+ compile [%x LSHIFT %shift, %x << %shift] to {expr:"bit32.lshift(\(%x as lua expr), \(%shift as lua expr))"}
+ compile [%x RSHIFT %shift, %x >>> %shift] to {expr:"bit32.rshift(\(%x as lua expr), \(%shift as lua expr))"}
+ compile [%x ARSHIFT %shift, %x >> %shift] to {expr:"bit32.arshift(\(%x as lua expr), \(%shift as lua expr))"}
+ # TODO: implement OR, XOR, AND for multiple operands?
+
+ # Unary operators
+ compile [- %] to {expr:"(- \(% as lua expr))"}
+ compile [not %] to {expr:"(not \(% as lua expr))"}
+
+# Update operators
+immediately:
+ parse [%var + <- %, %var +<- %] as: %var <- (%var + %)
+ parse [%var - <- %, %var -<- %] as: %var <- (%var - %)
+ parse [%var * <- %, %var *<- %] as: %var <- (%var * %)
+ parse [%var / <- %, %var /<- %] as: %var <- (%var / %)
+ parse [%var ^ <- %, %var ^<- %] as: %var <- (%var ^ %)
+ parse [%var and <- %] as: %var <- (%var and %)
+ parse [%var or <- %] as: %var <- (%var or %)
+ parse [wrap %var around %] as: %var <- (%var wrapped around %)