From 505fec2a61d2571317cc4bbc36ec0f4822a63f9d Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Fri, 2 Feb 2018 15:48:28 -0800 Subject: 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. --- core/operators.nom | 209 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 core/operators.nom (limited to 'core/operators.nom') 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 %) -- cgit v1.2.3