aboutsummaryrefslogtreecommitdiff
path: root/lib/core/operators.nom
diff options
context:
space:
mode:
Diffstat (limited to 'lib/core/operators.nom')
-rw-r--r--lib/core/operators.nom263
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 $))