aboutsummaryrefslogtreecommitdiff
path: root/nomsu.moon
diff options
context:
space:
mode:
authorBruce Hill <bitbucket@bruce-hill.com>2018-06-18 15:44:29 -0700
committerBruce Hill <bitbucket@bruce-hill.com>2018-06-18 15:44:35 -0700
commit16d127abb507751808eca65108710d3de1fd3cab (patch)
tree53a78ad2e88a95048eea76426e62c05c2c5102a5 /nomsu.moon
parentfc71b0331b6122c774585c3ab93e6e55978ecaf2 (diff)
Initial working version.
Diffstat (limited to 'nomsu.moon')
-rwxr-xr-xnomsu.moon619
1 files changed, 262 insertions, 357 deletions
diff --git a/nomsu.moon b/nomsu.moon
index 459ad7c..b99d6f4 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -10,29 +10,20 @@
-- nomsu:run(your_nomsu_code)
-- Or from the command line:
-- lua nomsu.lua your_file.nom
-export lpeg, re
-_pairs, _ipairs = pairs, ipairs
-if jit
- package.cpath = "./luajit_lpeg/?.so;"..package.cpath
- package.path = "./luajit_lpeg/?.lua;"..package.path
-
- export bit32
- bit32 = require('bit')
-
lpeg = require 'lpeg'
re = require 're'
lpeg.setmaxstack 10000
{:P,:R,:V,:S,:Cg,:C,:Cp,:B,:Cmt,:Carg} = lpeg
utils = require 'utils'
-new_uuid = require 'uuid'
{:repr, :stringify, :min, :max, :equivalent, :set, :is_list, :sum} = utils
colors = setmetatable({}, {__index:->""})
export colored
colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..tostring(msg or '')..colors.reset)})
{:insert, :remove, :concat} = table
+unpack or= table.unpack
{:match, :sub, :rep, :gsub, :format, :byte, :match, :find} = string
debug_getinfo = debug.getinfo
-{:Nomsu, :Lua, :Source} = require "code_obj"
+{:NomsuCode, :LuaCode, :Source} = require "code_obj"
STDIN, STDOUT, STDERR = "/dev/fd/0", "/dev/fd/1", "/dev/fd/2"
string.as_lua_id = (str)->
@@ -138,103 +129,53 @@ do
AST = require "nomsu_tree"
-NOMSU_DEFS = with {}
- -- Newline supports either windows-style CR+LF or unix-style LF
- .nl = P("\r")^-1 * P("\n")
- .ws = S(" \t")
- .tonumber = tonumber
- string_escapes = n:"\n", t:"\t", b:"\b", a:"\a", v:"\v", f:"\f", r:"\r"
- digit, hex = R('09'), R('09','af','AF')
- .escaped_char = (P("\\")*S("xX")*C(hex*hex)) / => string.char(tonumber(@, 16))
- .escaped_char += (P("\\")*C(digit*(digit^-2))) / => string.char(tonumber @)
- .escaped_char += (P("\\")*C(S("ntbavfr"))) / string_escapes
- .operator_char = S("'~`!@$^&*-+=|<>?/")
- .utf8_char = (
- 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"))
- .ident_char = R("az","AZ","09") + P("_") + .utf8_char
-
- -- If the line begins with #indent+4 spaces, the pattern matches *those* spaces
- -- and adds them to the current indent (not any more).
- .indent = Cmt Carg(1), (start, userdata)=>
- indented = userdata.indent..' '
- if sub(@, start, start+#indented-1) == indented
- userdata.indent = indented
- return start + #indented
- -- If the number of leading space characters is <= the number of spaces in the current
- -- indent minus 4, this pattern matches and decrements the current indent exactly once.
- .dedent = Cmt Carg(1), (start, userdata)=>
- dedented = sub(userdata.indent, 1, -5)
- if #match(@, "^[ ]*", start) <= #dedented
- userdata.indent = dedented
- return start
- -- If the number of leading space characters is >= the number of spaces in the current
- -- indent, this pattern matches and does not modify the indent.
- .nodent = Cmt Carg(1), (start, userdata)=>
- if sub(@, start, start+#userdata.indent-1) == userdata.indent
- return start + #userdata.indent
-
- .userdata = Carg(1)
-
- .error = (src,end_pos,start_pos,err_msg,userdata)->
- seen_errors = userdata.errors
- if seen_errors[start_pos]
- return true
- if utils.size(seen_errors) >= 10
- seen_errors[start_pos+1] = colored.bright colored.yellow colored.onred "Too many errors, canceling parsing..."
- return #src+1
- err_pos = start_pos
- line_no = pos_to_line(src, err_pos)
- src = FILE_CACHE[userdata.source.filename]
- line_starts = LINE_STARTS[src]
- prev_line = line_no == 1 and "" or src\sub(line_starts[line_no-1] or 1, line_starts[line_no]-2)
- err_line = src\sub(line_starts[line_no], (line_starts[line_no+1] or 0)-2)
- next_line = src\sub(line_starts[line_no+1] or -1, (line_starts[line_no+2] or 0)-2)
- i = err_pos-line_starts[line_no]
- pointer = ("-")\rep(i) .. "^"
- err_msg = colored.bright colored.yellow colored.onred (err_msg or "Parse error").." at #{userdata.source.filename}:#{line_no}:"
- if #prev_line > 0 then err_msg ..= "\n"..colored.dim(prev_line)
- err_line = colored.white(err_line\sub(1, i))..colored.bright(colored.red(err_line\sub(i+1,i+1)))..colored.dim(err_line\sub(i+2,-1))
- err_msg ..= "\n#{err_line}\n#{colored.red pointer}"
- if #next_line > 0 then err_msg ..= "\n"..colored.dim(next_line)
- --error(err_msg)
- seen_errors[start_pos] = err_msg
- return true
-
-setmetatable(NOMSU_DEFS, {__index:(key)=>
- make_node = (start, value, stop, userdata)->
- local source
- with userdata.source
- source = Source(.filename, .start + start-1, .start + stop-1)
- value.source = source
- setmetatable(value, AST[key])
- if value.__init then value\__init!
- for i=1,#value do assert(value[i])
- return value
-
- self[key] = make_node
- return make_node
-})
-
-NOMSU_PATTERN = do
- -- Just for cleanliness, I put the language spec in its own file using a slightly modified
- -- version of the lpeg.re syntax.
- peg_tidier = re.compile [[
- file <- {~ %nl* (def/comment) (%nl+ (def/comment))* %nl* ~}
- def <- anon_def / captured_def
- anon_def <- ({ident} (" "*) ":"
- {((%nl " "+ [^%nl]*)+) / ([^%nl]*)}) -> "%1 <- %2"
- captured_def <- ({ident} (" "*) "(" {ident} ")" (" "*) ":"
- {((%nl " "+ [^%nl]*)+) / ([^%nl]*)}) -> "%1 <- (({} %3 {} %%userdata) -> %2)"
- ident <- [a-zA-Z_][a-zA-Z0-9_]*
- comment <- "--" [^%nl]*
- ]]
- nomsu_peg = peg_tidier\match(FILE_CACHE["nomsu.peg"])
- re.compile(nomsu_peg, NOMSU_DEFS)
-
-class NomsuCompiler
- compile_error = (tok, err_format_string, ...)->
+_list_mt =
+ __eq:utils.equivalent
+ -- Could consider adding a __newindex to enforce list-ness, but would hurt performance
+ __tostring: =>
+ "["..concat([repr(b) for b in *@], ", ").."]"
+list = (t)-> setmetatable(t, _list_mt)
+
+_dict_mt =
+ __eq:utils.equivalent
+ __tostring: =>
+ "{"..concat(["#{repr(k)}: #{repr(v)}" for k,v in pairs @], ", ").."}"
+dict = (t)-> setmetatable(t, _dict_mt)
+
+MAX_LINE = 80 -- For beautification purposes, try not to make lines much longer than this value
+NomsuCompiler = setmetatable({}, {__index: (k)=> if _self = rawget(@, "self") then _self[k] else nil})
+with NomsuCompiler
+ ._ENV = NomsuCompiler
+ .nomsu = NomsuCompiler
+ parse = require("parser")
+ .parse = (...)=> parse(...)
+
+ -- Mapping from source string (e.g. "@core/metaprogramming.nom[1:100]") to a mapping
+ -- from lua line number to nomsu line number
+ .source_map = {}
+ -- Discretionary/convenience stuff
+ to_add = {
+ repr:repr, stringify:stringify, utils:utils, lpeg:lpeg, re:re,
+ -- Lua stuff:
+ :next, :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, :type, :select, :debug, :math, :io, :load,
+ :pairs, :ipairs,
+ -- Nomsu types:
+ :list, :dict,
+ }
+ for k,v in pairs(to_add) do NomsuCompiler[k] = v
+ for k,v in pairs(AST) do NomsuCompiler[k] = v
+ .LuaCode = LuaCode
+ .NomsuCode = NomsuCode
+ .Source = Source
+ .ARG_ORDERS = setmetatable({}, {__mode:"k"})
+ .ALIASES = setmetatable({}, {__mode:"k"})
+ .LOADED = {}
+ .AST = AST
+
+ .compile_error = (tok, err_format_string, ...)=>
file = FILE_CACHE[tok.source.filename]
line_no = pos_to_line(file, tok.source.start)
line_start = LINE_STARTS[file][line_no]
@@ -245,105 +186,132 @@ class NomsuCompiler
src = ' '..src\gsub('\n', '\n ')
err_msg = err_format_string\format(src, ...)
error("#{tok.source.filename}:#{line_no}: "..err_msg, 0)
- new: =>
- -- Weak-key mapping from objects to randomly generated unique IDs
- NaN_surrogate = {}
- nil_surrogate = {}
- @ids = setmetatable({}, {
- __mode: "k"
- __index: (key)=>
- if key == nil then return @[nil_surrogate]
- elseif key != key then return @[NaN_surrogate]
- id = new_uuid!
- @[key] = id
- return id
- })
- -- Mapping from source string (e.g. "@core/metaprogramming.nom[1:100]") to a mapping
- -- from lua line number to nomsu line number
- @source_map = {}
-
- _list_mt =
- __eq:utils.equivalent
- -- Could consider adding a __newindex to enforce list-ness, but would hurt performance
- __tostring: =>
- "["..concat([repr(b) for b in *@], ", ").."]"
- list = (t)-> setmetatable(t, _list_mt)
- _dict_mt =
- __eq:utils.equivalent
- __tostring: =>
- "{"..concat(["#{repr(k)}: #{repr(v)}" for k,v in pairs @], ", ").."}"
- dict = (t)-> setmetatable(t, _dict_mt)
- @environment = {
- -- Discretionary/convenience stuff
- nomsu:self, repr:repr, stringify:stringify, utils:utils, lpeg:lpeg, re:re,
- :compile_error
- -- Lua stuff:
- :next, :unpack, :setmetatable, :coroutine, :rawequal, :getmetatable, :pcall,
- :error, :package, :os, :require, :tonumber, :tostring, :string, :xpcall, :module,
- :print, :loadfile, :rawset, :_VERSION, :collectgarbage, :rawget, :bit32, :rawlen,
- :table, :assert, :dofile, :loadstring, :type, :select, :debug, :math, :io, :load,
- :pairs, :ipairs,
- -- Nomsu types:
- :list, :dict,
- }
- for k,v in pairs(AST) do @environment[k] = v
- @environment.Lua = Lua
- @environment.Nomsu = Nomsu
- @environment.Source = Source
- @environment.ARG_ORDERS = setmetatable({}, {__mode:"k"})
- @environment.ALIASES = setmetatable({}, {__mode:"k"})
- @environment.compile_time = (fn)->
- @environment.COMPILE_TIME[fn] = true
- return fn
- @environment.COMPILE_TIME = {}
- @environment.LOADED = {}
- @environment.AST = AST
- @environment._ENV = @environment
- setmetatable @environment,
- __index: (k)=>
- if _self = rawget(@, "self")
- return _self[k]
- @initialize_core!
+
+ -- This is a bit of a hack, but this code handles arbitrarily complex
+ -- math expressions like 2*x + 3^2 without having to define a single
+ -- action for every possibility.
+ math_expression = re.compile [[ ([+-] " ")* "%" (" " [*/^+-] (" " [+-])* " %")+ !. ]]
+ add_lua_bits = (lua, code)=>
+ for bit in *code
+ if type(bit) == "string"
+ lua\append bit
+ else
+ bit_lua = @compile(bit)
+ unless bit_lua.is_value
+ @compile_error bit,
+ "Cannot use:\n%s\nas a string interpolation value, since it's not an expression."
+ lua\append bit_lua
+ return lua
+
+ add_lua_string_bits = (lua, code)=>
+ line_len = 0
+ if code.type != "Text"
+ lua\append ", ", @compile(code)
+ return
+ for bit in *code
+ bit_lua = if type(bit) == "string"
+ repr(bit)
+ else
+ bit_lua = @compile(bit)
+ unless bit_lua.is_value
+ @compile_error bit,
+ "Cannot use:\n%s\nas a string interpolation value, since it's not an expression."
+ bit_lua
+ line_len += #tostring(bit_lua)
+ if line_len > MAX_LINE
+ lua\append ",\n "
+ line_len = 4
+ else
+ lua\append ", "
+ lua\append bit_lua
+
+ .COMPILE_ACTIONS = setmetatable {
+ compile_math_expr: (tree, ...)=>
+ lua = LuaCode.Value(tree.source)
+ for i,tok in ipairs tree
+ if type(tok) == 'string'
+ lua\append tok
+ else
+ tok_lua = @compile(tok)
+ unless tok_lua.is_value
+ @compile_error tok,
+ "Non-expression value inside math expression:\n%s"
+ if tok.type == "Action"
+ tok_lua\parenthesize!
+ lua\append tok_lua
+ if i < #tree
+ lua\append " "
+ return lua
+
+ ["Lua %"]: (tree, _code)=>
+ lua = LuaCode.Value(_code.source, "LuaCode(", repr(tostring _code.source))
+ add_lua_string_bits(@, lua, _code)
+ lua\append ")"
+ return lua
- parse: (nomsu_code)=>
- assert(type(nomsu_code) != 'string')
- userdata = {
- source_code:nomsu_code, indent: "", errors: {},
- source: nomsu_code.source,
- }
- tree = NOMSU_PATTERN\match(tostring(nomsu_code), nil, userdata)
- unless tree
- error "In file #{colored.blue filename} failed to parse:\n#{colored.onyellow colored.black nomsu_code}"
-
- if next(userdata.errors)
- keys = utils.keys(userdata.errors)
- table.sort(keys)
- errors = [userdata.errors[k] for k in *keys]
- io.stderr\write(concat(errors, "\n\n").."\n")
- os.exit!
-
- return tree
-
- run: (nomsu_code)=>
- tree = assert(@parse(nomsu_code))
- if type(tree) == 'number' -- Happens if pattern matches, but there are no captures, e.g. an empty string
+ ["Lua value %"]: (tree, _code)=>
+ lua = LuaCode.Value(_code.source, "LuaCode.Value(", repr(tostring _code.source))
+ add_lua_string_bits(@, lua, _code)
+ lua\append ")"
+ return lua
+
+ ["lua > %"]: (tree, _code)=>
+ if _code.type != "Text"
+ return LuaCode tree.source, "nomsu:run_lua(", @compile(_code), ");"
+ return add_lua_bits(@, LuaCode(tree.source), _code)
+
+ ["= lua %"]: (tree, _code)=>
+ if _code.type != "Text"
+ return LuaCode.Value tree.source, "nomsu:run_lua(", @compile(_code), ":as_statements('return '))"
+ return add_lua_bits(@, LuaCode.Value(tree.source), _code)
+
+ ["use %"]: (tree, _path)=>
+ unless _path.type == 'Text' and #_path == 1 and type(_path[1]) == 'string'
+ return LuaCode(tree.source, "nomsu:run_file(#{@compile(_path)});")
+ path = _path[1]
+ @run_file(path)
+ return LuaCode(tree.source, "nomsu:run_file(#{repr path});")
+ }, {
+ __index: (stub)=>
+ if math_expression\match(stub)
+ return @compile_math_expr
+ }
+
+ .fork = =>
+ setmetatable({COMPILE_ACTIONS:setmetatable({}, {__index:@COMPILE_ACTIONS})}, {__index:@})
+
+ .run = (to_run, source=nil)=>
+ tree = if AST.is_syntax_tree(to_run) then tree else @parse(to_run, source or to_run.source)
+ if tree == nil -- Happens if pattern matches, but there are no captures, e.g. an empty string
return nil
- lua = @tree_to_lua(tree)\as_statements!
- lua\declare_locals!
- lua\prepend "-- File: #{nomsu_code.source or ""}\n"
- if @compile_fn
- self.compile_fn(lua, nomsu_code.source.filename)
- return @run_lua(lua)
+ if tree.type == "FileChunks"
+ ret = nil
+ all_lua = {}
+ for chunk in *tree
+ lua = @compile(chunk)\as_statements!
+ lua\declare_locals!
+ lua\prepend "-- File: #{chunk.source or ""}\n"
+ insert all_lua, tostring(lua)
+ ret = @run_lua(lua)
+ if @on_compile
+ self.on_compile(concat(all_lua, "\n"), (source or to_run.source).filename)
+ return ret
+ else
+ lua = @compile(tree, compile_actions)\as_statements!
+ lua\declare_locals!
+ lua\prepend "-- File: #{source or to_run.source or ""}\n"
+ if @on_compile
+ self.on_compile(lua, (source or to_run.source).filename)
+ return @run_lua(lua)
_running_files = {} -- For detecting circular imports
- run_file: (filename)=>
- loaded = @environment.LOADED
- if loaded[filename]
- return loaded[filename]
+ .run_file = (filename)=>
+ if @LOADED[filename]
+ return @LOADED[filename]
ret = nil
for filename in all_files(filename)
- if loaded[filename]
- ret = loaded[filename]
+ if @LOADED[filename]
+ ret = @LOADED[filename]
continue
for i,running in ipairs _running_files
@@ -355,31 +323,31 @@ class NomsuCompiler
insert _running_files, filename
if match(filename, "%.lua$")
file = assert(FILE_CACHE[filename], "Could not find file: #{filename}")
- ret = @run_lua(Lua(Source(filename, 1, #file), file))
+ ret = @run_lua file, Source(filename, 1, #file)
elseif match(filename, "%.nom$") or match(filename, "^/dev/fd/[012]$")
if not @skip_precompiled -- Look for precompiled version
lua_filename = gsub(filename, "%.nom$", ".lua")
file = FILE_CACHE[lua_filename]
if file
- ret = @run_lua(Lua(Source(filename, 1, #file), file))
+ ret = @run_lua file, Source(filename, 1, #file)
remove _running_files
continue
file = file or FILE_CACHE[filename]
if not file
error("File does not exist: #{filename}", 0)
- ret = @run(Nomsu(Source(filename,1,#file), file))
+ ret = @run file, Source(filename,1,#file)
else
error("Invalid filetype for #{filename}", 0)
- loaded[filename] = ret or true
+ @LOADED[filename] = ret or true
remove _running_files
- loaded[filename] = ret or true
+ @LOADED[filename] = ret or true
return ret
- run_lua: (lua)=>
+ .run_lua = (lua, source=nil)=>
assert(type(lua) != 'string', "Attempt to run lua string instead of Lua (object)")
lua_string = tostring(lua)
- run_lua_fn, err = load(lua_string, tostring(lua.source), "t", @environment)
+ run_lua_fn, err = load(lua_string, nil and tostring(source or lua.source), "t", self)
if not run_lua_fn
n = 1
fn = ->
@@ -387,14 +355,14 @@ class NomsuCompiler
("\n%-3d|")\format(n)
line_numbered_lua = "1 |"..lua_string\gsub("\n", fn)
error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack line_numbered_lua}\n\n#{err}", 0)
- source_key = tostring(lua.source)
+ source_key = tostring(source or lua.source)
unless @source_map[source_key]
map = {}
offset = 1
- source = lua.source
+ source or= lua.source
nomsu_str = tostring(FILE_CACHE[source.filename]\sub(source.start, source.stop))
lua_line = 1
- nomsu_line = pos_to_line(nomsu_str, lua.source.start)
+ nomsu_line = pos_to_line(nomsu_str, source.start)
fn = (s)->
if type(s) == 'string'
for nl in s\gmatch("\n")
@@ -413,26 +381,26 @@ class NomsuCompiler
return run_lua_fn!
- MAX_LINE = 80 -- For beautification purposes, try not to make lines much longer than this value
- math_expression = re.compile [[ ([+-] " ")* "%" (" " [*/^+-] (" " [+-])* " %")+ !. ]]
- tree_to_lua: (tree)=>
+ .compile = (tree)=>
+ assert(LuaCode)
switch tree.type
when "Action"
stub = tree.stub
- action = @environment['A'..string.as_lua_id(stub)]
- if action and @environment.COMPILE_TIME[action]
+ if compile_action = @COMPILE_ACTIONS[stub]
args = [arg for arg in *tree when type(arg) != "string"]
- -- Force all compile-time actions to take a tree location
- if arg_orders = @environment.ARG_ORDERS[stub]
+ if arg_orders = @ARG_ORDERS[stub]
args = [args[p] for p in *arg_orders]
-- Force Lua to avoid tail call optimization for debugging purposes
- -- TODO: use tail call
- ret = action(tree, unpack(args))
+ -- TODO: use tail call?
+ ret = compile_action(@, tree, unpack(args))
if not ret
- compile_error tree,
+ @compile_error tree,
"Compile-time action:\n%s\nfailed to produce any Lua"
return ret
- lua = Lua.Value(tree.source)
+
+ action = @['A'..string.as_lua_id(stub)]
+
+ lua = LuaCode.Value(tree.source)
if not action and math_expression\match(stub)
-- This is a bit of a hack, but this code handles arbitrarily complex
-- math expressions like 2*x + 3^2 without having to define a single
@@ -441,9 +409,9 @@ class NomsuCompiler
if type(tok) == 'string'
lua\append tok
else
- tok_lua = @tree_to_lua(tok)
+ tok_lua = @compile(tok)
unless tok_lua.is_value
- compile_error tok,
+ @compile_error tok,
"Non-expression value inside math expression:\n%s"
if tok.type == "Action"
tok_lua\parenthesize!
@@ -455,15 +423,15 @@ class NomsuCompiler
args = {}
for i, tok in ipairs tree
if type(tok) == "string" then continue
- arg_lua = @tree_to_lua(tok)
+ arg_lua = @compile(tok)
unless arg_lua.is_value
- compile_error tok,
+ @compile_error tok,
"Cannot use:\n%s\nas an argument to %s, since it's not an expression, it produces: %s",
stub, repr arg_lua
insert args, arg_lua
if action
- if arg_orders = @environment.ARG_ORDERS[stub]
+ if arg_orders = @ARG_ORDERS[stub]
args = [args[p] for p in *arg_orders]
lua\append "A",string.as_lua_id(stub),"("
@@ -479,19 +447,19 @@ class NomsuCompiler
return repr(t)
bits = [make_tree(bit) for bit in *t]
return t.type.."("..repr(tostring t.source)..", "..table.concat(bits, ", ")..")"
- Lua.Value tree.source, make_tree(tree[1])
+ LuaCode.Value tree.source, make_tree(tree[1])
when "Block"
- lua = Lua(tree.source)
+ lua = LuaCode(tree.source)
for i,line in ipairs tree
- line_lua = @tree_to_lua(line)
+ line_lua = @compile(line)
if i > 1
lua\append "\n"
lua\append line_lua\as_statements!
return lua
when "Text"
- lua = Lua.Value(tree.source)
+ lua = LuaCode.Value(tree.source)
string_buffer = ""
for i, bit in ipairs tree
if type(bit) == "string"
@@ -501,15 +469,15 @@ class NomsuCompiler
if #lua.bits > 0 then lua\append ".."
lua\append repr(string_buffer)
string_buffer = ""
- bit_lua = @tree_to_lua(bit)
+ bit_lua = @compile(bit)
unless bit_lua.is_value
src = ' '..gsub(tostring(@tree_to_nomsu(bit)), '\n','\n ')
line = "#{bit.source.filename}:#{pos_to_line(FILE_CACHE[bit.source.filename], bit.source.start)}"
- compile_error bit,
+ @compile_error bit,
"Cannot use:\n%s\nas a string interpolation value, since it's not an expression."
if #lua.bits > 0 then lua\append ".."
if bit.type != "Text"
- bit_lua = Lua.Value(bit.source, "stringify(",bit_lua,")")
+ bit_lua = LuaCode.Value(bit.source, "stringify(",bit_lua,")")
lua\append bit_lua
if string_buffer ~= "" or #lua.bits == 0
@@ -521,12 +489,12 @@ class NomsuCompiler
return lua
when "List"
- lua = Lua.Value tree.source, "list{"
+ lua = LuaCode.Value tree.source, "list{"
line_length = 0
for i, item in ipairs tree
- item_lua = @tree_to_lua(item)
+ item_lua = @compile(item)
unless item_lua.is_value
- compile_error item,
+ @compile_error item,
"Cannot use:\n%s\nas a list item, since it's not an expression."
lua\append item_lua
item_string = tostring(item_lua)
@@ -546,10 +514,10 @@ class NomsuCompiler
return lua
when "Dict"
- lua = Lua.Value tree.source, "dict{"
+ lua = LuaCode.Value tree.source, "dict{"
line_length = 0
for i, entry in ipairs tree
- entry_lua = @tree_to_lua(entry)
+ entry_lua = @compile(entry)
lua\append entry_lua
entry_lua_str = tostring(entry_lua)
-- TODO: maybe make this more accurate? It's only a heuristic, so eh...
@@ -570,29 +538,29 @@ class NomsuCompiler
when "DictEntry"
key, value = tree[1], tree[2]
- key_lua = @tree_to_lua(key)
+ key_lua = @compile(key)
unless key_lua.is_value
- compile_error tree[1],
+ @compile_error tree[1],
"Cannot use:\n%s\nas a dict key, since it's not an expression."
- value_lua = value and @tree_to_lua(value) or Lua.Value(key.source, "true")
+ value_lua = value and @compile(value) or LuaCode.Value(key.source, "true")
unless value_lua.is_value
- compile_error tree[2],
+ @compile_error tree[2],
"Cannot use:\n%s\nas a dict value, since it's not an expression."
key_str = match(tostring(key_lua), [=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
return if key_str
- Lua tree.source, key_str,"=",value_lua
+ LuaCode tree.source, key_str,"=",value_lua
elseif sub(tostring(key_lua),1,1) == "["
-- NOTE: this *must* use a space after the [ to avoid freaking out
-- Lua's parser if the inner expression is a long string. Lua
-- parses x[[[y]]] as x("[y]"), not as x["y"]
- Lua tree.source, "[ ",key_lua,"]=",value_lua
+ LuaCode tree.source, "[ ",key_lua,"]=",value_lua
else
- Lua tree.source, "[",key_lua,"]=",value_lua
+ LuaCode tree.source, "[",key_lua,"]=",value_lua
when "IndexChain"
- lua = @tree_to_lua(tree[1])
+ lua = @compile(tree[1])
unless lua.is_value
- compile_error tree[1],
+ @compile_error tree[1],
"Cannot index:\n%s\nsince it's not an expression."
first_char = sub(tostring(lua),1,1)
if first_char == "{" or first_char == '"' or first_char == "["
@@ -600,9 +568,9 @@ class NomsuCompiler
for i=2,#tree
key = tree[i]
- key_lua = @tree_to_lua(key)
+ key_lua = @compile(key)
unless key_lua.is_value
- compile_error key,
+ @compile_error key,
"Cannot use:\n%s\nas an index, since it's not an expression."
key_lua_str = tostring(key_lua)
if lua_id = match(key_lua_str, "^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$")
@@ -617,19 +585,19 @@ class NomsuCompiler
return lua
when "Number"
- Lua.Value(tree.source, tostring(tree[1]))
+ LuaCode.Value(tree.source, tostring(tree[1]))
when "Var"
- Lua.Value(tree.source, string.as_lua_id(tree[1]))
+ LuaCode.Value(tree.source, string.as_lua_id(tree[1]))
else
error("Unknown type: #{tree.type}")
- tree_to_nomsu: (tree, inline=false, can_use_colon=false)=>
+ .tree_to_nomsu = (tree, inline=false, can_use_colon=false)=>
switch tree.type
when "Action"
if inline
- nomsu = Nomsu(tree.source)
+ nomsu = NomsuCode(tree.source)
for i,bit in ipairs tree
if type(bit) == "string"
if i > 1
@@ -645,7 +613,7 @@ class NomsuCompiler
nomsu\append arg_nomsu
return nomsu
else
- nomsu = Nomsu(tree.source)
+ nomsu = NomsuCode(tree.source)
next_space = ""
line_len, last_colon = 0, nil
for i,bit in ipairs tree
@@ -679,9 +647,9 @@ class NomsuCompiler
-- These types carry their own indentation
if bit.type != "List" and bit.type != "Dict" and bit.type != "Text"
if i == 1
- arg_nomsu = Nomsu(bit.source, "(..)\n ", arg_nomsu)
+ arg_nomsu = NomsuCode(bit.source, "(..)\n ", arg_nomsu)
else
- arg_nomsu = Nomsu(bit.source, "\n ", arg_nomsu)
+ arg_nomsu = NomsuCode(bit.source, "\n ", arg_nomsu)
if last_colon == i-1 and (bit.type == "Action" or bit.type == "Block")
next_space = ""
@@ -697,12 +665,12 @@ class NomsuCompiler
nomsu = @tree_to_nomsu(tree[1], true)
if nomsu == nil and not inline
nomsu = @tree_to_nomsu(tree[1])
- return nomsu and Nomsu tree.source, "\\:\n ", nomsu
- return nomsu and Nomsu tree.source, "\\(", nomsu, ")"
+ return nomsu and NomsuCode tree.source, "\\:\n ", nomsu
+ return nomsu and NomsuCode tree.source, "\\(", nomsu, ")"
when "Block"
if inline
- nomsu = Nomsu(tree.source)
+ nomsu = NomsuCode(tree.source)
for i,line in ipairs tree
if i > 1
nomsu\append "; "
@@ -710,7 +678,7 @@ class NomsuCompiler
return nil unless line_nomsu
nomsu\append line_nomsu
return nomsu
- nomsu = Nomsu(tree.source)
+ nomsu = NomsuCode(tree.source)
for i, line in ipairs tree
line = assert(@tree_to_nomsu(line, nil, true), "Could not convert line to nomsu")
nomsu\append line
@@ -722,7 +690,7 @@ class NomsuCompiler
when "Text"
if inline
- nomsu = Nomsu(tree.source, '"')
+ nomsu = NomsuCode(tree.source, '"')
for bit in *tree
if type(bit) == 'string'
-- TODO: unescape better?
@@ -740,7 +708,7 @@ class NomsuCompiler
inline_version = @tree_to_nomsu(tree, true)
if inline_version and #inline_version <= MAX_LINE
return inline_version
- nomsu = Nomsu(tree.source, '".."\n ')
+ nomsu = NomsuCode(tree.source, '".."\n ')
for i, bit in ipairs tree
if type(bit) == 'string'
bit_lines = get_lines\match(bit)
@@ -779,7 +747,7 @@ class NomsuCompiler
when "List"
if inline
- nomsu = Nomsu(tree.source, "[")
+ nomsu = NomsuCode(tree.source, "[")
for i, item in ipairs tree
item_nomsu = @tree_to_nomsu(item, true)
return nil unless item_nomsu
@@ -792,8 +760,8 @@ class NomsuCompiler
inline_version = @tree_to_nomsu(tree, true)
if inline_version and #inline_version <= MAX_LINE
return inline_version
- nomsu = Nomsu(tree.source, "[..]")
- line = Nomsu(tree.source, "\n ")
+ nomsu = NomsuCode(tree.source, "[..]")
+ line = NomsuCode(tree.source, "\n ")
for item in *tree
item_nomsu = @tree_to_nomsu(item, true)
if item_nomsu and #line + #", " + #item_nomsu <= MAX_LINE
@@ -806,7 +774,7 @@ class NomsuCompiler
return nil unless item_nomsu
if #line.bits > 1
nomsu\append line
- line = Nomsu(line.source, "\n ")
+ line = NomsuCode(line.source, "\n ")
line\append item_nomsu
if #line.bits > 1
nomsu\append line
@@ -814,7 +782,7 @@ class NomsuCompiler
when "Dict"
if inline
- nomsu = Nomsu(tree.source, "{")
+ nomsu = NomsuCode(tree.source, "{")
for i, entry in ipairs tree
entry_nomsu = @tree_to_nomsu(entry, true)
return nil unless entry_nomsu
@@ -826,8 +794,8 @@ class NomsuCompiler
else
inline_version = @tree_to_nomsu(tree, true)
if inline_version then return inline_version
- nomsu = Nomsu(tree.source, "{..}")
- line = Nomsu(tree.source, "\n ")
+ nomsu = NomsuCode(tree.source, "{..}")
+ line = NomsuCode(tree.source, "\n ")
for entry in *tree
entry_nomsu = @tree_to_nomsu(entry)
return nil unless entry_nomsu
@@ -838,7 +806,7 @@ class NomsuCompiler
else
if #line.bits > 1
nomsu\append line
- line = Nomsu(line.source, "\n ")
+ line = NomsuCode(line.source, "\n ")
line\append entry_nomsu
if #line.bits > 1
nomsu\append line
@@ -852,22 +820,23 @@ class NomsuCompiler
key_nomsu\parenthesize!
value_nomsu = if value
@tree_to_nomsu(value, true)
- else Nomsu(tree.source, "")
+ else NomsuCode(tree.source, "")
if inline and not value_nomsu then return nil
if not value_nomsu
return nil if inline
value_nomsu = @tree_to_nomsu(value)
return nil unless value_nomsu
- return Nomsu tree.source, key_nomsu, ":", value_nomsu
+ return NomsuCode tree.source, key_nomsu, ":", value_nomsu
when "IndexChain"
- nomsu = Nomsu(tree.source)
+ nomsu = NomsuCode(tree.source)
for i, bit in ipairs tree
if i > 1
nomsu\append "."
local bit_nomsu
if bit.type == "Text" and #bit == 1 and type(bit[1]) == 'string'
- if (NOMSU_DEFS.ident_char^1)\match(bit[1])
+ -- TODO: support arbitrary words here, including operators and unicode
+ if bit[1]\match("[_a-zA-Z][_a-zA-Z0-9]*")
bit_nomsu = bit[1]
unless bit_nomsu then bit_nomsu = @tree_to_nomsu(bit, true)
return nil unless bit_nomsu
@@ -881,86 +850,22 @@ class NomsuCompiler
return nomsu
when "Number"
- return Nomsu(tree.source, tostring(tree[1]))
+ return NomsuCode(tree.source, tostring(tree[1]))
when "Var"
- return Nomsu(tree.source, "%", tree[1])
+ return NomsuCode(tree.source, "%", tree[1])
else
error("Unknown type: #{tree.type}")
- initialize_core: =>
- -- Sets up some core functionality
- nomsu = self
- with nomsu.environment
- .A_immediately_1 = .compile_time (_block)=>
- lua = nomsu\tree_to_lua(_block)\as_statements!
- lua\declare_locals!
- nomsu\run_lua(lua)
- return Lua(_block.source, "if IMMEDIATE then\n ", lua, "\nend")
-
- add_lua_string_bits = (lua, code)->
- line_len = 0
- if code.type != "Text"
- lua\append ", ", nomsu\tree_to_lua(code)
- return
- for bit in *code
- bit_lua = if type(bit) == "string"
- repr(bit)
- else
- bit_lua = nomsu\tree_to_lua(bit)
- unless bit_lua.is_value
- compile_error bit,
- "Cannot use:\n%s\nas a string interpolation value, since it's not an expression."
- bit_lua
- line_len += #tostring(bit_lua)
- if line_len > MAX_LINE
- lua\append ",\n "
- line_len = 4
- else
- lua\append ", "
- lua\append bit_lua
- .A_Lua_1 = .compile_time (_code)=>
- lua = Lua.Value(_code.source, "Lua(", repr(tostring _code.source))
- add_lua_string_bits(lua, _code)
- lua\append ")"
- return lua
-
- .A_Lua_value_1 = .compile_time (_code)=>
- lua = Lua.Value(_code.source, "Lua.Value(", repr(tostring _code.source))
- add_lua_string_bits(lua, _code)
- lua\append ")"
- return lua
- add_lua_bits = (lua, code)->
- for bit in *code
- if type(bit) == "string"
- lua\append bit
- else
- bit_lua = nomsu\tree_to_lua(bit)
- unless bit_lua.is_value
- compile_error bit,
- "Cannot use:\n%s\nas a string interpolation value, since it's not an expression."
- lua\append bit_lua
- return lua
- nomsu.environment["A"..string.as_lua_id("lua > 1")] = .compile_time (_code)=>
- if _code.type != "Text"
- return Lua @source, "nomsu:run_lua(", nomsu\tree_to_lua(_code), ");"
- return add_lua_bits(Lua(@source), _code)
- nomsu.environment["A"..string.as_lua_id("= lua 1")] = .compile_time (_code)=>
- if _code.type != "Text"
- return Lua.Value @source, "nomsu:run_lua(", nomsu\tree_to_lua(_code), ":as_statements('return '))"
- return add_lua_bits(Lua.Value(@source), _code)
- .A_use_1 = .compile_time (_path)=>
- unless _path.type == 'Text' and #_path == 1 and type(_path[1]) == 'string'
- return Lua(_path.source, "nomsu:run_file(#{nomsu\tree_to_lua(_path)});")
- path = _path[1]
- nomsu\run_file(path)
- return Lua(_path.source, "nomsu:run_file(#{repr path});")
+-- Command line interface:
+
+
-- Only run this code if this file was run directly with command line arguments, and not require()'d:
if arg and debug_getinfo(2).func != require
@@ -1000,8 +905,8 @@ OPTIONS
]=]
os.exit!
- nomsu = NomsuCompiler!
- nomsu.environment.arg = args.nomsu_args
+ nomsu = NomsuCompiler
+ nomsu.arg = args.nomsu_args
ok, to_lua = pcall -> require('moonscript.base').to_lua
if not ok then to_lua = nil
@@ -1022,7 +927,7 @@ OPTIONS
else debug_getinfo(thread,f,what)
if not info or not info.func then return info
if info.short_src or info.source or info.linedefine or info.currentline
- if arg_orders = nomsu.environment.ARG_ORDERS[info.func]
+ if arg_orders = nomsu.ARG_ORDERS[info.func]
info.name = next(arg_orders)
if map = nomsu.source_map[info.source]
if info.currentline
@@ -1077,7 +982,7 @@ OPTIONS
file = FILE_CACHE[filename]\sub(tonumber(start),tonumber(stop))
err_line = get_line(file, calling_fn.currentline)\sub(1,-2)
offending_statement = colored.bright(colored.red(err_line\match("^[ ]*(.*)")))
- if arg_orders = nomsu.environment.ARG_ORDERS[calling_fn.func]
+ if arg_orders = nomsu.ARG_ORDERS[calling_fn.func]
name = "action '#{next(arg_orders)}'"
else
name = "main chunk"
@@ -1145,9 +1050,9 @@ OPTIONS
nomsu.skip_precompiled = not args.optimized
if print_file == nil
- nomsu.environment.print = ->
+ nomsu.print = ->
elseif print_file != io.stdout
- nomsu.environment.print = (...)->
+ nomsu.print = (...)->
N = select("#",...)
if N > 0
print_file\write(tostring(select(1,...)))
@@ -1163,7 +1068,7 @@ OPTIONS
input_files[#input_files+1] = f
to_run[f] = true
- nomsu.compile_fn = if args.compile or args.verbose
+ nomsu.on_compile = if args.compile or args.verbose
(code, from_file)->
if to_run[from_file]
if args.verbose
@@ -1171,7 +1076,7 @@ OPTIONS
if args.compile and from_file\match("%.nom$")
output_filename = from_file\gsub("%.nom$", ".lua")
output_file = io.open(output_filename, 'w')
- output_file\write("local IMMEDIATE = true;\n", tostring(code))
+ output_file\write(tostring(code))
output_file\flush!
print ("Compiled %-25s -> %s")\format(from_file, output_filename)
output_file\close!
@@ -1181,7 +1086,8 @@ OPTIONS
for filename in *input_files
if args.syntax
-- Check syntax:
- ok,err = pcall nomsu.parse, nomsu, Nomsu(filename, io.open(filename)\read("*a"))
+ file_contents = io.open(filename)\read('*a')
+ ok,err = pcall nomsu.parse, nomsu, file_contents, Source(filename, 1, #file_contents)
if not ok
insert parse_errs, err
elseif print_file
@@ -1192,7 +1098,7 @@ OPTIONS
file = FILE_CACHE[filename]
if not file
error("File does not exist: #{filename}", 0)
- tree = nomsu\parse(Nomsu(Source(filename,1,#file), file))
+ tree = nomsu\parse(file, Source(filename,1,#file))
formatted = tostring(nomsu\tree_to_nomsu(tree))
if print_file
print_file\write(formatted, "\n")
@@ -1200,7 +1106,7 @@ OPTIONS
elseif filename == STDIN
file = io.input!\read("*a")
FILE_CACHE.stdin = file
- nomsu\run(Nomsu(Source('stdin',1,#file), file))
+ nomsu\run(file, Source('stdin',1,#file))
else
nomsu\run_file(filename)
@@ -1230,10 +1136,9 @@ OPTIONS
buff = concat(buff)
FILE_CACHE["REPL#"..repl_line] = buff
- code = Nomsu(Source("REPL#"..repl_line, 1, #buff), buff)
err_hand = (error_message)->
print_err_msg error_message
- ok, ret = xpcall(nomsu.run, err_hand, nomsu, code)
+ ok, ret = xpcall(nomsu.run, err_hand, nomsu, buff, Source("REPL#"..repl_line, 1, #buff))
if ok and ret != nil
print "= "..repr(ret)
elseif not ok