aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--README.md1
-rw-r--r--code_obj.lua2
-rw-r--r--code_obj.moon2
-rw-r--r--containers.lua54
-rw-r--r--containers.moon37
-rw-r--r--core/control_flow.nom2
-rw-r--r--core/math.nom81
-rw-r--r--core/metaprogramming.nom90
-rw-r--r--core/text.nom4
-rw-r--r--examples/how_do_i.nom105
-rw-r--r--lib/object.nom16
-rw-r--r--lib/os.nom2
-rw-r--r--nomsu.lua2
-rwxr-xr-xnomsu.moon2
-rw-r--r--nomsu_compiler.lua42
-rw-r--r--nomsu_compiler.moon34
-rw-r--r--utils.lua334
18 files changed, 289 insertions, 523 deletions
diff --git a/Makefile b/Makefile
index 867a726..e3db5a3 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@ MOON_FILES= code_obj.moon error_handling.moon files.moon nomsu.moon nomsu_compil
syntax_tree.moon containers.moon bitops.moon parser.moon pretty_errors.moon \
string2.moon
LUA_FILES= code_obj.lua consolecolors.lua error_handling.lua files.lua nomsu.lua nomsu_compiler.lua \
- syntax_tree.lua containers.lua bitops.lua utils.lua parser.lua pretty_errors.lua \
+ syntax_tree.lua containers.lua bitops.lua parser.lua pretty_errors.lua \
string2.lua
CORE_NOM_FILES= $(wildcard core/*.nom)
CORE_LUA_FILES= $(patsubst %.nom,%.lua,$(CORE_NOM_FILES))
diff --git a/README.md b/README.md
index 1de0910..1af0f90 100644
--- a/README.md
+++ b/README.md
@@ -52,7 +52,6 @@ All `.moon` files have been precompiled into corresponding `.lua` files, so you
* [syntax\_tree.moon](syntax_tree.moon) - Datastructures used for Nomsu Abstract Syntax Trees.
* [code\_obj.moon](code_obj.moon) - Datastructures used for incrementally building generated code, while preserving code origins.
* [error\_handling.moon](error_handling.moon) - The logic for producing good error messages within Lua that reference the Nomsu source code that led to them.
-* [utils.lua](utils.lua) - A set of utility actions used by nomsu.moon.
* [consolecolors.lua](consolecolors.lua) - Lua module that defines ANSI color codes for colored console output (used internally in nomsu.moon).
* [examples/how\_do\_i.nom](examples/how_do_i.nom) - A simple walkthrough of some of the features of Nomsu, written in Nomsu code. **This is a good place to start.**
* [core/\*.nom](core) - Core language definitions of stuff like control flow, operators, and metaprogramming, broken down into different files.
diff --git a/code_obj.lua b/code_obj.lua
index b5213c9..7d335e0 100644
--- a/code_obj.lua
+++ b/code_obj.lua
@@ -409,7 +409,7 @@ do
return {
nomsu_filename = self.source.filename,
lua_filename = tostring(self.source) .. ".lua",
- lua_file = self:stringify(),
+ lua_file = self:text(),
lua_to_nomsu = lua_to_nomsu,
nomsu_to_lua = nomsu_to_lua
}
diff --git a/code_obj.moon b/code_obj.moon
index 22969a4..cb8eae2 100644
--- a/code_obj.moon
+++ b/code_obj.moon
@@ -245,7 +245,7 @@ class LuaCode extends Code
walk self, 1
return {
nomsu_filename:@source.filename
- lua_filename:tostring(@source)..".lua", lua_file:@stringify!
+ lua_filename:tostring(@source)..".lua", lua_file:@text!
:lua_to_nomsu, :nomsu_to_lua
}
diff --git a/containers.lua b/containers.lua
index 3148dcf..9f8fa46 100644
--- a/containers.lua
+++ b/containers.lua
@@ -1,16 +1,9 @@
+local List, Dict
local insert, remove, concat
do
local _obj_0 = table
insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat
end
-local equivalent, nth_to_last, size
-do
- local _obj_0 = require('utils')
- equivalent, nth_to_last, size = _obj_0.equivalent, _obj_0.nth_to_last, _obj_0.size
-end
-local lpeg = require('lpeg')
-local re = require('re')
-local List, Dict
local as_nomsu
as_nomsu = function(self)
if type(self) == 'number' then
@@ -47,9 +40,23 @@ as_lua = function(self)
end
return tostring(self)
end
+local nth_to_last
+nth_to_last = function(self, n)
+ return self[#self - n + 1]
+end
local _list_mt = {
__type = "List",
- __eq = equivalent,
+ __eq = function(self, other)
+ if not (type(other) == 'table' and getmetatable(other) == getmetatable(self) and #other == #self) then
+ return false
+ end
+ for i, x in ipairs(self) do
+ if not (x == other[i]) then
+ return false
+ end
+ end
+ return true
+ end,
__tostring = function(self)
return "[" .. concat((function()
local _accum_0 = { }
@@ -75,7 +82,7 @@ local _list_mt = {
end)(), ", ") .. "]"
end,
as_lua = function(self)
- return "_List{" .. concat((function()
+ return "List{" .. concat((function()
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #self do
@@ -242,8 +249,29 @@ walk_items = function(self, i)
end
local _dict_mt = {
__type = "Dict",
- __eq = equivalent,
- __len = size,
+ __eq = function(self, other)
+ if not (type(other) == 'table' and getmetatable(other) == getmetatable(self)) then
+ return false
+ end
+ for k, v in pairs(self) do
+ if not (v == other[k]) then
+ return false
+ end
+ end
+ for k, v in pairs(other) do
+ if not (v == self[k]) then
+ return false
+ end
+ end
+ return true
+ end,
+ __len = function(self)
+ local n = 0
+ for _ in pairs(self) do
+ n = n + 1
+ end
+ return n
+ end,
__tostring = function(self)
return "{" .. concat((function()
local _accum_0 = { }
@@ -267,7 +295,7 @@ local _dict_mt = {
end)(), ", ") .. "}"
end,
as_lua = function(self)
- return "_Dict{" .. concat((function()
+ return "Dict{" .. concat((function()
local _accum_0 = { }
local _len_0 = 1
for k, v in pairs(self) do
diff --git a/containers.moon b/containers.moon
index 4671f6b..9591fa2 100644
--- a/containers.moon
+++ b/containers.moon
@@ -1,11 +1,6 @@
--- This file contains container classes, i.e. Lists, Dicts, and Sets
-
-{:insert,:remove,:concat} = table
-{:equivalent, :nth_to_last, :size} = require 'utils'
-lpeg = require 'lpeg'
-re = require 're'
-
+-- This file contains container classes, i.e. Lists and Dicts, plus some extended string functionality
local List, Dict
+{:insert,:remove,:concat} = table
as_nomsu = =>
if type(@) == 'number'
@@ -23,19 +18,26 @@ as_lua = =>
return _as_lua(@)
return tostring(@)
+nth_to_last = (n)=> @[#@-n+1]
+
-- List and Dict classes to provide basic equality/tostring functionality for the tables
-- used in Nomsu. This way, they retain a notion of whether they were originally lists or dicts.
_list_mt =
__type: "List"
- __eq:equivalent
+ __eq: (other)=>
+ unless type(other) == 'table' and getmetatable(other) == getmetatable(@) and #other == #@
+ return false
+ for i,x in ipairs(@)
+ return false unless x == other[i]
+ return true
-- Could consider adding a __newindex to enforce list-ness, but would hurt performance
__tostring: =>
"["..concat([as_nomsu(b) for b in *@], ", ").."]"
as_nomsu: =>
"["..concat([as_nomsu(b) for b in *@], ", ").."]"
as_lua: =>
- "_List{"..concat([as_lua(b) for b in *@], ", ").."}"
+ "List{"..concat([as_lua(b) for b in *@], ", ").."}"
__lt: (other)=>
assert type(@) == 'table' and type(other) == 'table', "Incompatible types for comparison"
for i=1,math.max(#@, #other)
@@ -65,7 +67,6 @@ _list_mt =
last: (=> @[#@]), first: (=> @[1])
_1_st_to_last:nth_to_last, _1_nd_to_last:nth_to_last
_1_rd_to_last:nth_to_last, _1_th_to_last:nth_to_last
- -- TODO: use stringify() to allow joining misc. objects?
joined: => table.concat([tostring(x) for x in *@]),
joined_with: (glue)=> table.concat([tostring(x) for x in *@], glue),
has: (item)=>
@@ -105,14 +106,24 @@ walk_items = (i)=>
_dict_mt =
__type: "Dict"
- __eq:equivalent
- __len:size
+ __eq: (other)=>
+ unless type(other) == 'table' and getmetatable(other) == getmetatable(@)
+ return false
+ for k,v in pairs(@)
+ return false unless v == other[k]
+ for k,v in pairs(other)
+ return false unless v == @[k]
+ return true
+ __len: =>
+ n = 0
+ for _ in pairs(@) do n += 1
+ return n
__tostring: =>
"{"..concat(["#{as_nomsu(k)}: #{as_nomsu(v)}" for k,v in pairs @], ", ").."}"
as_nomsu: =>
"{"..concat(["#{as_nomsu(k)}: #{as_nomsu(v)}" for k,v in pairs @], ", ").."}"
as_lua: =>
- "_Dict{"..concat(["[ #{as_lua(k)}]= #{as_lua(v)}" for k,v in pairs @], ", ").."}"
+ "Dict{"..concat(["[ #{as_lua(k)}]= #{as_lua(v)}" for k,v in pairs @], ", ").."}"
__ipairs: => walk_items, {table:@, key:nil}, 0
__band: (other)=>
Dict{k,v for k,v in pairs(@) when other[k] != nil}
diff --git a/core/control_flow.nom b/core/control_flow.nom
index bbc98f1..a692f95 100644
--- a/core/control_flow.nom
+++ b/core/control_flow.nom
@@ -560,7 +560,7 @@ test:
%lua = (..)
Lua "\
..do
- local \(mangle "stack \(%var.1)") = _List{\(%structure as lua expr)}
+ local \(mangle "stack \(%var.1)") = List{\(%structure as lua expr)}
while #\(mangle "stack \(%var.1)") > 0 do
\(%var as lua expr) = table.remove(\(mangle "stack \(%var.1)"), 1)"
%lua::append "\n "
diff --git a/core/math.nom b/core/math.nom
index 66f5aba..3bad78a 100644
--- a/core/math.nom
+++ b/core/math.nom
@@ -76,27 +76,54 @@ test:
externally (%n to the nearest %rounder) means (..)
=lua "(\%rounder)*math.floor((\%n / \%rounder) + .5)"
-# Any/all/none
+# Any/all
+externally [all of %items, all %items] all mean:
+ for % in %items:
+ unless %: return (no)
+ return (yes)
[all of %items, all %items] all compile to:
unless (%items.type is "List"):
- return (Lua value "utils.all(\(%items as lua expr))")
+ return %tree
%clauses = (((% as lua expr)::text) for % in %items)
return (Lua value "(\(%clauses::joined with " and "))")
-
[not all of %items, not all %items] all parse as (not (all of %items))
+
+externally [any of %items, any %items] all mean:
+ for % in %items:
+ if %: return (yes)
+ return (no)
[any of %items, any %items] all compile to:
unless (%items.type is "List"):
- return (Lua value "utils.any(\(%items as lua expr))")
+ return %tree
%clauses = (((% as lua expr)::text) for % in %items)
return (Lua value "(\(%clauses::joined with " or "))")
-
[none of %items, none %items] all parse as (not (any of %items))
+
+# Sum/product
+externally [sum of %items, sum %items] all mean:
+ %total = 0
+ for % in %items: %total += %
+ return %total
[sum of %items, sum %items] all compile to:
unless (%items.type is "List"):
- return (Lua value "utils.sum(\(%items as lua expr))")
+ return %tree
%clauses = (((% as lua expr)::text) for % in %items)
return (Lua value "(\(%clauses::joined with " + "))")
+externally [product of %items, product %items] all mean:
+ %prod = 1
+ for % in %items: %prod *= %
+ return %prod
+[product of %items, product %items] all compile to:
+ unless (%items.type is "List"):
+ return %tree
+ %clauses = (((% as lua expr)::text) for % in %items)
+ return (Lua value "(\(%clauses::joined with " * "))")
+
+externally [avg of %items, average of %items] all mean (..)
+ (sum of %items) / (size of %items)
+
+# Shorthand for control flow
[if all of %items %body, if all of %items then %body] all parse as (..)
if (all of %items) %body
@@ -136,42 +163,44 @@ externally (%n to the nearest %rounder) means (..)
unless none of %items %body else %else, unless none of %items then %body else %else
..all parse as (if (any of %items) %body else %else)
-[product of %items, product %items] all compile to:
- unless (%items.type is "List"):
- return (Lua value "utils.product(\(%items as lua expr))")
- %clauses = (((% as lua expr)::text) for % in %items)
- return (Lua value "(\(%clauses::joined with " * "))")
-
-externally [avg of %items, average of %items] all mean (..)
- =lua "(utils.sum(\%items)/#\%items)"
-
-[min of %items, smallest of %items, lowest of %items] all compile to (..)
- Lua value "utils.min(\(%items as lua expr))"
-
-[max of %items, biggest of %items, largest of %items, highest of %items] all compile \
-..to (Lua value "utils.max(\(%items as lua expr))")
+# Min/max
+externally [min of %items, smallest of %items, lowest of %items] all mean:
+ %best = (nil)
+ for % in %items:
+ if ((%best == (nil)) or (% < %best)):
+ %best = %
+ return %best
+
+externally [max of %items, biggest of %items, largest of %items, highest of %items] all mean:
+ %best = (nil)
+ for % in %items:
+ if ((%best == (nil)) or (% > %best)):
+ %best = %
+ return %best
test:
assume ((min of [3, -4, 1, 2] by % = (% * %)) == 1)
assume ((max of [3, -4, 1, 2] by % = (% * %)) == -4)
(min of %items by %item = %value_expr) parses as (..)
result of:
- set {%best:nil, %best_key:nil}
+ %best = (nil)
+ %best_key = (nil)
for %item in %items:
%key = %value_expr
if ((%best == (nil)) or (%key < %best_key)):
- set {%best:%item, %best_key:%key}
-
+ %best = %item
+ %best_key = %key
return %best
(max of %items by %item = %value_expr) parses as (..)
result of:
- set {%best:nil, %best_key:nil}
+ %best = (nil)
+ %best_key = (nil)
for %item in %items:
%key = %value_expr
if ((%best == (nil)) or (%key > %best_key)):
- set {%best:%item, %best_key:%key}
-
+ %best = %item
+ %best_key = %key
return %best
# Random functions
diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom
index 61c877f..b104987 100644
--- a/core/metaprogramming.nom
+++ b/core/metaprogramming.nom
@@ -63,29 +63,33 @@ test:
asdf
assume (%tmp is (nil)) or barf "compile to is leaking variables"
lua> "\
- ..COMPILE_ACTIONS["1 compiles to"] = function(nomsu, tree, \%actions, \%body)
- if \%actions.type ~= "List" then \%actions = {\%actions, type="List"} end
- local \%args = {"nomsu", "tree", unpack(table.map(\%actions[1]:get_args(), function(a) return nomsu:compile(a):text() end))}
- local lua = LuaCode(tree.source, "COMPILE_ACTIONS[", \%actions[1].stub:as_lua(),
+ ..COMPILE_ACTIONS["1 compiles to"] = function(nomsu, tree, \%action, \%body)
+ local \%args = List{\(\%nomsu), \(\%tree), unpack(\%action:get_args())}
+ local lua = LuaCode(tree.source, "COMPILE_ACTIONS[", \%action.stub:as_lua(),
"] = ", \(what (%args -> %body) compiles to))
+ return lua
+ end"
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+(%actions all compile to %body) compiles to:
+ lua> "\
+ ..if \%actions.type ~= "List" then
+ nomsu:compile_error(\%actions, "This should be a list of actions.")
+ end
+ local lua = LuaCode(tree.source, \(what (%actions.1 compiles to %body) compiles to))
+ local \%args = List{\(\%nomsu), \(\%tree), unpack(\%actions[1]:get_args())}
for i=2,#\%actions do
local alias = \%actions[i]
- local \%alias_args = {"nomsu", "tree", unpack(table.map(alias:get_args(), function(a) return nomsu:compile(a):text() \
- ..end))}
+ local \%alias_args = List{\(\%nomsu), \(\%tree), unpack(alias:get_args())}
lua:append("\\nCOMPILE_ACTIONS[", alias.stub:as_lua(), "] = ")
- if utils.equivalent(\%args, \%alias_args) then
+ if \%alias_args == \%args then
lua:append("COMPILE_ACTIONS[", \%actions[1].stub:as_lua(), "]")
else
- lua:append("function(")
- lua:concat_append(\%alias_args, ", ")
- lua:append(")\\n return COMPILE_ACTIONS[", \%actions[1].stub:as_lua(), "](")
- lua:concat_append(\%args, ", ")
- lua:append(")\\nend")
+ lua:append(\(what (%alias_args -> \(what %actions.1 compiles to)) compiles to))
end
end
- return lua
- end
- COMPILE_ACTIONS["1 all compile to"] = COMPILE_ACTIONS["1 compiles to"]"
+ return lua"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -112,27 +116,29 @@ test:
(baz %) parses as (foo %)
assume ((foo 1) == "outer")
-[%actions means %body, %actions all mean %body] all compile to:
+
+(%action means %body) compiles to:
lua> "\
- ..if \%actions.type ~= "List" then \%actions = {\%actions, type="List"} end
- local fn_name = \%actions[1].stub:as_lua_id()
- local \%args = table.map(\%actions[1]:get_args(), function(a) return nomsu:compile(a):text() end)
+ ..local fn_name = \%action.stub:as_lua_id()
+ local \%args = \%action:get_args()
local lua = LuaCode(tree.source, fn_name, " = ", \(what (%args -> %body) compiles to))
lua:add_free_vars({fn_name})
+ return lua"
+(%actions all mean %body) compiles to:
+ lua> "\
+ ..local fn_name = \%actions[1].stub:as_lua_id()
+ local \%args = List(\%actions[1]:get_args())
+ local lua = LuaCode(tree.source, \(what (%actions.1 means %body) compiles to))
for i=2,#\%actions do
local alias = \%actions[i]
local alias_name = alias.stub:as_lua_id()
lua:add_free_vars({alias_name})
- local \%alias_args = table.map(alias:get_args(), function(a) return nomsu:compile(a):text() end)
+ local \%alias_args = List(alias:get_args())
lua:append("\\n", alias_name, " = ")
- if utils.equivalent(\%args, \%alias_args) then
+ if \%args == \%alias_args then
lua:append(fn_name)
else
- lua:append("function(")
- lua:concat_append(\%alias_args, ", ")
- lua:append(")\\n return ", fn_name, "(")
- lua:concat_append(\%args, ", ")
- lua:append(")\\nend")
+ lua:append(\(what (%alias_args -> %actions.1) compiles to))
end
end
return lua"
@@ -143,10 +149,14 @@ test:
test:
assume ((baz1) == "baz1")
assume ((baz2) == "baz2")
-[externally %actions means %body, externally %actions all mean %body] all compile to:
+(externally %action means %body) compiles to:
+ lua> "\
+ ..local lua = \(what (%action means %body) compiles to)
+ lua:remove_free_vars({\%action.stub:as_lua_id()})
+ return lua"
+(externally %actions all mean %body) compiles to:
lua> "\
- ..local lua = \(what (%actions means %body) compiles to)
- if \%actions.type ~= "List" then \%actions = {\%actions, type="List"} end
+ ..local lua = \(what (%actions all mean %body) compiles to)
lua:remove_free_vars(table.map(\%actions, function(a) return a.stub:as_lua_id() end))
return lua"
@@ -169,10 +179,12 @@ test:
swap %tmp and %tmp2
assume ((%tmp == 2) and (%tmp2 == 1)) or barf "\
..'parse % as %' variable mangling failed."
-[%actions parses as %body, %actions all parse as %body] all compile to:
+(%actions all parse as %body) compiles to:
lua> "\
..local replacements = {}
- if \%actions.type ~= "List" then \%actions = {\%actions, type="List"} end
+ if \%actions.type ~= "List" then
+ nomsu:compile_error(\%actions, "This should be a list.")
+ end
for i,arg in ipairs(\%actions[1]:get_args()) do
replacements[arg[1]] = nomsu:compile(arg):text()
end
@@ -208,10 +220,12 @@ test:
local \%new_body = LuaCode(\%body.source,
"local mangle = mangler()",
"\\nreturn ", make_tree(\%body))
- local ret = \(what (%actions compiles to %new_body) compiles to)
+ local ret = \(what (%actions all compile to %new_body) compiles to)
return ret"
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+[%action parses as %body] all parse as ([%action] all parse as %body)
# TODO: add check for .is_value
(%tree as lua expr) compiles to (..)
@@ -287,10 +301,10 @@ externally (%tree with vars %replacements) means (..)
externally (match %tree with %patt) means:
lua> "\
- ..if \%patt.type == "Var" then return _Dict{[\%patt[1]]=\%tree} end
+ ..if \%patt.type == "Var" then return Dict{[\%patt[1]]=\%tree} end
if \%patt.type == "Action" and \%patt.stub ~= \%tree.stub then return nil end
if #\%patt ~= #\%tree then return nil end
- local matches = _Dict{}
+ local matches = Dict{}
for \%i=1,#\%patt do
if SyntaxTree:is_instance(\%tree[\%i]) then
local submatch = \(match %tree.%i with %patt.%i)
@@ -341,11 +355,11 @@ externally (type of %) means:
lua> "\
..local lua_type = \(lua type of %)
if lua_type == 'string' then return 'Text'
- elseif lua_type == 'table' then
+ elseif lua_type == 'table' or lua_type == 'userdata' then
local mt = getmetatable(\%)
if mt and mt.__type then return mt.__type end
- return 'Lua table'
- else return lua_type end"
+ end
+ return lua_type"
[% is a %type, % is an %type] all parse as ((type of %) == %type)
[% isn't a %type, % isn't an %type, % is not a %type, % is not an %type] all parse as (..)
diff --git a/core/text.nom b/core/text.nom
index 05ec409..40eb895 100644
--- a/core/text.nom
+++ b/core/text.nom
@@ -22,10 +22,10 @@ test:
assume ("asdf"::uppercase) == "ASDF"
assume ("asdf"::with "s" -> "X") == "aXdf"
assume ("one\ntwo\n"::lines) == ["one", "two", ""]
- (アクション %spec %body) parses as (externally %spec means %body)
+ (%spec とは %body) parses as (%spec means %body)
test:
%こんにちは = "こんにちは"
- アクション [% と言う] "\(%)世界"
+ (% と言う) とは "\(%)世界"
assume (%こんにちは と言う) == "こんにちは世界"
(%expr for %match in %text matching %patt) compiles to (..)
Lua value "\
diff --git a/examples/how_do_i.nom b/examples/how_do_i.nom
index ec75596..91d1ae3 100644
--- a/examples/how_do_i.nom
+++ b/examples/how_do_i.nom
@@ -16,6 +16,7 @@ use "lib"
say "Hello world!"
# How do I set a variable?
+# Variables have "%" prefix:
%foobar = 1
%text = "Hello world"
@@ -31,7 +32,7 @@ say %one_two
%foobar += 1
# How do I define a mutli-line string?
-# In Nomsu, "strings" are called "text", and multi-line text looks like:
+# In Nomsu, the name "text" is used, rather than "string", and multi-line text looks like:
%mutli_text = "\
..Start with a quote mark and a backslash and an indented "..", then put indented
lines below it. The indented lines will not include the indentation, except when
@@ -47,7 +48,7 @@ say "\
..Text can contain a backslash followed by a variable, list, dict, or parenthesized
expression. This escaped value will be converted to readable text, like so:
The value of %foobar is \%foobar, isn't that nice?
- These are some numbers: \[1 + 1, 2 + 1, 3 + 1]
+ These are some numbers: \[1, 2, 3]
The sum of 2 and 4 is \(2 + 4).
A backslash not followed by any of these, and not at the end of a line
@@ -66,16 +67,16 @@ say "\
say "Single-line text can contain escape sequences like \", \\, \000, and \n"
# How do I define a list?
-%my_list = [1, 2, "hello"]
+%my_list = ["first", "second", "third", 4]
# Really long lists can use [..] followed by a bunch of indented values delimited
by commas and/or newlines
-%my_really_long_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+%my_really_long_list = [..]
+ 10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, 90000, 100000, 110000
+ 120000, 130000, 140000, 150000, 160000, 170000
# How do I use a list?
-%my_list = ["first item", "second item", "third item"]
-
-# Lists are 1-indexed because they're implemented as Lua tables, so this prints "first item"
+# Lists are 1-indexed because they're implemented as Lua tables, so this prints "first"
say %my_list.1
# List entries can be modified like this:
@@ -86,12 +87,15 @@ say %my_list.1
%my_list::pop
# How do I define a dictionary/hash map?
-%my_dict = {x:99, y:101}
-%my_dict = {x:101, y:2, "99 bottles":99, 653:292}
+ One-word text keys don't need quotes, otherwise the key is an expression.
+ If the expression is more complex than a literal, it needs parentheses:
+%my_dict = {x:101, y:2, "how many bottles":99, 653:292, (5 + 6):11}
# How do I use a dict?
# Dicts are also implemented as Lua tables, so they're accessed and modified the same way as lists
say %my_dict.x
+say %my_dict."how many bottles"
+say %my_dict.653
%my_dict.x = 9999
# How do I do conditional branching?
@@ -104,21 +108,21 @@ if (1 > 10):
say "this will print"
# There's no "elseif", so for longer conditionals, a "when" branch is the best option
-if:
- (3 > 6) (3 > 5) (3 > 4):
- say "this won't print"
+when:
(3 > 3):
say "this won't print"
+ (3 > 6) (3 > 5) (3 > 4):
+ say "this won't print because none of the conditions on the line above are true"
(3 > 2):
say "this will print"
else:
say "this is the default case"
# How do I do a switch statement?
-if 3 is:
- 0 1 2:
+if (1 + 2) is:
+ 0 1:
say "this won't print"
- 3:
+ 2 3:
say "this will print"
else:
say "this won't print"
@@ -159,20 +163,22 @@ repeat:
# How do I do a 'goto'?
do:
%x = 1
- === %again ===
+ === (my loop) ===
say "GOTO loop #\%x"
%x += 1
- if (%x <= 3): go to %again
+ if (%x <= 3): go to (my loop)
say "finished going to"
# How do I define a function/method/procedure?
# In nomsu, they're called "action"s, and they can be declared like this:
(say both %first and also %second) means:
say %first
-
- # Function arguments are accessed just like variables
say %second
+# Actions can have parts of the action's name spread throughout.
+ Everything that's not a literal value is treated as part of the action's name
+say both "Hello" and also "world!"
+
# Actions can use "return" to return a value early
(first fibonacci above %n) means:
%f1 = 0
@@ -196,10 +202,6 @@ say (first fibonacci above 10)
I like "dogs" more than "cats"
I think "chihuahuas" are worse than "corgis"
-# Actions can have parts of the action's name spread throughout.
- Everything that's not a literal value is treated as part of the action's name
-say both "Hello" and also "again!"
-
# Actions can even start with a parameter
(%what_she_said is what she said) means:
say %what_she_said
@@ -207,14 +209,14 @@ say both "Hello" and also "again!"
"Howdy pardner" is what she said
-# The language only reserves []{}().,:;% as special characters, so actions
+# The language only reserves []{}().,:;%#\ as special characters, so actions
can have really funky names!
-(>> %foo_bar $$$^ --> % @&_~-^-~_~-^ %1 !) means:
+(>> %foo_bar $@&' --> % @&_~-^-~_~-^ %1 !) means:
say %foo_bar
say %
say %1
->> "wow" $$$^ --> "so flexible!" @&_~-^-~_~-^ "even numbers can be variables!" !
+>> "wow" $@&' --> "so flexible!" @&_~-^-~_~-^ "even numbers can be variables!" !
# There's also full unicode support
%こんにちは = "こんにちは"
@@ -251,9 +253,9 @@ say both (my favorite number) and also "foo"
lua> "io.write(\"The OS time is: \", os.time(), \"\\n\");"
say the time
-say "Math expression result is: \(=lua "(1 + 2*3 + 3*4)^2")"
+say "Math expression result is: \(=lua "(1 + 2*3 + 3*4)^2 % 5")"
-# Variables can be accessed via \%varname
+# Variables can be accessed via \%var
(square root of %n) means (=lua "math.sqrt(\%n)")
say "The square root of 2 is \(square root of 2)"
@@ -261,11 +263,13 @@ say "The square root of 2 is \(square root of 2)"
(if %condition is untrue %body) parses as (if (not %condition) %body)
# Or to transform nomsu code into custom lua code using "compile % to %"
-(if %condition on opposite day %body) compiles to (..)
- Lua "\
- ..if not \(%condition as lua expr) then
- \(%body as lua statements)
- end"
+(debug only %body) compiles to:
+ if %DEBUG_ENABLED:
+ return (Lua "-- Debug code:\n\(%body as lua statements)")
+ ..else:
+ return (Lua "-- (debug code removed for production)")
+
+%DEBUG_ENABLED = (yes)
# Constants can be defined as macros
(TWENTY) parses as 20
@@ -282,37 +286,44 @@ if (1 > (TWENTY)) is untrue:
say "Nomsu parsing macros work!"
say "It looks like a keyword, but there's no magic here!"
-if (1 > (TWENTY)) on opposite day:
+debug only:
say "Lua compiling macros work!"
- say "It looks like a keyword, but there's no magic here!"
# How do I use an action as a value?
# Well... it's always *possible* to fall back to Lua behavior for something like this:
(best of %items according to %key_fn) means:
- set {%best:nil, %best_key:nil}
+ %best = (nil)
+ %best_key = (nil)
for %item in %items:
- %key = (=lua "\%key_fn(\%item)")
+ %key = (%key_fn %item)
if ((%best is (nil)) or (%key > %best_key)):
- set {%best:%item, %best_key:%key}
+ %best = %item
+ %best_key = %key
return %best
+# Function literals look like: [%x] -> (%x * %x)
say (best of [2, -3, 4, -8] according to ([%x] -> (%x * %x)))
-# But nomsu was mostly designed so that you don't *need* to. Instead of creating a
- one-off function to pass to another function and get called a bunch of times, you
- could use a macro to generate a single block of code that inlines the expression you
- want to use:
-(best of %items where %item_var has score %key_expr) parses as (..)
+# Or, you can use ((foo %)'s meaning) to access the function that gets called by (foo %)
+(%x squared) means (%x * %x)
+say (best of [2, -3, 4, -8] according to ((% squared)'s meaning))
+
+# But nomsu was designed with flexible alternatives that are often better than passing functions.
+ For example, instead of calling a key function on every item, you could instead define a macro
+ that gives you a value based on an inlined expression:
+(best of %items where %item has score %key_expr) parses as (..)
result of:
- set {%best:nil, %best_key:nil}
- for %item_var in %items:
+ %best = (nil)
+ %best_key = (nil)
+ for %item in %items:
%key = %key_expr
if ((%best is (nil)) or (%key > %best_key)):
- set {%best:%item_var, %best_key:%key}
+ %best = %item
+ %best_key = %key
return %best
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-say (best of [2, -3, 4, -8] where %x has score (%x * %x))
+ say (best of [2, -3, 4, -8] where %x has score (%x * %x))
diff --git a/lib/object.nom b/lib/object.nom
index d5555df..6f6fc20 100644
--- a/lib/object.nom
+++ b/lib/object.nom
@@ -54,25 +54,21 @@ test:
(my action %actions %body) compiles to:
lua> "\
..local fn_name = \%actions[1].stub:as_lua_id()
- local \%args = table.map(\%actions[1]:get_args(), function(a) return tostring(nomsu:compile(a)) end)
- table.insert(\%args, 1, \(\%me as lua id))
+ local \%args = List(\%actions[1]:get_args())
+ table.insert(\%args, 1, \(\%me))
local lua = LuaCode(tree.source, "class.", fn_name, " = ", \(..)
what (%args -> %body) compiles to
..)
for i=2,#\%actions do
local alias = \%actions[i]
local alias_name = alias.stub:as_lua_id()
- local \%alias_args = table.map(alias:get_args(), function(a) return tostring(nomsu:compile(a)) end)
- table.insert(\%alias_args, 1, \(\%me as lua id))
+ local \%alias_args = List(alias:get_args())
+ table.insert(\%alias_args, 1, \(\%me))
lua:append("\\nclass.", alias_name, " = ")
- if utils.equivalent(\%args, \%alias_args) then
+ if \%args == \%alias_args then
lua:append("class.", fn_name)
else
- lua:append("function(")
- lua:concat_append(\%alias_args, ", ")
- lua:append(")\\n return class.", fn_name, "(")
- lua:concat_append(\%args, ", ")
- lua:append(")\\nend")
+ lua:append(\(what (%alias_args -> %actions.1) compiles to))
end
end
return lua"
diff --git a/lib/os.nom b/lib/os.nom
index 2bd91ad..00fe81f 100644
--- a/lib/os.nom
+++ b/lib/os.nom
@@ -32,7 +32,7 @@ test:
(%expr for file %f in %path) compiles to (..)
Lua value "\
..(function()
- local ret = _List{}
+ local ret = List{}
for i,\(%f as lua expr) in Files.walk(\(%path as lua expr)) do
ret[#ret+1] = \(%expr as lua statements)
end
diff --git a/nomsu.lua b/nomsu.lua
index bd1ee85..e7f8fdd 100644
--- a/nomsu.lua
+++ b/nomsu.lua
@@ -131,7 +131,7 @@ if not args or args.help then
os.exit(EXIT_FAILURE)
end
local nomsu = NomsuCompiler
-nomsu.environment.arg = NomsuCompiler.environment._List(args.nomsu_args)
+nomsu.environment.arg = NomsuCompiler.environment.List(args.nomsu_args)
if args.version then
nomsu:run([[(: use "core"; say (Nomsu version))]])
os.exit(EXIT_SUCCESS)
diff --git a/nomsu.moon b/nomsu.moon
index 2c4d21f..9637358 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -86,7 +86,7 @@ if not args or args.help
os.exit(EXIT_FAILURE)
nomsu = NomsuCompiler
-nomsu.environment.arg = NomsuCompiler.environment._List(args.nomsu_args)
+nomsu.environment.arg = NomsuCompiler.environment.List(args.nomsu_args)
if args.version
nomsu\run [[(: use "core"; say (Nomsu version))]]
diff --git a/nomsu_compiler.lua b/nomsu_compiler.lua
index 67e9621..c7fa456 100644
--- a/nomsu_compiler.lua
+++ b/nomsu_compiler.lua
@@ -2,10 +2,7 @@ local lpeg = require('lpeg')
local R, P, S
R, P, S = lpeg.R, lpeg.P, lpeg.S
local re = require('re')
-local utils = require('utils')
local Files = require('files')
-local stringify, equivalent
-stringify, equivalent = utils.stringify, utils.equivalent
local List, Dict, Text
do
local _obj_0 = require('containers')
@@ -187,10 +184,8 @@ do
load = load,
pairs = pairs,
ipairs = ipairs,
- _List = List,
- _Dict = Dict,
- stringify = stringify,
- utils = utils,
+ List = List,
+ Dict = Dict,
lpeg = lpeg,
re = re,
Files = Files,
@@ -431,6 +426,22 @@ do
end
NomsuCompiler.environment.COMPILE_ACTIONS = setmetatable({
__imported = Dict({ }),
+ [""] = function(self, tree, fn, ...)
+ local lua = LuaCode.Value(tree.source)
+ lua:append(self:compile(fn, compile_actions))
+ if not (lua:text():is_lua_id()) then
+ lua:parenthesize()
+ end
+ lua:append("(")
+ for i = 1, select('#', ...) do
+ if i > 1 then
+ lua:append(", ")
+ end
+ lua:append(self:compile(select(i, ...), compile_actions))
+ end
+ lua:append(")")
+ return lua
+ end,
["Lua"] = function(self, tree, code)
return add_lua_string_bits(self, 'statements', code)
end,
@@ -710,15 +721,12 @@ do
local filename = Source:from_string(info.source).filename
self:compile_error(tree, "The compile-time action here (" .. tostring(stub) .. ") failed to return any value.", "Look at the implementation of (" .. tostring(stub) .. ") in " .. tostring(filename) .. ":" .. tostring(info.linedefined) .. " and make sure it's returning something.")
end
- if SyntaxTree:is_instance(ret) then
- if ret == tree then
- local info = debug.getinfo(compile_action, "S")
- local filename = Source:from_string(info.source).filename
- self:compile_error(tree, "The compile-time action here (" .. tostring(stub) .. ") is producing an endless loop.", "Look at the implementation of (" .. tostring(stub) .. ") in " .. tostring(filename) .. ":" .. tostring(info.linedefined) .. " and make sure it's not just returning the original tree.")
- end
+ if not (SyntaxTree:is_instance(ret)) then
+ return ret
+ end
+ if ret ~= tree then
return self:compile(ret, compile_actions)
end
- return ret
end
local lua = LuaCode.Value(tree.source)
if tree.target then
@@ -871,7 +879,7 @@ do
lua:append("..")
end
if bit.type ~= "Text" then
- bit_lua = LuaCode.Value(bit.source, "stringify(", bit_lua, ")")
+ bit_lua = LuaCode.Value(bit.source, "tostring(", bit_lua, ")")
end
lua:append(bit_lua)
_continue_0 = true
@@ -891,7 +899,7 @@ do
end
return lua
elseif "List" == _exp_0 then
- local lua = LuaCode.Value(tree.source, "_List{")
+ local lua = LuaCode.Value(tree.source, "List{")
lua:concat_append((function()
local _accum_0 = { }
local _len_0 = 1
@@ -905,7 +913,7 @@ do
lua:append("}")
return lua
elseif "Dict" == _exp_0 then
- local lua = LuaCode.Value(tree.source, "_Dict{")
+ local lua = LuaCode.Value(tree.source, "Dict{")
lua:concat_append((function()
local _accum_0 = { }
local _len_0 = 1
diff --git a/nomsu_compiler.moon b/nomsu_compiler.moon
index a1194d9..6fe6501 100644
--- a/nomsu_compiler.moon
+++ b/nomsu_compiler.moon
@@ -12,9 +12,7 @@
lpeg = require 'lpeg'
{:R,:P,:S} = lpeg
re = require 're'
-utils = require 'utils'
Files = require 'files'
-{:stringify, :equivalent} = utils
{:List, :Dict, :Text} = require 'containers'
export colors, colored
colors = require 'consolecolors'
@@ -101,9 +99,9 @@ with NomsuCompiler
:table, :assert, :dofile, :loadstring, lua_type_of:type, :select, :math, :io, :load,
:pairs, :ipairs,
-- Nomsu types:
- _List:List, _Dict:Dict,
+ List:List, Dict:Dict,
-- Utilities and misc.
- stringify:stringify, utils:utils, lpeg:lpeg, re:re, Files:Files,
+ lpeg:lpeg, re:re, Files:Files,
:SyntaxTree, TESTS: Dict({}), globals: Dict({}),
:LuaCode, :NomsuCode, :Source
nomsu:NomsuCompiler
@@ -242,6 +240,17 @@ with NomsuCompiler
return lua
.environment.COMPILE_ACTIONS = setmetatable({
__imported: Dict{}
+ [""]: (tree, fn, ...)=>
+ lua = LuaCode.Value(tree.source)
+ lua\append @compile(fn, compile_actions)
+ lua\parenthesize! unless lua\text!\is_lua_id!
+ lua\append "("
+ for i=1,select('#',...)
+ lua\append(", ") if i > 1
+ lua\append @compile(select(i, ...), compile_actions)
+ lua\append ")"
+ return lua
+
["Lua"]: (tree, code)=>
return add_lua_string_bits(@, 'statements', code)
@@ -415,15 +424,10 @@ with NomsuCompiler
@compile_error tree,
"The compile-time action here (#{stub}) failed to return any value.",
"Look at the implementation of (#{stub}) in #{filename}:#{info.linedefined} and make sure it's returning something."
- if SyntaxTree\is_instance(ret)
- if ret == tree
- info = debug.getinfo(compile_action, "S")
- filename = Source\from_string(info.source).filename
- @compile_error tree,
- "The compile-time action here (#{stub}) is producing an endless loop.",
- "Look at the implementation of (#{stub}) in #{filename}:#{info.linedefined} and make sure it's not just returning the original tree."
+ unless SyntaxTree\is_instance(ret)
+ return ret
+ if ret != tree
return @compile(ret, compile_actions)
- return ret
lua = LuaCode.Value(tree.source)
if tree.target -- Method call
@@ -524,7 +528,7 @@ with NomsuCompiler
"Can't this as a string interpolation value, since it's not an expression."
if #lua.bits > 0 then lua\append ".."
if bit.type != "Text"
- bit_lua = LuaCode.Value(bit.source, "stringify(",bit_lua,")")
+ bit_lua = LuaCode.Value(bit.source, "tostring(",bit_lua,")")
lua\append bit_lua
if string_buffer ~= "" or #lua.bits == 0
@@ -536,13 +540,13 @@ with NomsuCompiler
return lua
when "List"
- lua = LuaCode.Value tree.source, "_List{"
+ lua = LuaCode.Value tree.source, "List{"
lua\concat_append([@compile(e, compile_actions) for e in *tree], ", ", ",\n ")
lua\append "}"
return lua
when "Dict"
- lua = LuaCode.Value tree.source, "_Dict{"
+ lua = LuaCode.Value tree.source, "Dict{"
lua\concat_append([@compile(e, compile_actions) for e in *tree], ", ", ",\n ")
lua\append "}"
return lua
diff --git a/utils.lua b/utils.lua
deleted file mode 100644
index d45dd2d..0000000
--- a/utils.lua
+++ /dev/null
@@ -1,334 +0,0 @@
--- A collection of helper utility functions
---
-local match, gmatch, gsub = string.match, string.gmatch, string.gsub
-local function is_list(t)
- if type(t) ~= 'table' then
- return false
- end
- local i = 1
- for _ in pairs(t) do
- if t[i] == nil then
- return false
- end
- i = i + 1
- end
- return true
-end
-
-local function size(t)
- local n = 0
- for _ in pairs(t) do
- n = n + 1
- end
- return n
-end
-
-local repr_behavior = function(x)
- local mt = getmetatable(x)
- if mt then
- local fn = rawget(mt, "__repr")
- if fn then return fn(x) end
- end
-end
-local function repr(x, mt_behavior)
- -- Create a string representation of the object that is close to the lua code that will
- -- reproduce the object (similar to Python's "repr" function)
- mt_behavior = mt_behavior or repr_behavior
- local x_type = type(x)
- if x_type == 'table' then
- local ret = mt_behavior(x)
- if ret then return ret end
- local ret = {}
- local i = 1
- for k, v in pairs(x) do
- if k == i then
- ret[#ret+1] = repr(v, mt_behavior)
- i = i + 1
- elseif type(k) == 'string' and match(k,"[_a-zA-Z][_a-zA-Z0-9]*") then
- ret[#ret+1] = k.."= "..repr(v, mt_behavior)
- else
- ret[#ret+1] = "["..repr(k, mt_behavior).."]= "..repr(v, mt_behavior)
- end
- end
- return "{"..table.concat(ret, ", ").."}"
- elseif x_type == 'string' then
- local escaped = gsub(x, "\\", "\\\\")
- escaped = gsub(escaped, "\n", "\\n")
- escaped = gsub(escaped, '"', '\\"')
- escaped = gsub(escaped, "[%c%z]", function(c) return ("\\%03d"):format(c:byte()) end)
- return '"'..escaped..'"'
- else
- return tostring(x)
- end
-end
-
-local stringify_behavior = function(x)
- local mt = getmetatable(x)
- if mt then
- local fn = rawget(mt, "__tostring")
- if fn then return fn(x) end
- end
-end
-local function stringify(x)
- if type(x) == 'string' then
- return x
- else
- return repr(x, stringify_behavior)
- end
-end
-
-local function split(str, sep)
- if sep == nil then
- sep = "%s"
- end
- local ret = {}
- for chunk in gmatch(str, "[^"..sep.."]+") do
- ret[#ret+1] = chunk
- end
- return ret
-end
-
-local function remove_from_list(list, item)
- local deleted, N = 0, #list
- for i=1,N do
- if list[i] == item then
- deleted = deleted + 1
- else
- list[i-deleted] = list[i]
- end
- end
- for i=N-deleted+1,N do list[i] = nil end
-end
-
-local function accumulate(co)
- local bits = {}
- for bit in coroutine.wrap(co) do
- bits[#bits+1] = bit
- end
- return bits
-end
-
-local function nth_to_last(list, n)
- return list[#list - n + 1]
-end
-
-local function keys(t)
- local ret = {}
- for k in pairs(t) do
- ret[#ret+1] = k
- end
- return ret
-end
-
-local function values(t)
- local ret = {}
- for _,v in pairs(t) do
- ret[#ret+1] = v
- end
- return ret
-end
-
-local function set(list)
- local ret = {}
- for i=1,#list do
- ret[list[i]] = true
- end
- return ret
-end
-
-local function deduplicate(list)
- local seen, deleted = {}, 0
- for i, item in ipairs(list) do
- if seen[item] then
- deleted = deleted + 1
- else
- seen[item] = true
- list[i-deleted] = list[i]
- end
- end
-end
-
-local function sum(t)
- local tot = 0
- for i=1,#t do
- tot = tot + t[i]
- end
- return tot
-end
-
-local function product(t)
- if #t > 5 and 0 < t[1] and t[1] < 1 then
- local log, log_prod = math.log, 0
- for i=1,#t do
- log_prod = log_prod + log(t[i])
- end
- return math.exp(log_prod)
- else
- local prod = 1
- for i=1,#t do
- prod = prod * t[i]
- end
- return prod
- end
-end
-
-local function all(t)
- for i=1,#t do
- if not t[i] then return false end
- end
- return true
-end
-
-local function any(t)
- for i=1,#t do
- if t[i] then return true end
- end
- return false
-end
-
-local function min(list, keyFn)
- if keyFn == nil then
- keyFn = (function(x)
- return x
- end)
- end
- if type(keyFn) == 'table' then
- local keyTable = keyFn
- keyFn = function(k)
- return keyTable[k]
- end
- end
- local best = list[1]
- local bestKey = keyFn(best)
- for i = 2, #list do
- local key = keyFn(list[i])
- if key < bestKey then
- best, bestKey = list[i], key
- end
- end
- return best
-end
-
-local function max(list, keyFn)
- if keyFn == nil then
- keyFn = (function(x)
- return x
- end)
- end
- if type(keyFn) == 'table' then
- local keyTable = keyFn
- keyFn = function(k)
- return keyTable[k]
- end
- end
- local best = list[1]
- local bestKey = keyFn(best)
- for i = 2, #list do
- local key = keyFn(list[i])
- if key > bestKey then
- best, bestKey = list[i], key
- end
- end
- return best
-end
-
-local function sort(list, keyFn, reverse)
- if keyFn == nil then
- keyFn = (function(x)
- return x
- end)
- end
- if reverse == nil then
- reverse = false
- end
- if type(keyFn) == 'table' then
- local keyTable = keyFn
- keyFn = function(k)
- return keyTable[k]
- end
- end
- table.sort(list, reverse
- and (function(x,y) return keyFn(x) > keyFn(y) end)
- or (function(x,y) return keyFn(x) < keyFn(y) end))
- return list
-end
-
-local function equivalent(x, y, depth)
- depth = depth or 0
- if rawequal(x, y) then
- return true
- end
- if type(x) ~= type(y) then
- return false
- end
- if type(x) ~= 'table' then return false end
- if getmetatable(x) ~= getmetatable(y) then
- return false
- end
- if depth >= 99 then
- error("Exceeded maximum comparison depth")
- end
- local checked = {}
- for k, v in pairs(x) do
- if not equivalent(y[k], v, depth + 1) then
- return false
- end
- checked[k] = true
- end
- for k, v in pairs(y) do
- if not checked[k] and not equivalent(x[k], v, depth + 1) then
- return false
- end
- end
- return true
-end
-
-local function key_for(t, value)
- for k, v in pairs(t) do
- if v == value then
- return k
- end
- end
- return nil
-end
-
-local function clamp(x, min, max)
- if x < min then
- return min
- elseif x > max then
- return max
- else
- return x
- end
-end
-
-local function mix(min, max, amount)
- return (1 - amount) * min + amount * max
-end
-
-local function sign(x)
- if x == 0 then
- return 0
- elseif x < 0 then
- return -1
- else
- return 1
- end
-end
-
-local function round(x, increment)
- if increment == nil then
- increment = 1
- end
- if x >= 0 then
- return math.floor(x / increment + .5) * increment
- else
- return math.ceil(x / increment - .5) * increment
- end
-end
-
-return {is_list=is_list, size=size, repr=repr, stringify=stringify, split=split,
- remove_from_list=remove_from_list, accumulate=accumulate, nth_to_last=nth_to_last,
- keys=keys, values=values, set=set, deduplicate=deduplicate, sum=sum, product=product,
- all=all, any=any, min=min, max=max, sort=sort, equivalent=equivalent, key_for=key_for,
- clamp=clamp, mix=mix, round=round}