Major overhaul, splitting nomsu_compiler into nomsu_environment,

nomsu_compiler, and nomsu_decompiler. Also added comprehensions.
This commit is contained in:
Bruce Hill 2018-11-08 15:23:22 -08:00
parent 1f3660f393
commit 652c29bdef
64 changed files with 2166 additions and 2475 deletions

View File

@ -12,10 +12,10 @@ UNINSTALL_VERSION=
MOON_FILES= code_obj.moon error_handling.moon files.moon nomsu.moon nomsu_compiler.moon \ MOON_FILES= code_obj.moon error_handling.moon files.moon nomsu.moon nomsu_compiler.moon \
syntax_tree.moon containers.moon bitops.moon parser.moon pretty_errors.moon \ syntax_tree.moon containers.moon bitops.moon parser.moon pretty_errors.moon \
string2.moon string2.moon nomsu_decompiler.moon nomsu_environment.moon importer.moon
LUA_FILES= code_obj.lua consolecolors.lua error_handling.lua files.lua nomsu.lua nomsu_compiler.lua \ LUA_FILES= code_obj.lua consolecolors.lua error_handling.lua files.lua nomsu.lua nomsu_compiler.lua \
syntax_tree.lua containers.lua bitops.lua parser.lua pretty_errors.lua \ syntax_tree.lua containers.lua bitops.lua parser.lua pretty_errors.lua \
string2.lua string2.lua nomsu_decompiler.lua nomsu_environment.lua importer.lua
CORE_NOM_FILES= $(wildcard core/*.nom) CORE_NOM_FILES= $(wildcard core/*.nom)
CORE_LUA_FILES= $(patsubst %.nom,%.lua,$(CORE_NOM_FILES)) CORE_LUA_FILES= $(patsubst %.nom,%.lua,$(CORE_NOM_FILES))
LIB_NOM_FILES= $(wildcard lib/*.nom) LIB_NOM_FILES= $(wildcard lib/*.nom)

View File

@ -265,6 +265,10 @@ do
end end
}) })
_base_0.__class = _class_0 _base_0.__class = _class_0
local self = _class_0
self.is_instance = function(self, x)
return type(x) == 'table' and x.__class == self
end
Code = _class_0 Code = _class_0
end end
do do
@ -373,7 +377,7 @@ do
if suffix == nil then if suffix == nil then
suffix = ";" suffix = ";"
end end
if not (self.is_value) then if self:text():matches(";$") or self:text() == "" then
return self return self
end end
local statements = LuaCode(self.source) local statements = LuaCode(self.source)
@ -415,7 +419,6 @@ do
} }
end, end,
parenthesize = function(self) parenthesize = function(self)
assert(self.is_value, "Cannot parenthesize lua statements")
self:prepend("(") self:prepend("(")
return self:append(")") return self:append(")")
end end
@ -426,7 +429,6 @@ do
__init = function(self, ...) __init = function(self, ...)
_class_0.__parent.__init(self, ...) _class_0.__parent.__init(self, ...)
self.free_vars = { } self.free_vars = { }
self.is_value = false
end, end,
__base = _base_0, __base = _base_0,
__name = "LuaCode", __name = "LuaCode",
@ -450,12 +452,6 @@ do
end end
}) })
_base_0.__class = _class_0 _base_0.__class = _class_0
local self = _class_0
self.Value = function(...)
local lua = LuaCode(...)
lua.is_value = true
return lua
end
if _parent_0.__inherited then if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0) _parent_0.__inherited(_parent_0, _class_0)
end end

View File

@ -50,6 +50,8 @@ class Code
--assert(@source and Source\is_instance(@source), "Source has the wrong type") --assert(@source and Source\is_instance(@source), "Source has the wrong type")
@append(...) @append(...)
@is_instance: (x)=> type(x) == 'table' and x.__class == @
text: => text: =>
if @__str == nil if @__str == nil
buff, indent = {}, 0 buff, indent = {}, 0
@ -165,12 +167,6 @@ class LuaCode extends Code
new: (...)=> new: (...)=>
super ... super ...
@free_vars = {} @free_vars = {}
@is_value = false
@Value = (...)->
lua = LuaCode(...)
lua.is_value = true
return lua
add_free_vars: (vars)=> add_free_vars: (vars)=>
return unless #vars > 0 return unless #vars > 0
@ -219,7 +215,7 @@ class LuaCode extends Code
return to_declare return to_declare
as_statements: (prefix="", suffix=";")=> as_statements: (prefix="", suffix=";")=>
unless @is_value if @text!\matches(";$") or @text! == ""
return self return self
statements = LuaCode(@source) statements = LuaCode(@source)
if prefix != "" if prefix != ""
@ -250,7 +246,6 @@ class LuaCode extends Code
} }
parenthesize: => parenthesize: =>
assert @is_value, "Cannot parenthesize lua statements"
@prepend "(" @prepend "("
@append ")" @append ")"

View File

@ -4,6 +4,8 @@
use "compatibility/compatibility.nom" use "compatibility/compatibility.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
upgrade action (%a = %b) to "2.3" as (%a == %b) upgrade action (%a = %b) to "2.3" as (%a == %b)
upgrade action (<- %) to "2.3" as (set %) upgrade action (<- %) to "2.3" as (set %)
upgrade action (assign %) to "2.3" as (set %) upgrade action (assign %) to "2.3" as (set %)

View File

@ -4,6 +4,8 @@
use "compatibility/compatibility.nom" use "compatibility/compatibility.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
upgrade %tree to "2.4" as: upgrade %tree to "2.4" as:
unless (%tree is "Action" syntax tree): return unless (%tree is "Action" syntax tree): return
if %tree.stub is: if %tree.stub is:

View File

@ -4,6 +4,8 @@
use "compatibility/compatibility.nom" use "compatibility/compatibility.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
upgrade action [hash %, sha1 %] to "2.5.5.5" as (..) upgrade action [hash %, sha1 %] to "2.5.5.5" as (..)
=lua "\ =lua "\
..\(base64 decode (hash %)):gsub('.', function(c) return ('%x02'):format(c) end)" ..\(base64 decode (hash %)):gsub('.', function(c) return ('%x02'):format(c) end)"

View File

@ -4,6 +4,8 @@
use "compatibility/compatibility.nom" use "compatibility/compatibility.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
upgrade action (for %1 where %2 matches %3 %4) to "2.5" as (..) upgrade action (for %1 where %2 matches %3 %4) to "2.5" as (..)
for %1 in %2 matching %3 %4 for %1 in %2 matching %3 %4

View File

@ -4,6 +4,8 @@
use "compatibility/compatibility.nom" use "compatibility/compatibility.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
upgrade %tree to "2" as: upgrade %tree to "2" as:
unless (%tree is "Action" syntax tree): return unless (%tree is "Action" syntax tree): return
if (%tree.stub is "if % % else %"): if (%tree.stub is "if % % else %"):

View File

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

View File

@ -4,6 +4,8 @@
use "compatibility/compatibility.nom" use "compatibility/compatibility.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
upgrade action [..] upgrade action [..]
append %item to %list, add %item to %list, to %list add %item, to %list append %item append %item to %list, add %item to %list, to %list add %item, to %list append %item
..to "3.6" as (%list::add %item) ..to "3.6" as (%list::add %item)

View File

@ -4,6 +4,8 @@
use "compatibility/compatibility.nom" use "compatibility/compatibility.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
upgrade action [%index st to last in %list] to "3.7" as (%list::%index st to last) upgrade action [%index st to last in %list] to "3.7" as (%list::%index st to last)
upgrade action [%index nd to last in %list] to "3.7" as (%list::%index nd to last) upgrade action [%index nd to last in %list] to "3.7" as (%list::%index nd to last)
upgrade action [%index rd to last in %list] to "3.7" as (%list::%index rd to last) upgrade action [%index rd to last in %list] to "3.7" as (%list::%index rd to last)

View File

@ -4,6 +4,8 @@
use "compatibility/compatibility.nom" use "compatibility/compatibility.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
upgrade action (method %spec %body) to "3" as (my action %spec %body) upgrade action (method %spec %body) to "3" as (my action %spec %body)
upgrade action (me) to "3" as %me upgrade action (me) to "3" as %me
upgrade action (@) to "3" as %me upgrade action (@) to "3" as %me

View File

@ -3,6 +3,8 @@
This file defines upgrades from Nomsu <4.8.10 to 4.8.10 (renaming "action" -> "means") This file defines upgrades from Nomsu <4.8.10 to 4.8.10 (renaming "action" -> "means")
use "compatibility/compatibility.nom" use "compatibility/compatibility.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
upgrade action "local action 1 2" to "4.8.10" via (..) upgrade action "local action 1 2" to "4.8.10" via (..)
[%tree, %end_version] ->: [%tree, %end_version] ->:
%spec = %tree.3 %spec = %tree.3

61
compatibility/4.9.nom Normal file
View File

@ -0,0 +1,61 @@
#!/usr/bin/env nomsu -V4.9.11.6
#
This file defines upgrades from Nomsu <4.9 to 4.9
use "compatibility/compatibility.nom"
upgrade action "local action 1 2" to "4.9" via (..)
[%tree, %end_version] ->:
%spec = %tree.3
%body = %tree.4
if %spec.type is:
"List":
if ((size of %spec) == 1):
return \(%spec.1 means %body)
..else:
return \(%spec all mean %body)
else:
return \(%spec means %body)
upgrade action "action 1 2" to "4.9" via (..)
[%tree, %end_version] ->:
%spec = %tree.2
%body = %tree.3
if %spec.type is:
"List":
if ((size of %spec) == 1):
return \(externally %spec.1 means %body)
..else:
return \(externally %spec all mean %body)
else:
return \(externally %spec means %body)
upgrade action "compile 1 to 2" to "4.9" via (..)
[%tree, %end_version] ->:
%spec = %tree.2
%body = %tree.4
if %spec.type is:
"List":
if ((size of %spec) == 1):
return \(%spec.1 compiles to %body)
..else:
return \(%spec all compile to %body)
else:
return \(%spec compiles to %body)
upgrade action "parse 1 as 2" to "4.9" via (..)
[%tree, %end_version] ->:
%spec = %tree.2
%body = %tree.4
if %spec.type is:
"List":
if ((size of %spec) == 1):
return \(%spec.1 parses as %body)
..else:
return \(%spec all parse as %body)
else:
return \(%spec parse as %body)
upgrade action (compile as %) to "4.9" as (what % compiles to)
upgrade action (action %) to "4.9" as (%'s meaning)
upgrade action (remove action %) to "4.9" as ((%'s meaning) = (nil))
upgrade action (if %) to "4.9" as (when %)

View File

@ -5,6 +5,8 @@
use "lib/os.nom" use "lib/os.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%UPGRADES = {} %UPGRADES = {}
externally (upgrade to %version via %upgrade_fn) means: externally (upgrade to %version via %upgrade_fn) means:
%UPGRADES.%version = %upgrade_fn %UPGRADES.%version = %upgrade_fn

View File

@ -424,7 +424,9 @@ do
byte(tostring(self), start, stop) byte(tostring(self), start, stop)
}) })
end, end,
[as_lua_id("with 1 ->")] = gsub, [as_lua_id("with 1 ->")] = function(...)
return (gsub(...))
end,
bytes = function(self) bytes = function(self)
return List({ return List({
byte(tostring(self), 1, -1) byte(tostring(self), 1, -1)

View File

@ -164,7 +164,7 @@ do
as_a_lua_identifier: as_lua_id, is_a_lua_identifier: is_lua_id, as_a_lua_identifier: as_lua_id, is_a_lua_identifier: is_lua_id,
as_a_lua_id: as_lua_id, is_a_lua_id: is_lua_id, as_a_lua_id: as_lua_id, is_a_lua_id: is_lua_id,
bytes_1_to: (start, stop)=> List{byte(tostring(@), start, stop)} bytes_1_to: (start, stop)=> List{byte(tostring(@), start, stop)}
[as_lua_id "with 1 ->"]: gsub [as_lua_id "with 1 ->"]: (...)-> (gsub(...))
bytes: => List{byte(tostring(@), 1, -1)}, bytes: => List{byte(tostring(@), 1, -1)},
lines: => List(lines(@)) lines: => List(lines(@))
line: line line: line

View File

@ -7,6 +7,8 @@ use "core/metaprogramming.nom"
use "core/control_flow.nom" use "core/control_flow.nom"
use "core/operators.nom" use "core/operators.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# List functionality: # List functionality:
test: test:
%list = [1, 2, 3, 4, 5] %list = [1, 2, 3, 4, 5]

View File

@ -8,6 +8,8 @@ use "core/text.nom"
use "core/operators.nom" use "core/operators.nom"
use "core/errors.nom" use "core/errors.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# No-Op # No-Op
test: do nothing test: do nothing
(do nothing) compiles to (Lua "") (do nothing) compiles to (Lua "")

View File

@ -4,6 +4,8 @@
use "core/metaprogramming.nom" use "core/metaprogramming.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test: test:
%nums = [] %nums = []
%co = (..) %co = (..)

View File

@ -4,6 +4,8 @@
use "core/metaprogramming.nom" use "core/metaprogramming.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(barf %msg) compiles to (Lua "error(\(=lua "\%msg and \(%msg as lua expr) or 'nil'"), 0);") (barf %msg) compiles to (Lua "error(\(=lua "\%msg and \(%msg as lua expr) or 'nil'"), 0);")
(compile error at %tree %msg) compiles to (..) (compile error at %tree %msg) compiles to (..)
Lua "nomsu:compile_error(\(%tree as lua expr), \(%msg as lua expr))" Lua "nomsu:compile_error(\(%tree as lua expr), \(%msg as lua expr))"
@ -12,7 +14,7 @@ use "core/metaprogramming.nom"
(assume %condition) compiles to: (assume %condition) compiles to:
lua> "\ lua> "\
..local \%assumption = 'Assumption failed: '..tostring(nomsu:tree_to_nomsu(\%condition))" ..local \%assumption = 'Assumption failed: '..tostring((\%condition):get_source_code())"
return (..) return (..)
Lua "\ Lua "\
..if not \(%condition as lua expr) then ..if not \(%condition as lua expr) then
@ -21,7 +23,7 @@ use "core/metaprogramming.nom"
(assume %a == %b) compiles to: (assume %a == %b) compiles to:
lua> "\ lua> "\
..local \%assumption = 'Assumption failed: '..tostring(nomsu:tree_to_nomsu(\(\(%a == %b))))" ..local \%assumption = 'Assumption failed: '..tostring(\(\(%a == %b) as nomsu))"
define mangler define mangler
return (..) return (..)
Lua "\ Lua "\

View File

@ -8,6 +8,8 @@ use "core/math.nom"
use "core/collections.nom" use "core/collections.nom"
use "core/control_flow.nom" use "core/control_flow.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%NaN_surrogate = {} %NaN_surrogate = {}
%nil_surrogate = {} %nil_surrogate = {}
%obj_by_id = {} %obj_by_id = {}

View File

@ -4,7 +4,9 @@
use "core/metaprogramming.nom" use "core/metaprogramming.nom"
(say %message) compiles to (..) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(say %message) compiles to:
lua> "\ lua> "\
..if \%message.type == "Text" then ..if \%message.type == "Text" then
return LuaCode(tree.source, "print(", \(%message as lua expr), ");"); return LuaCode(tree.source, "print(", \(%message as lua expr), ");");
@ -12,7 +14,7 @@ use "core/metaprogramming.nom"
return LuaCode(tree.source, "print(tostring(", \(%message as lua expr), "));"); return LuaCode(tree.source, "print(tostring(", \(%message as lua expr), "));");
end" end"
(ask %prompt) compiles to (..) (ask %prompt) compiles to:
lua> "\ lua> "\
..if \%prompt.type == "Text" then ..if \%prompt.type == "Text" then
return LuaCode.Value(tree.source, "(io.write(", \(%prompt as lua expr), ") and io.read())"); return LuaCode.Value(tree.source, "(io.write(", \(%prompt as lua expr), ") and io.read())");

View File

@ -8,6 +8,8 @@ use "core/operators.nom"
use "core/control_flow.nom" use "core/control_flow.nom"
use "core/collections.nom" use "core/collections.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Literals: # Literals:
test: test:
assume (all of [inf, NaN, pi, tau, golden ratio, e]) or barf "\ assume (all of [inf, NaN, pi, tau, golden ratio, e]) or barf "\
@ -204,7 +206,7 @@ test:
return %best return %best
# Random functions # Random functions
externally (seed random with %) means (..) externally (seed random with %) means:
lua> "\ lua> "\
..math.randomseed(\%); ..math.randomseed(\%);
for i=1,20 do math.random(); end" for i=1,20 do math.random(); end"

View File

@ -3,7 +3,9 @@
This File contains actions for making actions and compile-time actions and some helper This File contains actions for making actions and compile-time actions and some helper
functions to make that easier. functions to make that easier.
lua> "NOMSU_CORE_VERSION = 9" lua> "\
..NOMSU_CORE_VERSION = 10
NOMSU_LIB_VERSION = 7"
lua> "\ lua> "\
..do ..do
local mangle_index = 0 local mangle_index = 0
@ -15,17 +17,18 @@ lua> "\
end end
end end
end end
COMPILE_ACTIONS["define mangler"] = function(nomsu, tree) compile.action["define mangler"] = function(compile, tree)
return LuaCode(tree.source, "local mangle = mangler()") return LuaCode(tree.source, "local mangle = mangler()")
end" end"
lua> "\ lua> "\
..COMPILE_ACTIONS["1 ->"] = function(nomsu, tree, \%args, \%body) ..compile.action["1 ->"] = function(compile, tree, \%args, \%body)
local lua = LuaCode.Value(tree.source, "(function(") local lua = LuaCode(tree.source, "(function(")
if SyntaxTree:is_instance(\%args) and \%args.type == "Action" then \%args = \%args:get_args() end 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 nomsu:compile(a):text() or a 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, ", ") lua:concat_append(lua_args, ", ")
local body_lua = SyntaxTree:is_instance(\%body) and nomsu:compile(\%body):as_statements("return ") or \%body 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) body_lua:remove_free_vars(lua_args)
body_lua:declare_locals() body_lua:declare_locals()
lua:append(")\\n ", body_lua, "\\nend)") lua:append(")\\n ", body_lua, "\\nend)")
@ -33,10 +36,10 @@ lua> "\
end" end"
lua> "\ lua> "\
..COMPILE_ACTIONS["what 1 compiles to"] = function(nomsu, tree, \%action) ..compile.action["what 1 compiles to"] = function(compile, tree, \%action)
local lua = LuaCode.Value(tree.source, "COMPILE_ACTIONS[", \%action.stub:as_lua(), "](") local lua = LuaCode(tree.source, "compile.action[", \%action.stub:as_lua(), "](")
local lua_args = table.map(\%action:get_args(), function(a) return nomsu:compile(a) end) local lua_args = table.map(\%action:get_args(), function(a) return compile(a) end)
table.insert(lua_args, 1, "nomsu") table.insert(lua_args, 1, "compile")
table.insert(lua_args, 2, "tree") table.insert(lua_args, 2, "tree")
lua:concat_append(lua_args, ", ") lua:concat_append(lua_args, ", ")
lua:append(")") lua:append(")")
@ -46,10 +49,10 @@ lua> "\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test: test:
(five) compiles to (Lua value "5") (five) compiles to "5"
test: test:
assume ((five) == 5) or barf "Compile to expression failed." assume ((five) == 5) or barf "Compile to expression failed."
(loc x) compiles to (Lua "local x = 99;") (loc x) compiles to "local x = 99;"
test: test:
lua> "do" lua> "do"
loc x loc x
@ -63,9 +66,12 @@ test:
asdf asdf
assume (%tmp is (nil)) or barf "compile to is leaking variables" assume (%tmp is (nil)) or barf "compile to is leaking variables"
lua> "\ lua> "\
..COMPILE_ACTIONS["1 compiles to"] = function(nomsu, tree, \%action, \%body) ..compile.action["1 compiles to"] = function(compile, tree, \%action, \%body)
local \%args = List{\(\%nomsu), \(\%tree), unpack(\%action:get_args())} local \%args = List{\(\%compile), \(\%tree), unpack(\%action:get_args())}
local lua = LuaCode(tree.source, "COMPILE_ACTIONS[", \%action.stub:as_lua(), if \%body.type == "Text" then
\%body = SyntaxTree{source=\%body.source, type="Action", "Lua", \%body}
end
local lua = LuaCode(tree.source, "compile.action[", \%action.stub:as_lua(),
"] = ", \(what (%args -> %body) compiles to)) "] = ", \(what (%args -> %body) compiles to))
return lua return lua
end" end"
@ -75,16 +81,16 @@ lua> "\
(%actions all compile to %body) compiles to: (%actions all compile to %body) compiles to:
lua> "\ lua> "\
..if \%actions.type ~= "List" then ..if \%actions.type ~= "List" then
nomsu:compile_error(\%actions, "This should be a list of actions.") compile_error(\%actions, "This should be a list of actions.")
end end
local lua = LuaCode(tree.source, \(what (%actions.1 compiles to %body) compiles to)) local lua = LuaCode(tree.source, \(what (%actions.1 compiles to %body) compiles to))
local \%args = List{\(\%nomsu), \(\%tree), unpack(\%actions[1]:get_args())} local \%args = List{\(\%compile), \(\%tree), unpack(\%actions[1]:get_args())}
for i=2,#\%actions do for i=2,#\%actions do
local alias = \%actions[i] local alias = \%actions[i]
local \%alias_args = List{\(\%nomsu), \(\%tree), unpack(alias:get_args())} local \%alias_args = List{\(\%compile), \(\%tree), unpack(alias:get_args())}
lua:append("\\nCOMPILE_ACTIONS[", alias.stub:as_lua(), "] = ") lua:append("\\ncompile.action[", alias.stub:as_lua(), "] = ")
if \%alias_args == \%args then if \%alias_args == \%args then
lua:append("COMPILE_ACTIONS[", \%actions[1].stub:as_lua(), "]") lua:append("compile.action[", \%actions[1].stub:as_lua(), "]")
else else
lua:append(\(what (%alias_args -> \(what %actions.1 compiles to)) compiles to)) lua:append(\(what (%alias_args -> \(what %actions.1 compiles to)) compiles to))
end end
@ -95,17 +101,17 @@ lua> "\
(call %fn with %args) compiles to: (call %fn with %args) compiles to:
lua> "\ lua> "\
..local lua = LuaCode.Value(tree.source, nomsu:compile(\%fn), "(") ..local lua = LuaCode(tree.source, compile(\%fn), "(")
if \%args.type == 'List' then if \%args.type == 'List' then
lua:concat_append(table.map(\%args, function(a) return nomsu:compile(a) end), ", ") lua:concat_append(table.map(\%args, function(a) return compile(a) end), ", ")
else else
lua:append('unpack(', nomsu:compile(\%args), ')') lua:append('unpack(', compile(\%args), ')')
end end
lua:append(")") lua:append(")")
return lua" return lua"
test: test:
(foo %x) means (return "outer") (foo %x) means "outer"
with local [(foo %)'s meaning]: with local [(foo %)'s meaning]:
(foo %x) means: (foo %x) means:
%y = (%x + 1) %y = (%x + 1)
@ -162,7 +168,7 @@ test:
test: test:
assume (((say %)'s meaning) == (=lua "say")) assume (((say %)'s meaning) == (=lua "say"))
(%action's meaning) compiles to (Lua value (%action.stub as lua id)) (%action's meaning) compiles to (Lua (%action.stub as lua id))
test: test:
(swap %x and %y) parses as (..) (swap %x and %y) parses as (..)
@ -183,10 +189,10 @@ test:
lua> "\ lua> "\
..local replacements = {} ..local replacements = {}
if \%actions.type ~= "List" then if \%actions.type ~= "List" then
nomsu:compile_error(\%actions, "This should be a list.") compile_error(\%actions, "This should be a list.")
end end
for i,arg in ipairs(\%actions[1]:get_args()) do for i,arg in ipairs(\%actions[1]:get_args()) do
replacements[arg[1]] = nomsu:compile(arg):text() replacements[arg[1]] = compile(arg):text()
end end
local function make_tree(t) local function make_tree(t)
if SyntaxTree:is_instance(t) and t.type == "Var" then if SyntaxTree:is_instance(t) and t.type == "Var" then
@ -227,27 +233,17 @@ test:
[%action parses as %body] all parse as ([%action] all parse as %body) [%action parses as %body] all parse as ([%action] all parse as %body)
# TODO: add check for .is_value (%tree as lua expr) compiles to "\
(%tree as lua expr) compiles to (..) ..compile(\(=lua "compile(\%tree, nil, true)"), nil, true)"
Lua value "nomsu:compile(\(=lua "nomsu:compile(\%tree, nil, true)"), nil, true)"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(%tree as lua) compiles to (Lua value "nomsu:compile(\(%tree as lua expr))") (%tree as lua) compiles to "compile(\(%tree as lua expr))"
(%tree as lua statements) compiles to (..) (%tree as lua statements) compiles to "\
Lua value "nomsu:compile(\(%tree as lua expr)):as_statements()" ..compile(\(%tree as lua expr)):as_statements()"
(%tree as lua return) compiles to (..) (%tree as lua return) compiles to "\
Lua value "nomsu:compile(\(%tree as lua expr)):as_statements('return ')" ..compile(\(%tree as lua expr)):as_statements('return ')"
test:
assume ("\(\(foo \%x) as nomsu)" == "foo %x") or barf "\
..action source code failed."
(%tree as nomsu) compiles to (..)
Lua value "nomsu:tree_to_nomsu(\(%tree as lua expr))"
(%tree as inline nomsu) compiles to (..)
Lua value "nomsu:tree_to_inline_nomsu(\(%tree as lua expr), true)"
externally [%var as lua identifier, %var as lua id] all mean: externally [%var as lua identifier, %var as lua id] all mean:
lua> "\ lua> "\
@ -256,7 +252,7 @@ externally [%var as lua identifier, %var as lua id] all mean:
elseif SyntaxTree:is_instance(\%var) then elseif SyntaxTree:is_instance(\%var) then
local lua = \(%var as lua expr) local lua = \(%var as lua expr)
if not lua:text():match("^[_a-zA-Z][_a-zA-Z0-9]*$") then if not lua:text():match("^[_a-zA-Z][_a-zA-Z0-9]*$") then
nomsu:compile_error(\%var, "This is not a valid Lua identifier.") compile_error(\%var, "This is not a valid Lua identifier.")
end end
return lua return lua
else error("Unknown type: "..tostring(\%var)) else error("Unknown type: "..tostring(\%var))
@ -264,12 +260,11 @@ externally [%var as lua identifier, %var as lua id] all mean:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(% is syntax tree) compiles to (Lua value "SyntaxTree:is_instance(\(% as lua expr))") (% is syntax tree) compiles to "SyntaxTree:is_instance(\(% as lua expr))"
(% is %kind syntax tree) compiles to (..) externally (% is %kind syntax tree) means (..)
Lua value "SyntaxTree:is_instance(\(% as lua expr), \(%kind as lua expr))" =lua "SyntaxTree:is_instance(\%) and \%.type == \%kind"
(%tree with %t -> %replacement) compiles to (..) (%tree with %t -> %replacement) compiles to "\
Lua value "\
..\(%tree as lua expr):map(function(\(%t as lua expr)) ..\(%tree as lua expr):map(function(\(%t as lua expr))
\(%replacement as lua return) \(%replacement as lua return)
end)" end)"
@ -282,16 +277,14 @@ externally (%tree with vars %replacements) means (..)
end end
end)" end)"
(tree %tree with vars %replacements) compiles to (..) (tree %tree with vars %replacements) compiles to "\
Lua value "\
..\(=lua "(\%tree):as_lua()"):map(function(t) ..\(=lua "(\%tree):as_lua()"):map(function(t)
if t.type == "Var" then if t.type == "Var" then
return \(%replacements as lua expr)[t[1]] return \(%replacements as lua expr)[t[1]]
end end
end)" end)"
(%tree has subtree %match_tree) compiles to (..) (%tree has subtree %match_tree) compiles to "\
Lua value "\
..(function() ..(function()
local match_tree = \(%match_tree as lua expr) 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(coroutine.yield) end) do
@ -339,7 +332,7 @@ test:
..one ..one
"two"" "two""
..== "\"one\\n\\\"two\\\"\"" ..== "\"one\\n\\\"two\\\"\""
(quote %s) compiles to (Lua value "tostring(\(%s as lua expr)):as_lua()") (quote %s) compiles to (Lua "tostring(\(%s as lua expr)):as_lua()")
test: test:
assume (lua type of {}) == "table" assume (lua type of {}) == "table"
@ -367,64 +360,44 @@ externally (type of %) means:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test:
assume ((parse "foo %") == \(foo \%))
%a = (parse "\\1")
%b = \(\(1))
assume ((parse "\\1") == \(\(1)))
(parse %text) compiles to (Lua value "nomsu:parse(\(%text as lua expr))")
(parse %text from %filename) compiles to (..)
Lua value "\
..nomsu:parse(NomsuCode(Source(\(%filename as lua expr), 1, #\(%text as lua expr)), \(..)
%text as lua expr
..))"
test: test:
assume ((run "return (2 + 99)") == 101) assume ((run "return (2 + 99)") == 101)
external %passed = (no) external %passed = (no)
run "external %passed = (yes)" run "external %passed = (yes)"
assume %passed assume %passed
(run %nomsu_code) compiles to (..) assume (run \(return \(\(5) + \(5)))) == 10
Lua value "\ (run %nomsu_code) compiles to "\
..nomsu:run(NomsuCode(\(=lua "tostring(\(%nomsu_code.source)):as_lua()"), \(..) ..run_1_in(\(%nomsu_code as lua expr), _ENV)"
%nomsu_code as lua expr
..))"
test: [compile %block, compiled %block, %block compiled] all compile to "\
assume ((\(\(5) + \(5)) as value) == 10) or barf "%tree as value failed." ..compile(\(%block as lua))"
[run tree %tree, %tree as value] all compile to (..)
Lua value "nomsu:run(\(%tree as lua expr))"
[compile %block, compiled %block, %block compiled] all compile to (..)
Lua value "nomsu:compile(\(%block as lua))"
# Return statement is wrapped in a do..end block because Lua is unhappy if you # 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. put code after a return statement, unless you wrap it in a block.
(return) compiles to (Lua "do return; end") (return) compiles to "do return; end"
(return %return_value) compiles to (..) (return %return_value) compiles to "do return \(%return_value as lua expr) end"
Lua "do return \(%return_value as lua expr) end"
# Literals # Literals
(yes) compiles to (Lua value "true") (yes) compiles to "true"
(no) compiles to (Lua value "false") (no) compiles to "false"
[nothing, nil, null] all compile to (Lua value "nil") [nothing, nil, null] all compile to "nil"
(Nomsu syntax version) compiles to (Lua value "NOMSU_SYNTAX_VERSION") (Nomsu syntax version) compiles to "NOMSU_SYNTAX_VERSION"
(Nomsu compiler version) compiles to (Lua value "NOMSU_COMPILER_VERSION") (Nomsu compiler version) compiles to "NOMSU_COMPILER_VERSION"
(core version) compiles to (Lua value "NOMSU_CORE_VERSION") (core version) compiles to "NOMSU_CORE_VERSION"
(lib version) compiles to (Lua value "NOMSU_LIB_VERSION") (lib version) compiles to "NOMSU_LIB_VERSION"
(command line args) compiles to (Lua value "arg") (command line args) compiles to "command_line_args"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(with local compile actions %body) compiles to (..) (with local compile actions %body) compiles to "\
Lua "\
..do ..do
local nomsu = nomsu:fork() --local compile = _1_forked(compile)
local COMPILE_ACTIONS = nomsu.environment.COMPILE_ACTIONS local old_action = compile.action
compile.action = _1_forked(old_action)
\(%body as lua statements) \(%body as lua statements)
compile.action = old_action
end" end"
externally (Nomsu version) means: externally (Nomsu version) means:
use "lib/version.nom"
return "\ return "\
..\(Nomsu syntax version).\(core version).\(Nomsu compiler version).\(lib version)" ..\(Nomsu syntax version).\(core version).\(Nomsu compiler version).\(lib version)"

View File

@ -5,6 +5,8 @@
use "core/metaprogramming.nom" use "core/metaprogramming.nom"
use "core/errors.nom" use "core/errors.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test: test:
assume (all [1 < 2, 2 > 1, 1 <= 2, 2 >= 1, 1 == 1, 1 != 2]) assume (all [1 < 2, 2 > 1, 1 <= 2, 2 >= 1, 1 == 1, 1 != 2])
@ -27,14 +29,12 @@ test:
# Variable assignment operator # Variable assignment operator
(%var = %value) compiles to: (%var = %value) compiles to:
lua> "local \%var_lua = \(%var as lua expr)"
assume %var_lua.is_value or barf "Invalid target for assignment: \%var"
lua> "local \%value_lua = \(%value as lua expr)"
assume %value_lua.is_value or barf "Invalid value for assignment: \%value"
lua> "\ lua> "\
..local lua = LuaCode(tree.source, \%var_lua, ' = ', \%value_lua, ';') ..local \%var_lua = \(%var as lua expr)
local \%value_lua = \(%value as lua expr)
local lua = LuaCode(tree.source, \%var_lua, ' = ', \%value_lua, ';')
if \%var.type == 'Var' then if \%var.type == 'Var' then
lua:add_free_vars({nomsu:compile(\%var):text()}) lua:add_free_vars({compile(\%var):text()})
end end
return lua" return lua"
@ -58,13 +58,7 @@ test:
end end
end) end)
local target_lua = \(%target as lua) local target_lua = \(%target as lua)
if not target_lua.is_value then error("Invalid target for assignment: "..\(..)
%target as text
..) end
local value_lua = \(%value as lua) local value_lua = \(%value as lua)
if not value_lua.is_value then error("Invalid value for assignment: "..\(..)
%value as text
..) end
if \%target.type == "Var" then if \%target.type == "Var" then
lhs:add_free_vars({target_lua:text()}) lhs:add_free_vars({target_lua:text()})
end end
@ -87,16 +81,11 @@ test:
set global x local y set global x local y
assume ((%foozle == "inner") and (%y == "outer")) or barf "external failed." assume ((%foozle == "inner") and (%y == "outer")) or barf "external failed."
(external %var = %value) compiles to: (external %var = %value) compiles to "\(%var as lua) = \(%value as lua)"
%var_lua = (%var as lua)
assume %var_lua.is_value or barf "Invalid target for assignment: \%var"
%value_lua = (%value as lua)
assume %value_lua.is_value or barf "Invalid value for assignment: \%value"
return (Lua "\%var_lua = \%value_lua;")
test: test:
set {%foozle:"outer", %y:"outer"} set {%foozle:"outer", %y:"outer"}
externally (set global x local y) means (..) externally (set global x local y) means:
with external [%foozle]: with external [%foozle]:
%foozle = "inner" %foozle = "inner"
%y = "inner" %y = "inner"
@ -107,7 +96,7 @@ test:
(with external %externs %body) compiles to: (with external %externs %body) compiles to:
%body_lua = (%body as lua statements) %body_lua = (%body as lua statements)
lua> "\ lua> "\
..\%body_lua:remove_free_vars(table.map(\%externs, function(v) return nomsu:compile(v):text() end))" ..\%body_lua:remove_free_vars(table.map(\%externs, function(v) return compile(v):text() end))"
return %body_lua return %body_lua
test: test:
@ -131,9 +120,6 @@ test:
end end
local target_lua = \(%target as lua) local target_lua = \(%target as lua)
local value_lua = \(%value as lua) local value_lua = \(%value as lua)
if not value_lua.is_value then
error("Invalid value for assignment: "..tostring(\%value))
end
if i > 1 then if i > 1 then
lhs:append(", ") lhs:append(", ")
rhs:append(", ") rhs:append(", ")

View File

@ -7,6 +7,8 @@ use "core/operators.nom"
use "core/collections.nom" use "core/collections.nom"
use "core/control_flow.nom" use "core/control_flow.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test: test:
%x = "outer" %x = "outer"
with local %x: with local %x:

View File

@ -5,6 +5,8 @@
use "core/metaprogramming.nom" use "core/metaprogramming.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test: test:
assume "\[1, 2, 3]" == "[1, 2, 3]" assume "\[1, 2, 3]" == "[1, 2, 3]"
assume "foo = \(1 + 2)!" == "foo = 3!" assume "foo = \(1 + 2)!" == "foo = 3!"
@ -52,8 +54,8 @@ lua> "\
}; };
for name, e in pairs(escapes) do for name, e in pairs(escapes) do
local lua = "'"..e.."'" local lua = "'"..e.."'"
COMPILE_ACTIONS[name] = function(nomsu, tree) compile.action[name] = function(compile, tree)
return LuaCode.Value(tree.source, lua) return LuaCode(tree.source, lua)
end end
end end
end" end"

View File

@ -1,5 +1,13 @@
local files = require("files") local files = require("files")
local debug_getinfo = debug.getinfo local debug_getinfo = debug.getinfo
local RED = "\027[31m"
local BRIGHT_RED = "\027[31;1m"
local RESET = "\027[0m"
local YELLOW = "\027[33m"
local CYAN = "\027[36m"
local GREEN = "\027[32m"
local BLUE = "\027[34m"
local DIM = "\027[37;2m"
local ok, to_lua = pcall(function() local ok, to_lua = pcall(function()
return require('moonscript.base').to_lua return require('moonscript.base').to_lua
end) end)
@ -66,7 +74,7 @@ debug.getinfo = function(thread, f, what)
end end
local print_error local print_error
print_error = function(error_message, start_fn, stop_fn) print_error = function(error_message, start_fn, stop_fn)
io.stderr:write(tostring(colored.red("ERROR:")) .. " " .. tostring(colored.bright(colored.red((error_message or "")))) .. "\n") io.stderr:write(tostring(RED) .. "ERROR: " .. tostring(BRIGHT_RED) .. tostring(error_message or "") .. tostring(RESET) .. "\n")
io.stderr:write("stack traceback:\n") io.stderr:write("stack traceback:\n")
local level = 1 local level = 1
local found_start = false local found_start = false
@ -126,10 +134,10 @@ print_error = function(error_message, start_fn, stop_fn)
do do
local err_line = files.get_line(file, calling_fn.currentline) local err_line = files.get_line(file, calling_fn.currentline)
if err_line then if err_line then
local offending_statement = colored.bright(colored.red(err_line:match("^[ ]*(.*)"))) local offending_statement = tostring(BRIGHT_RED) .. tostring(err_line:match("^[ ]*(.*)")) .. tostring(RESET)
line = colored.yellow(tostring(filename) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name) .. "\n " .. tostring(offending_statement)) line = tostring(YELLOW) .. tostring(filename) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name) .. "\n " .. tostring(offending_statement) .. tostring(RESET)
else else
line = colored.yellow(tostring(filename) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name)) line = tostring(YELLOW) .. tostring(filename) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name) .. tostring(RESET)
end end
end end
else else
@ -184,20 +192,20 @@ print_error = function(error_message, start_fn, stop_fn)
for _ in file:sub(1, char):gmatch("\n") do for _ in file:sub(1, char):gmatch("\n") do
line_num = line_num + 1 line_num = line_num + 1
end end
line = colored.cyan(tostring(calling_fn.short_src) .. ":" .. tostring(line_num) .. " in " .. tostring(name or '?')) line = tostring(CYAN) .. tostring(calling_fn.short_src) .. ":" .. tostring(line_num) .. " in " .. tostring(name or '?') .. tostring(RESET)
else else
line_num = calling_fn.currentline line_num = calling_fn.currentline
if calling_fn.short_src == '[C]' then if calling_fn.short_src == '[C]' then
line = colored.green(tostring(calling_fn.short_src) .. " in " .. tostring(name or '?')) line = tostring(GREEN) .. tostring(calling_fn.short_src) .. " in " .. tostring(name or '?') .. tostring(RESET)
else else
line = colored.blue(tostring(calling_fn.short_src) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name or '?')) line = tostring(BLUE) .. tostring(calling_fn.short_src) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name or '?') .. tostring(RESET)
end end
end end
if file then if file then
do do
local err_line = files.get_line(file, line_num) local err_line = files.get_line(file, line_num)
if err_line then if err_line then
local offending_statement = colored.bright(colored.red(err_line:match("^[ ]*(.*)$"))) local offending_statement = tostring(BRIGHT_RED) .. tostring(err_line:match("^[ ]*(.*)$")) .. tostring(RESET)
line = line .. ("\n " .. offending_statement) line = line .. ("\n " .. offending_statement)
end end
end end
@ -206,7 +214,7 @@ print_error = function(error_message, start_fn, stop_fn)
end end
io.stderr:write(line, "\n") io.stderr:write(line, "\n")
if calling_fn.istailcall then if calling_fn.istailcall then
io.stderr:write(" " .. tostring(colored.dim(colored.white(" (...tail calls...)"))) .. "\n") io.stderr:write(" " .. tostring(DIM) .. "(...tail calls...)" .. tostring(RESET) .. "\n")
end end
if calling_fn.func == stop_fn then if calling_fn.func == stop_fn then
break break

View File

@ -3,6 +3,15 @@ files = require "files"
debug_getinfo = debug.getinfo debug_getinfo = debug.getinfo
export SOURCE_MAP export SOURCE_MAP
RED = "\027[31m"
BRIGHT_RED = "\027[31;1m"
RESET = "\027[0m"
YELLOW = "\027[33m"
CYAN = "\027[36m"
GREEN = "\027[32m"
BLUE = "\027[34m"
DIM = "\027[37;2m"
ok, to_lua = pcall -> require('moonscript.base').to_lua ok, to_lua = pcall -> require('moonscript.base').to_lua
if not ok then to_lua = -> nil if not ok then to_lua = -> nil
MOON_SOURCE_MAP = setmetatable {}, MOON_SOURCE_MAP = setmetatable {},
@ -40,7 +49,7 @@ debug.getinfo = (thread,f,what)->
return info return info
print_error = (error_message, start_fn, stop_fn)-> print_error = (error_message, start_fn, stop_fn)->
io.stderr\write("#{colored.red "ERROR:"} #{colored.bright colored.red (error_message or "")}\n") io.stderr\write("#{RED}ERROR: #{BRIGHT_RED}#{error_message or ""}#{RESET}\n")
io.stderr\write("stack traceback:\n") io.stderr\write("stack traceback:\n")
level = 1 level = 1
@ -76,10 +85,10 @@ print_error = (error_message, start_fn, stop_fn)->
else "main chunk" else "main chunk"
if err_line = files.get_line(file, calling_fn.currentline) if err_line = files.get_line(file, calling_fn.currentline)
offending_statement = colored.bright(colored.red(err_line\match("^[ ]*(.*)"))) offending_statement = "#{BRIGHT_RED}#{err_line\match("^[ ]*(.*)")}#{RESET}"
line = colored.yellow("#{filename}:#{calling_fn.currentline} in #{name}\n #{offending_statement}") line = "#{YELLOW}#{filename}:#{calling_fn.currentline} in #{name}\n #{offending_statement}#{RESET}"
else else
line = colored.yellow("#{filename}:#{calling_fn.currentline} in #{name}") line = "#{YELLOW}#{filename}:#{calling_fn.currentline} in #{name}#{RESET}"
else else
ok, file = pcall ->files.read(calling_fn.short_src) ok, file = pcall ->files.read(calling_fn.short_src)
if not ok then file = nil if not ok then file = nil
@ -111,21 +120,21 @@ print_error = (error_message, start_fn, stop_fn)->
char = MOON_SOURCE_MAP[file][calling_fn.currentline] char = MOON_SOURCE_MAP[file][calling_fn.currentline]
line_num = 1 line_num = 1
for _ in file\sub(1,char)\gmatch("\n") do line_num += 1 for _ in file\sub(1,char)\gmatch("\n") do line_num += 1
line = colored.cyan("#{calling_fn.short_src}:#{line_num} in #{name or '?'}") line = "#{CYAN}#{calling_fn.short_src}:#{line_num} in #{name or '?'}#{RESET}"
else else
line_num = calling_fn.currentline line_num = calling_fn.currentline
if calling_fn.short_src == '[C]' if calling_fn.short_src == '[C]'
line = colored.green("#{calling_fn.short_src} in #{name or '?'}") line = "#{GREEN}#{calling_fn.short_src} in #{name or '?'}#{RESET}"
else else
line = colored.blue("#{calling_fn.short_src}:#{calling_fn.currentline} in #{name or '?'}") line = "#{BLUE}#{calling_fn.short_src}:#{calling_fn.currentline} in #{name or '?'}#{RESET}"
if file if file
if err_line = files.get_line(file, line_num) if err_line = files.get_line(file, line_num)
offending_statement = colored.bright(colored.red(err_line\match("^[ ]*(.*)$"))) offending_statement = "#{BRIGHT_RED}#{err_line\match("^[ ]*(.*)$")}#{RESET}"
line ..= "\n "..offending_statement line ..= "\n "..offending_statement
io.stderr\write(line,"\n") io.stderr\write(line,"\n")
if calling_fn.istailcall if calling_fn.istailcall
io.stderr\write(" #{colored.dim colored.white " (...tail calls...)"}\n") io.stderr\write(" #{DIM}(...tail calls...)#{RESET}\n")
if calling_fn.func == stop_fn then break if calling_fn.func == stop_fn then break
io.stderr\flush! io.stderr\flush!

View File

@ -12,6 +12,8 @@ use "lib/os.nom"
# How do I import all the files in a directory? # How do I import all the files in a directory?
use "lib" use "lib"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# How do I print stuff? # How do I print stuff?
say "Hello world!" say "Hello world!"

View File

@ -25,9 +25,14 @@ local _FILE_CACHE = setmetatable({ }, {
__index = _SPOOFED_FILES __index = _SPOOFED_FILES
}) })
local _BROWSE_CACHE = { } local _BROWSE_CACHE = { }
local _anon_number = 0
Files.spoof = function(filename, contents) Files.spoof = function(filename, contents)
if not contents then
filename, contents = "<anonymous file #" .. tostring(_anon_number) .. ">", filename
_anon_number = _anon_number + 1
end
_SPOOFED_FILES[filename] = contents _SPOOFED_FILES[filename] = contents
return contents return filename
end end
Files.read = function(filename) Files.read = function(filename)
do do
@ -37,7 +42,9 @@ Files.read = function(filename)
end end
end end
if filename == 'stdin' then if filename == 'stdin' then
return Files.spoof('stdin', io.read('*a')) local contents = io.read('*a')
Files.spoof('stdin', contents)
return contents
end end
local file = io.open(filename) local file = io.open(filename)
if not (file) then if not (file) then

View File

@ -16,16 +16,22 @@ _FILE_CACHE = setmetatable {}, __index:_SPOOFED_FILES
_BROWSE_CACHE = {} _BROWSE_CACHE = {}
-- Create a fake file and put it in the cache -- Create a fake file and put it in the cache
_anon_number = 0
Files.spoof = (filename, contents)-> Files.spoof = (filename, contents)->
if not contents
filename, contents = "<anonymous file ##{_anon_number}>", filename
_anon_number += 1
_SPOOFED_FILES[filename] = contents _SPOOFED_FILES[filename] = contents
return contents return filename
-- Read a file's contents (searching first locally, then in the nomsupath) -- Read a file's contents (searching first locally, then in the nomsupath)
Files.read = (filename)-> Files.read = (filename)->
if file_contents = _FILE_CACHE[filename] if file_contents = _FILE_CACHE[filename]
return file_contents return file_contents
if filename == 'stdin' if filename == 'stdin'
return Files.spoof('stdin', io.read('*a')) contents = io.read('*a')
Files.spoof('stdin', contents)
return contents
file = io.open(filename) file = io.open(filename)
return nil unless file return nil unless file
contents = file\read("*a") contents = file\read("*a")

62
importer.lua Normal file
View File

@ -0,0 +1,62 @@
local import_to_1_from
import_to_1_from = function(host, to_import)
do
local host_mt = getmetatable(host)
if host_mt then
if host_mt.__import then
host_mt.__import(host, to_import)
return
end
end
end
for k, v in pairs(to_import) do
host[k] = v
end
end
local _imports = setmetatable({ }, {
__mode = "k"
})
local Importer = setmetatable({
__index = function(self, key)
return _imports[self][key]
end,
__import = function(self, to_import)
local imports = assert(_imports[self])
for k, v in pairs(to_import) do
local _continue_0 = false
repeat
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)
end
_continue_0 = true
until true
if not _continue_0 then
break
end
end
end
}, {
__call = function(self, t)
_imports[t] = { }
setmetatable(t, self)
return t
end
})
local _1_forked
_1_forked = function(self)
local f = Importer({ })
_imports[f] = assert(_imports[self])
import_to_1_from(f, self)
return f
end
return {
Importer = Importer,
import_to_1_from = import_to_1_from,
_1_forked = _1_forked
}

33
importer.moon Normal file
View File

@ -0,0 +1,33 @@
-- This file defines Importer, which is a type of table that can import from other tables
import_to_1_from = (host, to_import)->
if host_mt = getmetatable(host)
if host_mt.__import
host_mt.__import(host, to_import)
return
for k,v in pairs(to_import)
host[k] = v
_imports = setmetatable({}, {__mode:"k"})
Importer = setmetatable({
__index: (key)=> _imports[@][key]
__import: (to_import)=>
imports = assert _imports[@]
for k,v in pairs(to_import)
imports[k] = v
continue if v == to_import
conflict = @[k]
import_to_1_from(conflict, v) if type(conflict) == 'table'
}, {
__call: (t)=>
_imports[t] = {}
setmetatable(t, @)
return t
})
_1_forked = =>
f = Importer{}
_imports[f] = assert _imports[@]
import_to_1_from(f, @)
return f
return {:Importer, :import_to_1_from, :_1_forked}

View File

@ -3,7 +3,7 @@
This file defines actions for ANSI console color escape codes. This file defines actions for ANSI console color escape codes.
test: test:
bright "\(green)Color test passed." % = (bright "\(green)Color test passed.")
%colors = {..} %colors = {..}
normal:0, "reset color":0, bright:1, bold:1, dim:2, italic:3, underscore:4 normal:0, "reset color":0, bright:1, bold:1, dim:2, italic:3, underscore:4
"slow blink":5, "fast blink":6, reverse:7, inverse:7, inverted:7, hidden:8 "slow blink":5, "fast blink":6, reverse:7, inverse:7, inverted:7, hidden:8
@ -16,7 +16,7 @@ for %name = %colornum in %colors:
%colornum = "\%colornum" %colornum = "\%colornum"
#(=lua "COMPILE_ACTIONS").%name = (..) #(=lua "COMPILE_ACTIONS").%name = (..)
[%nomsu, %tree] -> (Lua value "'\\027[\(%colornum)m'") [%nomsu, %tree] -> (Lua value "'\\027[\(%colornum)m'")
(=lua "COMPILE_ACTIONS")."\%name" = (..) %compile.action.%name = (..)
[%nomsu, %tree, %text] ->: [%nomsu, %tree, %text] ->:
if %text: if %text:
return (Lua value "('\\027[\(%colornum)m'..\(%text as lua expr)..'\\027[0m')") return (Lua value "('\\027[\(%colornum)m'..\(%text as lua expr)..'\\027[0m')")

View File

@ -5,6 +5,8 @@
use "lib/os.nom" use "lib/os.nom"
use "lib/base64.nom" use "lib/base64.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
lua> "local \%use_sha1, \%hashlib = pcall(require, 'openssl.digest')" lua> "local \%use_sha1, \%hashlib = pcall(require, 'openssl.digest')"
test: test:

View File

@ -95,7 +95,7 @@ test:
return inst return inst
end, end,
}) })
nomsu.environment[class.name:as_lua_id()] = class _ENV[class.name:as_lua_id()] = class
class.__index = class class.__index = class
class.class = class class.class = class
class.__tostring = function(inst) class.__tostring = function(inst)

View File

@ -69,3 +69,6 @@ externally (source lines of %tree) means:
(line % in %file) for % in (line number of %source.start in %file) to (..) (line % in %file) for % in (line number of %source.start in %file) to (..)
line number of %source.stop in %file line number of %source.stop in %file
..::joined with "\n" ..::joined with "\n"
externally (spoof file %text) means (%Files.spoof %text)
externally (spoof file %filename = %text) means (%Files.spoof %filename %text)

View File

@ -72,9 +72,9 @@ test:
lua:append("class.", fn_name) lua:append("class.", fn_name)
else else
lua:append("function(") lua:append("function(")
lua:concat_append(table.map(\%alias_args, function(a) return nomsu:compile(a) end), ", ") lua:concat_append(table.map(\%alias_args, function(a) return compile(a) end), ", ")
lua:append(")\\n return class.", fn_name, "(") lua:append(")\\n return class.", fn_name, "(")
lua:concat_append(table.map(\%args, function(a) return nomsu:compile(a) end), ", ") lua:concat_append(table.map(\%args, function(a) return compile(a) end), ", ")
lua:append(")\\nend") lua:append(")\\nend")
end end
end end
@ -113,11 +113,11 @@ test:
end, end,
}) })
class.__members = \(%members as lua expr) class.__members = \(%members as lua expr)
nomsu.environment[("a "..class.name):as_lua_id()] = class _ENV[("a "..class.name):as_lua_id()] = class
nomsu.environment[("an "..class.name):as_lua_id()] = class _ENV[("an "..class.name):as_lua_id()] = class
nomsu.environment[("a "..class.name.." with"):as_lua_id()] = class _ENV[("a "..class.name.." with"):as_lua_id()] = class
nomsu.environment[("an "..class.name.." with"):as_lua_id()] = class _ENV[("an "..class.name.." with"):as_lua_id()] = class
nomsu.environment[class.name:as_lua_id()] = function() return class end _ENV[class.name:as_lua_id()] = function() return class end
class.__index = class class.__index = class
class.class = class class.class = class
local dict_tostring = getmetatable(Dict{}).__tostring local dict_tostring = getmetatable(Dict{}).__tostring

View File

@ -1,6 +1,8 @@
#!/usr/bin/env nomsu -V4.8.10 #!/usr/bin/env nomsu -V4.8.10
use "lib/object.nom" use "lib/object.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# The types are [..] # The types are [..]
"Number", "Var", "Block", "EscapedNomsu", "Text", "List", "Dict", "DictEntry", "Number", "Var", "Block", "EscapedNomsu", "Text", "List", "Dict", "DictEntry",
"IndexChain", "Action", "FileChunks", "Error", "Comment" "IndexChain", "Action", "FileChunks", "Error", "Comment"

View File

@ -4,6 +4,8 @@
indentation levels. indentation levels.
use "lib/things.nom" use "lib/things.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
a (Code Buffer) is a thing: a (Code Buffer) is a thing:
that can (set up) by: that can (set up) by:
assume %its.source assume %its.source

View File

@ -4,6 +4,8 @@ use "nomnom/code_obj.nom"
use "nomnom/parser.nom" use "nomnom/parser.nom"
use "nomnom/pretty_errors.nom" use "nomnom/pretty_errors.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
externally (report compile error at %tree %err) means: externally (report compile error at %tree %err) means:
barf (pretty "Compile Error" error at %tree %err) barf (pretty "Compile Error" error at %tree %err)

View File

@ -3,6 +3,8 @@
use "nomnom/code_obj.nom" use "nomnom/code_obj.nom"
use "nomnom/parser.nom" use "nomnom/parser.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# TODO: maybe re-implement the fancy coroutine checker that aborts early if nomsu gets too long # TODO: maybe re-implement the fancy coroutine checker that aborts early if nomsu gets too long
externally (%tree decompiled inline) means: externally (%tree decompiled inline) means:
assume (%tree is a "Syntax Tree") assume (%tree is a "Syntax Tree")
@ -175,10 +177,10 @@ externally (%tree decompiled) means:
"FileChunks": "FileChunks":
(%1 and %2 should clump) means: (%1 and %2 should clump) means:
if ((%1.type == "Action") and (%2.type == "Action")): if ((%1.type == "Action") and (%2.type == "Action")):
if (%1.stub == "use 1"): if (%1.stub == "use"):
return (%2.stub == "use 1") return (%2.stub == "use")
if (%1.stub == "test 1"): return (yes) if (%1.stub == "test"): return (yes)
if (%2.stub == "test 1"): return (no) if (%2.stub == "test"): return (no)
return (not ((recurse on %1)::is multi-line)) return (not ((recurse on %1)::is multi-line))
@ -199,14 +201,12 @@ externally (%tree decompiled) means:
return %nomsu return %nomsu
"Action": "Action":
%pos = %tree.source.start
%next_space = "" %next_space = ""
if %tree.target: if %tree.target:
%target_nomsu = (recurse on %tree.target) %target_nomsu = (recurse on %tree.target)
if ((%tree.target.type == "Action") and (%target_nomsu::is one line)): if ((%tree.target.type == "Action") and (%target_nomsu::is one line)):
%target_nomsu::parenthesize %target_nomsu::parenthesize
%nomsu::add %target_nomsu %nomsu::add %target_nomsu
%pos = %tree.target.source.stop
%next_space = ("\n..::" if (%target_nomsu::is multi-line) else "::") %next_space = ("\n..::" if (%target_nomsu::is multi-line) else "::")
for %bit in %tree at %i: for %bit in %tree at %i:
@ -290,8 +290,9 @@ externally (%tree decompiled) means:
"Var": "Var":
if ((%tree.(%i+1) is text) and (not (%tree.(%i+1)::matches "^[ \n\t,.:#(){}[%]]"))): if ((%tree.(%i+1) is text) and (not (%tree.(%i+1)::matches "^[ \n\t,.:#(){}[%]]"))):
%interp_nomsu::parenthesize %interp_nomsu::parenthesize
"List" "Dict": "List" "Dict":
do nothing
else:
%interp_nomsu::parenthesize %interp_nomsu::parenthesize
%nomsu::add %interp_nomsu %nomsu::add %interp_nomsu

View File

@ -2,6 +2,8 @@
# Some file utilities for searching for files recursively and using package.nomsupath # Some file utilities for searching for files recursively and using package.nomsupath
use "lib/os.nom" use "lib/os.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%_SPOOFED_FILES = {} %_SPOOFED_FILES = {}
%_FILE_CACHE = ({} with fallback % -> %_SPOOFED_FILES.%) %_FILE_CACHE = ({} with fallback % -> %_SPOOFED_FILES.%)
%_BROWSE_CACHE = {} %_BROWSE_CACHE = {}

View File

@ -1,6 +1,8 @@
#!/usr/bin/env nomsu -V4.8.10 #!/usr/bin/env nomsu -V4.8.10
use "lib/object.nom" use "lib/object.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
object (Source): object (Source):
externally (Source from text %text) means: externally (Source from text %text) means:
%match = (%text::matching groups "^@(.-)%[(%d+):(%d+)%]$") %match = (%text::matching groups "^@(.-)%[(%d+):(%d+)%]$")

View File

@ -114,14 +114,15 @@ index_chain (IndexChain):
-- Actions need either at least 1 word, or at least 2 tokens -- Actions need either at least 1 word, or at least 2 tokens
inline_action (Action): inline_action (Action):
!section_division !section_division
({:target: inline_arg :} ws* "::" ws*)? ({:target: (inline_expression / "(" inline_block ")") :} ws* "::" ws*)?
( (inline_arg (ws* (inline_arg / word))+) ( (inline_arg (ws* (inline_arg / word))+)
/ (word (ws* (inline_arg / word))*)) / (word (ws* (inline_arg / word))*))
(ws* inline_block)? (ws* inline_block)?
inline_arg: inline_expression / inline_block inline_arg: inline_expression / inline_block
action (Action): action (Action):
!section_division !section_division
({:target: arg :} ((ws* "\")? eol nl_nodent "..")? ws* "::" ((ws* "\")? eol nl_nodent "..")? ws*)? ({:target: (expression / "(" inline_block ")" / indented_block) :}
((ws* "\")? eol nl_nodent "..")? ws* "::" ((ws* "\")? eol nl_nodent "..")? ws*)?
( (arg (((ws* "\")? eol nl_nodent "..")? ws* (arg / word))+) ( (arg (((ws* "\")? eol nl_nodent "..")? ws* (arg / word))+)
/ (word (((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
@ -190,8 +191,8 @@ indented_list (List):
(","? unexpected_code)? (","? unexpected_code)?
list_line: list_line:
(inline_list_item ws* "," ws*)+ eol (inline_list_item ws* "," ws*)+ eol
/ (inline_list_item ws* "," ws*)* (action / expression) eol / (inline_list_item ws* "," ws*)* (action / expression / inline_block / indented_block) eol
inline_list_item: inline_action / inline_expression inline_list_item: inline_action / inline_expression / inline_block
inline_dict (Dict): inline_dict (Dict):
!('{..}') !('{..}')
@ -206,10 +207,14 @@ indented_dict (Dict):
dict_line: dict_line:
(inline_dict_entry ws* "," ws*)+ eol (inline_dict_entry ws* "," ws*)+ eol
/ (inline_dict_entry ws* "," ws*)* dict_entry eol / (inline_dict_entry ws* "," ws*)* dict_entry eol
dict_entry(DictEntry): _dict_entry(DictEntry):
dict_key (ws* ":" ws* (action / expression))? dict_key (ws* ":" ws* (action / expression))?
inline_dict_entry(DictEntry): dict_entry:
_dict_entry / inline_block / indented_block
_inline_dict_entry(DictEntry):
dict_key (ws* ":" ws* (inline_action / inline_expression)?)? dict_key (ws* ":" ws* (inline_action / inline_expression)?)?
inline_dict_entry:
_inline_dict_entry / inline_block
dict_key: dict_key:
text_word / inline_expression text_word / inline_expression

196
nomsu.lua
View File

@ -85,16 +85,21 @@ if not ok then
end end
local Files = require("files") local Files = require("files")
local Errhand = require("error_handling") local Errhand = require("error_handling")
local NomsuCompiler = require("nomsu_compiler")
local NomsuCode, LuaCode, Source local NomsuCode, LuaCode, Source
do do
local _obj_0 = require("code_obj") local _obj_0 = require("code_obj")
NomsuCode, LuaCode, Source = _obj_0.NomsuCode, _obj_0.LuaCode, _obj_0.Source NomsuCode, LuaCode, Source = _obj_0.NomsuCode, _obj_0.LuaCode, _obj_0.Source
end end
if not arg or debug.getinfo(2).func == require then local List, Dict, Text
return NomsuCompiler do
local _obj_0 = require('containers')
List, Dict, Text = _obj_0.List, _obj_0.Dict, _obj_0.Text
end end
local file_queue = { } local nomsu_environment = require('nomsu_environment')
if not arg or debug.getinfo(2).func == require then
return nomsu_environment
end
local file_queue = List({ })
local sep = "\3" local sep = "\3"
local parser = re.compile([[ args <- {| (flag %sep)* (({~ file ~} -> add_file) {:primary_file: %true :} %sep)? local parser = re.compile([[ args <- {| (flag %sep)* (({~ file ~} -> add_file) {:primary_file: %true :} %sep)?
{:nomsu_args: {| ({(!%sep .)*} %sep)* |} :} %sep? |} !. {:nomsu_args: {| ({(!%sep .)*} %sep)* |} :} %sep? |} !.
@ -116,12 +121,12 @@ local parser = re.compile([[ args <- {| (flag %sep)* (({~ file ~} -> add_file
number = lpeg.R("09") ^ 1 / tonumber, number = lpeg.R("09") ^ 1 / tonumber,
sep = lpeg.P(sep), sep = lpeg.P(sep),
add_file = function(f) add_file = function(f)
return table.insert(file_queue, f) return file_queue:add(f)
end, end,
add_exec_string = function(pos, s) add_exec_string = function(pos, s)
local name = "command line arg @" .. tostring(pos) .. ".nom" local name = "command line arg @" .. tostring(pos) .. ".nom"
Files.spoof(name, s) Files.spoof(name, s)
return table.insert(file_queue, name) return file_queue:add(name)
end end
}) })
local arg_string = table.concat(arg, sep) .. sep local arg_string = table.concat(arg, sep) .. sep
@ -130,24 +135,13 @@ if not args or args.help then
print(usage) print(usage)
os.exit(EXIT_FAILURE) os.exit(EXIT_FAILURE)
end end
local nomsu = NomsuCompiler nomsu_environment.command_line_args = List(args.nomsu_args)
nomsu.environment.arg = NomsuCompiler.environment.List(args.nomsu_args) nomsu_environment.optimization = args.optimization or 1
if args.version then if args.version then
nomsu:run([[(: use "core"; say (Nomsu version))]]) nomsu_environment.run_file_1_in('core', nomsu_environment)
nomsu_environment.run_1_in([[say (Nomsu version)]], nomsu_environment)
os.exit(EXIT_SUCCESS) os.exit(EXIT_SUCCESS)
end end
FILE_CACHE = setmetatable({ }, {
__index = function(self, filename)
local file = io.open(filename)
if not (file) then
return nil
end
local contents = file:read("*a")
file:close()
self[filename] = contents
return contents
end
})
local run local run
run = function() run = function()
local input_files = { } local input_files = { }
@ -172,62 +166,9 @@ run = function()
break break
end end
end end
nomsu.can_optimize = function(f)
if args.optimization == 0 then
return false
end
if args.compile and input_files[f] then
return false
end
return true
end
if not (args.no_core) then if not (args.no_core) then
nomsu:import_file('core') nomsu_environment.run_file_1_in('core', nomsu_environment)
end end
local get_file_and_source
get_file_and_source = function(filename)
local file, source
if filename == 'stdin' or filename:match("%.nom$") then
file = Files.read(filename)
if not file then
error("File does not exist: " .. tostring(filename), 0)
end
source = Source(filename, 1, #file)
else
return nil
end
source = Source(filename, 1, #file)
return file, source
end
local run_file
run_file = function(filename, lua_handler)
if lua_handler == nil then
lua_handler = nil
end
local file, source = get_file_and_source(filename)
if not (file) then
return
end
local tree = nomsu:parse(file, source)
if tree then
if tree.type ~= "FileChunks" then
tree = {
tree
}
end
for _index_0 = 1, #tree do
local chunk = tree[_index_0]
local lua = nomsu:compile(chunk):as_statements("return ")
lua:declare_locals()
lua:prepend("-- File: " .. tostring(source.filename:gsub("\n.*", "...")) .. "\n")
if lua_handler and input_files[filename] then
lua_handler(tostring(lua))
end
nomsu:run_lua(lua)
end
end
end
local parse_errs = { }
for _index_0 = 1, #file_queue do for _index_0 = 1, #file_queue do
local f = file_queue[_index_0] local f = file_queue[_index_0]
for _, filename in Files.walk(f) do for _, filename in Files.walk(f) do
@ -238,32 +179,57 @@ run = function()
break break
end end
if args.check_syntax then if args.check_syntax then
local file, source = get_file_and_source(filename) local code = Files.read(filename)
if not (file) then local source = Source(filename, 1, #code)
_continue_0 = true nomsu_environment._1_parsed(NomsuCode(source, code))
break
end
local tree = nomsu:parse(file, source)
print("Parse succeeded: " .. tostring(filename)) print("Parse succeeded: " .. tostring(filename))
end elseif args.compile then
if args.compile then
local output local output
if filename == 'stdin' then if filename == 'stdin' then
output = io.output() output = io.output()
else else
output = io.open(filename:gsub("%.nom$", ".lua"), "w") output = io.open(filename:gsub("%.nom$", ".lua"), "w")
end end
run_file(filename, function(lua) local code = Files.read(filename)
local source = Source(filename, 1, #code)
code = NomsuCode(source, code)
local tree = nomsu_environment._1_parsed(code)
if not (tree.type == 'FileChunks') then
tree = {
tree
}
end
for _index_1 = 1, #tree do
local chunk = tree[_index_1]
local lua = nomsu_environment.compile(chunk)
lua:declare_locals()
nomsu_environment.run_1_in(chunk, nomsu_environment)
output:write(tostring(lua), "\n") output:write(tostring(lua), "\n")
if args.verbose then if args.verbose then
return print(tostring(lua)) print(tostring(lua))
end
end end
end)
print(("Compiled %-25s -> %s"):format(filename, filename:gsub("%.nom$", ".lua"))) print(("Compiled %-25s -> %s"):format(filename, filename:gsub("%.nom$", ".lua")))
output:close() output:close()
elseif args.verbose then
local code = Files.read(filename)
local source = Source(filename, 1, #code)
code = NomsuCode(source, code)
local tree = nomsu_environment._1_parsed(code)
if not (tree.type == 'FileChunks') then
tree = {
tree
}
end end
if not args.check_syntax and not args.compile then for _index_1 = 1, #tree do
run_file(filename, (args.verbose and print or nil)) local chunk = tree[_index_1]
local lua = nomsu_environment.compile(chunk)
lua:declare_locals()
nomsu_environment.run_1_in(chunk, nomsu_environment)
print(tostring(lua))
end
else
nomsu_environment.run_file_1_in(filename, nomsu_environment, 0)
end end
_continue_0 = true _continue_0 = true
until true until true
@ -273,59 +239,7 @@ run = function()
end end
end end
if not (args.primary_file or args.exec_strings) then if not (args.primary_file or args.exec_strings) then
nomsu:run([[#!/usr/bin/env nomsu -V4 return nomsu_environment.run_file_1_in("tools/repl.nom", nomsu_environment)
use "lib/consolecolor.nom"
[quit, exit] all mean: lua> "os.exit(0)"
(help) means:
say "\
..This is the Nomsu v\(Nomsu version) interactive console.
You can type in Nomsu code here and hit 'enter' twice to run it.
To exit, type 'exit' or 'quit' and hit enter twice."
say "\
..
\(bright)\(underscore)Welcome to the Nomsu v\(Nomsu version) interactive console!\(reset color)
press 'enter' twice to run a command
"]])
for repl_line = 1, math.huge do
io.write(colored.bright(colored.yellow(">> ")))
local buff = { }
while true do
io.write(colors.bright)
local line = io.read("*L")
io.write(colors.reset)
if line == "\n" or not line then
if #buff > 0 then
io.write("\027[1A\027[2K")
end
break
end
line = line:gsub("\t", " ")
table.insert(buff, line)
io.write(colored.dim(colored.yellow(".. ")))
end
if #buff == 0 then
break
end
buff = table.concat(buff)
local pseudo_filename = "user input #" .. repl_line
Files.spoof(pseudo_filename, buff)
local err_hand
err_hand = function(error_message)
return Errhand.print_error(error_message)
end
local ret
ok, ret = xpcall(nomsu.run, err_hand, nomsu, NomsuCode(Source(pseudo_filename, 1, #buff), buff))
if ok and ret ~= nil then
if type(ret) == 'number' then
print("= " .. tostring(ret))
else
print("= " .. tostring(ret:as_nomsu()))
end
elseif not ok then
Errhand.print_error(ret)
end
end
end end
end end
local debugger local debugger

View File

@ -46,14 +46,18 @@ if not ok
os.exit(EXIT_FAILURE) os.exit(EXIT_FAILURE)
Files = require "files" Files = require "files"
Errhand = require "error_handling" Errhand = require "error_handling"
NomsuCompiler = require "nomsu_compiler" --NomsuCompiler = require "nomsu_compiler"
{:NomsuCode, :LuaCode, :Source} = require "code_obj" {:NomsuCode, :LuaCode, :Source} = require "code_obj"
--{:Importer, :import_to_1_from, :_1_forked} = require 'importer'
{:List, :Dict, :Text} = require 'containers'
--SyntaxTree = require "syntax_tree"
nomsu_environment = require('nomsu_environment')
-- If this file was reached via require(), then just return the Nomsu compiler -- If this file was reached via require(), then just return the Nomsu compiler
if not arg or debug.getinfo(2).func == require if not arg or debug.getinfo(2).func == require
return NomsuCompiler return nomsu_environment
file_queue = {} file_queue = List{}
sep = "\3" sep = "\3"
parser = re.compile([[ parser = re.compile([[
args <- {| (flag %sep)* (({~ file ~} -> add_file) {:primary_file: %true :} %sep)? args <- {| (flag %sep)* (({~ file ~} -> add_file) {:primary_file: %true :} %sep)?
@ -73,37 +77,25 @@ parser = re.compile([[
file <- ("-" -> "stdin") / {(!%sep .)+} file <- ("-" -> "stdin") / {(!%sep .)+}
]], { ]], {
true:lpeg.Cc(true), number:lpeg.R("09")^1/tonumber, sep:lpeg.P(sep) true:lpeg.Cc(true), number:lpeg.R("09")^1/tonumber, sep:lpeg.P(sep)
add_file: (f)-> table.insert(file_queue, f) add_file: (f)-> file_queue\add(f)
add_exec_string: (pos, s)-> add_exec_string: (pos, s)->
name = "command line arg @#{pos}.nom" name = "command line arg @#{pos}.nom"
Files.spoof(name, s) Files.spoof(name, s)
table.insert(file_queue, name) file_queue\add name
}) })
arg_string = table.concat(arg, sep)..sep arg_string = table.concat(arg, sep)..sep
args = parser\match(arg_string) args = parser\match(arg_string)
if not args or args.help if not args or args.help
print usage print usage
os.exit(EXIT_FAILURE) os.exit(EXIT_FAILURE)
nomsu_environment.command_line_args = List(args.nomsu_args)
nomsu = NomsuCompiler nomsu_environment.optimization = args.optimization or 1
nomsu.environment.arg = NomsuCompiler.environment.List(args.nomsu_args)
if args.version if args.version
nomsu\run [[(: use "core"; say (Nomsu version))]] nomsu_environment.run_file_1_in 'core', nomsu_environment
nomsu_environment.run_1_in([[say (Nomsu version)]], nomsu_environment)
os.exit(EXIT_SUCCESS) os.exit(EXIT_SUCCESS)
export FILE_CACHE
-- FILE_CACHE is a map from filename (string) -> string of file contents
FILE_CACHE = setmetatable {}, {
__index: (filename)=>
file = io.open(filename)
return nil unless file
contents = file\read("*a")
file\close!
self[filename] = contents
return contents
}
run = -> run = ->
input_files = {} input_files = {}
for f in *file_queue for f in *file_queue
@ -115,116 +107,52 @@ run = ->
for _,filename in Files.walk(f) for _,filename in Files.walk(f)
input_files[filename] = true input_files[filename] = true
nomsu.can_optimize = (f)->
return false if args.optimization == 0
return false if args.compile and input_files[f]
return true
unless args.no_core unless args.no_core
nomsu\import_file('core') nomsu_environment.run_file_1_in 'core', nomsu_environment
get_file_and_source = (filename)->
local file, source
if filename == 'stdin' or filename\match("%.nom$")
file = Files.read(filename)
if not file
error("File does not exist: #{filename}", 0)
source = Source(filename, 1, #file)
else return nil
source = Source(filename,1,#file)
return file, source
run_file = (filename, lua_handler=nil)->
file, source = get_file_and_source(filename)
return unless file
tree = nomsu\parse(file, source)
if tree
if tree.type != "FileChunks"
tree = {tree}
-- Each chunk's compilation is affected by the code in the previous chunks
-- (typically), so each chunk needs to compile and run before the next one
-- compiles.
for chunk in *tree
lua = nomsu\compile(chunk)\as_statements("return ")
lua\declare_locals!
lua\prepend "-- File: #{source.filename\gsub("\n.*", "...")}\n"
if lua_handler and input_files[filename] then lua_handler(tostring(lua))
nomsu\run_lua(lua)
parse_errs = {}
for f in *file_queue for f in *file_queue
for _,filename in Files.walk(f) for _,filename in Files.walk(f)
continue unless filename == "stdin" or filename\match("%.nom$") continue unless filename == "stdin" or filename\match("%.nom$")
if args.check_syntax if args.check_syntax
-- Check syntax -- Check syntax
file, source = get_file_and_source(filename) code = Files.read(filename)
continue unless file source = Source(filename, 1, #code)
tree = nomsu\parse(file, source) nomsu_environment._1_parsed(NomsuCode(source, code))
print("Parse succeeded: #{filename}") print("Parse succeeded: #{filename}")
elseif args.compile
if args.compile
-- Compile .nom files into .lua -- Compile .nom files into .lua
output = if filename == 'stdin' then io.output() output = if filename == 'stdin' then io.output()
else io.open(filename\gsub("%.nom$", ".lua"), "w") else io.open(filename\gsub("%.nom$", ".lua"), "w")
run_file filename, (lua)-> code = Files.read(filename)
source = Source(filename, 1, #code)
code = NomsuCode(source, code)
tree = nomsu_environment._1_parsed(code)
tree = {tree} unless tree.type == 'FileChunks'
for chunk in *tree
lua = nomsu_environment.compile(chunk)
lua\declare_locals!
nomsu_environment.run_1_in(chunk, nomsu_environment)
output\write(tostring(lua), "\n") output\write(tostring(lua), "\n")
if args.verbose then print(tostring(lua)) if args.verbose then print(tostring(lua))
print ("Compiled %-25s -> %s")\format(filename, filename\gsub("%.nom$", ".lua")) print ("Compiled %-25s -> %s")\format(filename, filename\gsub("%.nom$", ".lua"))
output\close! output\close!
elseif args.verbose
if not args.check_syntax and not args.compile code = Files.read(filename)
source = Source(filename, 1, #code)
code = NomsuCode(source, code)
tree = nomsu_environment._1_parsed(code)
tree = {tree} unless tree.type == 'FileChunks'
for chunk in *tree
lua = nomsu_environment.compile(chunk)
lua\declare_locals!
nomsu_environment.run_1_in(chunk, nomsu_environment)
print(tostring(lua))
else
-- Just run the file -- Just run the file
run_file filename, (args.verbose and print or nil) nomsu_environment.run_file_1_in(filename, nomsu_environment, 0)
unless args.primary_file or args.exec_strings unless args.primary_file or args.exec_strings
-- Run in interactive mode (REPL) nomsu_environment.run_file_1_in("tools/repl.nom", nomsu_environment)
nomsu\run [[
#!/usr/bin/env nomsu -V4
use "lib/consolecolor.nom"
[quit, exit] all mean: lua> "os.exit(0)"
(help) means:
say "\
..This is the Nomsu v\(Nomsu version) interactive console.
You can type in Nomsu code here and hit 'enter' twice to run it.
To exit, type 'exit' or 'quit' and hit enter twice."
say "\
..
\(bright)\(underscore)Welcome to the Nomsu v\(Nomsu version) interactive console!\(reset color)
press 'enter' twice to run a command
"]]
for repl_line=1,math.huge
io.write(colored.bright colored.yellow ">> ")
buff = {}
while true
io.write(colors.bright)
line = io.read("*L")
io.write(colors.reset)
if line == "\n" or not line
if #buff > 0
io.write("\027[1A\027[2K")
break -- Run buffer
line = line\gsub("\t", " ")
table.insert buff, line
io.write(colored.dim colored.yellow ".. ")
if #buff == 0
break -- Exit
buff = table.concat(buff)
-- TODO: support local variables
pseudo_filename = "user input #"..repl_line
Files.spoof(pseudo_filename, buff)
err_hand = (error_message)->
Errhand.print_error error_message
ok, ret = xpcall nomsu.run, err_hand, nomsu, NomsuCode(Source(pseudo_filename,1,#buff), buff)
if ok and ret != nil
if type(ret) == 'number'
print "= #{ret}"
else
print "= #{ret\as_nomsu!}"
elseif not ok
Errhand.print_error ret
debugger = if args.debugger == "nil" then {} debugger = if args.debugger == "nil" then {}
else require(args.debugger or 'error_handling') else require(args.debugger or 'error_handling')

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

411
nomsu_decompiler.lua Normal file
View File

@ -0,0 +1,411 @@
local NomsuCode
NomsuCode = require("code_obj").NomsuCode
local find, sub, match
do
local _obj_0 = string
find, sub, match = _obj_0.find, _obj_0.sub, _obj_0.match
end
local R, P, S
do
local _obj_0 = require('lpeg')
R, P, S = _obj_0.R, _obj_0.P, _obj_0.S
end
local re = require('re')
local MAX_LINE = 90
local utf8_char_patt = (R("\194\223") * R("\128\191") + R("\224\239") * R("\128\191") * R("\128\191") + R("\240\244") * R("\128\191") * R("\128\191") * R("\128\191"))
local operator_patt = S("'`~!@$^&*+=|<>?/-") ^ 1 * -1
local identifier_patt = (R("az", "AZ", "09") + P("_") + utf8_char_patt) ^ 1 * -1
local is_operator
is_operator = function(s)
return not not operator_patt:match(s)
end
local is_identifier
is_identifier = function(s)
return not not identifier_patt:match(s)
end
local inline_escaper = re.compile("{~ (%utf8_char / ('\"' -> '\\\"') / ('\n' -> '\\n') / ('\t' -> '\\t') / ('\b' -> '\\b') / ('\a' -> '\\a') / ('\v' -> '\\v') / ('\f' -> '\\f') / ('\r' -> '\\r') / ('\\' -> '\\\\') / ([^ -~] -> escape) / .)* ~}", {
utf8_char = utf8_char_patt,
escape = (function(self)
return ("\\%03d"):format(self:byte())
end)
})
local inline_escape
inline_escape = function(s)
return inline_escaper:match(s)
end
local escaper = re.compile("{~ (%utf8_char / ('\\' -> '\\\\') / [\n\r\t -~] / (. -> escape))* ~}", {
utf8_char = utf8_char_patt,
escape = (function(self)
return ("\\%03d"):format(self:byte())
end)
})
local escape
escape = function(s)
return escaper:match(s)
end
local tree_to_inline_nomsu
tree_to_inline_nomsu = function(tree)
local _exp_0 = tree.type
if "Action" == _exp_0 then
local nomsu = NomsuCode(tree.source)
if tree.target then
local inline_target = tree_to_inline_nomsu(tree.target)
if tree.target.type == "Action" then
inline_target:parenthesize()
end
nomsu:append(inline_target, "::")
end
for i, bit in ipairs(tree) do
if type(bit) == "string" then
local clump_words = (type(tree[i - 1]) == 'string' and is_operator(bit) ~= is_operator(tree[i - 1]))
if i > 1 and not clump_words then
nomsu:append(" ")
end
nomsu:append(bit)
else
local arg_nomsu = tree_to_inline_nomsu(bit)
if bit.type == "Block" then
if i > 1 and i < #tree then
nomsu:append(" ")
end
if not (i == #tree) then
arg_nomsu:parenthesize()
end
else
if i > 1 then
nomsu:append(" ")
end
if bit.type == "Action" then
arg_nomsu:parenthesize()
end
end
nomsu:append(arg_nomsu)
end
end
return nomsu
elseif "EscapedNomsu" == _exp_0 then
local inner_nomsu = tree_to_inline_nomsu(tree[1])
if not (tree[1].type == "List" or tree[1].type == "Dict" or tree[1].type == "Var") then
inner_nomsu:parenthesize()
end
return NomsuCode(tree.source, "\\", inner_nomsu)
elseif "Block" == _exp_0 then
local nomsu = NomsuCode(tree.source, ":")
for i, line in ipairs(tree) do
nomsu:append(i == 1 and " " or "; ")
nomsu:append(tree_to_inline_nomsu(line))
end
if #tree > 1 then
nomsu:parenthesize()
end
return nomsu
elseif "Text" == _exp_0 then
local add_text
add_text = function(nomsu, tree)
for i, bit in ipairs(tree) do
if type(bit) == 'string' then
local escaped = inline_escape(bit)
nomsu:append(inline_escape(bit))
elseif bit.type == "Text" then
add_text(nomsu, bit)
else
local interp_nomsu = tree_to_inline_nomsu(bit)
if bit.type ~= "Var" and bit.type ~= "List" and bit.type ~= "Dict" then
interp_nomsu:parenthesize()
elseif bit.type == "Var" and type(tree[i + 1]) == 'string' and not match(tree[i + 1], "^[ \n\t,.:;#(){}[%]]") then
interp_nomsu:parenthesize()
end
nomsu:append("\\", interp_nomsu)
end
end
end
local nomsu = NomsuCode(tree.source)
add_text(nomsu, tree)
return NomsuCode(tree.source, '"', nomsu, '"')
elseif "List" == _exp_0 or "Dict" == _exp_0 then
local nomsu = NomsuCode(tree.source, (tree.type == "List" and "[" or "{"))
for i, item in ipairs(tree) do
if i > 1 then
nomsu:append(", ")
end
nomsu:append(tree_to_inline_nomsu(item))
end
nomsu:append(tree.type == "List" and "]" or "}")
return nomsu
elseif "DictEntry" == _exp_0 then
local key, value = tree[1], tree[2]
local nomsu
if key.type == "Text" and #key == 1 and is_identifier(key[1]) then
nomsu = NomsuCode(key.source, key[1])
else
nomsu = tree_to_inline_nomsu(key)
end
if key.type == "Action" or key.type == "Block" then
nomsu:parenthesize()
end
assert(value.type ~= "Block", "Didn't expect to find a Block as a value in a dict")
nomsu:append(":")
if value then
local value_nomsu = tree_to_inline_nomsu(value)
if value.type == "Block" then
value_nomsu:parenthesize()
end
nomsu:append(value_nomsu)
end
return nomsu
elseif "IndexChain" == _exp_0 then
local nomsu = NomsuCode(tree.source)
for i, bit in ipairs(tree) do
if i > 1 then
nomsu:append(".")
end
local bit_nomsu
if i > 1 and bit.type == "Text" and #bit == 1 and type(bit[1]) == 'string' and is_identifier(bit[1]) then
bit_nomsu = bit[1]
else
bit_nomsu = tree_to_inline_nomsu(bit)
end
assert(bit.type ~= "Block")
if bit.type == "Action" or bit.type == "IndexChain" or (bit.type == "Number" and i < #tree) then
bit_nomsu:parenthesize()
end
nomsu:append(bit_nomsu)
end
return nomsu
elseif "Number" == _exp_0 then
return NomsuCode(tree.source, tostring(tree[1]))
elseif "Var" == _exp_0 then
return NomsuCode(tree.source, "%", tree[1])
elseif "FileChunks" == _exp_0 then
return error("Can't inline a FileChunks")
elseif "Comment" == _exp_0 then
return nil
elseif "Error" == _exp_0 then
return error("Can't compile errors")
else
return error("Unknown type: " .. tostring(tree.type))
end
end
local tree_to_nomsu
tree_to_nomsu = function(tree)
local nomsu = NomsuCode(tree.source)
local recurse
recurse = function(t)
local space = MAX_LINE - nomsu:trailing_line_len()
local inline = true
for subtree in coroutine.wrap(function()
return (t:map(coroutine.yield) and nil)
end) do
if subtree.type == "Block" then
if #subtree > 1 or #tree_to_inline_nomsu(subtree):text() > 20 then
inline = false
end
end
end
if inline then
local inline_nomsu = tree_to_inline_nomsu(t)
if #inline_nomsu:text() <= space then
if t.type == "Action" then
inline_nomsu:parenthesize()
end
return inline_nomsu
end
end
local indented = tree_to_nomsu(t)
if t.type == "Action" then
if indented:is_multiline() then
return NomsuCode(t.source, "(..)\n ", indented)
else
indented:parenthesize()
end
end
return indented
end
local _exp_0 = tree.type
if "FileChunks" == _exp_0 then
local should_clump
should_clump = function(prev_line, line)
if prev_line.type == "Action" and line.type == "Action" then
if prev_line.stub == "use" then
return line.stub == "use"
end
if prev_line.stub == "test" then
return true
end
if line.stub == "test" then
return false
end
end
return not recurse(prev_line):is_multiline()
end
for chunk_no, chunk in ipairs(tree) do
if chunk_no > 1 then
nomsu:append("\n\n" .. tostring(("~"):rep(80)) .. "\n\n")
end
if chunk.type == "Block" then
for line_no, line in ipairs(chunk) do
if line_no > 1 then
if should_clump(chunk[line_no - 1], line) then
nomsu:append("\n")
else
nomsu:append("\n\n")
end
end
nomsu:append(tree_to_nomsu(line))
end
else
nomsu:append(tree_to_nomsu(chunk))
end
end
if not (nomsu:match("\n$")) then
nomsu:append('\n')
end
return nomsu
elseif "Action" == _exp_0 then
local next_space = ""
if tree.target then
local target_nomsu = recurse(tree.target)
if (tree.target.type == "Block" or tree.target.type == "EscapedNomsu") and not target_nomsu:is_multiline() then
target_nomsu:parenthesize()
end
nomsu:append(target_nomsu)
nomsu:append(target_nomsu:is_multiline() and "\n..::" or "::")
end
for i, bit in ipairs(tree) do
if type(bit) == "string" then
if not (next_space == " " and (type(tree[i - 1]) == 'string' and is_operator(tree[i - 1]) ~= is_operator(bit))) then
nomsu:append(next_space)
end
nomsu:append(bit)
next_space = nomsu:trailing_line_len() > MAX_LINE and " \\\n.." or " "
else
local bit_nomsu = recurse(bit)
if i < #tree and (bit.type == "Block" or bit.type == "EscapedNomsu") and not bit_nomsu:is_multiline() then
bit_nomsu:parenthesize()
end
if next_space == " " and not bit_nomsu:is_multiline() and nomsu:trailing_line_len() + #bit_nomsu:text() > MAX_LINE then
next_space = " \\\n.."
end
if not (next_space == " " and bit.type == "Block") then
nomsu:append(next_space)
end
nomsu:append(bit_nomsu)
next_space = bit_nomsu:is_multiline() and "\n.." or " "
end
end
return nomsu
elseif "EscapedNomsu" == _exp_0 then
return NomsuCode(tree.source, "\\", recurse(tree[1]))
elseif "Block" == _exp_0 then
for i, line in ipairs(tree) do
local line_nomsu = tree_to_nomsu(line)
nomsu:append(line_nomsu)
if i < #tree then
nomsu:append(line_nomsu:match('\n[^\n]*\n') and "\n\n" or "\n")
end
end
return NomsuCode(tree.source, ":\n ", nomsu)
elseif "Text" == _exp_0 then
local max_line = math.floor(1.25 * MAX_LINE)
local add_text
add_text = function(tree)
for i, bit in ipairs(tree) do
if type(bit) == 'string' then
bit = escape(bit)
for j, line in ipairs(bit:lines()) do
if j > 1 then
nomsu:append("\n")
elseif #line > 10 and nomsu:trailing_line_len() > max_line then
nomsu:append("\\\n..")
end
while #line > 0 do
local space = max_line - nomsu:trailing_line_len()
local split = find(line, "[%p%s]", space)
if not split or split > space + 10 then
split = space + 10
end
if #line - split < 10 then
split = #line
end
local bite
bite, line = sub(line, 1, split), sub(line, split + 1, -1)
nomsu:append(bite)
if #line > 0 then
nomsu:append("\\\n..")
end
end
end
elseif bit.type == "Text" then
add_text(bit)
else
nomsu:append("\\")
local interp_nomsu = recurse(bit)
if not (interp_nomsu:is_multiline()) then
if bit.type == "Var" then
if type(tree[i + 1]) == 'string' and not match(tree[i + 1], "^[ \n\t,.:;#(){}[%]]") then
interp_nomsu:parenthesize()
end
elseif bit.type == "EscapedNomsu" or bit.type == "Block" then
interp_nomsu:parenthesize()
end
end
nomsu:append(interp_nomsu)
if interp_nomsu:is_multiline() then
nomsu:append("\n..")
end
end
end
end
add_text(tree)
return NomsuCode(tree.source, '"\\\n ..', nomsu, '"')
elseif "List" == _exp_0 or "Dict" == _exp_0 then
if #tree == 0 then
nomsu:append(tree.type == "List" and "[]" or "{}")
return nomsu
end
for i, item in ipairs(tree) do
local item_nomsu = tree_to_inline_nomsu(item)
if #item_nomsu:text() > MAX_LINE then
item_nomsu = recurse(item)
elseif item.type == "Block" or item.type == "EscapedNomsu" then
item_nomsu:parenthesize()
end
nomsu:append(item_nomsu)
if i < #tree then
nomsu:append((item_nomsu:is_multiline() or nomsu:trailing_line_len() + #tostring(item_nomsu) >= MAX_LINE) and '\n' or ', ')
end
end
if tree.type == "List" then
return NomsuCode(tree.source, "[..]\n ", nomsu)
else
return NomsuCode(tree.source, "{..}\n ", nomsu)
end
elseif "DictEntry" == _exp_0 then
local key, value = tree[1], tree[2]
if key.type == "Text" and #key == 1 and is_identifier(key[1]) then
nomsu = NomsuCode(key.source, key[1])
else
nomsu = tree_to_inline_nomsu(key)
end
if key.type == "Block" then
nomsu:parenthesize()
end
local value_nomsu = tree_to_nomsu(value)
if (value.type == "Block" or value.type == "EscapedNomsu") and not value_nomsu:is_multiline() then
value_nomsu:parenthesize()
end
nomsu:append(": ", value_nomsu)
return nomsu
elseif "Comment" == _exp_0 then
nomsu:append("#", tree[1]:gsub("\n", "\n "))
return nomsu
elseif "IndexChain" == _exp_0 or "Number" == _exp_0 or "Var" == _exp_0 or "Comment" == _exp_0 or "Error" == _exp_0 then
return tree_to_inline_nomsu(tree)
else
return error("Unknown type: " .. tostring(tree.type))
end
end
return {
tree_to_nomsu = tree_to_nomsu,
tree_to_inline_nomsu = tree_to_inline_nomsu
}

319
nomsu_decompiler.moon Normal file
View File

@ -0,0 +1,319 @@
{:NomsuCode} = require "code_obj"
{:find, :sub, :match} = string
{:R,:P,:S} = require 'lpeg'
re = require 're'
MAX_LINE = 90
-- Parsing helper functions
utf8_char_patt = (
R("\194\223")*R("\128\191") +
R("\224\239")*R("\128\191")*R("\128\191") +
R("\240\244")*R("\128\191")*R("\128\191")*R("\128\191"))
operator_patt = S("'`~!@$^&*+=|<>?/-")^1 * -1
identifier_patt = (R("az","AZ","09") + P("_") + utf8_char_patt)^1 * -1
is_operator = (s)->
return not not operator_patt\match(s)
is_identifier = (s)->
return not not identifier_patt\match(s)
inline_escaper = re.compile("{~ (%utf8_char / ('\"' -> '\\\"') / ('\n' -> '\\n') / ('\t' -> '\\t') / ('\b' -> '\\b') / ('\a' -> '\\a') / ('\v' -> '\\v') / ('\f' -> '\\f') / ('\r' -> '\\r') / ('\\' -> '\\\\') / ([^ -~] -> escape) / .)* ~}", {utf8_char: utf8_char_patt, escape:(=> ("\\%03d")\format(@byte!))})
inline_escape = (s)->
return inline_escaper\match(s)
escaper = re.compile("{~ (%utf8_char / ('\\' -> '\\\\') / [\n\r\t -~] / (. -> escape))* ~}",
{utf8_char: utf8_char_patt, escape:(=> ("\\%03d")\format(@byte!))})
escape = (s)->
return escaper\match(s)
tree_to_inline_nomsu = (tree)->
switch tree.type
when "Action"
nomsu = NomsuCode(tree.source)
if tree.target
inline_target = tree_to_inline_nomsu(tree.target)
if tree.target.type == "Action"
inline_target\parenthesize!
nomsu\append inline_target, "::"
for i,bit in ipairs tree
if type(bit) == "string"
clump_words = (type(tree[i-1]) == 'string' and is_operator(bit) != is_operator(tree[i-1]))
nomsu\append " " if i > 1 and not clump_words
nomsu\append bit
else
arg_nomsu = tree_to_inline_nomsu(bit)
if bit.type == "Block"
if i > 1 and i < #tree
nomsu\append " "
unless i == #tree
arg_nomsu\parenthesize!
else
nomsu\append " " if i > 1
if bit.type == "Action"
arg_nomsu\parenthesize!
nomsu\append arg_nomsu
return nomsu
when "EscapedNomsu"
inner_nomsu = tree_to_inline_nomsu(tree[1])
unless tree[1].type == "List" or tree[1].type == "Dict" or tree[1].type == "Var"
inner_nomsu\parenthesize!
return NomsuCode(tree.source, "\\", inner_nomsu)
when "Block"
nomsu = NomsuCode(tree.source, ":")
for i,line in ipairs tree
nomsu\append(i == 1 and " " or "; ")
nomsu\append tree_to_inline_nomsu(line)
nomsu\parenthesize! if #tree > 1
return nomsu
when "Text"
add_text = (nomsu, tree)->
for i, bit in ipairs tree
if type(bit) == 'string'
escaped = inline_escape(bit)
nomsu\append inline_escape(bit)
elseif bit.type == "Text"
add_text(nomsu, bit)
else
interp_nomsu = tree_to_inline_nomsu(bit)
if bit.type != "Var" and bit.type != "List" and bit.type != "Dict"
interp_nomsu\parenthesize!
elseif bit.type == "Var" and type(tree[i+1]) == 'string' and not match(tree[i+1], "^[ \n\t,.:;#(){}[%]]")
interp_nomsu\parenthesize!
nomsu\append "\\", interp_nomsu
nomsu = NomsuCode(tree.source)
add_text(nomsu, tree)
return NomsuCode(tree.source, '"', nomsu, '"')
when "List", "Dict"
nomsu = NomsuCode(tree.source, (tree.type == "List" and "[" or "{"))
for i, item in ipairs tree
nomsu\append ", " if i > 1
nomsu\append tree_to_inline_nomsu(item)
nomsu\append(tree.type == "List" and "]" or "}")
return nomsu
when "DictEntry"
key, value = tree[1], tree[2]
nomsu = if key.type == "Text" and #key == 1 and is_identifier(key[1])
NomsuCode(key.source, key[1])
else tree_to_inline_nomsu(key)
nomsu\parenthesize! if key.type == "Action" or key.type == "Block"
assert(value.type != "Block", "Didn't expect to find a Block as a value in a dict")
nomsu\append ":"
if value
value_nomsu = tree_to_inline_nomsu(value)
value_nomsu\parenthesize! if value.type == "Block"
nomsu\append value_nomsu
return nomsu
when "IndexChain"
nomsu = NomsuCode(tree.source)
for i, bit in ipairs tree
nomsu\append "." if i > 1
local bit_nomsu
bit_nomsu = if i > 1 and bit.type == "Text" and #bit == 1 and type(bit[1]) == 'string' and is_identifier(bit[1])
bit[1]
else tree_to_inline_nomsu(bit)
assert bit.type != "Block"
if bit.type == "Action" or bit.type == "IndexChain" or (bit.type == "Number" and i < #tree)
bit_nomsu\parenthesize!
nomsu\append bit_nomsu
return nomsu
when "Number"
return NomsuCode(tree.source, tostring(tree[1]))
when "Var"
return NomsuCode(tree.source, "%", tree[1])
when "FileChunks"
error("Can't inline a FileChunks")
when "Comment"
-- TODO: implement?
return nil
when "Error"
error("Can't compile errors")
else
error("Unknown type: #{tree.type}")
tree_to_nomsu = (tree)->
nomsu = NomsuCode(tree.source)
-- For concision:
recurse = (t)->
space = MAX_LINE - nomsu\trailing_line_len!
inline = true
for subtree in coroutine.wrap(-> (t\map(coroutine.yield) and nil))
if subtree.type == "Block"
if #subtree > 1 or #tree_to_inline_nomsu(subtree)\text! > 20
inline = false
if inline
inline_nomsu = tree_to_inline_nomsu(t)
if #inline_nomsu\text! <= space
if t.type == "Action"
inline_nomsu\parenthesize!
return inline_nomsu
indented = tree_to_nomsu(t)
if t.type == "Action"
if indented\is_multiline!
return NomsuCode(t.source, "(..)\n ", indented)
else indented\parenthesize!
return indented
switch tree.type
when "FileChunks"
should_clump = (prev_line, line)->
if prev_line.type == "Action" and line.type == "Action"
if prev_line.stub == "use" then return line.stub == "use"
if prev_line.stub == "test" then return true
if line.stub == "test" then return false
return not recurse(prev_line)\is_multiline!
for chunk_no, chunk in ipairs tree
nomsu\append "\n\n#{("~")\rep(80)}\n\n" if chunk_no > 1
if chunk.type == "Block"
for line_no, line in ipairs chunk
if line_no > 1
if should_clump(chunk[line_no-1], line)
nomsu\append "\n"
else
nomsu\append "\n\n"
nomsu\append tree_to_nomsu(line)
else
nomsu\append tree_to_nomsu(chunk)
nomsu\append('\n') unless nomsu\match("\n$")
return nomsu
when "Action"
next_space = ""
if tree.target
target_nomsu = recurse(tree.target)
if (tree.target.type == "Block" or tree.target.type == "EscapedNomsu") and not target_nomsu\is_multiline!
target_nomsu\parenthesize!
nomsu\append target_nomsu
nomsu\append(target_nomsu\is_multiline! and "\n..::" or "::")
for i,bit in ipairs tree
if type(bit) == "string"
unless next_space == " " and (type(tree[i-1]) == 'string' and is_operator(tree[i-1]) != is_operator(bit))
nomsu\append next_space
nomsu\append bit
next_space = nomsu\trailing_line_len! > MAX_LINE and " \\\n.." or " "
else
bit_nomsu = recurse(bit)
if i < #tree and (bit.type == "Block" or bit.type == "EscapedNomsu") and not bit_nomsu\is_multiline!
bit_nomsu\parenthesize!
if next_space == " " and not bit_nomsu\is_multiline! and nomsu\trailing_line_len! + #bit_nomsu\text! > MAX_LINE
next_space = " \\\n.."
unless next_space == " " and bit.type == "Block"
nomsu\append next_space
nomsu\append bit_nomsu
next_space = bit_nomsu\is_multiline! and "\n.." or " "
return nomsu
when "EscapedNomsu"
return NomsuCode tree.source, "\\", recurse(tree[1])
when "Block"
for i, line in ipairs tree
line_nomsu = tree_to_nomsu(line)
nomsu\append line_nomsu
if i < #tree
-- number of lines > 2 (TODO: improve this)
nomsu\append(line_nomsu\match('\n[^\n]*\n') and "\n\n" or "\n")
return NomsuCode(tree.source, ":\n ", nomsu)
when "Text"
-- Multi-line text has more generous wrap margins
max_line = math.floor(1.25*MAX_LINE)
add_text = (tree)->
for i, bit in ipairs tree
if type(bit) == 'string'
bit = escape(bit)
for j, line in ipairs bit\lines!
if j > 1
nomsu\append "\n"
elseif #line > 10 and nomsu\trailing_line_len! > max_line
nomsu\append "\\\n.."
while #line > 0
space = max_line - nomsu\trailing_line_len!
split = find(line, "[%p%s]", space)
if not split or split > space + 10
split = space + 10
if #line - split < 10
split = #line
bite, line = sub(line, 1, split), sub(line, split+1, -1)
nomsu\append bite
nomsu\append "\\\n.." if #line > 0
elseif bit.type == "Text"
add_text(bit)
else
nomsu\append "\\"
interp_nomsu = recurse(bit)
unless interp_nomsu\is_multiline!
if bit.type == "Var"
if type(tree[i+1]) == 'string' and not match(tree[i+1], "^[ \n\t,.:;#(){}[%]]")
interp_nomsu\parenthesize!
elseif bit.type == "EscapedNomsu" or bit.type == "Block"
interp_nomsu\parenthesize!
nomsu\append interp_nomsu
if interp_nomsu\is_multiline!
nomsu\append "\n.."
add_text(tree)
return NomsuCode(tree.source, '"\\\n ..', nomsu, '"')
when "List", "Dict"
if #tree == 0
nomsu\append(tree.type == "List" and "[]" or "{}")
return nomsu
for i, item in ipairs tree
item_nomsu = tree_to_inline_nomsu(item)
if #item_nomsu\text! > MAX_LINE
item_nomsu = recurse(item)
elseif item.type == "Block" or item.type == "EscapedNomsu"
item_nomsu\parenthesize!
nomsu\append item_nomsu
if i < #tree
nomsu\append((item_nomsu\is_multiline! or nomsu\trailing_line_len! + #tostring(item_nomsu) >= MAX_LINE) and '\n' or ', ')
return if tree.type == "List" then
NomsuCode(tree.source, "[..]\n ", nomsu)
else
NomsuCode(tree.source, "{..}\n ", nomsu)
when "DictEntry"
key, value = tree[1], tree[2]
nomsu = if key.type == "Text" and #key == 1 and is_identifier(key[1])
NomsuCode(key.source, key[1])
else tree_to_inline_nomsu(key)
nomsu\parenthesize! if key.type == "Block"
value_nomsu = tree_to_nomsu(value)
if (value.type == "Block" or value.type == "EscapedNomsu") and not value_nomsu\is_multiline!
value_nomsu\parenthesize!
nomsu\append ": ", value_nomsu
return nomsu
when "Comment"
nomsu\append "#", tree[1]\gsub("\n", "\n ")
return nomsu
when "IndexChain", "Number", "Var", "Comment", "Error"
return tree_to_inline_nomsu tree
else
error("Unknown type: #{tree.type}")
return {:tree_to_nomsu, :tree_to_inline_nomsu}

325
nomsu_environment.lua Normal file
View File

@ -0,0 +1,325 @@
local NomsuCode, LuaCode, Source
do
local _obj_0 = require("code_obj")
NomsuCode, LuaCode, Source = _obj_0.NomsuCode, _obj_0.LuaCode, _obj_0.Source
end
local Importer, import_to_1_from, _1_forked
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 List, Dict, Text
do
local _obj_0 = require('containers')
List, Dict, Text = _obj_0.List, _obj_0.Dict, _obj_0.Text
end
local SyntaxTree = require("syntax_tree")
local Files = require("files")
local make_parser = require("parser")
local pretty_error = require("pretty_errors")
local make_tree
make_tree = function(tree, userdata)
tree.source = Source(userdata.filename, tree.start, tree.stop)
tree.start, tree.stop = nil, nil
tree = SyntaxTree(tree)
return tree
end
local Parsers = { }
local max_parser_version = 0
for version = 1, 999 do
local peg_file
if package.nomsupath then
for path in package.nomsupath:gmatch("[^;]+") do
peg_file = io.open(path .. "/nomsu." .. tostring(version) .. ".peg")
if peg_file then
break
end
end
else
peg_file = io.open("nomsu." .. tostring(version) .. ".peg")
end
if not (peg_file) then
break
end
max_parser_version = version
local peg_contents = peg_file:read('*a')
peg_file:close()
Parsers[version] = make_parser(peg_contents, make_tree)
end
local tree_to_nomsu, tree_to_inline_nomsu
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 nomsu_environment = Importer({
NOMSU_COMPILER_VERSION = 12,
NOMSU_SYNTAX_VERSION = max_parser_version,
next = next,
unpack = unpack or table.unpack,
setmetatable = setmetatable,
coroutine = coroutine,
rawequal = rawequal,
getmetatable = getmetatable,
pcall = pcall,
error = error,
package = package,
os = os,
require = require,
tonumber = tonumber,
tostring = tostring,
string = string,
xpcall = xpcall,
module = module,
print = print,
loadfile = loadfile,
rawset = rawset,
_VERSION = _VERSION,
collectgarbage = collectgarbage,
rawget = rawget,
rawlen = rawlen,
table = table,
assert = assert,
dofile = dofile,
loadstring = loadstring,
lua_type_of = type,
select = select,
math = math,
io = io,
load = load,
pairs = pairs,
ipairs = ipairs,
jit = jit,
_VERSION = _VERSION,
bit = (jit or _VERSION == "Lua 5.2") and require('bitops') or nil,
List = List,
Dict = Dict,
lpeg = lpeg,
re = re,
Files = Files,
SyntaxTree = SyntaxTree,
TESTS = Dict({ }),
globals = Dict({ }),
LuaCode = LuaCode,
NomsuCode = NomsuCode,
Source = Source,
SOURCE_MAP = Importer({ }),
_1_as_nomsu = tree_to_nomsu,
_1_as_inline_nomsu = tree_to_inline_nomsu,
compile = require('nomsu_compiler'),
_1_forked = _1_forked,
import_to_1_from = import_to_1_from,
_1_parsed = function(nomsu_code)
if type(nomsu_code) == 'string' then
local filename = Files.spoof(nomsu_code)
nomsu_code = NomsuCode(Source(filename, 1, #nomsu_code), nomsu_code)
end
local source = nomsu_code.source
nomsu_code = tostring(nomsu_code)
local version = nomsu_code:match("^#![^\n]*nomsu[ ]+-V[ ]*([0-9.]+)")
local syntax_version = version and tonumber(version:match("^[0-9]+")) or max_parser_version
local parse = Parsers[syntax_version] or Parsers[max_parser_version]
local tree = parse(nomsu_code, source.filename)
local find_errors
find_errors = function(t)
if t.type == "Error" then
return coroutine.yield(t)
else
for k, v in pairs(t) do
local _continue_0 = false
repeat
if not (SyntaxTree:is_instance(v)) then
_continue_0 = true
break
end
find_errors(v)
_continue_0 = true
until true
if not _continue_0 then
break
end
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
local num_errs = #errs
if num_errs > 0 then
local err_strings
do
local _accum_0 = { }
local _len_0 = 1
for i, e in ipairs(errs) do
if i <= 3 then
_accum_0[_len_0] = pretty_error({
title = "Parse error",
error = e.error,
hint = e.hint,
source = e:get_source_code(),
start = e.source.start,
stop = e.source.stop,
filename = e.source.filename
})
_len_0 = _len_0 + 1
end
end
err_strings = _accum_0
end
if num_errs > #err_strings then
table.insert(err_strings, "\027[31;1m +" .. tostring(num_errs - #err_strings) .. " additional errors...\027[0m\n")
end
error(table.concat(err_strings, '\n\n'), 0)
end
return tree
end,
run_1_in = function(to_run, environment)
if type(to_run) == 'string' then
local filename = Files.spoof(to_run)
to_run = NomsuCode(Source(filename, 1, #to_run), to_run)
local ret = environment.run_1_in(to_run, environment)
return ret
elseif NomsuCode:is_instance(to_run) then
local tree = environment._1_parsed(to_run)
if tree == nil then
return nil
end
local ret = environment.run_1_in(tree, environment)
return ret
elseif SyntaxTree:is_instance(to_run) then
local filename = to_run.source.filename:gsub("\n.*", "...")
if to_run.type ~= "FileChunks" then
to_run = {
to_run
}
end
local ret = nil
for _index_0 = 1, #to_run do
local chunk = to_run[_index_0]
local lua = environment.compile(chunk)
lua:declare_locals()
lua:prepend("-- File: " .. tostring(filename) .. "\n")
ret = environment.run_1_in(lua, environment)
end
return ret
elseif LuaCode:is_instance(to_run) then
local source = to_run.source
local lua_string = to_run:text()
local run_lua_fn, err = load(lua_string, tostring(source), "t", environment)
if not run_lua_fn then
local lines
do
local _accum_0 = { }
local _len_0 = 1
for i, line in ipairs(Files.get_lines(lua_string)) do
_accum_0[_len_0] = ("%3d|%s"):format(i, line)
_len_0 = _len_0 + 1
end
lines = _accum_0
end
local line_numbered_lua = table.concat(lines, "\n")
error("Failed to compile generated code:\n\027[1;34m" .. tostring(line_numbered_lua) .. "\027[0m\n\n" .. tostring(err), 0)
end
local source_key = tostring(source)
if not (environment.SOURCE_MAP[source_key]) then
local map = { }
local file = Files.read(source.filename)
if not file then
error("Failed to find file: " .. tostring(source.filename))
end
local nomsu_str = file:sub(source.start, source.stop)
local lua_line = 1
local nomsu_line = Files.get_line_number(file, source.start)
local map_sources
map_sources = function(s)
if type(s) == 'string' then
for nl in s:gmatch("\n") do
map[lua_line] = map[lua_line] or nomsu_line
lua_line = lua_line + 1
end
else
if s.source and s.source.filename == source.filename then
nomsu_line = Files.get_line_number(file, s.source.start)
end
local _list_0 = s.bits
for _index_0 = 1, #_list_0 do
local b = _list_0[_index_0]
map_sources(b)
end
end
end
map_sources(to_run)
map[lua_line] = map[lua_line] or nomsu_line
map[0] = 0
environment.SOURCE_MAP[source_key] = map
end
return run_lua_fn()
else
return error("Attempt to run unknown thing: " .. tostring(to_run))
end
end,
FILE_CACHE = { },
run_file_1_in = function(path, environment, optimization)
if optimization == nil then
optimization = 1
end
if environment.FILE_CACHE[path] then
import_to_1_from(environment, environment.FILE_CACHE[path])
return
end
local mod = _1_forked(environment)
assert(mod._1_parsed)
mod._ENV = mod
for _, filename in Files.walk(path) do
local _continue_0 = false
repeat
if not (filename == "stdin" or filename:match("%.nom$")) then
_continue_0 = true
break
end
local lua_filename = filename:gsub("%.nom$", ".lua")
local code
if optimization ~= 0 and Files.read(lua_filename) then
local file = Files.read(lua_filename)
code = LuaCode(Source(filename, 1, #file), file)
else
local file = Files.read(filename)
code = NomsuCode(Source(filename, 1, #file), file)
end
environment.run_1_in(code, mod)
_continue_0 = true
until true
if not _continue_0 then
break
end
end
import_to_1_from(environment, mod)
environment.FILE_CACHE[path] = mod
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_code(),
start = tree.source.start,
stop = tree.source.stop,
filename = tree.source.filename
})
return error(err_str, 0)
end
})
nomsu_environment._ENV = nomsu_environment
SOURCE_MAP = nomsu_environment.SOURCE_MAP
return nomsu_environment

184
nomsu_environment.moon Normal file
View File

@ -0,0 +1,184 @@
-- This file defines the environment in which Nomsu code runs, including some
-- basic bootstrapping functionality.
{:NomsuCode, :LuaCode, :Source} = require "code_obj"
{:Importer, :import_to_1_from, :_1_forked} = require 'importer'
{:List, :Dict, :Text} = require 'containers'
SyntaxTree = require "syntax_tree"
Files = require "files"
make_parser = require("parser")
pretty_error = require("pretty_errors")
make_tree = (tree, userdata)->
tree.source = Source(userdata.filename, tree.start, tree.stop)
tree.start, tree.stop = nil, nil
tree = SyntaxTree(tree)
return tree
Parsers = {}
max_parser_version = 0
for version=1,999
local peg_file
if package.nomsupath
for path in package.nomsupath\gmatch("[^;]+")
peg_file = io.open(path.."/nomsu.#{version}.peg")
break if peg_file
else
peg_file = io.open("nomsu.#{version}.peg")
break unless peg_file
max_parser_version = version
peg_contents = peg_file\read('*a')
peg_file\close!
Parsers[version] = make_parser(peg_contents, make_tree)
{:tree_to_nomsu, :tree_to_inline_nomsu} = require "nomsu_decompiler"
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,
:error, :package, :os, :require, :tonumber, :tostring, :string, :xpcall, :module,
:print, :loadfile, :rawset, :_VERSION, :collectgarbage, :rawget, :rawlen,
:table, :assert, :dofile, :loadstring, lua_type_of:type, :select, :math, :io, :load,
:pairs, :ipairs, :jit, :_VERSION
bit: (jit or _VERSION == "Lua 5.2") and require('bitops') or nil
-- Nomsu types:
List:List, Dict:Dict,
-- Utilities and misc.
lpeg:lpeg, re:re, Files:Files,
:SyntaxTree, TESTS: Dict({}), globals: Dict({}),
:LuaCode, :NomsuCode, :Source
SOURCE_MAP: Importer({})
-- Nomsu functions:
_1_as_nomsu:tree_to_nomsu, _1_as_inline_nomsu:tree_to_inline_nomsu
compile: require('nomsu_compiler')
:_1_forked, :import_to_1_from
_1_parsed: (nomsu_code)->
if type(nomsu_code) == 'string'
filename = Files.spoof(nomsu_code)
nomsu_code = NomsuCode(Source(filename, 1, #nomsu_code), nomsu_code)
source = nomsu_code.source
nomsu_code = tostring(nomsu_code)
version = nomsu_code\match("^#![^\n]*nomsu[ ]+-V[ ]*([0-9.]+)")
syntax_version = version and tonumber(version\match("^[0-9]+")) or max_parser_version
parse = Parsers[syntax_version] or Parsers[max_parser_version]
tree = parse(nomsu_code, source.filename)
find_errors = (t)->
if t.type == "Error"
coroutine.yield 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))]
num_errs = #errs
if num_errs > 0
err_strings = [pretty_error{
title:"Parse error"
error:e.error, hint:e.hint, source:e\get_source_code!
start:e.source.start, stop:e.source.stop, filename:e.source.filename
} for i, e in ipairs(errs) when i <= 3]
if num_errs > #err_strings
table.insert(err_strings, "\027[31;1m +#{num_errs-#err_strings} additional errors...\027[0m\n")
error(table.concat(err_strings, '\n\n'), 0)
return tree
run_1_in: (to_run, environment)->
if type(to_run) == 'string'
filename = Files.spoof(to_run)
to_run = NomsuCode(Source(filename, 1, #to_run), to_run)
ret = environment.run_1_in(to_run, environment)
return ret
elseif NomsuCode\is_instance(to_run)
tree = environment._1_parsed(to_run)
return nil if tree == nil
ret = environment.run_1_in(tree, environment)
return ret
elseif SyntaxTree\is_instance(to_run)
filename = to_run.source.filename\gsub("\n.*", "...")
if to_run.type != "FileChunks"
to_run = {to_run}
-- Each chunk's compilation is affected by the code in the previous chunks
-- (typically), so each chunk needs to compile and run before the next one
-- compiles.
ret = nil
for chunk in *to_run
lua = environment.compile(chunk)
lua\declare_locals!
lua\prepend "-- File: #{filename}\n"
ret = environment.run_1_in(lua, environment)
return ret
elseif LuaCode\is_instance(to_run)
source = to_run.source
lua_string = to_run\text!
-- If you replace tostring(source) with "nil", source mapping won't happen
run_lua_fn, err = load(lua_string, tostring(source), "t", environment)
if not run_lua_fn
lines =[("%3d|%s")\format(i,line) for i, line in ipairs Files.get_lines(lua_string)]
line_numbered_lua = table.concat(lines, "\n")
error("Failed to compile generated code:\n\027[1;34m#{line_numbered_lua}\027[0m\n\n#{err}", 0)
source_key = tostring(source)
unless environment.SOURCE_MAP[source_key]
map = {}
file = Files.read(source.filename)
if not file
error "Failed to find file: #{source.filename}"
nomsu_str = file\sub(source.start, source.stop)
lua_line = 1
nomsu_line = Files.get_line_number(file, source.start)
map_sources = (s)->
if type(s) == 'string'
for nl in s\gmatch("\n")
map[lua_line] or= nomsu_line
lua_line += 1
else
if s.source and s.source.filename == source.filename
nomsu_line = Files.get_line_number(file, s.source.start)
for b in *s.bits do map_sources(b)
map_sources(to_run)
map[lua_line] or= nomsu_line
map[0] = 0
-- Mapping from lua line number to nomsu line numbers
environment.SOURCE_MAP[source_key] = map
return run_lua_fn!
else
error("Attempt to run unknown thing: "..tostring(to_run))
FILE_CACHE: {}
run_file_1_in: (path, environment, optimization=1)->
if environment.FILE_CACHE[path]
import_to_1_from(environment, environment.FILE_CACHE[path])
return
mod = _1_forked(environment)
assert mod._1_parsed
mod._ENV = mod
for _,filename in Files.walk(path)
continue unless filename == "stdin" or filename\match("%.nom$")
lua_filename = filename\gsub("%.nom$", ".lua")
-- TODO: don't automatically use precompiled version?
code = if optimization != 0 and Files.read(lua_filename)
file = Files.read(lua_filename)
LuaCode(Source(filename, 1, #file), file)
else
file = Files.read(filename)
NomsuCode(Source(filename, 1, #file), file)
environment.run_1_in(code, mod)
import_to_1_from(environment, mod)
environment.FILE_CACHE[path] = mod
compile_error_at: (tree, err_msg, hint=nil)->
err_str = pretty_error{
title: "Compile error"
error:err_msg, hint:hint, source:tree\get_source_code!
start:tree.source.start, stop:tree.source.stop, filename:tree.source.filename
}
error(err_str, 0)
}
nomsu_environment._ENV = nomsu_environment
-- Hacky use of globals:
export SOURCE_MAP
SOURCE_MAP = nomsu_environment.SOURCE_MAP
return nomsu_environment

View File

@ -22,6 +22,9 @@ as_lua = function(self)
end end
end end
end end
if self.as_lua then
return self:as_lua()
end
return error("Not supported: " .. tostring(self)) return error("Not supported: " .. tostring(self))
end end
local SyntaxTree local SyntaxTree

View File

@ -10,6 +10,7 @@ as_lua = =>
if mt = getmetatable(@) if mt = getmetatable(@)
if _as_lua = mt.as_lua if _as_lua = mt.as_lua
return _as_lua(@) return _as_lua(@)
return @as_lua! if @as_lua
error("Not supported: #{@}") error("Not supported: #{@}")
--types = {"Number", "Var", "Block", "EscapedNomsu", "Text", "List", "Dict", "DictEntry", --types = {"Number", "Var", "Block", "EscapedNomsu", "Text", "List", "Dict", "DictEntry",

View File

@ -7,6 +7,8 @@
use "lib/os.nom" use "lib/os.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%args = (command line args) %args = (command line args)
%inplace = (no) %inplace = (no)
if (%args.1 is "-i"): if (%args.1 is "-i"):

View File

@ -7,6 +7,8 @@
use "lib/os.nom" use "lib/os.nom"
use "lib/consolecolor.nom" use "lib/consolecolor.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%stub = (command line args).1 %stub = (command line args).1
say "Looking for stub: \%stub..." say "Looking for stub: \%stub..."
%files = ((command line args).% for % in 2 to (size of (command line args))) %files = ((command line args).% for % in 2 to (size of (command line args)))

View File

@ -5,6 +5,8 @@
use "lib/os.nom" use "lib/os.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
externally (print tree %t at indent %indent) means: externally (print tree %t at indent %indent) means:
if %t.type is: if %t.type is:
"Action": "Action":

61
tools/repl.nom Normal file
View File

@ -0,0 +1,61 @@
#!/usr/bin/env nomsu -V4
use "lib/consolecolor.nom"
use "lib/os.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[quit, exit] all mean: lua> "os.exit(0)"
(help) means:
say "\
..This is the Nomsu v\(Nomsu version) interactive console.
You can type in Nomsu code here and hit 'enter' twice to run it.
To exit, type 'exit' or 'quit' and hit enter twice."
say "\
..
\(bright)\(underscore)Welcome to the Nomsu v\(Nomsu version) interactive console!\(reset color)
press 'enter' twice to run a command
"
%repl_line = 0
repeat:
%repl_line += 1
%io.write (bright (yellow ">> "))
%buff = []
repeat:
%io.write (bright)
%line = (%io.read "*L")
%io.write (reset color)
if ((%line == "\n") or (not %line)):
if ((size of %buff) > 0):
%io.write "\027[1A\027[2K"
go to (run buffer)
%buff::add (%line::with "\t" -> " ")
%io.write (dim (yellow ".. "))
=== (run buffer) ===
if ((size of %buff) == 0):
stop
%buff = (%buff::joined)
# TODO: support local variables
spoof file %buff
try:
%ret = (run %buff)
..and if it barfs %err:
say %err
..or if it succeeds:
if (type of %ret) is:
"nil":
do nothing
"boolean":
say "= \("yes" if %ret else "no")"
"table":
if %ret.as_nomsu:
say "= \(%ret::as nomsu)"
..else:
say "= \%ret"
else:
say "= \%ret"

View File

@ -7,6 +7,8 @@
use "lib/os.nom" use "lib/os.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%args = (command line args) %args = (command line args)
%inplace = (no) %inplace = (no)
if (%args.1 is "-i"): if (%args.1 is "-i"):

View File

@ -6,6 +6,8 @@
use "lib/os.nom" use "lib/os.nom"
use "lib/consolecolor.nom" use "lib/consolecolor.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%args = (command line args) %args = (command line args)
if (%args.1 == "-v"): if (%args.1 == "-v"):
%args::remove index 1 %args::remove index 1

View File

@ -8,6 +8,8 @@
use "compatibility" use "compatibility"
use "lib/os.nom" use "lib/os.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%args = (command line args) %args = (command line args)
%inplace = (no) %inplace = (no)
%start_version = (nil) %start_version = (nil)