aboutsummaryrefslogtreecommitdiff
path: root/nomsu_compiler.moon
diff options
context:
space:
mode:
Diffstat (limited to 'nomsu_compiler.moon')
-rw-r--r--nomsu_compiler.moon290
1 files changed, 127 insertions, 163 deletions
diff --git a/nomsu_compiler.moon b/nomsu_compiler.moon
index eda79d8..d1b76f4 100644
--- a/nomsu_compiler.moon
+++ b/nomsu_compiler.moon
@@ -229,31 +229,27 @@ with NomsuCompiler
return @["# compile math expr #"]
}
- .run = (to_run, source=nil)=>
+ .run = (to_run, source=nil, version=nil)=>
source or= to_run.source or Source(to_run, 1, #to_run)
if type(source) == 'string' then source = Source\from_string(source)
if not files.read(source.filename) then files.spoof(source.filename, to_run)
- tree = if AST.is_syntax_tree(to_run) then to_run else @parse(to_run, source)
+ tree = if AST.is_syntax_tree(to_run) then to_run else @parse(to_run, source, version)
if tree == nil -- Happens if pattern matches, but there are no captures, e.g. an empty string
return nil
- if tree.type == "File"
- -- 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
- all_lua = {}
- for chunk in *tree
- lua = @compile(chunk)\as_statements("return ")
- lua\declare_locals!
- lua\prepend "-- File: #{source.filename\gsub("\n.*", "...")}\n"
- insert all_lua, tostring(lua)
- ret = @run_lua(lua)
- return ret
- else
- lua = @compile(tree)\as_statements("return ")
+ 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.
+ ret = nil
+ all_lua = {}
+ for chunk in *tree
+ lua = @compile(chunk)\as_statements("return ")
lua\declare_locals!
lua\prepend "-- File: #{source.filename\gsub("\n.*", "...")}\n"
- return @run_lua(lua)
+ insert all_lua, tostring(lua)
+ ret = @run_lua(lua)
+ return ret
_running_files = {} -- For detecting circular imports
.run_file = (filename)=>
@@ -362,7 +358,8 @@ with NomsuCompiler
unless AST.is_syntax_tree(t)
return repr(t)
bits = [make_tree(bit) for bit in *t]
- return t.type.."("..repr(tostring t.source)..", "..table.concat(bits, ", ")..")"
+ insert bits, 1, repr(tostring t.source)
+ return t.type.."("..concat(bits, ", ")..")"
return LuaCode.Value tree.source, make_tree(tree[1])
when "Block"
@@ -474,44 +471,45 @@ with NomsuCompiler
when "Var"
return LuaCode.Value(tree.source, string.as_lua_id(tree[1]))
- when "File"
- error("Cannot convert File to a single block of lua, since each chunk's "..
+ when "FileChunks"
+ error("Cannot convert FileChunks to a single block of lua, since each chunk's "..
"compilation depends on the earlier chunks")
else
error("Unknown type: #{tree.type}")
- MIN_COLON_LEN = 25 -- For beautification purposes, don't bother using colon syntax for short bits
+ MIN_COLON_LEN = 20 -- For beautification purposes, don't bother using colon syntax for short bits
.tree_to_nomsu = (tree, options)=>
options or= {}
unless options.pop_comments
comments = [{comment:c, pos:p} for p,c in pairs(tree.comments or {}) when tree.source.start <= p and p <= tree.source.stop]
table.sort comments, (a,b)-> a.pos < b.pos
comment_i = 1
- options.pop_comments = (pos)->
+ options.pop_comments = (pos, prefix='')->
nomsu = NomsuCode(tree.source)
while comments[comment_i] and comments[comment_i].pos <= pos
comment = comments[comment_i].comment
nomsu\append("#"..(gsub(comment, "\n", "\n ")).."\n")
if comment\match("^\n.") then nomsu\append("\n") -- for aesthetics
comment_i += 1
- return #nomsu.bits == 0 and '' or nomsu
+ return '' if #nomsu.bits == 0
+ nomsu\prepend prefix
+ return nomsu
recurse = (t, opts)->
opts or= {}
opts.pop_comments = options.pop_comments
return @tree_to_nomsu(t, opts)
- {:inline, :can_use_colon, :pop_comments} = options
+ {:inline, :pop_comments} = options
switch tree.type
- when "File"
- return nil if inline
+ when "FileChunks"
+ error("Cannot inline a FileChunks") if inline
nomsu = NomsuCode(tree.source)
for i, chunk in ipairs tree
- if i > 1
- nomsu\append "\n\n#{("~")\rep(80)}\n\n"
+ nomsu\append "\n\n#{("~")\rep(80)}\n\n" if i > 1
nomsu\append pop_comments(chunk.source.start)
- nomsu\append recurse(chunk)
+ nomsu\append recurse(chunk, top:true)
nomsu\append pop_comments(tree.source.stop)
return nomsu
@@ -520,97 +518,78 @@ with NomsuCompiler
nomsu = NomsuCode(tree.source)
for i,bit in ipairs tree
if type(bit) == "string"
- if i > 1
- nomsu\append " "
+ nomsu\append " " if i > 1
nomsu\append bit
else
arg_nomsu = recurse(bit,inline:true)
return nil unless arg_nomsu
- if bit.type == "Action" or bit.type == "Block"
- if bit.type == "Action" and i == #tree and #tostring(arg_nomsu) >= MIN_COLON_LEN
- nomsu\append ":"
- else
- arg_nomsu\parenthesize!
unless i == 1
nomsu\append " "
+ if bit.type == "Action" or (bit.type == "Block" and (#bit > 1 or i < #tree))
+ arg_nomsu\parenthesize!
nomsu\append arg_nomsu
return nomsu
else
nomsu = NomsuCode(tree.source)
next_space = ""
- line_len, last_colon = 0, nil
for i,bit in ipairs tree
if type(bit) == "string"
- line_len += #next_space + #bit
nomsu\append next_space, bit
next_space = " "
else
- arg_nomsu = if last_colon == i-1 and bit.type == "Action" then nil
- elseif bit.type == "Block" then nil
- else recurse(bit,inline:true)
-
- if arg_nomsu and line_len + #tostring(arg_nomsu) < MAX_LINE
- if bit.type == "Action"
- if can_use_colon and i > 1 and #tostring(arg_nomsu) >= MIN_COLON_LEN
- nomsu\append match(next_space,"[^ ]*"), ": ", arg_nomsu
- next_space = "\n.."
- line_len = 2
- last_colon = i
- else
- nomsu\append next_space, "(", arg_nomsu, ")"
- line_len += #next_space + 2 + #tostring(arg_nomsu)
- next_space = " "
+ arg_nomsu = if bit.type == "Block" and #bit > 1 then nil
+ else assert recurse(bit,inline:true)
+ next_space = match(next_space, "[^ ]*") if bit.type == "Block"
+ nomsu\append next_space
+ if arg_nomsu and nomsu.trailing_line_len + #tostring(arg_nomsu) < MAX_LINE
+ if bit.type == "Block"
+ nomsu\append arg_nomsu
+ next_space = "\n.."
else
- nomsu\append next_space, arg_nomsu
- line_len += #next_space + #tostring(arg_nomsu)
+ arg_nomsu\parenthesize! if bit.type == "Action"
+ nomsu\append arg_nomsu
next_space = " "
else
- arg_nomsu = recurse(bit, can_use_colon:true)
- return nil unless arg_nomsu
+ arg_nomsu = assert recurse(bit)
-- These types carry their own indentation
- if bit.type != "List" and bit.type != "Dict" and bit.type != "Text"
- if i == 1
- arg_nomsu = NomsuCode(bit.source, "(..)\n ", pop_comments(bit.source.start), arg_nomsu)
- else
- arg_nomsu = NomsuCode(bit.source, "\n ", pop_comments(bit.source.start), arg_nomsu)
-
- if last_colon == i-1 and (bit.type == "Action" or bit.type == "Block")
- next_space = ""
- nomsu\append next_space, arg_nomsu
+ if bit.type != "List" and bit.type != "Dict" and bit.type != "Text" and bit.type != "Block"
+ nomsu\append NomsuCode(bit.source, "(..)\n ", pop_comments(bit.source.start), arg_nomsu)
+ else
+ nomsu\append arg_nomsu
next_space = "\n.."
- line_len = 2
- if next_space == " " and #(match(tostring(nomsu),"[^\n]*$")) > MAX_LINE
- next_space = "\n.."
+ if next_space == " " and nomsu.trailing_line_len > MAX_LINE
+ next_space = "\n.."
return nomsu
when "EscapedNomsu"
- nomsu = recurse(tree[1], inline:true)
- if nomsu == nil and not inline
- nomsu = recurse(tree[1])
- return nomsu and NomsuCode tree.source, "\\:\n ", pop_comments(tree.source.start), nomsu
- return nomsu and NomsuCode tree.source, "\\(", nomsu, ")"
+ nomsu = NomsuCode(tree.source, "\\(", assert(recurse(tree[1], inline:true)), ")")
+ if inline or #tostring(nomsu) <= MAX_LINE
+ return nomsu
+ nomsu = assert recurse(tree[1])
+ switch tree[1].type
+ when "List", "Dict", "Text", "Block"
+ return NomsuCode tree.source, "\\", nomsu
+ else
+ return NomsuCode tree.source, "\\(..)\n ", pop_comments(tree.source.start), nomsu
when "Block"
if inline
- nomsu = NomsuCode(tree.source)
+ nomsu = NomsuCode(tree.source, ":")
for i,line in ipairs tree
- if i > 1
- nomsu\append "; "
- line_nomsu = recurse(line,inline:true)
- return nil unless line_nomsu
- nomsu\append line_nomsu
+ nomsu\append(i == 1 and " " or "; ")
+ nomsu\append assert(recurse(line, inline:true))
return nomsu
nomsu = NomsuCode(tree.source)
for i, line in ipairs tree
nomsu\append pop_comments(line.source.start)
- line = assert(recurse(line, can_use_colon:true), "Could not convert line to nomsu")
+ line = assert(recurse(line), "Could not convert line to nomsu")
nomsu\append line
if i < #tree
nomsu\append "\n"
if match(tostring(line), "\n")
nomsu\append "\n"
- return nomsu
+ return options.top and nomsu or NomsuCode(tree.source, ":\n ", nomsu)
when "Text"
if inline
@@ -624,7 +603,7 @@ with NomsuCompiler
nomsu\append(make_text(bit))
else
interp_nomsu = assert recurse(bit, inline:true)
- if bit.type != "Var" and bit.type != "List" and bit.type != "Dict" and bit.type != "Text"
+ if bit.type != "Var" and bit.type != "List" and bit.type != "Dict"
interp_nomsu\parenthesize!
nomsu\append "\\", interp_nomsu
return nomsu
@@ -662,7 +641,7 @@ with NomsuCompiler
else
interp_nomsu = recurse(bit, inline:true)
if interp_nomsu
- if bit.type != "Var" and bit.type != "List" and bit.type != "Dict" and bit.type != "Text"
+ if bit.type != "Var" and bit.type != "List" and bit.type != "Dict"
interp_nomsu\parenthesize!
nomsu\append "\\", interp_nomsu
else
@@ -678,113 +657,98 @@ with NomsuCompiler
if inline
nomsu = NomsuCode(tree.source, "[")
for i, item in ipairs tree
- item_nomsu = recurse(item, inline:true)
- return nil unless item_nomsu
- if i > 1
- nomsu\append ", "
- nomsu\append item_nomsu
+ nomsu\append ", " if i > 1
+ nomsu\append assert(recurse(item, inline:true))
nomsu\append "]"
return nomsu
else
inline_version = recurse(tree, inline:true)
- if inline_version and #inline_version <= MAX_LINE
+ if inline_version and #tostring(inline_version) <= MAX_LINE
return inline_version
- nomsu = NomsuCode(tree.source, "[..]")
- line = NomsuCode(tree.source, "\n ")
- line_comments = if #tree > 0
- pop_comments(tree[1].source.start)
- else ''
+ assert #tree > 0
+ nomsu = NomsuCode(tree.source, pop_comments(tree[1].source.start))
for i, item in ipairs tree
- item_nomsu = recurse(item, inline:true)
- if item_nomsu and #tostring(line) + #", " + #item_nomsu <= MAX_LINE
- if #line.bits > 1
- line\append ", "
- line\append item_nomsu
+ item_nomsu = assert recurse(item, inline:true)
+ item_nomsu\parenthesize! if item.type == "Block"
+ if nomsu.trailing_line_len + #tostring(item_nomsu) <= MAX_LINE
+ nomsu\append ", " if nomsu.trailing_line_len > 0
+ nomsu\append item_nomsu
else
- unless item_nomsu
+ if #tostring(item_nomsu) > MAX_LINE
item_nomsu = recurse(item)
- return nil unless item_nomsu
- if #line.bits > 1
- if #tostring(line_comments) > 0
- nomsu\append '\n ', line_comments
- nomsu\append line
- line = NomsuCode(line.source, "\n ")
- line_comments = if i < #tree
- pop_comments(tree[i+1].source.start)
- else ''
- line\append item_nomsu
- if #line.bits > 1
- nomsu\append line
- return nomsu
+ switch item.type
+ when "List", "Dict", "Text", "Block"
+ nomsu\append item_nomsu
+ else
+ nomsu\append "(..)\n ", item_nomsu
+ nomsu\append "\n" if i < #tree
+ else
+ nomsu\append '\n' if nomsu.trailing_line_len > 0
+ nomsu\append pop_comments(item.source.start), item_nomsu
+ nomsu\append pop_comments(tree.source.stop, '\n')
+ return NomsuCode(tree.source, "[..]\n ", nomsu)
when "Dict"
if inline
nomsu = NomsuCode(tree.source, "{")
for i, entry in ipairs tree
- entry_nomsu = recurse(entry, inline:true)
- return nil unless entry_nomsu
- if i > 1
- nomsu\append ", "
- nomsu\append entry_nomsu
+ nomsu\append ", " if i > 1
+ nomsu\append assert(recurse(entry, inline:true))
nomsu\append "}"
return nomsu
else
inline_version = recurse(tree, inline:true)
- if inline_version then return inline_version
- nomsu = NomsuCode(tree.source, "{..}")
- line = NomsuCode(tree.source, "\n ")
- line_comments = if #tree > 0
- pop_comments(tree[1].source.start)
- else ''
- for i, entry in ipairs tree
- entry_nomsu = recurse(entry)
- return nil unless entry_nomsu
- if #line + #tostring(entry_nomsu) <= MAX_LINE
- if #line.bits > 1
- line\append ", "
- line\append entry_nomsu
+ if inline_version and #tostring(inline_version) <= MAX_LINE
+ return inline_version
+ assert #tree > 0
+ nomsu = NomsuCode(tree.source, pop_comments(tree[1].source.start))
+ for i, item in ipairs tree
+ item_nomsu = assert recurse(item, inline:true)
+ item_nomsu\parenthesize! if item.type == "Block"
+ if nomsu.trailing_line_len + #tostring(item_nomsu) <= MAX_LINE
+ nomsu\append ", " if nomsu.trailing_line_len > 0
+ nomsu\append item_nomsu
else
- if #line.bits > 1
- if #tostring(line_comments) > 0
- nomsu\append "\n ", line_comments
- nomsu\append line
- line = NomsuCode(line.source, "\n ")
- line_comments = if i < #tree
- pop_comments(tree[1].source.start)
- else ''
- line\append entry_nomsu
- if #line.bits > 1
- nomsu\append line
- return nomsu
+ if #tostring(item_nomsu) > MAX_LINE
+ item_nomsu = recurse(item)
+ switch item.type
+ when "List", "Dict", "Text", "Block"
+ nomsu\append item_nomsu
+ else
+ nomsu\append "(..)\n ", item_nomsu
+ nomsu\append "\n" if i < #tree
+ else
+ nomsu\append '\n' if nomsu.trailing_line_len > 0
+ nomsu\append pop_comments(item.source.start), item_nomsu
+ nomsu\append pop_comments(tree.source.stop, '\n')
+ return NomsuCode(tree.source, "{..}\n ", nomsu)
when "DictEntry"
key, value = tree[1], tree[2]
- key_nomsu = recurse(key, inline:true)
- return nil unless key_nomsu
- if key.type == "Action" or key.type == "Block"
- key_nomsu\parenthesize!
+ key_nomsu = assert recurse(key, inline:true)
+ key_nomsu\parenthesize! if key.type == "Action" or key.type == "Block"
value_nomsu = if value
- recurse(value, inline:true)
+ assert recurse(value, inline:true)
else NomsuCode(tree.source, "")
- if inline and not value_nomsu then return nil
- if not value_nomsu
- return nil if inline
- value_nomsu = recurse(value)
- return nil unless value_nomsu
- return NomsuCode tree.source, key_nomsu, ":", value_nomsu
+ assert(value.type != "Block", "Didn't expect to find a Block as a value in a dict")
+ value_nomsu\parenthesize! if value.type == "Block"
+ if inline or #tostring(key_nomsu) + 2 + #tostring(value_nomsu) <= MAX_LINE
+ return NomsuCode tree.source, key_nomsu, ": ", value_nomsu
+ value_nomsu = recurse(value)
+ if value.type == "List" or value.type == "Dict" or value.type == "Text"
+ return NomsuCode tree.source, key_nomsu, ": ", value_nomsu
+ else
+ return NomsuCode tree.source, key_nomsu, ": (..)\n ", value_nomsu
when "IndexChain"
nomsu = NomsuCode(tree.source)
for i, bit in ipairs tree
- if i > 1
- nomsu\append "."
+ nomsu\append "." if i > 1
local bit_nomsu
- if bit.type == "Text" and #bit == 1 and type(bit[1]) == 'string'
+ bit_nomsu = if bit.type == "Text" and #bit == 1 and type(bit[1]) == 'string' and bit[1]\match("[_a-zA-Z][_a-zA-Z0-9]*")
-- 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 = recurse(bit, inline:true)
- return nil unless bit_nomsu
+ bit[1]
+ else assert recurse(bit, inline:true)
switch bit.type
when "Action", "Block", "IndexChain"
bit_nomsu\parenthesize!