aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcompile_lib.sh5
-rw-r--r--lib/collections.nom180
-rw-r--r--lib/control_flow.nom99
-rw-r--r--lib/math.nom46
-rw-r--r--lib/metaprogramming.nom30
-rw-r--r--lib/operators.nom69
-rw-r--r--lib/text.nom9
-rw-r--r--nomsu.lua3
-rwxr-xr-xnomsu.moon6
-rw-r--r--tests/all.nom28
-rw-r--r--tests/collections.nom43
-rw-r--r--tests/control_flow.nom191
-rw-r--r--tests/math.nom17
-rw-r--r--tests/metaprogramming.nom55
-rw-r--r--tests/operators.nom76
-rw-r--r--tests/text.nom13
-rw-r--r--utils.lua4
17 files changed, 681 insertions, 193 deletions
diff --git a/compile_lib.sh b/compile_lib.sh
index c8d573e..8cdfcdb 100755
--- a/compile_lib.sh
+++ b/compile_lib.sh
@@ -24,3 +24,8 @@ for file in $(cat lib/core.nom | lua -e "for filename in io.read('*a'):gmatch('u
./nomsu.moon -c $file
echo "done."
done
+for file in $(cat tests/all.nom | lua -e "for filename in io.read('*a'):gmatch('run file \"([^\"]*)\"') do print(filename) end") ; do
+ printf "Compiling $file ..."
+ ./nomsu.moon -c $file
+ echo "done."
+done
diff --git a/lib/collections.nom b/lib/collections.nom
index c31bbee..ac1b019 100644
--- a/lib/collections.nom
+++ b/lib/collections.nom
@@ -9,68 +9,55 @@ use "lib/operators.nom"
# List/dict functions:
# Indexing
-compile [..]
- %index st to last in %list, %index nd to last in %list, %index rd to last in %list
- %index th to last in %list
-..to {expr:"utils.nth_to_last(\(%list as lua expr), \(%index as lua expr))"}
+immediately
+ compile [..]
+ %index st to last in %list, %index nd to last in %list, %index rd to last in %list
+ %index th to last in %list
+ ..to {expr:"utils.nth_to_last(\(%list as lua expr), \(%index as lua expr))"}
-parse [first in %list, first %list] as: 1 st in %list
-parse [last in %list, last %list] as: 1 st to last in %list
+immediately
+ parse [first in %list, first %list] as: 1 st in %list
+ parse [last in %list, last %list] as: 1 st to last in %list
# Membership testing
-action [%item is in %list, %list contains %item, %list has %item]
- for %key = %value in %list
- if (%key is %item): return (yes)
- return (no)
-
-action [..]
- %item isn't in %list, %item is not in %list
- %list doesn't contain %item, %list does not contain %item
- %list doesn't have %item, %list does not have %item
-..
- for %key = %value in %list
- if (%key is %item): return (no)
- return (yes)
-
-# Note: it's important to have the space after "[" to prevent confusion if %index is a string
-compile [%list has key %index, %list has index %index] to {..}
- expr: ".."
- ((\(%list as lua expr))[ \(%index as lua expr)] ~= nil)
-
-# Note: it's important to have the space after "[" to prevent confusion if %index is a string
-compile [..]
- %list doesn't have key %index, %list does not have key %index
- %list doesn't have index %index, %list does not have index %index
-..to {expr:"((\(%list as lua expr))[ \(%index as lua expr)] ~= nil)"}
-
-compile [length of %list, size of %list, size %list, number of %list, len %list] to
- {expr:"utils.size(\(%list as lua expr))"}
-
-compile [append %item to %list, add %item to %list] to
- {statements:"table.insert(\(%list as lua expr), \(%item as lua expr))"}
-
-compile [pop from %list, remove last from %list] to
- {statements:"table.remove(\(%list as lua expr))"}
+immediately
+ action [%item is in %list, %list contains %item, %list has %item]
+ for %key = %value in %list
+ if (%key is %item): return (yes)
+ return (no)
+
+ action [..]
+ %item isn't in %list, %item is not in %list
+ %list doesn't contain %item, %list does not contain %item
+ %list doesn't have %item, %list does not have %item
+ ..
+ for %key = %value in %list
+ if (%key is %item): return (no)
+ return (yes)
-compile [remove index %index from %list] to
- {statements:"table.remove(\(%list as lua expr), \(%index as lua expr))"}
+immediately
+ # Note: it's important to have the space after "[" to prevent confusion if %index is a string
+ compile [%list has key %index, %list has index %index] to {..}
+ expr: ".."
+ ((\(%list as lua expr))[ \(%index as lua expr)] ~= nil)
+ # Note: it's important to have the space after "[" to prevent confusion if %index is a string
+ compile [..]
+ %list doesn't have key %index, %list does not have key %index
+ %list doesn't have index %index, %list does not have index %index
+ ..to {expr:"((\(%list as lua expr))[ \(%index as lua expr)] == nil)"}
-action [flatten %lists]
- %flat <- []
- for %list in %lists
- for %item in %list
- add %item to %flat
- return %flat
+ compile [length of %list, size of %list, size %list, number of %list, len %list] to
+ {expr:"utils.size(\(%list as lua expr))"}
-action [entries in %dict]
- [{key:%k, value:%v} for %k=%v in %dict]
+ compile [append %item to %list, add %item to %list] to
+ {statements:"table.insert(\(%list as lua expr), \(%item as lua expr))"}
-action [keys in %dict]
- [%k for %k=%v in %dict]
+ compile [pop from %list, remove last from %list] to
+ {statements:"table.remove(\(%list as lua expr))"}
-action [values in %dict]
- [%v for %k=%v in %dict]
+ compile [remove index %index from %list] to
+ {statements:"table.remove(\(%list as lua expr), \(%index as lua expr))"}
# List Comprehension
immediately
@@ -98,7 +85,7 @@ immediately
(function()
local comprehension = {};
for \(%key as lua expr), \(%value as lua expr) in pairs(\(%iterable as lua expr)) do
- comprehension[i] = \(%expression as lua expr)
+ table.insert(comprehension, \(%expression as lua expr));
end
return comprehension;
end)()
@@ -136,38 +123,59 @@ immediately
return comprehension;
end)()
+immediately
+ action [%lists flattened]
+ %flat <- []
+ for %list in %lists
+ for %item in %list
+ add %item to %flat
+ return %flat
+
+ parse [entries in %dict] as: {key:%k, value:%v} for %k = %v in %dict
+ parse [keys in %dict] as: %k for %k = %v in %dict
+ parse [values in %dict] as: %v for %k = %v in %dict
+
# Sorting:
-compile [sort %items] to {statements:"table.sort(\(%items as lua expr))"}
-compile [sort %items by %key_expr] to {..}
- statements: ".."
- utils.sort(\(%items as lua expr), function(\(\% as lua expr))
- return \(%key_expr as lua expr);
- end)
-
-action [%items sorted]
- %copy <- (% for all %items)
- sort %copy
- return %copy
-action [%items sorted by %key]
- %copy <- (% for all %items)
- sort %copy by %key
- return %copy
-
-action [unique %items]
- [%k for %k=%v in (%=(yes) for all %items)]
-
-# Metatable stuff
-compile [set %dict's metatable to %metatable] to {..}
- statements: "setmetatable(\(%dict as lua expr), \(%metatable as lua expr));"
-
-compile [new counter] to {expr:"setmetatable({}, {__index=function() return 0; end})"}
-
-compile [new default dict] to {..}
- expr: ".."
- setmetatable({}, {__index=function(self, key)
- t = {};
- self[key] = t;
- return t;
- end})
+immediately
+ compile [sort %items] to {statements:"table.sort(\(%items as lua expr))"}
+ compile [sort %items by %key_expr] to {..}
+ statements: ".."
+ utils.sort(\(%items as lua expr), function(\(\% as lua expr))
+ return \(%key_expr as lua expr);
+ end)
+
+immediately
+ action [%items sorted, sorted %items]
+ %copy <- (% for all %items)
+ sort %copy
+ return %copy
+ action [%items sorted by %key]
+ %copy <- (% for all %items)
+ sort %copy by %key
+ return %copy
+
+ action [unique %items]
+ %unique <- []
+ %seen <- {}
+ for all %items
+ unless: % in %seen
+ add % to %unique
+ (% in %seen) <- (yes)
+ return %unique
+
+immediately
+ # Metatable stuff
+ compile [set %dict's metatable to %metatable] to {..}
+ statements: "setmetatable(\(%dict as lua expr), \(%metatable as lua expr));"
+
+ compile [new counter] to {expr:"setmetatable({}, {__index=function() return 0; end})"}
+
+ compile [new default dict] to {..}
+ expr: ".."
+ setmetatable({}, {__index=function(self, key)
+ t = {};
+ self[key] = t;
+ return t;
+ end})
# TODO: maybe make a generator/coroutine?
diff --git a/lib/control_flow.nom b/lib/control_flow.nom
index a6f460a..cfaa7ad 100644
--- a/lib/control_flow.nom
+++ b/lib/control_flow.nom
@@ -70,9 +70,9 @@ immediately
# GOTOs
immediately
compile [=== %label ===, --- %label ---, *** %label ***] to {..}
- statements:"::label_\(nomsu "var_to_lua_identifier" [%label])::;"
+ statements:"::label_\(%label as lua identifier)::;"
compile [go to %label] to {..}
- statements:"goto label_\(nomsu "var_to_lua_identifier" [%label]);"
+ statements:"goto label_\(%label as lua identifier);"
# Basic loop control
immediately
@@ -98,14 +98,14 @@ immediately
# While loops
immediately
- compile [do next repetition] to {statements:"goto continue_repeat;"}
+ compile [do next repeat] to {statements:"goto continue_repeat;"}
compile [stop repeating] to {statements:"goto stop_repeat;"}
compile [repeat while %condition %body] to
%body_lua <- (%body as lua)
%body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");")
if %body has subtree % where
- ((%'s "type") = "FunctionCall") and ((%'s "stub") is "do next repetition")
- ..: <- %body_statments + "\n::continue_repeat::;"
+ ((%'s "type") = "FunctionCall") and ((%'s "stub") is "do next repeat")
+ ..: %body_statments +<- "\n::continue_repeat::;"
%code <- ".."
while \(%condition as lua expr) do
\%body_statements
@@ -128,8 +128,8 @@ immediately
%body_lua <- (%body as lua)
%body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");")
if %body has subtree % where
- ((%'s "type") = "FunctionCall") and ((%'s "stub") is "do next repetition")
- ..: <- %body_statements + "\n::continue_repeat::;"
+ ((%'s "type") = "FunctionCall") and ((%'s "stub") is "do next repeat")
+ ..: %body_statements +<- "\n::continue_repeat::;"
%code <- ".."
for i=1,\(%n as lua expr) do
\%body_statements
@@ -147,9 +147,9 @@ immediately
# For loop control flow:
immediately
compile [stop %var] to {..}
- statements:"goto stop_\(nomsu "var_to_lua_identifier" [%var]);"
+ statements:"goto stop_\(%var as lua identifier);"
compile [do next %var] to {..}
- statements:"goto continue_\(nomsu "var_to_lua_identifier" [%var]);"
+ statements:"goto continue_\(%var as lua identifier);"
# Numeric range for loops
immediately
@@ -163,7 +163,7 @@ immediately
((%'s "type") = "FunctionCall") and
((%'s "stub") is "do next %") and
((3rd in (%'s "value"))'s "value") is (%var's "value")
- ..: <- %body_statements + "\n::continue_\(nomsu "var_to_lua_identifier" [%var])::;"
+ ..: %body_statements +<- "\n::continue_\(%var as lua identifier)::;"
# This uses Lua's approach of only allowing loop-scoped variables in a loop
assume ((%var's "type") is "Var") or barf "Loop expected variable, not: \(%var's source code)"
@@ -180,7 +180,7 @@ immediately
%code <- ".."
do -- scope for stopping for-loop
\%code
- ::stop_\(nomsu "var_to_lua_identifier" [%var])::;
+ ::stop_\(%var as lua identifier)::;
end -- end of scope for stopping for-loop
return {statements:%code, locals:%body_lua's "locals"}
@@ -202,7 +202,7 @@ immediately
((%'s "type") = "FunctionCall") and
((%'s "stub") is "do next %") and
((3rd in (%'s "value"))'s "value") is (%var's "value")
- ..: <- %body_statements + "\n::continue_\(nomsu "var_to_lua_identifier" [%var])::;"
+ ..: %body_statements +<- "\n::continue_\(%var as lua identifier)::;"
# This uses Lua's approach of only allowing loop-scoped variables in a loop
assume ((%var's "type") is "Var") or barf "Loop expected variable, not: \(%var's source code)"
%code <- ".."
@@ -217,7 +217,7 @@ immediately
%code <- ".."
do -- scope for stopping for-loop
\%code
- ::stop_\(nomsu "var_to_lua_identifier" [%var])::;
+ ::stop_\(%var as lua identifier)::;
end -- end of scope for stopping for-loop
return {statements:%code, locals:%body_lua's "locals"}
@@ -232,13 +232,13 @@ immediately
((%'s "type") = "FunctionCall") and
((%'s "stub") is "do next %") and
((3rd in (%'s "value"))'s "value") is (%key's "value")
- ..: <- %body_statements + "\n::continue_\(nomsu "var_to_lua_identifier" [%key])::;"
+ ..: %body_statements +<- "\n::continue_\(%key as lua identifier)::;"
if %body has subtree % where
((%'s "type") = "FunctionCall") and
((%'s "stub") is "do next %") and
((3rd in (%'s "value"))'s "value") is (%value's "value")
- ..: <- %body_statements + "\n::continue_\(nomsu "var_to_lua_identifier" [%value])::;"
+ ..: %body_statements +<- "\n::continue_\(%value as lua identifier)::;"
# This uses Lua's approach of only allowing loop-scoped variables in a loop
assume ((%key's "type") is "Var") or barf "Loop expected variable, not: \(%key's source code)"
@@ -253,13 +253,13 @@ immediately
((%'s "type") = "FunctionCall") and
((%'s "stub") is "stop %") and
((2nd in (%'s "value"))'s "value") is (%key's "value")
- ..: <- %stop_labels + "\n::stop_\(nomsu "var_to_lua_identifier" [%key])::;"
+ ..: %stop_labels +<- "\n::stop_\(%key as lua identifier)::;"
if %body has subtree % where
((%'s "type") = "FunctionCall") and
((%'s "stub") is "stop %") and
((2nd in (%'s "value"))'s "value") is (%value's "value")
- ..: <- %stop_labels + "\n::stop_\(nomsu "var_to_lua_identifier" [%value])::;"
+ ..: %stop_labels +<- "\n::stop_\(%value as lua identifier)::;"
if: %stop_labels is not ""
%code <- ".."
@@ -290,7 +290,7 @@ immediately
assume %condition or barf ".."
Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else"
if: %action is (nil)
- lua> "table.insert(\%fallthroughs, \%condition);"
+ lua> "table.insert(\%fallthroughs, \(%condition as lua expr));"
do next %func_call
%action <- (%action as lua)
%action_statements <- ((%action's "statements") or "\(%action's "expr");")
@@ -298,19 +298,19 @@ immediately
lua> "table.insert(\%locals, \%local);"
if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'"
- <- %code + ".."
+ assume (not %is_first) or barf "'else' clause cannot be first in 'when % = ?' block"
+ %code +<- ".."
else
\%action_statements
%seen_else <- (yes)
..else
assume (not %seen_else) or barf "'else' clause needs to be last in 'when' block"
- %condition <- (%condition as lua expr)
- for all %fallthroughs
- <- %condition + " or \(% as lua)"
- <- %code + ".."
+ lua> "table.insert(\%fallthroughs, \(%condition as lua expr));"
+ %condition_code <- (%fallthroughs joined with " or ")
+ %code +<- ".."
- \("if" if %is_first else "elseif") \%condition then
+ \("if" if %is_first else "elseif") \%condition_code then
\%action_statements
%fallthroughs <- []
@@ -318,7 +318,7 @@ immediately
assume (%fallthroughs = []) or barf "Unfinished fallthrough conditions in 'when' block"
if: %code is not ""
- <- %code + "\nend"
+ %code +<- "\nend"
lua> "utils.deduplicate(\%locals);"
return {statements:%code, locals:%locals}
@@ -340,7 +340,7 @@ immediately
assume %condition or barf ".."
Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else"
if: %action is (nil)
- lua> "table.insert(\%fallthroughs, \%condition)"
+ lua> "table.insert(\%fallthroughs, \(%condition as lua expr))"
do next %func_call
%action <- (%action as lua)
@@ -350,7 +350,7 @@ immediately
if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'"
assume (not %is_first) or barf "'else' clause cannot be first in 'when % = ?' block"
- <- %code + ".."
+ %code +<- ".."
else
\%action_statements
@@ -358,17 +358,14 @@ immediately
%seen_else <- (yes)
..else
assume (not %seen_else) or barf "'else' clause needs to be last in 'when % = ?' block"
- %clause <- ""
- if: ((%condition's "type") is "Text") or ((%condition's "type") is "Number")
- %clause <- "branch_value == (\(%condition as lua expr))"
- ..else
- %clause <- "utils.equivalent(branch_value, \(%condition as lua expr))"
- for all %fallthroughs
+ lua> "table.insert(\%fallthroughs, \(%condition as lua expr));"
+ for %i = % in %fallthroughs
if: ((%'s "type") is "Text") or ((%'s "type") is "Number")
- <- %clause + " or branch_value == (\(%condition as lua expr))"
+ (%i'th in %fallthroughs) <- "branch_value == \%"
..else
- <- %clause + " or utils.equivalent(branch_value, \(%condition as lua expr))"
- <- %code + ".."
+ (%i'th in %fallthroughs) <- "utils.equivalent(branch_value, \%)"
+ %clause <- (%fallthroughs joined with " or ")
+ %code +<- ".."
\("if" if %is_first else "elseif") \%clause then
\%action_statements
@@ -379,7 +376,7 @@ immediately
assume (%fallthroughs = []) or barf "Unfinished fallthrough conditions in 'when' block"
assume (%code is not "") or barf "No body for 'when % = ?' block!"
unless %seen_else
- <- %code + "\nend"
+ %code +<- "\nend"
%code <- ".."
do --when % = ?
local branch_value = \(%branch_value as lua expr);\
@@ -391,14 +388,16 @@ immediately
# Try/except
immediately
compile [..]
- try %action and if it succeeds %success or if it fails %fallback
- try %action and if it fails %fallback or if it succeeds %success
+ try %action and if it succeeds %success or if it barfs %fallback
+ try %action and if it barfs %fallback or if it succeeds %success
..to
%locals <- []
- %action <- (%action as lua)
+ %action_lua <- (%action as lua)
+ %success_lua <- (%success as lua)
+ %fallback_lua <- (%fallback as lua)
%fallback <- (%fallback as lua)
- for %sub_locals in [%action's "locals", %success's "locals", %fallback's "locals"]
- for %local in %sub_locals
+ for %block in [%action_lua, %success_lua, %fallback_lua]
+ for %local in ((%block's "locals") or [])
lua> "table.insert(\%locals, \%local);"
lua> "utils.deduplicate(\%locals);"
return {..}
@@ -407,26 +406,26 @@ immediately
do
local fell_through = false;
local ok, ret = pcall(function()
- \((%action's "statements") or "\(%action's "expr");")
+ \((%action_lua's "statements") or "\(%action_lua's "expr");")
fell_through = true;
end);
if ok then
- \((%success's "statements") or "\(%success's "expr");")
+ \((%success_lua's "statements") or "\(%success_lua's "expr");")
end
if not ok then
- \((%fallback's "statements") or "\(%fallback's "expr");")
+ \((%fallback_lua's "statements") or "\(%fallback_lua's "expr");")
elseif not fell_through then
return ret;
end
end
parse [try %action] as
try %action and if it succeeds: do nothing
- ..or if it fails: do nothing
- parse [try %action and if it fails %fallback] as
+ ..or if it barfs: do nothing
+ parse [try %action and if it barfs %fallback] as
try %action and if it succeeds: do nothing
- ..or if it fails %fallback
+ ..or if it barfs %fallback
parse [try %action and if it succeeds %success] as
- try %action and if it succeeds %success or if it fails: do nothing
+ try %action and if it succeeds %success or if it barfs: do nothing
# Do/finally:
immediately
@@ -441,7 +440,7 @@ immediately
compile [do %action then always %final_action] to
%action <- (%action as lua)
- %final_action <- (%action as lua)
+ %final_action <- (%final_action as lua)
%locals <- []
for %sub_locals in [%action's "locals", %final_action's "locals"]
for %local in %sub_locals
diff --git a/lib/math.nom b/lib/math.nom
index ea1769a..13c6cc3 100644
--- a/lib/math.nom
+++ b/lib/math.nom
@@ -2,6 +2,8 @@
This file defines some common math literals and functions
use "lib/metaprogramming.nom"
+use "lib/text.nom"
+use "lib/operators.nom"
use "lib/control_flow.nom"
# Literals:
@@ -10,10 +12,10 @@ compile [not a number, NaN, nan] to {expr:"(0/0)"}
compile [pi, Pi, PI] to {expr:"math.pi"}
compile [tau, Tau, TAU] to {expr:"(2*math.pi)"}
compile [golden ratio] to {expr:"((1+math.sqrt(5))/2)"}
-compile [e] to {expr:"math.e"}
+compile [e] to {expr:"math.exp(1)"}
# Functions:
-compile [% as number] to {expr:"tonumber(\(% as lua expr))"}
+compile [% as a number] to {expr:"tonumber(\(% as lua expr))"}
compile [absolute value %, | % |, abs %] to {expr:"math.abs(\(% as lua expr))"}
compile [square root %, √%, sqrt %] to {expr:"math.sqrt(\(% as lua expr))"}
compile [sine %, sin %] to {expr:"math.sin(\(% as lua expr))"}
@@ -36,24 +38,32 @@ action [%n to the nearest %rounder]
=lua "(\%rounder)*math.floor((\%n / \%rounder) + .5)"
# Any/all/none
-compile [all of %items, all %items] to {..}
- expr:
- "(\(joined ((% as lua expr) for all (%items' "value")) with " and "))"
- ..if ((%items' "type") is "List") else "utils.all(\(%items as lua expr))"
+compile [all of %items, all %items] to
+ unless: (%items' "type") is "List"
+ return {expr:"utils.all(\(%items as lua expr))"}
+ %clauses <- []
+ for all (%items' "value"): lua> "table.insert(\%clauses, \(% as lua expr));"
+ return {expr:"(\(%clauses joined with " and "))"}
parse [not all of %items, not all %items] as: not (all of %items)
-compile [any of %items, any %items] to {..}
- expr:
- "(\(joined ((% as lua expr) for all (%items' "value")) with " or "))"
- ..if ((%items' "type") is "List") else "utils.any(\(%items as lua expr))"
+compile [any of %items, any %items] to
+ unless: (%items' "type") is "List"
+ return {expr:"utils.any(\(%items as lua expr))"}
+ %clauses <- []
+ for all (%items' "value"): lua> "table.insert(\%clauses, \(% as lua expr));"
+ return {expr:"(\(%clauses joined with " or "))"}
parse [none of %items, none %items] as: not (any of %items)
-compile [sum of %items, sum %items] to {..}
- expr:
- "(\(joined ((% as lua expr) for all (%items' "value")) with " + "))"
- ..if ((%items' "type") is "List") else "utils.sum(\(%items as lua expr))"
-compile [product of %items, product %items] to {..}
- expr:
- "(\(joined ((% as lua expr) for all (%items' "value")) with " * "))"
- ..if ((%items' "type") is "List") else "utils.product(\(%items as lua expr))"
+compile [sum of %items, sum %items] to
+ unless: (%items' "type") is "List"
+ return {expr:"utils.sum(\(%items as lua expr))"}
+ %clauses <- []
+ for all (%items' "value"): lua> "table.insert(\%clauses, \(% as lua expr));"
+ return {expr:"(\(%clauses joined with " + "))"}
+compile [product of %items, product %items] to
+ unless: (%items' "type") is "List"
+ return {expr:"utils.product(\(%items as lua expr))"}
+ %clauses <- []
+ for all (%items' "value"): lua> "table.insert(\%clauses, \(% as lua expr));"
+ return {expr:"(\(%clauses joined with " * "))"}
action [avg of %items, average of %items]
=lua "(utils.sum(\%items)/#\%items)"
compile [min of %items, smallest of %items, lowest of %items] to {..}
diff --git a/lib/metaprogramming.nom b/lib/metaprogramming.nom
index f80c144..8f2bde9 100644
--- a/lib/metaprogramming.nom
+++ b/lib/metaprogramming.nom
@@ -136,10 +136,9 @@ immediately
compile [repr %obj] to {expr:"repr(\(%obj as lua expr))"}
compile [type of %obj] to {expr:"type(\(%obj as lua expr))"}
-compile [nomsu] to {expr:"nomsu"}
-
-compile [nomsu's %key] to {expr:"nomsu[\(%key as lua expr)]"}
-compile [nomsu %method %args] to {expr:"nomsu[\(%method as lua expr)](nomsu, unpack(\(%args as lua expr)))"}
+immediately
+ compile [nomsu] to {expr:"nomsu"}
+ compile [%var as lua identifier] to {expr:"nomsu:var_to_lua_identifier(\(%var as lua expr))"}
action [action %names metadata]
=lua "ACTION_METADATA[ACTIONS[\%names]]"
@@ -155,9 +154,10 @@ action [help %action]
end
# Compiler tools
-parse [run %code] as: nomsu "run" [%code]
-parse [enable debugging] as: lua> "nomsu.debug = true"
-parse [disable debugging] as: lua> "nomsu.debug = false"
+immediately
+ compile [run %code] to {expr: "nomsu:run(\(%code as lua expr), '\(!! code location !!)')"}
+ parse [enable debugging] as: lua> "nomsu.debug = true;"
+ parse [disable debugging] as: lua> "nomsu.debug = false;"
immediately
compile [say %message] to
@@ -177,12 +177,20 @@ immediately
# Error functions
immediately
- compile [barf!] to {statements:"error(nil, 0);"}
+ compile [barf] to {statements:"error(nil, 0);"}
compile [barf %msg] to {statements:"error(\(%msg as lua expr), 0);"}
- compile [assume %condition] to {..}
- statements:"if not \(%condition as lua expr) then error('Assumption failed: '..\%condition:get_src(), 0); end"
+ compile [assume %condition] to
+ lua> "local \%assumption = 'Assumption failed: '..\%condition:get_src();"
+ return {..}
+ statements:".."
+ if not \(%condition as lua expr) then
+ error(\(repr %assumption), 0);
+ end
compile [assume %condition or barf %msg] to {..}
- statements:"if not \(%condition as lua expr) then error(\(%msg as lua expr), 0); end"
+ statements:".."
+ if not \(%condition as lua expr) then
+ error(\(%msg as lua expr), 0);
+ end
# Literals
immediately
diff --git a/lib/operators.nom b/lib/operators.nom
index 8be0ea0..670e7b7 100644
--- a/lib/operators.nom
+++ b/lib/operators.nom
@@ -36,6 +36,7 @@ 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))"}
+ # TODO: optimize case of [%x,%y] = [1,2]
compile [%a is %b, %a = %b, %a == %b] to
lua> ".."
local safe = {Text=true, Number=true};
@@ -69,6 +70,30 @@ immediately
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)"
@@ -76,6 +101,21 @@ immediately
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 <- []
@@ -118,21 +158,6 @@ immediately
%s
end]]):format(locals_code, declaration_code, \%body_lua.statements or (\%body_lua.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..';')"}
-
immediately
# Math Operators
compile [%x + %y] to {expr:"(\(%x as lua expr) + \(%y as lua expr))"}
@@ -174,11 +199,11 @@ immediately
# Update operators
immediately
- parse [<- %var + %] as: %var <- (%var + %)
- parse [<- %var - %] as: %var <- (%var - %)
- parse [<- %var * %] as: %var <- (%var * %)
- parse [<- %var / %] as: %var <- (%var / %)
- parse [<- %var ^ %] as: %var <- (%var ^ %)
- parse [<- %var and %] as: %var <- (%var and %)
- parse [<- %var or %] as: %var <- (%var or %)
+ 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 %)
diff --git a/lib/text.nom b/lib/text.nom
index cbd0b3f..cd424b9 100644
--- a/lib/text.nom
+++ b/lib/text.nom
@@ -13,14 +13,15 @@ action [%texts joined with %glue]
parse [joined %texts, %texts joined] as: %texts joined with ""
compile [capitalized %text, %text capitalized] to
- {expr:"(\(%text as lua expr)):gsub('%l', string.upper, 1)"}
+ {expr:"((\(%text as lua expr)):gsub('%l', string.upper, 1))"}
compile [%text with %sub instead of %patt, %text s/%patt/%sub] to
{expr:"((\(%text as lua expr)):gsub(\(%patt as lua expr), \(%sub as lua expr)))"}
-compile [indented %text, %text indented] to {expr:"\%text:gsub('\\n','\\n'..(' '))"}
+# TODO: figure out whether indent/dedent should affect first line
+compile [indented %text, %text indented] to {expr:"((\%text):gsub('\\n','\\n'..(' ')))"}
compile [dedented %obj, %obj dedented] to {expr:"nomsu:dedent(\(%obj as lua expr))"}
-compile [%text indented %n times] to {expr:"\%text:gsub('\\n','\\n'..(' '):rep(\%n))"}
+compile [%text indented %n times] to {expr:"((\%text):gsub('\\n','\\n'..(' '):rep(\%n)))"}
# Text literals
lua> ".."
@@ -31,7 +32,7 @@ lua> ".."
};
for name, e in pairs(escapes) do
local lua = "'"..e.."'";
- nomsu:define_compile_action(name, \(!! code location !!), function() return {expr=text}; end);
+ nomsu:define_compile_action(name, \(!! code location !!), function() return {expr=lua}; end);
end
local colors = {
["reset color"]="\\\\27[0m", bright="\\\\27[1m", dim="\\\\27[2m", underscore="\\\\27[4m",
diff --git a/nomsu.lua b/nomsu.lua
index afa4cb0..d2acc1a 100644
--- a/nomsu.lua
+++ b/nomsu.lua
@@ -1311,6 +1311,9 @@ do
self:define_compile_action("immediately %block", get_line_no(), function(_block)
local lua = nomsu:tree_to_lua(_block)
local lua_code = lua.statements or (lua.expr .. ";")
+ if lua.locals and #lua.locals > 0 then
+ lua_code = "local " .. tostring(concat(lua.locals, ", ")) .. ";\n" .. tostring(lua_code)
+ end
nomsu:run_lua(lua_code)
return {
statements = "if IMMEDIATE then\n" .. tostring(lua_code) .. "\nend",
diff --git a/nomsu.moon b/nomsu.moon
index 491a584..e2e2f10 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -29,6 +29,7 @@ do
if type(i) == 'number' then return string.sub(@, i, i)
elseif type(i) == 'table' then return string.sub(@, i[1], i[2])
else return string[i]
+ -- Can't use this because it breaks some LPEG stuff
--STRING_METATABLE.__mul = (other)=> string.rep(@, other)
-- TODO:
@@ -39,6 +40,7 @@ do
-- Add compiler options for optimization level (compile-fast vs. run-fast, etc.)
-- Do a pass on all actions to enforce parameters-are-nouns heuristic
-- Maybe do some sort of lazy definitions of actions that defer until they're used in code
+-- Add a ((%x foo %y) where {x:"asdf", y:"fdsa"}) compile-time action for substitution
lpeg.setmaxstack 10000 -- whoa
{:P,:R,:V,:S,:Cg,:C,:Cp,:B,:Cmt} = lpeg
@@ -223,6 +225,7 @@ class NomsuCompiler
-- TODO: repair
error("Not currently functional.", 0)
+ -- TODO: figure out whether indent/dedent should affect first line
dedent: (code)=>
unless code\find("\n")
return code
@@ -900,10 +903,11 @@ class NomsuCompiler
insert concat_parts, lua.expr
return concat(concat_parts)
- -- TODO: fix how 'immediately' works with locals, e.g. "immediately: %x <- 5"
@define_compile_action "immediately %block", get_line_no!, (_block)->
lua = nomsu\tree_to_lua(_block)
lua_code = lua.statements or (lua.expr..";")
+ if lua.locals and #lua.locals > 0
+ lua_code = "local #{concat lua.locals, ", "};\n#{lua_code}"
nomsu\run_lua(lua_code)
return statements:"if IMMEDIATE then\n#{lua_code}\nend", locals:lua.locals
diff --git a/tests/all.nom b/tests/all.nom
new file mode 100644
index 0000000..5b95786
--- /dev/null
+++ b/tests/all.nom
@@ -0,0 +1,28 @@
+use "lib/core.nom"
+
+# Prerequisites:
+%var <- 99
+assume (%var = 99) or barf "Var assignment doesn't work."
+
+try: barf "barf"
+..and if it succeeds: barf "try % and if it succeeds failed."
+
+try: do nothing
+..and if it barfs: barf "try % and if it barfs failed."
+
+immediately
+ assume (%ONCE is (nil)) or barf "immediately is executing twice"
+ export %ONCE <- "ONCE"
+ %foo <- "immediately local"
+assume (%ONCE = "ONCE") or barf "Globals don't work."
+assume (%foo is (nil)) or barf "Immediately is leaking locals."
+
+# Run per-file test suites
+run file "tests/metaprogramming.nom"
+run file "tests/text.nom"
+run file "tests/operators.nom"
+run file "tests/control_flow.nom"
+run file "tests/math.nom"
+run file "tests/collections.nom"
+
+say "All tests passed!"
diff --git a/tests/collections.nom b/tests/collections.nom
new file mode 100644
index 0000000..ec79653
--- /dev/null
+++ b/tests/collections.nom
@@ -0,0 +1,43 @@
+#..
+ Tests for the stuff defined in lib/control_flow.nom
+
+use "lib/core.nom"
+
+assume ((2nd to last in [1,2,3,4,5]) = 4)
+assume ((first in [1,2]) = 1)
+assume ((last in [1,2]) = 2)
+assume (3 is in [1,2,3,4,5])
+assume (99 isn't in [1,2,3])
+assume ({x:no} has key "x")
+assume ({x:no} doesn't have key "y")
+assume (not ({x:no} doesn't have key "x"))
+assume ((size of [1,2,3]) = 3)
+%list <- [1,2,3,4,5]
+append 6 to %list
+assume ((last in %list) = 6)
+pop from %list
+assume ((last in %list) = 5)
+remove index 1 from %list
+assume ((first in %list) = 2)
+assume (((% * %) for all [1,2,3]) = [1,4,9])
+assume ((%k = (%v * %v) for %k = %v in {x:1,y:2,z:3}) = {x:1,y:4,z:9})
+assume ((%k for %k = %v in {x:1}) = ["x"])
+assume ((% = (% * %) for all [1,2,3]) = {1:1,2:4,3:9})
+assume (([[1,2],[3,4]] flattened) = [1,2,3,4])
+assume ((entries in {x:1}) = [{key:"x",value:1}])
+assume ((keys in {x:1}) = ["x"])
+assume ((values in {x:1}) = [1])
+assume ((sorted [3,1,2]) = [1,2,3])
+%x <- [3,1,2]
+sort %x
+assume (%x = [1,2,3])
+sort %x by (-%)
+assume (%x = [3,2,1])
+%keys <- {1:999,2:0,3:50}
+sort %x by (% in %keys)
+assume (%x = [2,3,1])
+assume ((unique [1,2,1,3,2,3]) = [1,2,3])
+%c <- (new counter)
+for all ["x","y","x","x","y"]
+ (% in %c) +<- 1
+assume (%c = {x:3,y:2})
diff --git a/tests/control_flow.nom b/tests/control_flow.nom
new file mode 100644
index 0000000..f0fc092
--- /dev/null
+++ b/tests/control_flow.nom
@@ -0,0 +1,191 @@
+#..
+ Tests for the stuff defined in lib/control_flow.nom
+
+use "lib/core.nom"
+do nothing
+
+action [test conditionals]
+ if: yes
+ %loc1 <- (yes)
+ if: no
+ barf "entered if 'no' conditional"
+
+ unless: yes
+ barf "entered unless 'yes' conditional"
+
+ if: yes
+ %loc2 <- (yes)
+ ..else
+ barf "entered if 'yes' else conditional"
+
+ unless: no
+ %loc3 <- (yes)
+ ..else
+ barf "entered unless 'no' else conditional"
+
+assume (all of [%loc1 = (nil), %loc2 = (nil), %loc3 = (nil)]) or barf "conditionals leaking locals"
+
+assume ((5 if (yes) else 1) = 5)
+assume ((5 if (no) else 1) = 1)
+action [return nil]: return (nil)
+assume (((return nil) if (yes) else 99) = (nil))
+
+go to %skip
+error "go to failed."
+--- %skip ---
+
+%tot <- 0
+for %x in [1,2,3]
+ %tot +<- %x
+assume (%tot = 6) or barf "for-loop failed"
+
+%tot <- 0
+for all [1,2,3]
+ %tot +<- %
+assume (%tot = 6) or barf "for-all-loop failed"
+
+%x <- 0
+repeat
+ %x +<- 1
+ if (%x = 3): stop repeating
+ if (%x > 3): barf "Failed to stop repeat loop"
+assume (%x = 3) or barf "Failed to repeat"
+
+%x <- 0
+repeat 5 times
+ %x +<- 1
+assume (%x = 5) or barf "Failed to repeat 5 times"
+
+<- {%x:0,%y:0}
+for all [1,2,3]
+ repeat 5 times
+ do next repeat
+ %x +<- 1
+ %y +<- 1
+assume ([%x,%y] = [0,3]) or barf "Failed to continue repeat"
+
+<- {%x:0,%y:0}
+for all [1,2,3]
+ repeat 5 times
+ do next %
+ %x +<- 1
+ %y +<- 1
+assume ([%x,%y] = [0,0]) or barf "Failed to continue for"
+
+<- {%x:0,%y:0}
+for all [1,2,3]
+ repeat 5 times
+ stop repeating
+ %x +<- 1
+ %y +<- 1
+assume ([%x,%y] = [0,3]) or barf "Failed to stop repeat"
+
+<- {%x:0,%y:0}
+for all [1,2,3]
+ repeat 5 times
+ stop %
+ %x +<- 1
+ %y +<- 1
+assume ([%x,%y] = [0,0]) or barf "Failed to stop for"
+
+%x <- 0
+repeat while: %x < 10
+ %x +<- 1
+assume (%x = 10) or barf "repeat-while failed"
+
+%x <- 0
+repeat until: %x = 10
+ %x +<- 1
+assume (%x = 10) or barf "repeat-until failed"
+
+%x <- 0
+for %i from 1 to 3: %x +<- %i
+assume (%x = 6) or barf "Numeric for range failed"
+
+%x <- 0
+for %i from 3 to 1 via -1: %x +<- %i
+assume (%x = 6) or barf "backwards numeric for range failed"
+
+%x <- 0
+for all 1 to 3: %x +<- %
+assume (%x = 6) or barf "terse numeric for range failed"
+
+%result <- {}
+for %key = %value in {x:1,y:2}
+ (%result's ("\%key\%key")) <- (%value * 11)
+assume (%result = {xx:11,yy:22}) or barf "key/value iteration failed"
+
+for %key = %value in {x:1,y:2}
+ stop %key
+ barf "stopping key failed"
+
+for %key = %value in {x:1,y:2}
+ stop %value
+ barf "stopping value failed"
+
+for %key = %value in {x:1,y:2}
+ do next %key
+ barf "skipping key failed"
+
+for %key = %value in {x:1,y:2}
+ do next %value
+ barf "skipping value failed"
+
+action [barfer]: barf "this should never be reached"
+when
+ * (no): barf "'when' fail"
+ * (no)
+ * (3 > 4): barf "'when' fail 2"
+ * (yes)
+ * (barfer): do nothing
+ * (99 > 1): barf "Fell through incorrectly"
+
+%else_worked <- (no)
+when
+ * (no): barf
+ * else: %else_worked <- (yes)
+assume %else_worked or barf "when..else failed"
+
+action [test when scope]
+ when
+ * (yes): %leaked <- (yes)
+test when scope
+assume (not %leaked) or barf "'when' is leaking locals"
+
+%when_worked <- (no)
+when 4 = ?
+ * 1
+ * 2: barf "'when = ?' fail"
+ * 3
+ * 4
+ * (barfer): %when_worked <- (yes)
+assume %when_worked
+
+%when_worked <- (no)
+when 5 = ?
+ * 6: barf
+ * else: %when_worked <- (yes)
+assume %when_worked
+
+try: barf
+..and if it succeeds: barf "try failed."
+
+%worked <- (no)
+try: barf
+..and if it barfs: %worked <- (yes)
+assume %worked or barf "try/catch failed"
+
+%x <- 1
+do
+ %x <- 2
+assume (%x = 2) or barf "'do' is redefining locals"
+
+%x <- 1
+try
+ %x <- 2
+ do
+ barf
+ ..then always
+ %x <- 1
+..and if it barfs: do nothing
+assume (%x = 1) or barf "do/then always failed"
diff --git a/tests/math.nom b/tests/math.nom
new file mode 100644
index 0000000..245fe52
--- /dev/null
+++ b/tests/math.nom
@@ -0,0 +1,17 @@
+#..
+ Tests for the stuff defined in lib/control_flow.nom
+
+use "lib/core.nom"
+
+assume (all of [inf, pi, tau, golden ratio, e]) or barf "math constants failed"
+%nan <- (NaN)
+assume (%nan != %nan) or barf "NaN failed"
+assume (("5" as a number) = 5)
+assume
+ all of [..]
+ abs 5, |5|, sqrt 5, √(5), sine 5, cosine 5, tangent 5, arc sine 5, arc cosine 5,
+ arc tangent 5, arc tangent 5/10, hyperbolic sine 5, hyperbolic cosine 5,
+ hyperbolic tangent 5, e^5, ln 5, log_(2) 5, floor 5, ceiling 5, round 5,
+..or barf "math functions failed"
+assume ((463 to the nearest 100) = 500) or barf "rounding failed"
+assume ((2.6 to the nearest 0.25) = 2.5) or barf "rounding failed"
diff --git a/tests/metaprogramming.nom b/tests/metaprogramming.nom
new file mode 100644
index 0000000..37acd3e
--- /dev/null
+++ b/tests/metaprogramming.nom
@@ -0,0 +1,55 @@
+#..
+ Tests for the stuff defined in lib/metaprogramming.nom
+
+use "lib/core.nom"
+
+immediately
+ compile [five] to {expr:"5"}
+assume ((five) = 5) or barf "Compile to expression failed."
+
+immediately
+ compile [loc x] to {statements:"_x = 99", locals:["_x"]}
+lua> "do"
+loc x
+assume (%x is 99) or barf "Compile to statements with locals failed."
+lua> "end"
+assume (%x is (nil)) or barf "Failed to properly localize a variable."
+
+immediately
+ compile [asdf] to
+ %tmp <- ""
+ return {statements:%tmp}
+asdf
+assume (%tmp is (nil)) or barf "compile to is leaking variables"
+
+action [foo %x]
+ %y <- (%x + 1)
+ return %y
+assume ((foo 10) = 11) or barf "Action didn't work."
+assume (%y is (nil)) or barf "Action leaked a local into globals."
+
+immediately
+ parse [baz %] as: foo %
+assume ((baz 10) = 11) or barf "Parse as action failed."
+
+immediately
+ parse [V] as: five
+assume ((V) = 5) or barf "Parse as compile action failed."
+
+remove action "foo %"
+try: foo 99
+..and if it succeeds: barf "Failed to delete action"
+
+assume ((\(5 + 5) as value) = 10) or barf "%tree as value failed."
+
+assume ((\(foo %x)'s source code) = "foo %x") or barf "source code failed."
+
+assume ((repr [1,2]) = "{1, 2}") or barf "repr failed."
+
+assume ((type of {}) = "table") or barf "type of failed."
+
+assume ((nomsu) = (=lua "nomsu")) or barf "nomsu failed"
+
+assume (("x" as lua identifier) = (\%x as lua identifier)) or barf "converting to identifier failed."
+
+assume ((run "return 99") = 99) or barf "run % failed."
diff --git a/tests/operators.nom b/tests/operators.nom
new file mode 100644
index 0000000..e3bd0b9
--- /dev/null
+++ b/tests/operators.nom
@@ -0,0 +1,76 @@
+#..
+ Tests for the stuff defined in lib/operators.nom
+
+use "lib/core.nom"
+
+assume (({x:5}'s "x") = 5) or barf "indexing doesn't work."
+try: % <- ({}'s "[[[\n]]]")
+..and if it barfs: barf "failed to index a table literal with a string containing brackets n stuff"
+
+<-{%x:10,%y:20}
+assume ((%x = 10) and (%y = 20)) or barf "mutli-assignment failed."
+<-{%x:%y, %y:%x}
+assume ((%y = 10) and (%x = 20)) or barf "swapping vars failed."
+
+% <- [%x < %y, %x <= %y, %x > %y, %x >= %y, %x = %y, %x is %y, %x != %y, %x isn't %y, %x is not %y]
+
+assume ({} is {}) or barf "Equality check failed."
+assume (({}'s id) is not ({}'s id)) or barf "Identity check failed."
+
+<-{%x:"outer",%y:"outer"}
+action [set global x local y]
+ export %x <- "inner"
+ %y <- "inner"
+set global x local y
+assume ((%x = "inner") and (%y = "outer")) or barf "export failed."
+
+<-{%x:"outer",%y:"outer"}
+action [set global x local y]
+ exporting [%x]
+ %x <- "inner"
+ %y <- "inner"
+set global x local y
+assume ((%x = "inner") and (%y = "outer")) or barf "export failed."
+
+<-{%x:1,%y:2}
+with [%z, %x<-999]
+ %z <- 999
+ assume (%z = 999) or barf "'with' failed."
+ assume (%x = 999) or barf "'with' assignment failed."
+assume (%x = 1) or barf "'with' scoping failed"
+assume (%z = (nil)) or barf "'with' scoping failed"
+
+assume ((1+2*3-4/2^2) = 6) or barf "math expressions not working properly"
+assume ((5 wrapped around 2) = 1) or barf "mod not working"
+assume (1 <= 2 < 3) or barf "chained operator fail."
+%value <- -999
+action [flipflop]
+ export %value <- (-%value)
+ return %value
+assume (not (1 < (flipflop) < 1)) or barf "3-way inequality evaluated middle term twice"
+assume (((yes) and (yes)) = (yes))
+action [barfer]
+ barf "short circuiting failed"
+assume (((no) and (barfer)) = (no))
+assume ((no) or (yes))
+assume ((yes) or (barfer))
+
+assume ((1 OR 2) = 3)
+assume ((3 XOR 2) = 1)
+assume ((3 AND 2) = 2)
+assume ((NOT (NOT 6)) = 6)
+assume ((1<<1) = 2)
+assume ((2>>1) = 1)
+assume ((2>>>1) = 1)
+#.. Ugh, Lua is stupid when it comes to bitwise arithmetic on negative numbers, so I'm
+ skipping the tests for those.
+
+assume ((-(5)) = -5)
+assume ((not (yes)) = (no))
+%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"
diff --git a/tests/text.nom b/tests/text.nom
new file mode 100644
index 0000000..ea18d42
--- /dev/null
+++ b/tests/text.nom
@@ -0,0 +1,13 @@
+#..
+ Tests for the stuff defined in lib/text.nom
+
+use "lib/core.nom"
+
+assume ((["x","y"] joined with ",") = "x,y") or barf "joined with failed"
+assume ((["x","y"] joined) = "xy") or barf "joined failed"
+assume (("asdf" capitalized) = "Asdf") or barf "capitalized failed"
+assume (("asdf" with "X" instead of "s") = "aXdf") or barf "substitution failed"
+# TODO: add tests for indent/dedent
+assume ("\n" = (newline)) or barf "Text literals failed."
+%x <- "\(green)hello\(reset color)"
+assume (("x" + "y") = "xy")
diff --git a/utils.lua b/utils.lua
index 80585a5..937252f 100644
--- a/utils.lua
+++ b/utils.lua
@@ -284,7 +284,7 @@ local function sort(list, keyFn, reverse)
end
local function equivalent(x, y, depth)
- depth = depth or 1
+ depth = depth or -1
if x == y then
return true
end
@@ -296,6 +296,8 @@ local function equivalent(x, y, depth)
end
if depth == 0 then
return false
+ elseif depth < -999 then
+ error("Exceeded maximum comparison depth")
end
local checked = {}
for k, v in pairs(x) do