From ad94ed3653e2b7a9f68855670a32617aa80a637c Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Wed, 16 May 2018 19:08:16 -0700 Subject: Moved all the tree->lua and tree->nomsu code back into single functions in nomsu.moon, and cleaned up how Vars are treated, since they are not atomic. --- nomsu.moon | 479 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 459 insertions(+), 20 deletions(-) (limited to 'nomsu.moon') diff --git a/nomsu.moon b/nomsu.moon index 7e60cfd..196bedf 100755 --- a/nomsu.moon +++ b/nomsu.moon @@ -306,7 +306,7 @@ class NomsuCompiler stub = assert(stub_pattern\match(alias)) stub_args = assert(var_pattern\match(alias)) (is_compile_action and @environment.COMPILE_ACTIONS or @environment.ACTIONS)[stub] = fn - arg_orders[stub] = [fn_arg_positions[Types.Var.as_lua_id(a)] for a in *stub_args] + arg_orders[stub] = [fn_arg_positions[Types.Var(a)\as_lua_id!] for a in *stub_args] @environment.ARG_ORDERS[fn] = arg_orders define_compile_action: (signature, fn)=> @@ -341,7 +341,7 @@ class NomsuCompiler if #tostring(nomsu_code) == 0 then return nil tree = @parse(nomsu_code) assert tree, "Failed to parse: #{nomsu_code}" - lua = tree\as_lua(@)\as_statements! + lua = @tree_to_lua(tree)\as_statements! lua\declare_locals! lua\prepend "-- File: #{nomsu_code.source or ""}\n" if compile_fn @@ -401,12 +401,461 @@ class NomsuCompiler 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) 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)=> + switch tree.type + when "Action" + stub = tree\get_stub! + compile_action = @environment.COMPILE_ACTIONS[stub] + if compile_action + args = [arg for arg in *tree when arg.type != "Word"] + -- Force all compile-time actions to take a tree location + args = [args[p-1] for p in *@environment.ARG_ORDERS[compile_action][stub]] + -- Force Lua to avoid tail call optimization for debugging purposes + ret = compile_action(tree, unpack(args)) + if not ret then error("Failed to produce any Lua") + return ret + action = rawget(@environment.ACTIONS, stub) + lua = Lua.Value! + 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 + -- action for every possibility. + for i,tok in ipairs tree + if tok.type == "Word" + lua\append tok.value + else + tok_lua = @tree_to_lua(tok) + unless tok_lua.is_value + error("non-expression value inside math expression: #{colored.yellow repr(tok)}") + if tok.type == "Action" + tok_lua\parenthesize! + lua\append tok_lua + if i < #tree + lua\append " " + return lua + + args = {} + for i, tok in ipairs tree + if tok.type == "Word" then continue + arg_lua = @tree_to_lua(tok) + unless arg_lua.is_value + error "Cannot use:\n#{colored.yellow repr(tok)}\nas an argument to #{stub}, since it's not an expression, it produces: #{repr arg_lua}", 0 + insert args, arg_lua + + if action + args = [args[p] for p in *@environment.ARG_ORDERS[action][stub]] + + -- Not really worth bothering with ACTIONS.foo(...) style since almost every action + -- has arguments, so it won't work + lua\append "ACTIONS[",repr(stub),"](" + for i, arg in ipairs args + lua\append arg + if i < #args then lua\append ", " + lua\append ")" + return lua + + when "EscapedNomsu" + make_tree = (t)-> + if type(t) != 'userdata' + return repr(t) + if t.is_multi + bits = [make_tree(bit) for bit in *t] + return t.type.."("..table.concat(bits, ", ")..")" + else + return t.type.."("..make_tree(t.value)..")" + Lua.Value nil, make_tree(tree.value) + + when "Block" + lua = Lua! + for i,line in ipairs tree + line_lua = @tree_to_lua(line) + if i > 1 + lua\append "\n" + lua\append line_lua\as_statements! + return lua + + when "Text" + lua = Lua.Value! + string_buffer = "" + for bit in *tree + if type(bit) == "string" + string_buffer ..= bit + continue + if string_buffer ~= "" + if #lua.bits > 0 then lua\append ".." + lua\append repr(string_buffer) + string_buffer = "" + bit_lua = @tree_to_lua(bit) + unless bit_lua.is_value + error "Cannot use #{colored.yellow repr(bit)} as a string interpolation value, since it's not an expression.", 0 + if #lua.bits > 0 then lua\append ".." + if bit.type != "Text" + bit_lua = Lua.Value(nil, "stringify(",bit_lua,")") + lua\append bit_lua + + if string_buffer ~= "" or #lua.bits == 0 + if #lua.bits > 0 then lua\append ".." + lua\append repr(string_buffer) + + if #lua.bits > 1 + lua\parenthesize! + return lua + + when "List" + lua = Lua.Value nil, "{" + line_length = 0 + for i, item in ipairs tree + item_lua = @tree_to_lua(item) + unless item_lua.is_value + error "Cannot use #{colored.yellow repr(item)} as a list item, since it's not an expression.", 0 + lua\append item_lua + item_string = tostring(item_lua) + last_line = item_string\match("[^\n]*$") + if item_string\match("\n") + line_length = #last_line + else + line_length += #last_line + if i < #tree + if line_length >= MAX_LINE + lua\append ",\n " + line_length = 0 + else + lua\append ", " + line_length += 2 + lua\append "}" + return lua + + when "Dict" + lua = Lua.Value nil, "{" + line_length = 0 + for i, entry in ipairs tree + entry_lua = @tree_to_lua(entry) + lua\append entry_lua + entry_lua_str = tostring(entry_lua) + -- TODO: maybe make this more accurate? It's only a heuristic, so eh... + last_line = entry_lua_str\match("\n([^\n]*)$") + if last_line + line_length = #last_line + else + line_length += #entry_lua_str + if i < #tree + if line_length >= MAX_LINE + lua\append ",\n " + line_length = 0 + else + lua\append ", " + line_length += 2 + lua\append "}" + return lua + + when "DictEntry" + key, value = tree[1], tree[2] + key_lua = @tree_to_lua(key) + unless key_lua.is_value + error "Cannot use #{colored.yellow repr(key)} as a dict key, since it's not an expression.", 0 + value_lua = value and @tree_to_lua(value) or Lua.Value(nil, "true") + unless value_lua.is_value + error "Cannot use #{colored.yellow repr(value)} as a dict value, since it's not an expression.", 0 + key_str = tostring(key_lua)\match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=]) + return if key_str + Lua nil, key_str,"=",value_lua + elseif tostring(key_lua)\sub(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 nil, "[ ",key_lua,"]=",value_lua + else + Lua nil, "[",key_lua,"]=",value_lua + + when "IndexChain" + lua = @tree_to_lua(tree[1]) + unless lua.is_value + error "Cannot index #{colored.yellow repr(tree[1])}, since it's not an expression.", 0 + first_char = tostring(lua)\sub(1,1) + if first_char == "{" or first_char == '"' or first_char == "[" + lua\parenthesize! + + for i=2,#tree + key = tree[i] + key_lua = @tree_to_lua(key) + unless key_lua.is_value + error "Cannot use #{colored.yellow repr(key)} as an index, since it's not an expression.", 0 + key_lua_str = tostring(key_lua) + if lua_id = key_lua_str\match("^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$") + lua\append ".#{lua_id}" + elseif key_lua_str\sub(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\append "[ ",key_lua," ]" + else + lua\append "[",key_lua,"]" + return lua + + when "Number" + Lua.Value(nil, tostring(tree.value)) + + when "Var" + Lua.Value(nil, tree\as_lua_id!) + + when "Word" + error("Cannot convert a Word to lua") + + when "Comment" + Lua(nil, "--"..tree.value\gsub("\n","\n--").."\n") + + else + error("Unknown type: #{tree.type}") + + + tree_to_nomsu: (tree, inline=false, can_use_colon=false)=> + switch tree.type + when "Action" + if inline + nomsu = Nomsu! + for i,bit in ipairs tree + if bit.type == "Word" + if i > 1 + nomsu\append " " + nomsu\append bit.value + else + arg_nomsu = @tree_to_nomsu(bit,true) + return nil unless arg_nomsu + unless i == 1 + nomsu\append " " + if bit.type == "Action" or bit.type == "Block" + arg_nomsu\parenthesize! + nomsu\append arg_nomsu + return nomsu + else + nomsu = Nomsu! + next_space = "" + -- TODO: track line length as we go and use 80-that instead of 80 for wrapping + last_colon = nil + for i,bit in ipairs tree + if bit.type == "Word" + nomsu\append next_space, bit.value + next_space = " " + else + arg_nomsu = if last_colon == i-1 and bit.type == "Action" then nil + elseif bit.type == "Block" then nil + else @tree_to_nomsu(bit,true) + + if arg_nomsu and #arg_nomsu < MAX_LINE + if bit.type == "Action" + if can_use_colon and i > 1 + nomsu\append next_space\match("[^ ]*"), ": ", arg_nomsu + next_space = "\n.." + last_colon = i + else + nomsu\append next_space, "(", arg_nomsu, ")" + next_space = " " + else + nomsu\append next_space, arg_nomsu + next_space = " " + else + arg_nomsu = @tree_to_nomsu(bit, nil, true) + return nil unless nomsu + -- These types carry their own indentation + if bit.type != "List" and bit.type != "Dict" and bit.type != "Text" + if i == 1 + arg_nomsu = Nomsu(nil, "(..)\n ", arg_nomsu) + else + arg_nomsu = Nomsu(nil, "\n ", arg_nomsu) + + if last_colon == i-1 and (bit.type == "Action" or bit.type == "Block") + next_space = "" + nomsu\append next_space, arg_nomsu + next_space = "\n.." + + if next_space == " " and #(tostring(nomsu)\match("[^\n]*$")) > MAX_LINE + next_space = "\n.." + return nomsu + + when "EscapedNomsu" + nomsu = @tree_to_nomsu(tree.value, true) + if nomsu == nil and not inline + nomsu = @tree_to_nomsu(tree.value) + return nomsu and Nomsu nil, "\\:\n ", nomsu + return nomsu and Nomsu nil, "\\(", nomsu, ")" + + when "Block" + if inline + nomsu = Nomsu! + for i,line in ipairs @ + if i > 1 + nomsu\append "; " + line_nomsu = @tree_to_nomsu(line,true) + return nil unless line_nomsu + nomsu\append line_nomsu + return nomsu + nomsu = Nomsu! + for i, line in ipairs @ + line = assert(@tree_to_nomsu(line, nil, true), "Could not convert line to nomsu") + nomsu\append line + if i < #@ + nomsu\append "\n" + if tostring(line)\match("\n") + nomsu\append "\n" + return nomsu + + when "Text" + if inline + nomsu = Nomsu(nil, '"') + for bit in *tree + if type(bit) == 'string' + -- TODO: unescape better? + nomsu\append (bit\gsub("\\","\\\\")\gsub("\n","\\n")) + else + interp_nomsu = @tree_to_nomsu(bit, true) + if interp_nomsu + if bit.type != "Word" and bit.type != "List" and bit.type != "Dict" and bit.type != "Text" + interp_nomsu\parenthesize! + nomsu\append "\\", interp_nomsu + else return nil + nomsu\append '"' + return nomsu + else + inline_version = @tree_to_nomsu(tree, true) + if inline_version and #inline_version <= MAX_LINE + return inline_version + nomsu = Nomsu(nil, '".."\n ') + for i, bit in ipairs @ + if type(bit) == 'string' + nomsu\append (bit\gsub("\\","\\\\")\gsub("\n","\n ")) + else + interp_nomsu = @tree_to_nomsu(bit, true) + if interp_nomsu + if bit.type != "Word" and bit.type != "List" and bit.type != "Dict" and bit.type != "Text" + interp_nomsu\parenthesize! + nomsu\append "\\", interp_nomsu + else + interp_nomsu = @tree_to_nomsu(bit) + return nil unless interp_nomsu + nomsu\append "\\\n ", interp_nomsu + if i < #@ + nomsu\append "\n .." + return nomsu + + when "List" + if inline + nomsu = Nomsu(nil, "[") + for i, item in ipairs tree + item_nomsu = @tree_to_nomsu(item, true) + return nil unless item_nomsu + if i > 1 + nomsu\append ", " + nomsu\append item_nomsu + nomsu\append "]" + return nomsu + else + inline_version = @tree_to_nomsu(tree, true) + if inline_version and #inline_version <= MAX_LINE + return inline_version + nomsu = Nomsu(nil, "[..]") + line = Nomsu(nil, "\n ") + for item in *tree + item_nomsu = @tree_to_nomsu(item, true) + if item_nomsu and #line + #", " + #item_nomsu <= MAX_LINE + if #line.bits > 1 + line\append ", " + line\append item_nomsu + else + unless item_nomsu + item_nomsu = @tree_to_nomsu(item) + return nil unless item_nomsu + if #line.bits > 1 + nomsu\append line + line = Nomsu(nil, "\n ") + line\append item_nomsu + if #line.bits > 1 + nomsu\append line + return nomsu + + when "Dict" + if inline + nomsu = Nomsu(nil, "{") + for i, entry in ipairs tree + entry_nomsu = @tree_to_nomsu(entry, true) + return nil unless entry_nomsu + if i > 1 + nomsu\append ", " + nomsu\append entry_nomsu + nomsu\append "}" + return nomsu + else + inline_version = @tree_to_nomsu(tree, true) + if inline_version then return inline_version + nomsu = Nomsu(nil, "{..}") + line = Nomsu(nil, "\n ") + for entry in *tree + entry_nomsu = @tree_to_nomsu(entry) + return nil unless entry_nomsu + if #line + #tostring(entry_nomsu) <= MAX_LINE + if #line.bits > 1 + line\append ", " + line\append entry_nomsu + else + if #line.bits > 1 + nomsu\append line + line = Nomsu(nil, "\n ") + line\append entry_nomsu + if #line.bits > 1 + nomsu\append line + return nomsu + + when "DictEntry" + key, value = tree[1], tree[2] + key_nomsu = @tree_to_nomsu(key, true) + return nil unless key_nomsu + if key.type == "Action" or key.type == "Block" + key_nomsu\parenthesize! + value_nomsu = if value + @tree_to_nomsu(value, true) + else Nomsu(nil, "") + 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 nil, key_nomsu, ":", value_nomsu + + when "IndexChain" + nomsu = Nomsu! + for i, bit in ipairs tree + if i > 1 + nomsu\append "." + bit_nomsu = @tree_to_nomsu(bit, true) + return nil unless bit_nomsu + if bit.type == "Action" or bit.type == "Block" + bit_nomsu\parenthesize! + nomsu\append bit_nomsu + return nomsu + + when "Number" + return Nomsu(nil, tostring(tree.value)) + + when "Var" + return Nomsu(nil, "%", tree.value) + + when "Word" + return Nomsu(nil, tree.value) + + when "Comment" + return nil if inline + return Nomsu(nil, "#", tree.value\gsub("\n", "\n ")) + + else + error("Unknown type: #{tree.type}") tree_to_value: (tree)=> -- Special case for text literals if tree.type == 'Text' and #tree == 1 and type(tree[1]) == 'string' return tree[1] - lua = Lua(nil, "return ",tree\as_lua(@),";") + lua = Lua(nil, "return ",@tree_to_lua(tree),";") return @run_lua(lua) walk_tree: (tree, depth=0)=> @@ -418,35 +867,25 @@ class NomsuCompiler else @walk_tree(v, depth+1) - tree_with_replacements: (tree, replacements)=> - return tree unless next(replacements) - if next(replacements).type == "Var" - replacements = {tostring(k\as_lua(@)),v for k,v in pairs(replacements)} - tree\map (t)-> - if t.type == "Var" - id = tostring(t\as_lua(self)) - if replacements[id] != nil - return replacements[id] - initialize_core: => -- Sets up some core functionality nomsu = self @define_compile_action "immediately %block", (_block)=> - lua = _block\as_lua(nomsu)\as_statements! + lua = nomsu\tree_to_lua(_block)\as_statements! lua\declare_locals! nomsu\run_lua(lua) return Lua(nil, "if IMMEDIATE then\n ", lua, "\nend") add_lua_string_bits = (lua, code)-> if code.type != "Text" - lua\append ", ", code\as_lua(nomsu) + lua\append ", ", nomsu\tree_to_lua(code) return for bit in *code lua\append ", " if type(bit) == "string" lua\append repr(bit) else - bit_lua = bit\as_lua(nomsu) + bit_lua = nomsu\tree_to_lua(bit) unless bit_lua.is_value error "Cannot use #{colored.yellow repr(bit)} as a string interpolation value, since it's not an expression." lua\append bit_lua @@ -468,7 +907,7 @@ class NomsuCompiler if type(bit) == "string" lua\append bit else - bit_lua = bit\as_lua(nomsu) + bit_lua = nomsu\tree_to_lua(bit) unless bit_lua.is_value error "Cannot use #{colored.yellow repr(bit)} as a string interpolation value, since it's not an expression.", 0 lua\append bit_lua @@ -476,12 +915,12 @@ class NomsuCompiler @define_compile_action "lua> %code", (_code)=> if _code.type != "Text" - return Lua nil, "nomsu:run_lua(", _code\as_lua(nomsu), ");" + return Lua nil, "nomsu:run_lua(", nomsu\tree_to_lua(_code), ");" return add_lua_bits(Lua!, _code) @define_compile_action "=lua %code", (_code)=> if _code.type != "Text" - return Lua.Value nil, "nomsu:run_lua(", _code\as_lua(nomsu), ":as_statements('return '))" + return Lua.Value nil, "nomsu:run_lua(", nomsu\tree_to_lua(_code), ":as_statements('return '))" return add_lua_bits(Lua.Value!, _code) @define_compile_action "use %path", (_path)=> @@ -669,7 +1108,7 @@ OPTIONS -- Auto-format for input_file in all_files(input) tree = nomsu\parse(io.open(input_file)\read("*a")) - formatted = tostring(tree\as_nomsu!) + formatted = tostring(@tree_to_nomsu(tree)) if output_file output_file\write(formatted, "\n") output_file\flush! -- cgit v1.2.3