diff options
Diffstat (limited to 'lib/core/operators.nom')
| -rw-r--r-- | lib/core/operators.nom | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/lib/core/operators.nom b/lib/core/operators.nom new file mode 100644 index 0000000..dee76b6 --- /dev/null +++ b/lib/core/operators.nom @@ -0,0 +1,263 @@ +#!/usr/bin/env nomsu -V6.14 +# + This file contains definitions of operators like "+" and "and". + +use "core/metaprogramming" + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test: + assume (all [1 < 2, 2 > 1, 1 <= 2, 2 >= 1, 1 == 1, 1 != 2]) + +# Comparison Operators +($x < $y) compiles to "(\($x as lua expr) < \($y as lua expr))" +($x > $y) compiles to "(\($x as lua expr) > \($y as lua expr))" +($x <= $y) compiles to "(\($x as lua expr) <= \($y as lua expr))" +($x >= $y) compiles to "(\($x as lua expr) >= \($y as lua expr))" +[$a is $b, $a == $b] all compile to "(\($a as lua expr) == \($b as lua expr))" +[$a isn't $b, $a is not $b, $a not= $b, $a != $b] all compile to + "(\($a as lua expr) ~= \($b as lua expr))" + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test: + $x = 10 + assume ($x == 10) + [$x, $y] = [10, 20] + unless (($x == 10) and ($y == 20)): + fail "mutli-assignment failed." + [$x, $y] = [$y, $x] + unless (($y == 10) and ($x == 20)): + fail "swapping vars failed." + $vals = [4, 5] + [$x, $y] = (unpack $vals) + unless (($x == 4) and ($y == 5)): + fail "unpacking failed" + +# Variable assignment operator +($var = $value) compiles to: + lua> (" + local lua = LuaCode() + if \$var.type == "List" then + for i, \$assignment in ipairs(\$var) do + if i > 1 then lua:add(", ") end + local assignment_lua = \($assignment as lua expr) + lua:add(assignment_lua) + if \$assignment.type == 'Var' then + lua:add_free_vars({assignment_lua:text()}) + end + end + lua:add(' = ') + if \$value.type == "List" then + if #\$value ~= #\$var then + compile_error_at(\$value, + "This assignment has too "..(#\$value > #\$var and "many" or "few").." values.", + "Make sure it has the same number of values on the left and right hand side \ + ..of the '=' operator.") + end + for i, \$val in ipairs(\$value) do + if i > 1 then lua:add(", ") end + local val_lua = \($val as lua expr) + lua:add(val_lua) + end + lua:add(";") + else + lua:add(\($value as lua expr), ';') + end + else + local var_lua = \($var as lua expr) + lua:add(var_lua) + if \$var.type == 'Var' then + lua:add_free_vars({var_lua:text()}) + end + lua:add(' = ', \($value as lua expr)) + end + return lua + ") + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test: + [$foozle, $y] = ["outer", "outer"] + externally (set global x local y) means: + external $foozle = "inner" + $y = "inner" + set global x local y + unless (($foozle == "inner") and ($y == "outer")): fail "external failed." +(external $var = $value) compiles to: + $lua = ((SyntaxTree {.type = "Action", .source = $var.source, .1 = $var, .2 = "=", .3 = $value}) as lua) + $lua, remove free vars + return $lua +test: + [$foozle, $y] = ["outer", "outer"] + externally (set global x local y) means: + with external [$foozle]: + $foozle = "inner" + $y = "inner" + set global x local y + unless (($foozle == "inner") and ($y == "outer")): + fail "'with external' failed." + +(with external $externs $body) compiles to: + $body_lua = ($body as lua) + lua> (" + \$body_lua:remove_free_vars(table.map(\$externs, function(v) return \(nomsu environment):compile(v):text() end)) + ") + return $body_lua + +test: + [$x, $y] = [1, 2] + with [$z, $x = 999]: + assume $z == (nil) + $z = 999 + unless ($z == 999): + fail "'with' failed." + + unless ($x == 999): + fail "'with' assignment failed." + + unless ($x == 1): + fail "'with' scoping failed" + + unless ($z == (nil)): + fail "'with' scoping failed" + +(with $assignments $body) compiles to: + lua> (" + local \$defs = LuaCode() + for i, \$item in ipairs(\$assignments) do + if i > 1 then \$defs:add("\\n") end + local item_lua = \($item as lua) + if \$item.type == 'Action' and \$item.stub == '1 =' then + item_lua:remove_free_vars(item_lua.free_vars) + end + \$defs:add("local ", item_lua, ";") + end + ") + + return + Lua (" + do + \$defs + \($body as lua) + end -- 'with' block + ") + +# Math Operators +test: + unless ((5 wrapped around 2) == 1): + fail "mod not working" + +[$x wrapped around $y, $x mod $y] all compile to + "((\($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) +test: + $calls = 0 + (one) means: + external $calls = ($calls + 1) + return 1 + + unless (0 <= (one) <= 2): + fail "Three-way chained comparison failed." + + unless ($calls == 1): + fail "Three-way comparison evaluated middle value multiple times" +($x < $y < $z) parses as ((($a $b $c) -> (($a < $b) and ($b < $c))) $x $y $z) +($x <= $y < $z) parses as ((($a $b $c) -> (($a <= $b) and ($b < $c))) $x $y $z) +($x < $y <= $z) parses as ((($a $b $c) -> (($a < $b) and ($b <= $c))) $x $y $z) +($x <= $y <= $z) parses as ((($a $b $c) -> (($a <= $b) and ($b <= $c))) $x $y $z) +($x > $y > $z) parses as ((($a $b $c) -> (($a > $b) and ($b > $c))) $x $y $z) +($x >= $y > $z) parses as ((($a $b $c) -> (($a >= $b) and ($b > $c))) $x $y $z) +($x > $y >= $z) parses as ((($a $b $c) -> (($a > $b) and ($b >= $c))) $x $y $z) +($x >= $y >= $z) parses as ((($a $b $c) -> (($a >= $b) and ($b >= $c))) $x $y $z) + +# TODO: optimize for common case where x,y,z are all either variables or number literals +# Boolean Operators +test: + (barfer) means (fail "short circuiting failed") + assume (((no) and (barfer)) == (no)) + assume ((no) or (yes)) + assume ((yes) or (barfer)) +($x and $y) compiles to "(\($x as lua expr) and \($y as lua expr))" +($x or $y) compiles to "(\($x as lua expr) or \($y as lua expr))" + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# Bitwise Operators +# TODO: implement OR, XOR, AND for multiple operands? +test: + assume ((~ (~ 5)) == 5) + assume ((1 | 4) == 5) + assume ((1 ~ 3) == 2) + assume ((1 & 3) == 1) + assume ((1 << 2) == 4) + assume ((4 >> 2) == 1) + +# Lua 5.3 introduced bit operators like | and &. Use them when possible, otherwise + fall back to bit.bor(), bit.band(), etc. +lua> "if \((is jit) or ((Lua version) == "Lua 5.2")) then" +[NOT $, ~ $] all compile to "bit.bnot(\($ as lua expr))" +[$x OR $y, $x | $y] all compile to + "bit.bor(\($x as lua expr), \($y as lua expr))" + +[$x XOR $y, $x ~ $y] all compile to + "bit.bxor(\($x as lua expr), \($y as lua expr))" + +[$x AND $y, $x & $y] all compile to + "bit.band(\($x as lua expr), \($y as lua expr))" + +[$x LSHIFT $shift, $x << $shift] all compile to + "bit.lshift(\($x as lua expr), \($shift as lua expr))" + +[$x RSHIFT $shift, $x >> $shift] all compile to + "bit.rshift(\($x as lua expr), \($shift as lua expr))" + +lua> "else" +[NOT $, ~ $] all compile to "~(\($ as lua expr))" +[$x OR $y, $x | $y] all compile to "(\($x as lua expr) | \($y as lua expr))" +[$x XOR $y, $x ~ $y] all compile to "(\($x as lua expr) ~ \($y as lua expr))" +[$x AND $y, $x & $y] all compile to "(\($x as lua expr) & \($y as lua expr))" +[$x LSHIFT $shift, $x << $shift] all compile to + "(\($x as lua expr) << \($shift as lua expr))" + +[$x RSHIFT $shift, $x >> $shift] all compile to + "(\($x as lua expr) >> \($shift as lua expr))" + +lua> "end" + +# Unary operators +test: + assume ((- 5) == -5) + assume ((not (yes)) == (no)) +(- $) compiles to "(- \($ as lua expr))" +(not $) compiles to "(not \($ as lua expr))" +test: + assume ((size of [1, 2, 3]) == 3) + assume ((#[1, 2, 3]) == 3) +[#$list, size of $list] all compile to "(#\($list as lua expr))" +($list is empty) compiles to "(#\($list as lua expr) == 0)" + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# Update operators +test: + $x = 1 + $x += 1 + unless ($x == 2): + fail "+= failed" + $x *= 2 + unless ($x == 4): + fail "*= failed" + wrap $x around 3 + unless ($x == 1): + fail "wrap around failed" +($var += $) parses as ($var = (($var or 0) + $)) +($var -= $) parses as ($var = (($var or 0) - $)) +($var *= $) parses as ($var = (($var or 1) * $)) +($var /= $) parses as ($var = ($var / $)) +($var ^= $) parses as ($var = ($var ^ $)) +($var and= $) parses as ($var = ($var and $)) +($var or= $) parses as ($var = ($var or $)) +(wrap $var around $) parses as ($var = ($var wrapped around $)) |
