In-progress (but working) overhaul of some elements including: function

calls, lib/thing.nom API, multi-assignments, varargs, etc.
This commit is contained in:
Bruce Hill 2018-11-17 14:38:05 -08:00
parent 34a3dd22a4
commit 7f47d42040
40 changed files with 553 additions and 459 deletions

View File

@ -77,7 +77,6 @@ local Code
do
local _class_0
local _base_0 = {
is_code = true,
text = function(self)
if self.__str == nil then
local buff, indent = { }, 0
@ -169,7 +168,7 @@ do
_continue_0 = true
break
end
if b.is_code then
if type(b) ~= 'string' then
b.dirty = error
end
bits[#bits + 1] = b
@ -223,7 +222,7 @@ do
end
end
bits[#bits + 1] = b
if b.is_code then
if type(b) ~= 'string' then
b.dirty = error
end
if not (type(b) == 'string') then
@ -246,7 +245,7 @@ do
end
for i = 1, n do
local b = select(i, ...)
if b.is_code then
if type(b) ~= 'string' then
b.dirty = error
end
bits[i] = b

View File

@ -42,7 +42,6 @@ class Source
return Source(@filename, @start+offset, @stop)
class Code
is_code: true
new: (...)=>
@bits = {}
@append(...)
@ -101,9 +100,7 @@ class Code
assert(b, "code bit is nil")
assert(not Source\is_instance(b), "code bit is a Source")
if b == '' then continue
b.dirty = error if b.is_code
--if type(b) != 'string' and not (type(b) == 'table' and b.is_code)
-- b = b\as_lua!
b.dirty = error if type(b) != 'string'
bits[#bits+1] = b
@dirty!
@ -140,7 +137,7 @@ class Code
else
bits[#bits+1] = joiner
bits[#bits+1] = b
b.dirty = error if b.is_code
b.dirty = error if type(b) != 'string'
unless type(b) == 'string'
b = b\text!
line = match(b, "\n([^\n]*)$")
@ -157,9 +154,7 @@ class Code
bits[i] = bits[i-n]
for i=1,n
b = select(i, ...)
b.dirty = error if b.is_code
--if type(b) != 'string' and not (type(b) == 'table' and b.is_code)
-- b = b\as_lua!
b.dirty = error if type(b) != 'string'
bits[i] = b
@dirty!

View File

@ -1,4 +1,4 @@
#!/usr/bin/env nomsu -V4.10.12.7
#!/usr/bin/env nomsu -V4.11
#
This file defines upgrades from Nomsu <2.3 to Nomsu 2.3

View File

@ -7,7 +7,7 @@ use "compatibility/compatibility.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
upgrade action "traceback" to "3.5.5.6" via (..)
[%] -> (barf "'traceback' has been deprecated")
-> (barf "'traceback' has been deprecated")
upgrade action "traceback 1" to "3.5.5.6" via (..)
[%] -> (barf "'traceback 1' has been deprecated")
-> (barf "'traceback 1' has been deprecated")

View File

@ -10,4 +10,4 @@ upgrade action (method %spec %body) to "3" as (my action %spec %body)
upgrade action (me) to "3" as %me
upgrade action (@) to "3" as %me
upgrade action "as" to "3" via (..)
[] -> (barf "Object API has changed. Use (%obj::action ...) instead of (as %obj: action ...)")
-> (barf "Object API has changed. Use (%obj::action ...) instead of (as %obj: action ...)")

View File

@ -44,24 +44,32 @@ upgrade action (% as lua statements) to "4.10.12.7" as (% as lua)
upgrade action (compile error at %pos %err hint %hint) to "4.10.12.7" as (..)
compile error at %pos %err %hint
# In old code, it was okay to have imports at the top of the file in the same chunk,
but changes to the API now require imports to be in their own file chunk in order
for compilation to work properly.
upgrade %tree to "4.10.12.7" as:
if (%tree.type == "FileChunks"):
%first_chunk = %tree.1
%first_has_use = (no)
%i = 1
repeat while (%i < (size of %first_chunk)):
if %first_has_use:
if ((%first_chunk.%i.type != "Action") or (%first_chunk.%i.stub != "use")):
%chunk2 = (%SyntaxTree {type: "Block"})
for %j in %i to (size of %first_chunk.%i):
%chunk2.((size of %chunk2) + 1) = %first_chunk.%i.%j
for %j in %i to (size of %first_chunk.%i):
%first_chunk.%i.%j = (nil)
%table.insert %tree 2 %chunk2
return %tree
%has_use = (no)
repeat while (%i <= (size of %first_chunk)):
if ((%first_chunk.%i.type == "Action") and (%first_chunk.%i.stub == "use")):
%has_use = (yes)
..else:
if ((%first_chunk.type == "Action") and (%first_chunk.stub == "use")):
%first_has_use = (yes)
if %has_use: go to (insert chunk)
%i += 1
return
=== (insert chunk) ===
[%chunk1, %chunk2] = [..]
SyntaxTree {type: "Block", source: %first_chunk.source}
SyntaxTree {type: "Block", source: %first_chunk.source}
for %j in 1 to (%i - 1):
%chunk1.%j = %first_chunk.%j
for %j in %i to (size of %first_chunk):
%chunk2.(%j - %i + 1) = %first_chunk.%j
%new_tree = (SyntaxTree {source: %tree.source, type: "FileChunks", 1: %chunk1, 2: %chunk2})
for %i in 2 to (size of %tree):
%new_tree.(%i + 1) = %tree.%i
return %new_tree

View File

@ -1,12 +1,34 @@
#!/usr/bin/env nomsu -V4.11
#
This file defines upgrades from Nomsu <4.11 to Nomsu 4.11
(deleting (if all of ...), etc. shorthand)
(overhaul of function literals, deleting (if all of ...), etc. shorthand)
use "compatibility/compatibility.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Overhaul of function literals:
upgrade action "call 1 with" to "4.11" via (..)
for (%tree %end_version):
%tree2 = {type: "Action", source: %tree.source, 1: %tree.2}
for %arg in %tree.4 at %i:
%tree2.(%i + 1) = %arg
return (SyntaxTree %tree2)
upgrade action (-> %yield_value) to "4.11" as (yield %yield_value)
# Replace set {%x:1, %y:2} with [%x, %y] = [1, 2]
upgrade action "set" to "4.11" via (..)
for (%tree %end_version):
[%lhs, %rhs] = [\[], \[]]
%lhs.source = %tree.(2).source
%rhs.source = %tree.(2).source
for %entry in %tree.2 at %i:
%lhs.%i = %entry.1
%rhs.%i = %entry.2
return (SyntaxTree {type: "Action", source: %tree.source, 1: %lhs, 2: "=", 3: %rhs})
# Deprecating shorthand functions:
upgrade action [if all of %items %body, if all of %items then %body] to "4.11" as (..)
if (all of %items) %body

View File

@ -6,7 +6,7 @@ use "compatibility/compatibility.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
upgrade action "local action" to "4.8.10" via (..)
[%tree, %end_version] ->:
for (%tree %end_version):
%spec = %tree.3
%body = %tree.4
if %spec.type is:
@ -20,7 +20,7 @@ upgrade action "local action" to "4.8.10" via (..)
return \(%spec means %body)
upgrade action "action" to "4.8.10" via (..)
[%tree, %end_version] ->:
for (%tree %end_version):
%spec = %tree.2
%body = %tree.3
if %body:
@ -37,7 +37,7 @@ upgrade action "action" to "4.8.10" via (..)
return \(%spec's meaning)
upgrade action "compile 1 to" to "4.8.10" via (..)
[%tree, %end_version] ->:
for (%tree %end_version):
%spec = %tree.2
%body = %tree.4
if %spec.type is:
@ -51,7 +51,7 @@ upgrade action "compile 1 to" to "4.8.10" via (..)
return \(%spec compiles to %body)
upgrade action "parse 1 as" to "4.8.10" via (..)
[%tree, %end_version] ->:
for (%tree %end_version):
%spec = %tree.2
%body = %tree.4
if %spec.type is:

View File

@ -6,6 +6,6 @@ use "compatibility/compatibility.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
upgrade action "if" to "4.9" via (..)
[%tree, %end_version] ->:
for (%tree %end_version):
if ((size of %tree) > 2): return %tree
return \(when %tree.2)

View File

@ -15,7 +15,7 @@ externally (upgrade action %stub to %version via %upgrade_fn) means:
%ACTION_UPGRADES.%version.%stub = %upgrade_fn
(upgrade %tree to %version as %body) parses as (..)
upgrade to %version via ([%, %end_version] -> (% with %tree -> %body))
upgrade to %version via ((% %end_version) -> (% with %tree -> %body))
(upgrade action %actions to %version as %body) compiles to:
if (%actions is "Action" syntax tree):
@ -91,7 +91,7 @@ externally [..]
for %k = %v in %tree:
add %k = (%v upgraded from %start_version to %end_version)
set %with_upgraded_args's metatable to (%tree's metatable)
%tree = (call %UPGRADES.%ver with [%with_upgraded_args, %end_version])
%tree = (%UPGRADES.%ver %with_upgraded_args %end_version)
%tree.shebang = "#!/usr/bin/env nomsu -V\%end_version\n"
return %tree

View File

@ -491,21 +491,21 @@ do
result[#result + 1] = tmp
end
return List(result)
end,
from_1_to = sub,
from = sub,
character = function(self, i)
return sub(self, i, i)
end
}
setmetatable(text_methods, {
__index = string2
})
setmetatable(string2, {
__index = error
})
getmetatable("").__methods = text_methods
getmetatable("").__index = function(self, i)
if type(i) == 'number' then
return sub(self, i, i)
elseif type(i) == 'table' then
return sub(self, i[1], i[2])
else
return text_methods[i]
end
end
getmetatable("").__index = text_methods
getmetatable("").__add = function(self, x)
return tostring(self) .. tostring(x)
end

View File

@ -196,16 +196,13 @@ do
i = tmp[1]
result[#result+1] = tmp
return List(result)
from_1_to: sub, from: sub,
character: (i)=> sub(@, i, i)
setmetatable(text_methods, {__index:string2})
setmetatable(string2, {__index:error})
getmetatable("").__methods = text_methods
getmetatable("").__index = (i)=>
-- Use [] for accessing text characters, or s[{3,4}] for s:sub(3,4)
if type(i) == 'number' then return sub(@, i, i)
elseif type(i) == 'table' then return sub(@, i[1], i[2])
else return text_methods[i]
getmetatable("").__index = text_methods
getmetatable("").__add = (x)=> tostring(@)..tostring(x)
return {:List, :Dict}

View File

@ -72,7 +72,7 @@ test:
# Metatable stuff
test:
%t = {}
set %t's metatable to {__tostring: [%] -> "XXX"}
set %t's metatable to {__tostring: % -> "XXX"}
assume ("\%t" == "XXX")
(set %dict's metatable to %metatable) compiles to "\

View File

@ -506,7 +506,7 @@ test:
assume ((result of: return 99) == 99)
# Inline thunk:
(result of %body) compiles to "\(what ([] -> %body) compiles to)()"
(result of %body) compiles to "\(what (-> %body) compiles to)()"
test:
%t = [1, [2, [[3], 4], 5, [[[6]]]]]
%flat = []

View File

@ -3,29 +3,40 @@
This file defines the code that creates and manipulates coroutines
use "core/metaprogramming.nom"
use "core/operators.nom"
use "core/control_flow.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test:
%nums = []
%co = (..)
coroutine:
-> 4
-> 5
repeat 3 times: -> 6
->:
yield 4
yield 5
repeat 3 times: yield 6
%nums = []
for % in coroutine %co:
%nums::add %
assume (%nums == [4, 5, 6, 6, 6]) or barf "Coroutine iteration failed"
[coroutine %body, generator %body] all compile to "\
..(function()
\(%body as lua)
end)"
(-> %) compiles to "coroutine.yield(true, \((% as lua expr) if % else "nil"))"
%d = {x:0}
%co2 = (..)
coroutine:
%d.x += 1
yield 1
%d.x += 1
yield
%d.x += 1
repeat while ((coroutine status of %co2) != "dead"):
resume %co2
assume %d.x == 3
(coroutine %body) parses as (coroutine from (-> %body))
(for % in coroutine %co %body) compiles to "\
..for _junk,\(% as lua expr) in coroutine.wrap(\(%co as lua expr)) do
..for \(% as lua expr) in coroutine_wrap(\(%co as lua expr)) do
\(%body as lua)
end"

View File

@ -16,7 +16,7 @@ use "core/control_flow.nom"
set %obj_by_id's metatable to {__mode: "v"}
%id_by_obj = {}
set %id_by_obj's metatable to {..}
__mode: "k", __index: [%self, %key] ->:
__mode: "k", __index: for (%self %key):
if (%key == (nil)):
return %self.%nil_surrogate

View File

@ -70,7 +70,7 @@ externally [all of %items, all %items] all mean:
return (no)
return (yes)
[all of %items, all %items] all compile to:
#[all of %items, all %items] all compile to:
unless (%items.type is "List"):
return \(all of %items)
@ -89,7 +89,7 @@ externally [any of %items, any %items] all mean:
return (yes)
return (no)
[any of %items, any %items] all compile to:
#[any of %items, any %items] all compile to:
unless (%items.type is "List"):
return \(any of %items)
@ -110,7 +110,7 @@ externally [sum of %items, sum %items] all mean:
%total += %
return %total
[sum of %items, sum %items] all compile to:
#[sum of %items, sum %items] all compile to:
unless (%items.type is "List"):
return \(sum of %items)
@ -128,7 +128,7 @@ externally [product of %items, product %items] all mean:
%prod *= %
return %prod
[product of %items, product %items] all compile to:
#[product of %items, product %items] all compile to:
unless (%items.type is "List"):
return \(product of %items)

View File

@ -3,7 +3,8 @@
This File contains actions for making actions and compile-time actions and some helper
functions to make that easier.
lua> "NOMSU_CORE_VERSION = 10\nNOMSU_LIB_VERSION = 7"
lua> "NOMSU_CORE_VERSION = 11"
lua> "NOMSU_LIB_VERSION = 8"
lua> "\
..do
local mangle_index = 0
@ -21,23 +22,38 @@ lua> "\
lua> "\
..compile.action["1 ->"] = function(compile, \%args, \%body)
local lua = LuaCode("(function(")
if SyntaxTree:is_instance(\%args) and \%args.type == "Action" then \%args = \%args:get_args() end
local lua_args = table.map(\%args, function(a) return SyntaxTree:is_instance(a) and compile(a):text(\
..) or a end)
lua:concat_append(lua_args, ", ")
if \%args and not \%body then \%args, \%body = {}, \%args end
local body_lua = SyntaxTree:is_instance(\%body) and compile(\%body) or \%body
if SyntaxTree:is_instance(\%body) and \%body.type ~= "Block" then body_lua:prepend("return ") end
body_lua:remove_free_vars(lua_args)
local lua = LuaCode("(function(")
if SyntaxTree:is_instance(\%args) and \%args.type == "Action" then \%args = \%args:get_args()
elseif SyntaxTree:is_instance(\%args) and \%args.type == "Var" then \%args = {\%args} end
for i, arg in ipairs(\%args) do
local arg_lua = SyntaxTree:is_instance(arg) and compile(arg):text() or arg
if arg_lua == "..." then
if i < #\%args then
compile_error_at(SyntaxTree:is_instance(arg) and arg or nil,
"Extra arguments must come last.", "Try removing any arguments after (*extra arguments*)")
end
elseif not arg_lua:is_lua_id() then
compile_error_at(SyntaxTree:is_instance(arg) and arg or nil,
"This does not compile to a Lua identifier, so it can't be used as a function argument.",
"This should probably be a Nomsu variable instead (like %x).")
end
lua:append(i > 1 and ", " or "", arg_lua)
body_lua:remove_free_vars({arg_lua})
end
body_lua:declare_locals()
lua:append(")\\n ", body_lua, "\\nend)")
return lua
end"
end
compile.action["->"] = compile.action["1 ->"]
compile.action["for"] = compile.action["1 ->"]"
lua> "\
..compile.action["what 1 compiles to"] = function(compile, \%action)
local lua = LuaCode("compile.action[", \%action.stub:as_lua(), "](")
local lua_args = table.map(\%action:get_args(), function(a) return compile(a) end)
local lua_args = table.map(\%action:get_args(), compile)
table.insert(lua_args, 1, "compile")
lua:concat_append(lua_args, ", ")
lua:append(")")
@ -100,17 +116,6 @@ lua> "\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(call %fn with %args) compiles to:
lua> "\
..local lua = LuaCode(compile(\%fn), "(")
if \%args.type == 'List' then
lua:concat_append(table.map(\%args, function(a) return compile(a) end), ", ")
else
lua:append('unpack(', compile(\%args), ')')
end
lua:append(")")
return lua"
test:
(foo %x) means "outer"
with local [(foo %)'s meaning]:
@ -124,27 +129,36 @@ test:
(%action means %body) compiles to:
lua> "\
..local fn_name = \%action.stub:as_lua_id()
local \%args = \%action:get_args()
local lua = LuaCode(fn_name, " = ", \(what (%args -> %body) compiles to))
lua:add_free_vars({fn_name})
..
local lua = LuaCode()
local fn_name = \%action.stub:as_lua_id()
if \%action.target then lua:append(compile(\%action.target), ".")
else lua:add_free_vars({fn_name}) end
lua:append(fn_name, " = ", \(what (%action -> %body) compiles to), ";")
return lua"
(%actions all mean %body) compiles to:
lua> "\
..local fn_name = \%actions[1].stub:as_lua_id()
local target = \%actions[1].target and compile(\%actions[1].target) or nil
local \%args = List(\%actions[1]:get_args())
local lua = \(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 = List(alias:get_args())
lua:append("\\n", alias_name, " = ")
if \%args == \%alias_args then
lua:append(fn_name)
lua:append("\\n")
if alias.target then
lua:append(compile(alias.target), ".")
else
lua:append(\(what (%alias_args -> %actions.1) compiles to))
lua:add_free_vars({alias_name})
end
lua:append(alias_name, " = ")
if \%args == \%alias_args then
if target then lua:append(target, ".") end
lua:append(fn_name, ";")
else
lua:append(\(what (%alias_args -> %actions.1) compiles to), ";")
end
end
return lua"
@ -182,11 +196,11 @@ test:
%y = %tmp
test:
set {%1: 1, %2: 2}
[%1, %2] = [1, 2]
swap %1 and %2
assume ((%1 == 2) and (%2 == 1)) or barf "\
..'parse % as %' failed on 'swap % and %'"
set {%tmp: 1, %tmp2: 2}
[%tmp, %tmp2] = [1, 2]
swap %tmp and %tmp2
assume ((%tmp == 2) and (%tmp2 == 1)) or barf "\
..'parse % as %' variable mangling failed."
@ -239,9 +253,15 @@ test:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[%action parses as %body] all parse as ([%action] all parse as %body)
(%tree as lua expr) compiles to "compile(\(=lua "compile(\%tree, true)"), true)"
#(%tree as lua expr) compiles to "compile(\(=lua "compile(\%tree, true)"), true)"
externally (%tree as lua expr) means:
lua> "\
..local tree_lua = compile(\%tree)
if \%tree.type == 'Block' then
tree_lua = LuaCode:from(\%tree.source, '(function()\n ', tree_lua, '\nend)()')
end
return tree_lua"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
externally [%var as lua identifier, %var as lua id] all mean:
lua> "\
@ -256,7 +276,14 @@ externally [%var as lua identifier, %var as lua id] all mean:
else error("Unknown type: "..tostring(\%var))
end"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test:
(num args (*extra arguments*)) means (select "#" (*extra arguments*))
assume (num args 1 2 3) == 3
(extra args (*extra arguments*)) means [*extra arguments*]
assume (extra args 1 2 3) == [1, 2, 3]
(third arg (*extra arguments*)) means (select 3 (*extra arguments*))
assume (third arg 5 6 7 8) == 7
(*extra arguments*) compiles to "..."
(% is syntax tree) compiles to "SyntaxTree:is_instance(\(% as lua expr))"
externally (% is %kind syntax tree) means (..)
@ -288,7 +315,7 @@ externally (%tree with vars %replacements) means (..)
(%tree has subtree %match_tree) compiles to "\
..(function()
local match_tree = \(%match_tree as lua expr)
for subtree in coroutine.wrap(function() \(%tree as lua expr):map(coroutine.yield) end) do
for subtree in coroutine_wrap(function() \(%tree as lua expr):map(yield) end) do
if subtree == match_tree then return true end
end
end)()"
@ -366,10 +393,20 @@ test:
[compile %block, compiled %block, %block compiled] all compile to "\
..compile(\(%block as lua))"
test:
(foo) means: return 100 200 300
assume (select 2 (foo)) == 200
# Return statement is wrapped in a do..end block because Lua is unhappy if you
put code after a return statement, unless you wrap it in a block.
(return %return_value) compiles to "\
..do return \(=lua "\%return_value and \(%return_value as lua expr) or ''") end"
(return (*extra arguments*)) compiles to:
lua> "\
..local lua = \(Lua "do return ")
for i=1,select('#',...) do
if i > 1 then lua:append(", ") end
lua:append(_1_as_lua((select(i, ...))))
end
lua:append(" end")
return lua"
# Literals
(yes) compiles to "true"

View File

@ -24,57 +24,58 @@ test:
test:
%x = 10
assume (%x == 10)
[%x, %y] = [10, 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."
%vals = [4, 5]
[%x, %y] = (unpack %vals)
assume ((%x == 4) and (%y == 5)) or barf "unpacking failed"
# Variable assignment operator
(%var = %value) compiles to:
lua> "\
..local \%var_lua = \(%var as lua expr)
local \%value_lua = \(%value as lua expr)
local lua = LuaCode(\%var_lua, ' = ', \%value_lua, ';')
..
local lua = LuaCode()
if \%var.type == "List" then
for i, \%assignment in ipairs(\%var) do
if i > 1 then lua:append(", ") end
local assignment_lua = \(%assignment as lua expr)
lua:append(assignment_lua)
if \%assignment.type == 'Var' then
lua:add_free_vars({assignment_lua:text()})
end
end
lua:append(' = ')
if \%value.type == "List" then
if #\%value ~= #\%var then
compile_error_at(\%value,
"This assignment has too "..(#\%value > #\%var and "many" or "few").." values.",
"Make sure it has the same number of values on the left and right hand side of the '=' operator.")
end
for i, \%val in ipairs(\%value) do
if i > 1 then lua:append(", ") end
local val_lua = \(%val as lua expr)
lua:append(val_lua)
end
lua:append(";")
else
lua:append(\(%value as lua expr), ';')
end
else
local var_lua = \(%var as lua expr)
lua:append(var_lua)
if \%var.type == 'Var' then
lua:add_free_vars({compile(\%var):text()})
lua:add_free_vars({var_lua:text()})
end
lua:append(' = ', \(%value as lua expr), ';')
end
return lua"
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."
# Simultaneous mutli-assignments like: x,y,z = 1,x,3;
# TODO: deprecate?
(set %assignments) compiles to:
assume (%assignments.type is "Dict") or barf "\
..Expected a Dict for the assignments part of '<- %' statement, not \%assignments"
lua> "\
..local lhs, rhs = LuaCode(), LuaCode()
for i, item in ipairs(\%assignments) do
local \%target, \%value = item[1], item[2]
\%value = \%value:map(function(t)
if SyntaxTree:is_instance(t) and t.type == "Action" and t.stub == "?" then
return \%target
end
end)
local target_lua = \(%target as lua)
local value_lua = \(%value as lua)
if \%target.type == "Var" then
lhs:add_free_vars({target_lua:text()})
end
if i > 1 then
lhs:append(", ")
rhs:append(", ")
end
lhs:append(target_lua)
rhs:append(value_lua)
end
return LuaCode(lhs, " = ", rhs, ";")"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test:
set {%foozle: "outer", %y: "outer"}
[%foozle, %y] = ["outer", "outer"]
externally (set global x local y) means:
external %foozle = "inner"
%y = "inner"
@ -82,7 +83,7 @@ test:
assume ((%foozle == "inner") and (%y == "outer")) or barf "external failed."
(external %var = %value) compiles to "\(%var as lua) = \(%value as lua)"
test:
set {%foozle: "outer", %y: "outer"}
[%foozle, %y] = ["outer", "outer"]
externally (set global x local y) means:
with external [%foozle]:
%foozle = "inner"
@ -97,7 +98,7 @@ test:
return %body_lua
test:
set {%x: 1, %y: 2}
[%x, %y] = [1, 2]
with {%z: nil, %x: 999}:
%z = 999
assume (%z == 999) or barf "'with' failed."
@ -149,29 +150,14 @@ test:
assume (%calls == 1) or barf "\
..Three-way comparison evaluated middle value multiple times"
(%x < %y < %z) parses as (..)
call ([%a, %b, %c] -> ((%a < %b) and (%b < %c))) with [%x, %y, %z]
(%x <= %y < %z) parses as (..)
call ([%a, %b, %c] -> ((%a <= %b) and (%b < %c))) with [%x, %y, %z]
(%x < %y <= %z) parses as (..)
call ([%a, %b, %c] -> ((%a < %b) and (%b <= %c))) with [%x, %y, %z]
(%x <= %y <= %z) parses as (..)
call ([%a, %b, %c] -> ((%a <= %b) and (%b <= %c))) with [%x, %y, %z]
(%x > %y > %z) parses as (..)
call ([%a, %b, %c] -> ((%a > %b) and (%b > %c))) with [%x, %y, %z]
(%x >= %y > %z) parses as (..)
call ([%a, %b, %c] -> ((%a >= %b) and (%b > %c))) with [%x, %y, %z]
(%x > %y >= %z) parses as (..)
call ([%a, %b, %c] -> ((%a > %b) and (%b >= %c))) with [%x, %y, %z]
(%x >= %y >= %z) parses as (..)
call ([%a, %b, %c] -> ((%a >= %b) and (%b >= %c))) with [%x, %y, %z]
(%x < %y < %z) parses as (((%a %b %c) -> ((%a < %b) and (%b < %c))) %x %y %z)
(%x <= %y < %z) parses as (((%a %b %c) -> ((%a <= %b) and (%b < %c))) %x %y %z)
(%x < %y <= %z) parses as (((%a %b %c) -> ((%a < %b) and (%b <= %c))) %x %y %z)
(%x <= %y <= %z) parses as (((%a %b %c) -> ((%a <= %b) and (%b <= %c))) %x %y %z)
(%x > %y > %z) parses as (((%a %b %c) -> ((%a > %b) and (%b > %c))) %x %y %z)
(%x >= %y > %z) parses as (((%a %b %c) -> ((%a >= %b) and (%b > %c))) %x %y %z)
(%x > %y >= %z) parses as (((%a %b %c) -> ((%a > %b) and (%b >= %c))) %x %y %z)
(%x >= %y >= %z) parses as (((%a %b %c) -> ((%a >= %b) and (%b >= %c))) %x %y %z)
# TODO: optimize for common case where x,y,z are all either variables or number literals
# Boolean Operators
@ -249,9 +235,9 @@ test:
assume (%x == 4) or barf "*= failed"
wrap %x around 3
assume (%x == 1) or barf "wrap around failed"
(%var += %) parses as (%var = (%var + %))
(%var -= %) parses as (%var = (%var - %))
(%var *= %) parses as (%var = (%var * %))
(%var += %) parses as (%var = ((%var or 0) + %))
(%var -= %) parses as (%var = ((%var or 0) - %))
(%var *= %) parses as (%var = ((%var or 1) * %))
(%var /= %) parses as (%var = (%var / %))
(%var ^= %) parses as (%var = (%var ^ %))
(%var and= %) parses as (%var = (%var and %))

View File

@ -63,4 +63,4 @@ externally (%num as hex) means:
for %name = %str in %escapes:
with {%lua: Lua (quote %str)}:
%compile.action.%name = ([] -> %lua)
%compile.action.%name = (-> %lua)

View File

@ -219,11 +219,11 @@ I think "chihuahuas" are worse than "corgis"
# 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
%こんにちは = "こんにちは"
@ -305,8 +305,8 @@ debug only:
%best_key = %key
return %best
# Function literals look like: [%x] -> (%x * %x)
say (best of [2, -3, 4, -8] according to ([%x] -> (%x * %x)))
# Function literals look like: %x -> (%x * %x)
say (best of [2, -3, 4, -8] according to (%x -> (%x * %x)))
# Or, you can use ((foo %)'s meaning) to access the function that gets called by (foo %)
(%x squared) means (%x * %x)

View File

@ -1,15 +1,27 @@
local import_to_1_from
import_to_1_from = function(host, to_import)
import_to_1_from = function(host, to_import, prefix)
if prefix == nil then
prefix = nil
end
do
local host_mt = getmetatable(host)
if host_mt then
if host_mt.__import then
host_mt.__import(host, to_import)
host_mt.__import(host, to_import, prefix)
return
end
end
end
for k, v in pairs(to_import) do
if k == to_import then
k = host
end
if v == to_import then
v = host
end
if prefix and type(k) == 'string' then
k = prefix .. k
end
host[k] = v
end
end
@ -20,19 +32,30 @@ local Importer = setmetatable({
__index = function(self, key)
return _imports[self][key]
end,
__import = function(self, to_import)
__import = function(self, to_import, prefix)
if prefix == nil then
prefix = nil
end
local imports = assert(_imports[self])
for k, v in pairs(to_import) do
local _continue_0 = false
repeat
if prefix and type(k) == 'string' then
k = prefix .. k
end
imports[k] = v
if v == to_import then
_continue_0 = true
break
end
local conflict = self[k]
if type(conflict) == 'table' then
import_to_1_from(conflict, v)
do
local conflict_mt = getmetatable(host)
if conflict_mt then
if conflict_mt.__import then
conflict_mt.__import(conflict, v, prefix)
end
end
end
_continue_0 = true
until true
@ -49,8 +72,8 @@ local Importer = setmetatable({
end
})
local _1_forked
_1_forked = function(self)
local f = Importer({ })
_1_forked = function(self, t)
local f = Importer(t or { })
_imports[f] = assert(_imports[self])
import_to_1_from(f, self)
return f

View File

@ -1,22 +1,36 @@
-- This file defines Importer, which is a type of table that can import from other tables
import_to_1_from = (host, to_import)->
import_to_1_from = (host, to_import, prefix=nil)->
if host_mt = getmetatable(host)
if host_mt.__import
host_mt.__import(host, to_import)
host_mt.__import(host, to_import, prefix)
return
for k,v in pairs(to_import)
if k == to_import then k = host
if v == to_import then v = host
if prefix and type(k) == 'string'
--print "PREFIXING #{k} -> #{prefix..k}"
k = prefix..k
--print("IMPORTED (#{k})")
host[k] = v
_imports = setmetatable({}, {__mode:"k"})
Importer = setmetatable({
__index: (key)=> _imports[@][key]
__import: (to_import)=>
__import: (to_import, prefix=nil)=>
imports = assert _imports[@]
for k,v in pairs(to_import)
if prefix and type(k) == 'string'
k = prefix..k
--print("IMPORTED (#{k})")
imports[k] = v
continue if v == to_import
conflict = @[k]
import_to_1_from(conflict, v) if type(conflict) == 'table'
if conflict_mt = getmetatable(host)
if conflict_mt.__import
conflict_mt.__import(conflict, v, prefix)
--__newindex: (k,v)=>
-- print("DEFINED (#{k})")
-- rawset(@, k, v)
}, {
__call: (t)=>
_imports[t] = {}
@ -24,8 +38,8 @@ Importer = setmetatable({
return t
})
_1_forked = =>
f = Importer{}
_1_forked = (t)=>
f = Importer(t or {})
_imports[f] = assert _imports[@]
import_to_1_from(f, @)
return f

View File

@ -4,12 +4,14 @@
https://tools.ietf.org/html/rfc4648
%b64_str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
%reverse_b64 = {: for %i in 1 to (size of %b64_str): add %b64_str.%i = (%i - 1)}
%reverse_b64."=" = 0
%b64_chars = [: for % in 1 to (size of %b64_str): add (%b64_str::character %)]
%reverse_b64 = {: for %c in %b64_chars at %i: add %c = (%i - 1)}
%reverse_b64."=" = 64
set %reverse_b64's metatable to {__index: -> 0}
test:
%cases = ["", "Zg==", "Zm8=", "Zm9v", "Zm9vYg==", "Zm9vYmE=", "Zm9vYmFy"]
for %len = %encoded in %cases:
%plain = "foobar".[1, %len - 1]
%plain = ("foobar"::from 1 to (%len - 1))
assume (base64 %plain) == %encoded
assume (base64 decode %encoded) == %plain
@ -17,20 +19,20 @@ externally [base64 %str, base64 encode %str, %str base64] all mean:
%chars = []
for %i in 1 to (size of %str) via 3:
%bytes = [=lua "\%str:byte(\%i, \(%i + 2))"]
%chars::add %b64_str.(((%bytes.1 & 252) >> 2) + 1)
%chars::add %b64_chars.(((%bytes.1 & 252) >> 2) + 1)
if (size of %bytes) is:
3:
%chars::add %b64_str.(((%bytes.1 & 3) << 4) + ((%bytes.2 & 240) >> 4) + 1)
%chars::add %b64_str.(((%bytes.2 & 15) << 2) + ((%bytes.3 & 192) >> 6) + 1)
%chars::add %b64_str.((%bytes.3 & 63) + 1)
%chars::add %b64_chars.(((%bytes.1 & 3) << 4) + ((%bytes.2 & 240) >> 4) + 1)
%chars::add %b64_chars.(((%bytes.2 & 15) << 2) + ((%bytes.3 & 192) >> 6) + 1)
%chars::add %b64_chars.((%bytes.3 & 63) + 1)
2:
%chars::add %b64_str.(((%bytes.1 & 3) << 4) + ((%bytes.2 & 240) >> 4) + 1)
%chars::add %b64_str.(((%bytes.2 & 15) << 2) + 1)
%chars::add %b64_chars.(((%bytes.1 & 3) << 4) + ((%bytes.2 & 240) >> 4) + 1)
%chars::add %b64_chars.(((%bytes.2 & 15) << 2) + 1)
%chars::add "="
1:
%chars::add %b64_str.(((%bytes.1 & 3) << 4) + 1)
%chars::add %b64_chars.(((%bytes.1 & 3) << 4) + 1)
%chars::add "="
%chars::add "="
return (%chars::joined)
@ -39,10 +41,10 @@ externally (chr %) means (=lua "string.char(\%)")
externally [decode base64 %str, %str base64 decoded, base64 decode %str] all mean:
%chars = []
for %i in 1 to (size of %str) via 4:
%indices = [: for % in %i to (%i + 3): add %reverse_b64.(%str.%)]
%indices = [: for %j in %i to (%i + 3): add %reverse_b64.(%str::character %j)]
%chars::add (chr ((%indices.1 << 2) + ((%indices.2 & 48) >> 4)))
if (%str.(%i + 2) == "="): stop
if ((%str::character (%i + 2)) == "="): stop
%chars::add (chr (((%indices.2 & 15) << 4) + ((%indices.3 & 60) >> 2)))
if (%str.(%i + 3) == "="): stop
if ((%str::character (%i + 3)) == "="): stop
%chars::add (chr (((%indices.3 & 3) << 6) + %indices.4))
return (%chars::joined)

View File

@ -19,7 +19,7 @@ for %name = %colornum in %colors:
#(=lua "COMPILE_ACTIONS").%name = (..)
[%nomsu, %tree] -> (Lua "'\\027[\(%colornum)m'")
%compile.action.%name = (..)
[%nomsu, %text] ->:
for (%compile %text):
if %text:
return (Lua "('\\027[\(%colornum)m'..\(%text as lua expr)..'\\027[0m')")
..else:

View File

@ -2,32 +2,28 @@
#
A library for simple object oriented programming.
%globals.METAMETHOD_MAP = {..}
"as text": "__tostring", "clean up": "__gc", "+": "__add", "-": "__sub"
"*": "__mul", "/": "__div", negative: "__unm", "//": "__idiv", mod: "__mod"
"^": "__pow", "&": "__band", "|": "__bor", "~": "__bxor", "~": "__bnot"
"<<": "__bshl", ">>": "__bshr", "==": "__eq", "<": "__lt", "<=": "__le"
"set 1 =": "__newindex", size: "__len", iterate: "__ipairs", "iterate all": "__pairs"
test:
an (Empty) is a thing
a (Dog) is a thing:
that can (set up) by:
[%it, %its] = [Dog, Dog]
(%its::set up) means:
%its.barks or= 0
whose [bark, woof] all mean:
[%its::bark, %its::woof] all mean:
%barks = [: for % in 1 to %its.barks: add "Bark!"]
return (%barks::joined with " ")
that can (get pissed off) by: %its.barks += 1
(%it::gets pissed off) means:
%it.barks += 1
(Dog).genus = "Canus"
%d = (a Dog with {barks: 2})
assume "\%d" == "Dog {barks: 2}"
assume (type of %d) == "Dog"
assume (%d is a "Dog")
assume %d.barks == 2
assume ((%d::bark) == "Bark! Bark!")
assume ((%d::woof) == "Bark! Bark!")
%d::get pissed off
%d::gets pissed off
assume (%d.barks == 3)
assume ((%d::bark) == "Bark! Bark! Bark!")
assume (%d.genus == "Canus")
@ -40,112 +36,101 @@ test:
assume ((%d::bark) == "Bark!")
a (Corgi) is a thing:
that can [set up, get pissed off] like a (Dog)
whose (sploot) means "splooted"
whose [bark, woof] all mean:
[%it, %its] = [Corgi, Corgi]
%it [set up, gets pissed off] like a (Dog)
(%it::as text) means "Dogloaf \({: for %k = %v in %it: add %k = %v })"
(%its::sploot) means "sploooot"
[%its::bark, %its::woof] all mean:
%barks = [: for % in 1 to %its.barks: add "Yip!"]
return (%barks::joined with " ")
%corg = (a Corgi)
assume (%corg.barks == 0)
assume "\%corg" == "Dogloaf {barks: 0}"
with {%d: a Corgi with {barks: 1}}:
assume ((%d::sploot) == "splooted") or barf "subclass method failed"
assume ((%d::sploot) == "sploooot") or barf "subclass method failed"
assume ((%d::bark) == "Yip!") or barf "inheritance failed"
assume ((%d::woof) == "Yip!")
with {%d: a Dog with {barks: 2}}:
assume ((%d::bark) == "Bark! Bark!")
[..]
that can %actions by %body, whose %actions means %body
whose %actions all mean %body
..all compile to:
unless (%actions.type == "List"):
%actions = [%actions]
lua> "\
..local fn_name = \%actions[1].stub:as_lua_id()
local \%args = List{\(\%its), unpack(\%actions[1]:get_args())}
local lua = LuaCode("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 = List{\(\%its), unpack(alias:get_args())}
lua:append("\\nclass.", alias_name, " = ")
if \%alias_args == \%args then
lua:append("class.", fn_name)
else
lua:append("function(")
lua:concat_append(table.map(\%alias_args, function(a) return compile(a) end), ", ")
lua:append(")\\n return class.", fn_name, "(")
lua:concat_append(table.map(\%args, function(a) return compile(a) end), ", ")
lua:append(")\\nend")
end
end
return lua"
a (Vec) is a thing with {x, y}:
%its = (Vec)
(%its::+ %other) means (Vec {x: %its.x + %other.x, y: %its.y + %other.y})
assume ((Vec {x: 1, y:2 }) + (Vec {x: 10, y: 10})) == (Vec {x: 11, y: 12})
assume (((Vec {x: 1, y:2 }) + (Vec {x: 10, y: 10})) != (Vec {x: 0, y: 0}))
[..]
that can %actions like a %class, that can %actions like an %class
that has %actions like a %class, that has %actions like an %class
%it can %actions like a %class, %it can %actions like an %class
%it has %actions like a %class, %it has %actions like an %class
%it %actions like a %class, %it %actions like an %class
..all compile to:
%lua = (Lua "")
%class_expr = (%class as lua expr)
%lines = []
for %a in %actions:
%lines::add "class.\(%a.stub::as lua id) = \%class_expr.\(%a.stub::as lua id)"
%lines::add "\(%it as lua expr).\(%a.stub::as lua id) = \%class_expr.\(%a.stub::as lua id)"
%lua::add %lines joined with "\n"
return %lua
%METAMETHOD_MAP = {..}
"as text": "__tostring", "clean up": "__gc", "+": "__add", "-": "__sub"
"*": "__mul", "/": "__div", negative: "__unm", "//": "__idiv", mod: "__mod"
"^": "__pow", "&": "__band", "|": "__bor", "~": "__bxor", "~": "__bnot"
"<<": "__bshl", ">>": "__bshr", "==": "__eq", "<": "__lt", "<=": "__le"
"set 1 =": "__newindex", size: "__len", iterate: "__ipairs", "iterate all": "__pairs"
((% as text like a dict)'s meaning) = ({}'s metatable).__tostring
externally (..)
a class named %classname with %members ((initialize %it)'s meaning)
..means:
%class = {__type: %classname}
%class.__index = %class
%class.class = %class
%class.__tostring = (% -> "\(%.__type) \(% as text like a dict)")
%class.__eq = ({}'s metatable).__eq
%class.__len = ({}'s metatable).__len
if %members:
%class.__members = %members
%class.__newindex = (..)
for (%its %key = %value):
if %members.%key:
rawset %its %key %value
..else:
barf "Cannot set \%key, it's not one of the allowed member fields."
set %class's metatable to {..}
__tostring: (%class) -> %class.__type
__call: for (%class with %initial_values):
if (%initial_values == (nil)): return %class
set %initial_values's metatable to %class
if %initial_values.set_up: %initial_values::set up
return %initial_values
if ((initialize)'s meaning):
initialize %class
for %stub = %metamethod in %METAMETHOD_MAP:
if %class.(%stub::as lua id):
%class.%metamethod = %class.(%stub::as lua id)
return %class
[..]
a %classname is a thing with %members %class_body
an %classname is a thing with %members %class_body
..all compile to:
unless (%classname.type == "Action"):
compile error at %classname "\
..Expected this to be an action, not a \%classname.type"
for % in %classname:
unless (% is text):
compile error at % "Class names should not have arguments."
%class_id = (%classname.stub::as lua id)
if %class_body:
%body_lua = (%class_body as lua)
%body_lua::remove free vars [%class_id]
%body_lua::declare locals
return (..)
Lua "\
..do
local class = {name=\(quote %classname.stub)}
class.__type = class.name
setmetatable(class, {
__tostring=function(cls) return cls.name end,
__call=function(cls, inst)
inst = setmetatable(inst or {}, cls)
if inst.set_up then inst:set_up() end
return inst
end,
})
class.__members = \(%members as lua expr)
_ENV[("a "..class.name):as_lua_id()] = class
_ENV[("an "..class.name):as_lua_id()] = class
_ENV[("a "..class.name.." with"):as_lua_id()] = class
_ENV[("an "..class.name.." with"):as_lua_id()] = class
_ENV[class.name:as_lua_id()] = function() return class end
class.__index = class
class.class = class
local dict_tostring = getmetatable(Dict{}).__tostring
class.__tostring = function(inst)
return inst.name..dict_tostring(inst)
end
\((%class_body as lua) if %class_body else "")
for stub,metamethod in pairs(globals.METAMETHOD_MAP) do
class[metamethod] = class[stub:as_lua_id()]
end
if class.__members then
assert(select(2, next(class.__members)) == true)
getmetatable(class).__newindex = function(its, key, value)
if class.__members[key] then
rawset(its, key, value)
else error("Not a valid member: "..tostring(key)) end
end
end
end"
..\%class_id = a_class_named_1_with(\(quote %classname.stub), \(%members as lua)\(..)
(Lua ", function(\%class_id)\n \%body_lua\nend") if %class_body else ""
..)
a_\%class_id = function(initial_values) return \(%classname.stub::as lua id)(initial_values or {}) end
an_\%class_id, a_\(%class_id)_with, an_\(%class_id)_with = \
..a_\%class_id, a_\%class_id, a_\%class_id"
[a %classname is a thing %class_body, an %classname is a thing] all parse as (..)
a %classname is a thing with (nil) %class_body

View File

@ -1,3 +0,0 @@
#!/usr/bin/env nomsu -V4.10.12.7
# This file sets the current library version.
lua> "NOMSU_LIB_VERSION = 7"

View File

@ -118,14 +118,14 @@ inline_action (Action):
( (inline_arg (ws* (inline_arg / word))+)
/ (word (ws* (inline_arg / word))*))
(ws* inline_block)?
inline_arg: inline_expression / inline_block
inline_arg: inline_expression / inline_block / "(" ws* ")"
action (Action):
!section_division
({:target: (expression / "(" inline_block ")" / indented_block) :}
((ws* "\")? eol nl_nodent "..")? ws* "::" ((ws* "\")? eol nl_nodent "..")? ws*)?
( (arg (((ws* "\")? eol nl_nodent "..")? ws* (arg / word))+)
/ (word (((ws* "\")? eol nl_nodent "..")? ws* (arg / word))*))
arg: expression / inline_block / indented_block
arg: expression / inline_block / indented_block / "(" ws* ")"
word: !number { operator_char+ / ident_char+ }

View File

@ -202,12 +202,12 @@ run = function()
for chunk_no, chunk in ipairs(tree) do
local lua = nomsu_environment.compile(chunk)
lua:declare_locals()
nomsu_environment.run_1_in(chunk, nomsu_environment)
output:write((chunk_no > 1) and '\n' or '', "-- File " .. tostring(filename) .. " chunk #" .. tostring(chunk_no) .. "\n")
output:write(tostring(lua), "\n")
lua:prepend((chunk_no > 1) and '\n' or '', "-- File " .. tostring(filename) .. " chunk #" .. tostring(chunk_no) .. "\n")
if args.verbose then
print(tostring(lua))
print(lua:text())
end
nomsu_environment.run_1_in(chunk, nomsu_environment)
output:write(lua:text(), "\n")
end
print(("Compiled %-25s -> %s"):format(filename, filename:gsub("%.nom$", ".lua")))
output:close()
@ -221,12 +221,12 @@ run = function()
tree
}
end
for _index_1 = 1, #tree do
local chunk = tree[_index_1]
for chunk_no, chunk in ipairs(tree) do
local lua = nomsu_environment.compile(chunk)
lua:declare_locals()
nomsu_environment.run_1_in(chunk, nomsu_environment)
print(tostring(lua))
lua:prepend((chunk_no > 1) and '\n' or '', "-- File " .. tostring(filename) .. " chunk #" .. tostring(chunk_no) .. "\n")
print(lua:text())
nomsu_environment.run_1_in(lua, nomsu_environment)
end
else
nomsu_environment.run_file_1_in(filename, nomsu_environment, 0)

View File

@ -131,10 +131,10 @@ run = ->
for chunk_no, chunk in ipairs tree
lua = nomsu_environment.compile(chunk)
lua\declare_locals!
lua\prepend((chunk_no > 1) and '\n' or '', "-- File #{filename} chunk ##{chunk_no}\n")
if args.verbose then print(lua\text!)
nomsu_environment.run_1_in(chunk, nomsu_environment)
output\write((chunk_no > 1) and '\n' or '', "-- File #{filename} chunk ##{chunk_no}\n")
output\write(tostring(lua), "\n")
if args.verbose then print(tostring(lua))
output\write(lua\text!, "\n")
print ("Compiled %-25s -> %s")\format(filename, filename\gsub("%.nom$", ".lua"))
output\close!
elseif args.verbose
@ -143,11 +143,12 @@ run = ->
code = NomsuCode\from(source, code)
tree = nomsu_environment._1_parsed(code)
tree = {tree} unless tree.type == 'FileChunks'
for chunk in *tree
for chunk_no, chunk in ipairs tree
lua = nomsu_environment.compile(chunk)
lua\declare_locals!
nomsu_environment.run_1_in(chunk, nomsu_environment)
print(tostring(lua))
lua\prepend((chunk_no > 1) and '\n' or '', "-- File #{filename} chunk ##{chunk_no}\n")
print(lua\text!)
nomsu_environment.run_1_in(lua, nomsu_environment)
else
-- Just run the file
nomsu_environment.run_file_1_in(filename, nomsu_environment, 0)

View File

@ -29,6 +29,7 @@ do
local _obj_0 = require('importer')
Importer, import_to_1_from, _1_forked = _obj_0.Importer, _obj_0.import_to_1_from, _obj_0._1_forked
end
local Files = require("files")
table.map = function(t, fn)
return setmetatable((function()
local _accum_0 = { }
@ -42,18 +43,28 @@ table.map = function(t, fn)
end
local pretty_error = require("pretty_errors")
local compile_error
compile_error = function(tree, err_msg, hint)
compile_error = function(source, err_msg, hint)
if hint == nil then
hint = nil
end
local file
if SyntaxTree:is_instance(source) then
file = source:get_source_file()
source = source.source
elseif type(source) == 'string' then
source = Source:from_string(source)
end
if source and not file then
file = Files.read(source.filename)
end
local err_str = pretty_error({
title = "Compile error",
error = err_msg,
hint = hint,
source = tree:get_source_file(),
start = tree.source.start,
stop = tree.source.stop,
filename = tree.source.filename
source = file,
start = source.start,
stop = source.stop,
filename = source.filename
})
return error(err_str, 0)
end
@ -78,7 +89,7 @@ local compile = setmetatable({
if i > 1 then
lua:append(", ")
end
lua:append(compile(select(i, ...)))
lua:append(compile((select(i, ...))))
end
lua:append(")")
return lua
@ -138,6 +149,9 @@ local compile = setmetatable({
["use"] = function(compile, path)
return LuaCode("run_file_1_in(" .. tostring(compile(path)) .. ", _ENV, OPTIMIZATION)")
end,
["use 1 with prefix"] = function(compile, path, prefix)
return LuaCode("run_file_1_in(" .. tostring(compile(path)) .. ", _ENV, OPTIMIZATION, ", compile(prefix), ")")
end,
["tests"] = function(compile)
return LuaCode("TESTS")
end,
@ -165,18 +179,13 @@ local compile = setmetatable({
end
})
}, {
__import = function(self, other)
import_to_1_from(self.action, other.action)
end,
__call = function(compile, tree, force_value)
if force_value == nil then
force_value = false
end
__import = import_to_1_from,
__call = function(compile, tree)
local _exp_0 = tree.type
if "Action" == _exp_0 then
local stub = tree.stub
local compile_action = compile.action[stub]
if not compile_action and math_expression:match(stub) then
if not compile_action and not tree.target and math_expression:match(stub) then
local lua = LuaCode:from(tree.source)
for i, tok in ipairs(tree) do
if type(tok) == 'string' then
@ -226,7 +235,7 @@ local compile = setmetatable({
if tree.target then
local target_lua = compile(tree.target)
local target_text = target_lua:text()
if target_text:match("^%(.*%)$") or target_text:match("^[_a-zA-Z][_a-zA-Z0-9.]*$") then
if target_text:match("^%(.*%)$") or target_text:match("^[_a-zA-Z][_a-zA-Z0-9.]*$") or tree.target.type == "IndexChain" then
lua:append(target_lua, ":")
else
lua:append("(", target_lua, "):")
@ -241,7 +250,10 @@ local compile = setmetatable({
_continue_0 = true
break
end
local arg_lua = compile(tok, true)
local arg_lua = compile(tok)
if tok.type == "Block" then
arg_lua = LuaCode:from(tok.source, "(function()\n ", arg_lua, "\nend)()")
end
insert(args, arg_lua)
_continue_0 = true
until true
@ -287,28 +299,14 @@ local compile = setmetatable({
lua:append("}")
return lua
elseif "Block" == _exp_0 then
if not force_value then
local lua = LuaCode:from(tree.source)
lua:concat_append((function()
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #tree do
local line = tree[_index_0]
_accum_0[_len_0] = compile(line)
_len_0 = _len_0 + 1
end
return _accum_0
end)(), "\n")
return lua
else
local lua = LuaCode:from(tree.source)
lua:append("((function()")
for i, line in ipairs(tree) do
lua:append("\n ", compile(line))
if i > 1 then
lua:append("\n")
end
lua:append(compile(line))
end
lua:append("\nend)())")
return lua
end
elseif "Text" == _exp_0 then
local lua = LuaCode:from(tree.source)
local string_buffer = ""
@ -352,7 +350,7 @@ local compile = setmetatable({
end
return lua
elseif "List" == _exp_0 or "Dict" == _exp_0 then
local lua = LuaCode:from(tree.source, tostring(tree.type) .. "{")
local lua = LuaCode:from(tree.source)
local i = 1
local sep = ''
while i <= #tree do
@ -371,7 +369,11 @@ local compile = setmetatable({
end
i = i + 1
end
lua:append("}")
if lua:is_multiline() then
lua = LuaCode:from(tree.source, tostring(tree.type) .. "{\n ", lua, "\n}")
else
lua = LuaCode:from(tree.source, tostring(tree.type) .. "{", lua, "}")
end
if i <= #tree then
lua = LuaCode:from(tree.source, "(function()\n local comprehension = ", lua)
if tree.type == "List" then
@ -441,4 +443,7 @@ local compile = setmetatable({
end
end
})
return compile
return {
compile = compile,
compile_error = compile_error
}

View File

@ -19,19 +19,26 @@ unpack or= table.unpack
{:LuaCode, :Source} = require "code_obj"
SyntaxTree = require "syntax_tree"
{:Importer, :import_to_1_from, :_1_forked} = require 'importer'
Files = require "files"
table.map = (t, fn)-> setmetatable([fn(v) for _,v in ipairs(t)], getmetatable(t))
-- TODO:
-- Re-implement nomsu-to-lua comment translation?
-- TODO: de-duplicate this
pretty_error = require("pretty_errors")
compile_error = (tree, err_msg, hint=nil)->
compile_error = (source, err_msg, hint=nil)->
local file
if SyntaxTree\is_instance(source)
file = source\get_source_file!
source = source.source
elseif type(source) == 'string'
source = Source\from_string(source)
if source and not file
file = Files.read(source.filename)
err_str = pretty_error{
title: "Compile error"
error:err_msg, hint:hint, source:tree\get_source_file!
start:tree.source.start, stop:tree.source.stop, filename:tree.source.filename
error:err_msg, hint:hint, source:file
start:source.start, stop:source.stop, filename:source.filename
}
error(err_str, 0)
{:tree_to_nomsu, :tree_to_inline_nomsu} = require "nomsu_decompiler"
@ -53,7 +60,7 @@ compile = setmetatable({
lua\append "("
for i=1,select('#',...)
lua\append(", ") if i > 1
lua\append compile(select(i, ...))
lua\append compile((select(i, ...)))
lua\append ")"
return lua
@ -94,11 +101,10 @@ compile = setmetatable({
["= lua"]: (compile, code)-> compile.action["lua >"](compile, code)
["use"]: (compile, path)->
--if path.type == 'Text' and #path == 1 and type(path[1]) == 'string'
-- unless import_to_1_from(compile, path[1])
-- compile_error tree, "Could not find anything to import for #{path}"
return LuaCode("run_file_1_in(#{compile(path)}, _ENV, OPTIMIZATION)")
["use"]: (compile, path)-> LuaCode("run_file_1_in(#{compile(path)}, _ENV, OPTIMIZATION)")
["use 1 with prefix"]: (compile, path, prefix)->
LuaCode("run_file_1_in(#{compile(path)}, _ENV, OPTIMIZATION, ", compile(prefix), ")")
["tests"]: (compile)-> LuaCode("TESTS")
["test"]: (compile, body)->
@ -114,15 +120,13 @@ compile = setmetatable({
["nomsu environment"]: (compile)-> LuaCode("_ENV")
}
}, {
__import: (other)=>
import_to_1_from(@action, other.action)
return
__call: (compile, tree, force_value=false)->
__import: import_to_1_from
__call: (compile, tree)->
switch tree.type
when "Action"
stub = tree.stub
compile_action = compile.action[stub]
if not compile_action and math_expression\match(stub)
if not compile_action and not tree.target and math_expression\match(stub)
lua = LuaCode\from(tree.source)
for i,tok in ipairs tree
if type(tok) == 'string'
@ -155,7 +159,9 @@ compile = setmetatable({
if tree.target -- Method call
target_lua = compile tree.target
target_text = target_lua\text!
if target_text\match("^%(.*%)$") or target_text\match("^[_a-zA-Z][_a-zA-Z0-9.]*$")
-- TODO: this parenthesizing is maybe overly conservative
if target_text\match("^%(.*%)$") or target_text\match("^[_a-zA-Z][_a-zA-Z0-9.]*$") or
tree.target.type == "IndexChain"
lua\append target_lua, ":"
else
lua\append "(", target_lua, "):"
@ -163,7 +169,9 @@ compile = setmetatable({
args = {}
for i, tok in ipairs tree
if type(tok) == "string" then continue
arg_lua = compile(tok, true)
arg_lua = compile(tok)
if tok.type == "Block"
arg_lua = LuaCode\from(tok.source, "(function()\n ", arg_lua, "\nend)()")
insert args, arg_lua
lua\concat_append args, ", "
lua\append ")"
@ -196,16 +204,10 @@ compile = setmetatable({
return lua
when "Block"
if not force_value
lua = LuaCode\from(tree.source)
lua\concat_append([compile(line) for line in *tree], "\n")
return lua
else
lua = LuaCode\from(tree.source)
lua\append("((function()")
for i, line in ipairs(tree)
lua\append "\n ", compile(line)
lua\append("\nend)())")
for i, line in ipairs tree
if i > 1 then lua\append "\n"
lua\append compile(line)
return lua
when "Text"
@ -234,7 +236,7 @@ compile = setmetatable({
return lua
when "List", "Dict"
lua = LuaCode\from tree.source, "#{tree.type}{"
lua = LuaCode\from tree.source
i = 1
sep = ''
while i <= #tree
@ -250,7 +252,12 @@ compile = setmetatable({
lua\append item_lua
sep = ', '
i += 1
lua\append "}"
if lua\is_multiline!
lua = LuaCode\from tree.source, "#{tree.type}{\n ", lua, "\n}"
else
lua = LuaCode\from tree.source, "#{tree.type}{", lua, "}"
-- List/dict comprehenstion
if i <= #tree
lua = LuaCode\from tree.source, "(function()\n local comprehension = ", lua
@ -269,6 +276,7 @@ compile = setmetatable({
lua\append "comprehension[#comprehension+1] = ", compile(tree[i])
i += 1
lua\append "\n return comprehension\nend)()"
return lua
when "DictEntry"
@ -329,4 +337,4 @@ compile = setmetatable({
})
return compile
return {:compile, :compile_error}

View File

@ -88,6 +88,9 @@ tree_to_inline_nomsu = function(tree)
nomsu:append(arg_nomsu)
end
end
if #tree == 1 and type(tree[1]) ~= "string" then
nomsu:append("()")
end
return nomsu
elseif "EscapedNomsu" == _exp_0 then
local inner_nomsu = tree_to_inline_nomsu(tree[1])
@ -326,6 +329,13 @@ tree_to_nomsu = function(tree)
end
end
nomsu:append(next_space, words)
next_space = " "
end
if #tree == 1 and type(tree[1]) ~= "string" then
if next_space == " " then
next_space = ""
end
nomsu:append(next_space, "()")
end
return nomsu
elseif "EscapedNomsu" == _exp_0 then

View File

@ -58,6 +58,8 @@ tree_to_inline_nomsu = (tree)->
if bit.type == "Action"
arg_nomsu\parenthesize!
nomsu\append arg_nomsu
if #tree == 1 and type(tree[1]) != "string"
nomsu\append "()"
return nomsu
when "EscapedNomsu"
@ -251,6 +253,11 @@ tree_to_nomsu = (tree)->
elseif word_buffer[1] == "'"
next_space = ""
nomsu\append next_space, words
next_space = " "
if #tree == 1 and type(tree[1]) != "string"
if next_space == " " then next_space = ""
nomsu\append next_space, "()"
return nomsu

View File

@ -51,7 +51,11 @@ do
local _obj_0 = require("nomsu_decompiler")
tree_to_nomsu, tree_to_inline_nomsu = _obj_0.tree_to_nomsu, _obj_0.tree_to_inline_nomsu
end
local compile = require('nomsu_compiler')
local compile, compile_error
do
local _obj_0 = require('nomsu_compiler')
compile, compile_error = _obj_0.compile, _obj_0.compile_error
end
local _currently_running_files = List({ })
local nomsu_environment = Importer({
NOMSU_COMPILER_VERSION = 12,
@ -59,10 +63,14 @@ local nomsu_environment = Importer({
next = next,
unpack = unpack or table.unpack,
setmetatable = setmetatable,
coroutine = coroutine,
rawequal = rawequal,
getmetatable = getmetatable,
pcall = pcall,
yield = coroutine.yield,
resume = coroutine.resume,
coroutine_status_of = coroutine.status,
coroutine_wrap = coroutine.wrap,
coroutine_from = coroutine.create,
error = error,
package = package,
os = os,
@ -115,6 +123,7 @@ local nomsu_environment = Importer({
_1_as_inline_nomsu = tree_to_inline_nomsu,
compile = compile,
_1_as_lua = compile,
compile_error_at = compile_error,
_1_forked = _1_forked,
import_to_1_from = import_to_1_from,
_1_parsed = function(nomsu_code)
@ -131,10 +140,11 @@ local nomsu_environment = Importer({
if tree.shebang then
tree.version = tree.shebang:match("nomsu %-V[ ]*([%d.]*)")
end
local errs = { }
local find_errors
find_errors = function(t)
if t.type == "Error" then
return coroutine.yield(t)
errs[#errs + 1] = t
else
for k, v in pairs(t) do
local _continue_0 = false
@ -152,18 +162,7 @@ local nomsu_environment = Importer({
end
end
end
local errs
do
local _accum_0 = { }
local _len_0 = 1
for err in coroutine.wrap(function()
return find_errors(tree)
end) do
_accum_0[_len_0] = err
_len_0 = _len_0 + 1
end
errs = _accum_0
end
find_errors(tree)
local num_errs = #errs
if num_errs > 0 then
local err_strings
@ -278,12 +277,15 @@ local nomsu_environment = Importer({
end
end,
FILE_CACHE = { },
run_file_1_in = function(path, environment, optimization)
run_file_1_in = function(path, environment, optimization, prefix)
if prefix == nil then
prefix = nil
end
if not optimization then
optimization = environment.OPTIMIZATION
end
if environment.FILE_CACHE[path] then
import_to_1_from(environment, environment.FILE_CACHE[path])
import_to_1_from(environment, environment.FILE_CACHE[path], prefix)
return
end
if _currently_running_files:has(path) then
@ -294,7 +296,6 @@ local nomsu_environment = Importer({
end
_currently_running_files:add(path)
local mod = _1_forked(environment)
mod._ENV = mod
for _, filename in Files.walk(path) do
local _continue_0 = false
repeat
@ -318,24 +319,9 @@ local nomsu_environment = Importer({
break
end
end
import_to_1_from(environment, mod)
import_to_1_from(environment, mod, prefix)
environment.FILE_CACHE[path] = mod
return _currently_running_files:remove()
end,
compile_error_at = function(tree, err_msg, hint)
if hint == nil then
hint = nil
end
local err_str = pretty_error({
title = "Compile error",
error = err_msg,
hint = hint,
source = tree:get_source_file(),
start = tree.source.start,
stop = tree.source.stop,
filename = tree.source.filename
})
return error(err_str, 0)
end
})
nomsu_environment._ENV = nomsu_environment

View File

@ -31,12 +31,14 @@ for version=1,999
Parsers[version] = make_parser(peg_contents, make_tree)
{:tree_to_nomsu, :tree_to_inline_nomsu} = require "nomsu_decompiler"
compile = require('nomsu_compiler')
{:compile, :compile_error} = require('nomsu_compiler')
_currently_running_files = List{} -- Used to check for circular imports in run_file_1_in
nomsu_environment = Importer{
NOMSU_COMPILER_VERSION: 12, NOMSU_SYNTAX_VERSION: max_parser_version
-- Lua stuff:
:next, unpack: unpack or table.unpack, :setmetatable, :coroutine, :rawequal, :getmetatable, :pcall,
:next, unpack: unpack or table.unpack, :setmetatable, :rawequal, :getmetatable, :pcall,
yield:coroutine.yield, resume:coroutine.resume, coroutine_status_of:coroutine.status,
coroutine_wrap:coroutine.wrap, coroutine_from: coroutine.create,
:error, :package, :os, :require, :tonumber, :tostring, :string, :xpcall, :module,
say:print, :loadfile, :rawset, :_VERSION, :collectgarbage, :rawget, :rawlen,
:table, :assert, :dofile, :loadstring, lua_type_of:type, :select, :math, :io, :load,
@ -53,9 +55,9 @@ nomsu_environment = Importer{
SOURCE_MAP: Importer({})
-- Nomsu functions:
_1_as_nomsu:tree_to_nomsu, _1_as_inline_nomsu:tree_to_inline_nomsu
compile: compile, _1_as_lua: compile,
:_1_forked, :import_to_1_from
_1_as_nomsu:tree_to_nomsu, _1_as_inline_nomsu:tree_to_inline_nomsu,
compile: compile, _1_as_lua: compile, compile_error_at:compile_error,
:_1_forked, :import_to_1_from,
_1_parsed: (nomsu_code)->
if type(nomsu_code) == 'string'
@ -69,14 +71,15 @@ nomsu_environment = Importer{
tree = parse(nomsu_code, source.filename)
if tree.shebang
tree.version = tree.shebang\match("nomsu %-V[ ]*([%d.]*)")
errs = {}
find_errors = (t)->
if t.type == "Error"
coroutine.yield t
errs[#errs+1] = t
else
for k,v in pairs(t)
continue unless SyntaxTree\is_instance(v)
find_errors(v)
errs = [err for err in coroutine.wrap(-> find_errors(tree))]
find_errors(tree)
num_errs = #errs
if num_errs > 0
err_strings = [pretty_error{
@ -152,11 +155,11 @@ nomsu_environment = Importer{
error("Attempt to run unknown thing: "..tostring(to_run))
FILE_CACHE: {}
run_file_1_in: (path, environment, optimization)->
run_file_1_in: (path, environment, optimization, prefix=nil)->
if not optimization
optimization = environment.OPTIMIZATION
if environment.FILE_CACHE[path]
import_to_1_from(environment, environment.FILE_CACHE[path])
import_to_1_from(environment, environment.FILE_CACHE[path], prefix)
return
if _currently_running_files\has(path)
i = _currently_running_files\index_of(path)
@ -165,7 +168,6 @@ nomsu_environment = Importer{
error("Circular import detected:\n "..circle\joined_with("\n..imports "))
_currently_running_files\add path
mod = _1_forked(environment)
mod._ENV = mod
for _,filename in Files.walk(path)
continue unless filename == "stdin" or filename\match("%.nom$")
lua_filename = filename\gsub("%.nom$", ".lua")
@ -177,17 +179,9 @@ nomsu_environment = Importer{
file = Files.read(filename)
NomsuCode\from(Source(filename, 1, #file), file)
environment.run_1_in(code, mod)
import_to_1_from(environment, mod)
import_to_1_from(environment, mod, prefix)
environment.FILE_CACHE[path] = mod
_currently_running_files\remove!
compile_error_at: (tree, err_msg, hint=nil)->
err_str = pretty_error{
title: "Compile error"
error:err_msg, hint:hint, source:tree\get_source_file!
start:tree.source.start, stop:tree.source.stop, filename:tree.source.filename
}
error(err_str, 0)
}
nomsu_environment._ENV = nomsu_environment

View File

@ -147,16 +147,16 @@ do
end,
get_args = function(self)
assert(self.type == "Action", "Only actions have arguments")
local _accum_0 = { }
local _len_0 = 1
local args = {
self.target
}
for _index_0 = 1, #self do
local tok = self[_index_0]
if type(tok) ~= 'string' then
_accum_0[_len_0] = tok
_len_0 = _len_0 + 1
args[#args + 1] = tok
end
end
return _accum_0
return args
end,
get_stub = function(self)
local stub_bits = { }
@ -198,7 +198,8 @@ do
local Files = require('files')
local f = Files.read(s.filename)
return f
end
end,
__mode = "k"
})
self.is_instance = function(self, t)
return type(t) == 'table' and getmetatable(t) == self.__base

View File

@ -39,11 +39,13 @@ class SyntaxTree
table.insert(bits, "[ #{as_lua(k)}]=#{as_lua(v)}")
return "SyntaxTree{#{table.concat(bits, ", ")}}"
@source_code_for_tree: setmetatable({}, {__index:(t)=>
@source_code_for_tree: setmetatable({}, {
__index:(t)=>
s = t.source
Files = require 'files'
f = Files.read(s.filename)
return f
__mode: "k"
})
get_source_file: => @@source_code_for_tree[@]
get_source_code: => @@source_code_for_tree[@]\sub(@source.start, @source.stop)
@ -73,7 +75,10 @@ class SyntaxTree
get_args: =>
assert(@type == "Action", "Only actions have arguments")
return [tok for tok in *@ when type(tok) != 'string']
args = {@target}
for tok in *@
if type(tok) != 'string' then args[#args+1] = tok
return args
get_stub: =>
stub_bits = {}

View File

@ -16,8 +16,9 @@ if (%args.1 is "-i"):
%args::remove index 1
for %path in %args:
if (%path == "-"): %path = "stdin"
for file %filename in %path:
unless (%filename::matches "%.nom$"): do next %filename
unless ((%filename::matches "%.nom$") or (%filename == "stdin")): do next %filename
%contents = (read file %filename)
%code = (NomsuCode from (Source %filename 1 (size of %contents)) %contents)
%tree = (%code parsed)
@ -25,4 +26,4 @@ for %path in %args:
if %inplace:
write %formatted to file %filename
..else:
say %formatted
say %formatted inline

View File

@ -11,7 +11,7 @@ use "lib/consolecolor.nom"
%stub = (command line args).1
say "Looking for stub: \%stub..."
%files = ((command line args).% for % in 2 to (size of (command line args)))
%files = [: for % in 2 to (size of (command line args)): add (command line args).%]
for %path in %files:
for file %filename in %path:
unless (%filename::matches "%.nom$"): do next %filename