diff options
| author | Bruce Hill <bitbucket@bruce-hill.com> | 2018-07-15 19:41:22 -0700 |
|---|---|---|
| committer | Bruce Hill <bitbucket@bruce-hill.com> | 2018-07-15 19:43:28 -0700 |
| commit | be06fc096aa28358914bd6a76b4ea380a04b8873 (patch) | |
| tree | 1f6c300bc8fbeb6cd84008124fca1e43f5513616 /nomsu_compiler.moon | |
| parent | 8a44869c4a548692a19afe5258e2540f7d351c8a (diff) | |
Major changes to how versioning and parsing work. This should be a
better path going forward to handling upgrades. Old syntax files will
stick around for compatibility purposes. Old syntax can be parsed into
valid syntax trees via the old syntax (.peg) files, and then old syntax
trees should be valid and can be upgraded via the normal code path. This
change has lots of improvements to Nomsu codegen too.
Diffstat (limited to 'nomsu_compiler.moon')
| -rw-r--r-- | nomsu_compiler.moon | 290 |
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! |
