2018-10-30 23:42:04 -07:00
|
|
|
#!/usr/bin/env nomsu -V4.8.10
|
2018-05-15 18:55:55 -07:00
|
|
|
#
|
2018-01-11 18:51:21 -08:00
|
|
|
This file contains definitions of operators like "+" and "and".
|
|
|
|
|
2018-02-02 15:48:28 -08:00
|
|
|
use "core/metaprogramming.nom"
|
2018-06-14 22:17:26 -07:00
|
|
|
use "core/errors.nom"
|
2017-09-21 00:10:26 -07:00
|
|
|
|
2018-11-08 15:23:22 -08:00
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
2018-07-22 16:13:33 -07:00
|
|
|
test:
|
|
|
|
assume (all [1 < 2, 2 > 1, 1 <= 2, 2 >= 1, 1 == 1, 1 != 2])
|
|
|
|
|
2018-01-11 18:51:21 -08:00
|
|
|
# Comparison Operators
|
2018-10-30 23:42:04 -07:00
|
|
|
(%x < %y) compiles to (Lua value "(\(%x as lua expr) < \(%y as lua expr))")
|
|
|
|
(%x > %y) compiles to (Lua value "(\(%x as lua expr) > \(%y as lua expr))")
|
|
|
|
(%x <= %y) compiles to (Lua value "(\(%x as lua expr) <= \(%y as lua expr))")
|
|
|
|
(%x >= %y) compiles to (Lua value "(\(%x as lua expr) >= \(%y as lua expr))")
|
|
|
|
[%a is %b, %a == %b] all compile to (..)
|
2018-06-18 15:44:29 -07:00
|
|
|
Lua value "(\(%a as lua expr) == \(%b as lua expr))"
|
2018-07-17 23:08:13 -07:00
|
|
|
|
2018-10-30 23:42:04 -07:00
|
|
|
[%a isn't %b, %a is not %b, %a not= %b, %a != %b] all compile to (..)
|
2018-06-18 15:44:29 -07:00
|
|
|
Lua value "(\(%a as lua expr) ~= \(%b as lua expr))"
|
2018-07-17 23:08:13 -07:00
|
|
|
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
2018-01-07 16:51:29 -08:00
|
|
|
|
2018-07-22 16:13:33 -07:00
|
|
|
test:
|
|
|
|
%x = 10
|
|
|
|
assume (%x == 10)
|
|
|
|
|
2018-01-23 19:42:01 -08:00
|
|
|
# Variable assignment operator
|
2018-10-30 23:42:04 -07:00
|
|
|
(%var = %value) compiles to:
|
2018-09-14 19:17:09 -07:00
|
|
|
lua> "\
|
2018-11-08 15:23:22 -08:00
|
|
|
..local \%var_lua = \(%var as lua expr)
|
|
|
|
local \%value_lua = \(%value as lua expr)
|
|
|
|
local lua = LuaCode(tree.source, \%var_lua, ' = ', \%value_lua, ';')
|
2018-06-18 15:44:29 -07:00
|
|
|
if \%var.type == 'Var' then
|
2018-11-08 15:23:22 -08:00
|
|
|
lua:add_free_vars({compile(\%var):text()})
|
2018-06-18 15:44:29 -07:00
|
|
|
end
|
2018-09-14 19:17:09 -07:00
|
|
|
return lua"
|
2018-06-18 15:44:29 -07:00
|
|
|
|
2018-07-22 16:13:33 -07:00
|
|
|
test:
|
|
|
|
set {%x:10, %y:20}
|
|
|
|
assume ((%x == 10) and (%y == 20)) or barf "mutli-assignment failed."
|
|
|
|
set {%x:%y, %y:%x}
|
|
|
|
assume ((%y == 10) and (%x == 20)) or barf "swapping vars failed."
|
|
|
|
|
2018-06-18 15:44:29 -07:00
|
|
|
# Simultaneous mutli-assignments like: x,y,z = 1,x,3;
|
2018-10-30 23:42:04 -07:00
|
|
|
(set %assignments) compiles to:
|
2018-09-14 19:17:09 -07:00
|
|
|
assume (%assignments.type is "Dict") or barf "\
|
|
|
|
..Expected a Dict for the assignments part of '<- %' statement, not \%assignments"
|
|
|
|
lua> "\
|
|
|
|
..local lhs, rhs = LuaCode(tree.source), LuaCode(tree.source)
|
2018-06-18 15:44:29 -07:00
|
|
|
for i, item in ipairs(\%assignments) do
|
|
|
|
local \%target, \%value = item[1], item[2]
|
2018-05-27 18:28:23 -07:00
|
|
|
\%value = \%value:map(function(t)
|
2018-10-31 15:54:18 -07:00
|
|
|
if SyntaxTree:is_instance(t) and t.type == "Action" and t.stub == "?" then
|
2018-06-18 15:44:29 -07:00
|
|
|
return \%target
|
2018-05-27 18:28:23 -07:00
|
|
|
end
|
|
|
|
end)
|
2018-06-18 15:44:29 -07:00
|
|
|
local target_lua = \(%target as lua)
|
|
|
|
local value_lua = \(%value as lua)
|
|
|
|
if \%target.type == "Var" then
|
2018-11-02 14:38:24 -07:00
|
|
|
lhs:add_free_vars({target_lua:text()})
|
2018-06-18 15:44:29 -07:00
|
|
|
end
|
|
|
|
if i > 1 then
|
|
|
|
lhs:append(", ")
|
|
|
|
rhs:append(", ")
|
|
|
|
end
|
|
|
|
lhs:append(target_lua)
|
|
|
|
rhs:append(value_lua)
|
|
|
|
end
|
2018-09-14 19:17:09 -07:00
|
|
|
return LuaCode(tree.source, lhs, " = ", rhs, ";")"
|
2018-06-18 15:44:29 -07:00
|
|
|
|
2018-07-17 23:08:13 -07:00
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
2018-06-18 15:44:29 -07:00
|
|
|
|
2018-07-22 16:13:33 -07:00
|
|
|
test:
|
|
|
|
set {%foozle:"outer", %y:"outer"}
|
2018-10-30 23:42:04 -07:00
|
|
|
externally (set global x local y) means:
|
2018-07-22 16:13:33 -07:00
|
|
|
external %foozle = "inner"
|
|
|
|
%y = "inner"
|
2018-07-23 14:40:20 -07:00
|
|
|
|
2018-07-22 16:13:33 -07:00
|
|
|
set global x local y
|
|
|
|
assume ((%foozle == "inner") and (%y == "outer")) or barf "external failed."
|
2018-11-08 15:23:22 -08:00
|
|
|
(external %var = %value) compiles to "\(%var as lua) = \(%value as lua)"
|
2018-05-27 18:28:23 -07:00
|
|
|
|
2018-07-22 16:13:33 -07:00
|
|
|
test:
|
|
|
|
set {%foozle:"outer", %y:"outer"}
|
2018-11-08 15:23:22 -08:00
|
|
|
externally (set global x local y) means:
|
2018-07-22 16:13:33 -07:00
|
|
|
with external [%foozle]:
|
|
|
|
%foozle = "inner"
|
|
|
|
%y = "inner"
|
2018-07-23 14:40:20 -07:00
|
|
|
|
2018-07-22 16:13:33 -07:00
|
|
|
set global x local y
|
2018-09-14 19:17:09 -07:00
|
|
|
assume ((%foozle == "inner") and (%y == "outer")) or barf "\
|
|
|
|
..'with external' failed."
|
2018-10-30 23:42:04 -07:00
|
|
|
(with external %externs %body) compiles to:
|
2018-07-18 01:27:56 -07:00
|
|
|
%body_lua = (%body as lua statements)
|
2018-09-14 19:17:09 -07:00
|
|
|
lua> "\
|
2018-11-08 15:23:22 -08:00
|
|
|
..\%body_lua:remove_free_vars(table.map(\%externs, function(v) return compile(v):text() end))"
|
2018-06-18 15:44:29 -07:00
|
|
|
return %body_lua
|
|
|
|
|
2018-07-22 16:13:33 -07:00
|
|
|
test:
|
|
|
|
set {%x:1, %y:2}
|
|
|
|
with {%z:nil, %x:999}:
|
|
|
|
%z = 999
|
|
|
|
assume (%z == 999) or barf "'with' failed."
|
|
|
|
assume (%x == 999) or barf "'with' assignment failed."
|
2018-07-23 14:40:20 -07:00
|
|
|
|
2018-07-22 16:13:33 -07:00
|
|
|
assume (%x == 1) or barf "'with' scoping failed"
|
|
|
|
assume (%z == (nil)) or barf "'with' scoping failed"
|
2018-10-30 23:42:04 -07:00
|
|
|
(with %assignments %body) compiles to:
|
2018-07-18 01:27:56 -07:00
|
|
|
%lua = (%body as lua statements)
|
2018-09-14 19:17:09 -07:00
|
|
|
lua> "\
|
|
|
|
..local lhs, rhs = LuaCode(tree.source), LuaCode(tree.source)
|
2018-06-18 15:44:29 -07:00
|
|
|
local vars = {}
|
|
|
|
for i, item in ipairs(\%assignments) do
|
|
|
|
local \%target, \%value = item[1], item[2]
|
|
|
|
if not \%target.type == "Var" then
|
|
|
|
error("Invalid target for 'with' assignment: "..tostring(\%target))
|
2018-04-13 15:29:16 -07:00
|
|
|
end
|
2018-06-18 15:44:29 -07:00
|
|
|
local target_lua = \(%target as lua)
|
|
|
|
local value_lua = \(%value as lua)
|
|
|
|
if i > 1 then
|
|
|
|
lhs:append(", ")
|
|
|
|
rhs:append(", ")
|
2018-04-19 17:23:44 -07:00
|
|
|
end
|
2018-06-18 15:44:29 -07:00
|
|
|
lhs:append(target_lua)
|
|
|
|
rhs:append(value_lua)
|
2018-06-18 18:10:59 -07:00
|
|
|
if \%target.type == "Var" then
|
2018-11-02 14:38:24 -07:00
|
|
|
vars[i] = target_lua:text()
|
2018-06-18 18:10:59 -07:00
|
|
|
end
|
2018-06-18 15:44:29 -07:00
|
|
|
end
|
|
|
|
\%lua:remove_free_vars(vars)
|
2018-09-14 19:17:09 -07:00
|
|
|
\%lua:prepend("local ", lhs, " = ", rhs, ";\\n")"
|
2018-07-17 23:08:13 -07:00
|
|
|
|
|
|
|
return (..)
|
2018-09-14 19:17:09 -07:00
|
|
|
Lua "\
|
|
|
|
..do
|
2018-07-20 20:27:15 -07:00
|
|
|
\%lua
|
2018-09-14 19:17:09 -07:00
|
|
|
end -- 'with' block"
|
2018-06-18 15:44:29 -07:00
|
|
|
|
|
|
|
# Math Operators
|
2018-07-22 16:13:33 -07:00
|
|
|
test:
|
|
|
|
assume ((5 wrapped around 2) == 1) or barf "mod not working"
|
2018-10-30 23:42:04 -07:00
|
|
|
[%x wrapped around %y, %x mod %y] all compile to (..)
|
2018-10-29 13:00:08 -07:00
|
|
|
Lua value "((\(%x as lua expr)) % (\(%y as lua expr)))"
|
2018-07-17 23:08:13 -07:00
|
|
|
|
2018-06-18 15:44:29 -07:00
|
|
|
# 3-part chained comparisons
|
|
|
|
# (uses a lambda to avoid re-evaluating middle value, while still being an expression)
|
2018-07-22 16:13:33 -07:00
|
|
|
test:
|
|
|
|
%calls = 0
|
2018-10-30 23:42:04 -07:00
|
|
|
(one) means:
|
2018-07-22 16:13:33 -07:00
|
|
|
external %calls = (%calls + 1)
|
|
|
|
return 1
|
2018-07-23 14:40:20 -07:00
|
|
|
|
2018-07-22 16:13:33 -07:00
|
|
|
assume (0 <= (one) <= 2) or barf "Three-way chained comparison failed."
|
2018-09-14 19:17:09 -07:00
|
|
|
assume (%calls == 1) or barf "\
|
|
|
|
..Three-way comparison evaluated middle value multiple times"
|
2018-10-30 23:42:04 -07:00
|
|
|
(%x < %y < %z) parses as (..)
|
2018-07-26 12:38:17 -07:00
|
|
|
call ([%a, %b, %c] -> ((%a < %b) and (%b < %c))) with [%x, %y, %z]
|
2018-07-18 17:55:29 -07:00
|
|
|
|
2018-10-30 23:42:04 -07:00
|
|
|
(%x <= %y < %z) parses as (..)
|
2018-07-26 12:38:17 -07:00
|
|
|
call ([%a, %b, %c] -> ((%a <= %b) and (%b < %c))) with [%x, %y, %z]
|
2018-07-18 17:55:29 -07:00
|
|
|
|
2018-10-30 23:42:04 -07:00
|
|
|
(%x < %y <= %z) parses as (..)
|
2018-07-26 12:38:17 -07:00
|
|
|
call ([%a, %b, %c] -> ((%a < %b) and (%b <= %c))) with [%x, %y, %z]
|
2018-07-18 17:55:29 -07:00
|
|
|
|
2018-10-30 23:42:04 -07:00
|
|
|
(%x <= %y <= %z) parses as (..)
|
2018-07-26 12:38:17 -07:00
|
|
|
call ([%a, %b, %c] -> ((%a <= %b) and (%b <= %c))) with [%x, %y, %z]
|
2018-07-18 17:55:29 -07:00
|
|
|
|
2018-10-30 23:42:04 -07:00
|
|
|
(%x > %y > %z) parses as (..)
|
2018-07-26 12:38:17 -07:00
|
|
|
call ([%a, %b, %c] -> ((%a > %b) and (%b > %c))) with [%x, %y, %z]
|
2018-07-18 17:55:29 -07:00
|
|
|
|
2018-10-30 23:42:04 -07:00
|
|
|
(%x >= %y > %z) parses as (..)
|
2018-07-26 12:38:17 -07:00
|
|
|
call ([%a, %b, %c] -> ((%a >= %b) and (%b > %c))) with [%x, %y, %z]
|
2018-07-18 17:55:29 -07:00
|
|
|
|
2018-10-30 23:42:04 -07:00
|
|
|
(%x > %y >= %z) parses as (..)
|
2018-07-26 12:38:17 -07:00
|
|
|
call ([%a, %b, %c] -> ((%a > %b) and (%b >= %c))) with [%x, %y, %z]
|
2018-07-18 17:55:29 -07:00
|
|
|
|
2018-10-30 23:42:04 -07:00
|
|
|
(%x >= %y >= %z) parses as (..)
|
2018-07-26 12:38:17 -07:00
|
|
|
call ([%a, %b, %c] -> ((%a >= %b) and (%b >= %c))) with [%x, %y, %z]
|
2018-06-18 15:44:29 -07:00
|
|
|
|
2018-07-17 23:08:13 -07:00
|
|
|
# TODO: optimize for common case where x,y,z are all either variables or number literals
|
2018-06-18 15:44:29 -07:00
|
|
|
# Boolean Operators
|
2018-07-22 16:13:33 -07:00
|
|
|
test:
|
2018-10-30 23:42:04 -07:00
|
|
|
(barfer) means (barf "short circuiting failed")
|
2018-07-22 16:13:33 -07:00
|
|
|
assume (((no) and (barfer)) == (no))
|
|
|
|
assume ((no) or (yes))
|
|
|
|
assume ((yes) or (barfer))
|
2018-10-30 23:42:04 -07:00
|
|
|
(%x and %y) compiles to (Lua value "(\(%x as lua expr) and \(%y as lua expr))")
|
|
|
|
(%x or %y) compiles to (Lua value "(\(%x as lua expr) or \(%y as lua expr))")
|
2018-06-18 15:44:29 -07:00
|
|
|
|
2018-07-30 14:26:55 -07:00
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
2018-06-18 15:44:29 -07:00
|
|
|
# Bitwise Operators
|
2018-07-30 14:26:55 -07:00
|
|
|
# TODO: implement OR, XOR, AND for multiple operands?
|
|
|
|
test:
|
2018-08-29 19:38:14 -07:00
|
|
|
assume ((~ (~ 5)) == 5)
|
2018-07-30 14:26:55 -07:00
|
|
|
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.
|
|
|
|
%use_bitops = ((is jit) or ((Lua version) == "Lua 5.2"))
|
2018-10-30 23:42:04 -07:00
|
|
|
[NOT %, ~ %] all compile to (..)
|
2018-07-30 14:26:55 -07:00
|
|
|
Lua value (..)
|
|
|
|
(%use_bitops and "bit.bnot(\(% as lua expr))") or "~(\(% as lua expr))"
|
|
|
|
|
2018-10-30 23:42:04 -07:00
|
|
|
[%a OR %b, %a | %b] all compile to (..)
|
2018-07-30 14:26:55 -07:00
|
|
|
Lua value (..)
|
2018-09-14 19:17:09 -07:00
|
|
|
(%use_bitops and "bit.bor(\(%a as lua expr), \(%b as lua expr))") or "\
|
|
|
|
..(\(%a as lua expr) | \(%b as lua expr))"
|
2018-07-30 14:26:55 -07:00
|
|
|
|
2018-10-30 23:42:04 -07:00
|
|
|
[%a XOR %b, %a ~ %b] all compile to (..)
|
2018-07-30 14:26:55 -07:00
|
|
|
Lua value (..)
|
2018-09-14 19:17:09 -07:00
|
|
|
(%use_bitops and "bit.bxor(\(%a as lua expr), \(%b as lua expr))") or "\
|
|
|
|
..(\(%a as lua expr) ~ \(%b as lua expr))"
|
2018-07-17 23:08:13 -07:00
|
|
|
|
2018-10-30 23:42:04 -07:00
|
|
|
[%a AND %b, %a & %b] all compile to (..)
|
2018-07-30 14:26:55 -07:00
|
|
|
Lua value (..)
|
2018-09-14 19:17:09 -07:00
|
|
|
(%use_bitops and "bit.band(\(%a as lua expr), \(%b as lua expr))") or "\
|
|
|
|
..(\(%a as lua expr) & \(%b as lua expr))"
|
2018-07-17 23:08:13 -07:00
|
|
|
|
2018-10-30 23:42:04 -07:00
|
|
|
[%x LSHIFT %shift, %x << %shift] all compile to (..)
|
2018-07-30 14:26:55 -07:00
|
|
|
Lua value (..)
|
2018-09-14 19:17:09 -07:00
|
|
|
(%use_bitops and "bit.lshift(\(%x as lua expr), \(%shift as lua expr))") or "\
|
|
|
|
..(\(%x as lua expr) << \(%shift as lua expr))"
|
2018-06-18 15:44:29 -07:00
|
|
|
|
2018-10-30 23:42:04 -07:00
|
|
|
[%x RSHIFT %shift, %x >> %shift] all compile to (..)
|
2018-07-30 14:26:55 -07:00
|
|
|
Lua value (..)
|
2018-09-14 19:17:09 -07:00
|
|
|
(%use_bitops and "bit.rshift(\(%x as lua expr), \(%shift as lua expr))") or "\
|
|
|
|
..(\(%x as lua expr) >> \(%shift as lua expr))"
|
2018-07-17 23:08:13 -07:00
|
|
|
|
2018-06-18 15:44:29 -07:00
|
|
|
# Unary operators
|
2018-07-22 16:13:33 -07:00
|
|
|
test:
|
|
|
|
assume ((- 5) == -5)
|
|
|
|
assume ((not (yes)) == (no))
|
2018-10-30 23:42:04 -07:00
|
|
|
(- %) compiles to (Lua value "(- \(% as lua expr))")
|
|
|
|
(not %) compiles to (Lua value "(not \(% as lua expr))")
|
2018-07-30 15:05:41 -07:00
|
|
|
|
2018-07-20 20:27:15 -07:00
|
|
|
test:
|
2018-08-29 19:38:14 -07:00
|
|
|
assume ((size of [1, 2, 3]) == 3)
|
2018-10-30 23:42:04 -07:00
|
|
|
[size of %list, size of %list, size of %list, size of %list] all compile to (..)
|
2018-07-22 15:01:05 -07:00
|
|
|
Lua value "(#\(%list as lua expr))"
|
|
|
|
|
2018-10-30 23:42:04 -07:00
|
|
|
(%list is empty) compiles to (Lua value "(#\(%list as lua expr) == 0)")
|
2018-06-18 15:44:29 -07:00
|
|
|
|
2018-07-17 23:08:13 -07:00
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
2017-09-21 00:10:26 -07:00
|
|
|
|
2018-01-23 19:22:20 -08:00
|
|
|
# Update operators
|
2018-07-22 16:13:33 -07:00
|
|
|
test:
|
|
|
|
%x = 1
|
|
|
|
%x += 1
|
|
|
|
assume (%x == 2) or barf "+= failed"
|
|
|
|
%x *= 2
|
|
|
|
assume (%x == 4) or barf "*= failed"
|
|
|
|
wrap %x around 3
|
|
|
|
assume (%x == 1) or barf "wrap around failed"
|
2018-10-30 23:42:04 -07:00
|
|
|
(%var += %) parses as (%var = (%var + %))
|
|
|
|
(%var -= %) parses as (%var = (%var - %))
|
|
|
|
(%var *= %) parses as (%var = (%var * %))
|
|
|
|
(%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 %))
|