From 54fc7fc4404c02df2c38a7ae121e61e9b8bca78c Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 17 Apr 2018 14:18:23 -0700 Subject: Moving tree_to_lua into each of the Nomsu tree types, which are now in their own file. --- nomsu.moon | 296 +++++++------------------------------------------------------ 1 file changed, 33 insertions(+), 263 deletions(-) (limited to 'nomsu.moon') diff --git a/nomsu.moon b/nomsu.moon index d22b891..21263ac 100755 --- a/nomsu.moon +++ b/nomsu.moon @@ -18,12 +18,14 @@ lpeg.setmaxstack 10000 utils = require 'utils' new_uuid = require 'uuid' immutable = require 'immutable' +export Tuple +Tuple = immutable(nil, {name:"Tuple"}) {:repr, :stringify, :min, :max, :equivalent, :set, :is_list, :sum} = utils colors = setmetatable({}, {__index:->""}) colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..tostring(msg or '')..colors.reset)}) {:insert, :remove, :concat} = table debug_getinfo = debug.getinfo -{:Lua, :LuaValue, :Location} = require "lua_obj" +{:Lua, :Location} = require "lua_obj" -- TODO: -- consider non-linear codegen, rather than doing thunks for things like comprehensions @@ -88,16 +90,7 @@ do -- Can't use this because it breaks some LPEG stuff --STRING_METATABLE.__mul = (other)=> string.rep(@, other) -Types = {} -type_tostring = => - "#{@name}(#{repr(@value)})" -type_with_value = (value)=> getmetatable(self)(@source, value) -Tuple = immutable(nil, {name:"Tuple"}) -for t in *{"File", "Nomsu", "Block", "List", "FunctionCall", "Text", "Dict", "Number", "Word", "Var", "Comment", "IndexChain"} - Types[t] = immutable({"value","source"}, {type:t, name:t, __tostring:type_tostring, with_value:type_with_value}) -Types.DictEntry = immutable({"key","value"}, {name:"DictEntry"}) -Types.is_node = (n)-> - type(n) == 'userdata' and n.type +Types = require "nomsu_tree" NOMSU_DEFS = with {} -- Newline supports either windows-style CR+LF or unix-style LF @@ -218,7 +211,6 @@ class NomsuCompiler for k,v in pairs(Types) do @environment[k] = v @environment.Tuple = Tuple @environment.Lua = Lua - @environment.LuaValue = LuaValue @environment.Location = Location @environment.ACTIONS = setmetatable({}, {__index:(key)=> error("Attempt to run undefined action: #{key}", 0) @@ -305,7 +297,8 @@ class NomsuCompiler tree = @parse(nomsu_code, filename) assert tree, "Failed to parse: #{nomsu_code}" assert tree.type == "File", "Attempt to run non-file: #{tree.type}" - lua = @tree_to_lua(tree)\as_statements! + lua = @tree_to_lua(tree) + lua\convert_to_statements! lua\declare_locals! lua\prepend "-- File: #{filename}\n" return @run_lua(lua, filename..".lua") @@ -404,7 +397,7 @@ class NomsuCompiler if #tok.value > 1 then return nil nomsu = inline_expression tok.value return nomsu and "(: #{nomsu})" - when "FunctionCall" + when "Action" buff = "" for i,bit in ipairs tok.value if bit.type == "Word" @@ -416,7 +409,7 @@ class NomsuCompiler return nil unless nomsu unless i == 1 or bit.type == "Block" buff ..= " " - buff ..= if bit.type == "FunctionCall" + buff ..= if bit.type == "Action" "("..nomsu..")" else nomsu return buff @@ -441,7 +434,7 @@ class NomsuCompiler bit.key.value else inline_expression bit.key return nil unless key_nomsu - if bit.key.type == "FunctionCall" + if bit.key.type == "Action" key_nomsu = "("..key_nomsu..")" value_nomsu = inline_expression bit.value return nil unless value_nomsu @@ -482,7 +475,7 @@ class NomsuCompiler return nil unless nomsu buff ..= "\n "..@indent(nomsu) return buff - when "FunctionCall" + when "Action" nomsu = expression(tok) return nil unless nomsu return "(..)\n "..@indent(nomsu) @@ -515,7 +508,7 @@ class NomsuCompiler for bit in *tok.value key_nomsu = inline_expression bit.key return nil unless key_nomsu - if bit.key.type == "FunctionCall" + if bit.key.type == "Action" key_nomsu = "("..key_nomsu..")" value_nomsu = inline_expression bit.value if value_nomsu and #key_nomsu + #value_nomsu < max_line @@ -560,14 +553,14 @@ class NomsuCompiler switch tok.type when "Block" if #tok.value == 1 - nomsu = if tok.value[1].type == "FunctionCall" + nomsu = if tok.value[1].type == "Action" inline_expression(tok.value[1]) else noeol_expression(tok.value[1]) if nomsu and #(nomsu\match("[^\n]*")) < max_line return ": "..nomsu return noeol_expression(tok) - when "FunctionCall" + when "Action" -- The hard task buff = "" for i,bit in ipairs tok.value @@ -579,12 +572,12 @@ class NomsuCompiler else nomsu = inline_expression(bit) if nomsu and #nomsu < max_line - if bit.type == "FunctionCall" + if bit.type == "Action" nomsu = "("..nomsu..")" else nomsu = expression(bit) return nil unless nomsu - if bit.type == "FunctionCall" + if bit.type == "Action" nomsu = "(..)\n "..@indent(nomsu) if i < #tok.value nomsu ..= "\n.." @@ -636,238 +629,14 @@ class NomsuCompiler else error("Unsupported value_to_nomsu type: #{type(value)}", 0) - @math_patt: re.compile [[ "%" (" " [*/^+-] " %")+ ]] tree_to_lua: (tree)=> - -- Return , - assert tree, "No tree provided." - if not Types.is_node(tree) - --error("Invalid tree: #{repr(tree)}", 0) - error("Invalid tree") - switch tree.type - when "File" - if #tree.value == 1 - return @tree_to_lua(tree.value[1]) - file_lua = Lua(tree.source) - declared_locals = {} - for i, line in ipairs tree.value - line_lua = @tree_to_lua line - if not line_lua - error("No lua produced by #{repr line}", 0) - line_lua = line_lua\as_statements! - if i < #tree.value - file_lua\append "\n" - - file_lua\append line_lua - file_lua\declare_locals! - return file_lua - - when "Comment" - return Lua(tree.source, "--"..tree.value\gsub("\n","\n--").."\n") - - when "Nomsu" - --return Lua(tree.source, repr(tree.value)) - return Lua(tree.source, "nomsu:parse(",tree.source\get_text!,", ",repr(tree.source.filename),")") - - when "Block" - block_lua = Lua(tree.source) - for i,arg in ipairs tree.value - lua = @tree_to_lua arg - if i == 1 and #tree.value == 1 and lua.is_value - return lua - lua = lua\as_statements! - if i < #tree.value - block_lua\append "\n" - block_lua\append lua - return block_lua - - when "FunctionCall" - insert @compilestack, tree - - stub = @tree_to_stub tree - action = rawget(@environment.ACTIONS, stub) - - metadata = @action_metadata[action] - if metadata and metadata.compile_time - args = [arg for arg in *tree.value when arg.type != "Word"] - -- Force all compile-time actions to take a tree location - if metadata and metadata.arg_orders - new_args = [args[p-1] for p in *metadata.arg_orders[stub]] - args = new_args - lua = action(tree, unpack(args)) - remove @compilestack - return lua - elseif not metadata and @@math_patt\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. - lua = LuaValue(tree.source) - for i,tok in ipairs tree.value - if tok.type == "Word" - lua\append tok.value - else - tok_lua = @tree_to_lua(tok) - unless tok_lua.is_value - src = tok.source\get_text! - error("non-expression value inside math expression: #{colored.yellow src}") - lua\append tok_lua - if i < #tree.value - lua\append " " - remove @compilestack - lua\parenthesize! - return lua - - args = {} - for i, tok in ipairs tree.value - if tok.type == "Word" then continue - arg_lua = @tree_to_lua(tok) - unless arg_lua.is_value - line, src = tok.source\get_line!, tok.source\get_text! - error "#{line}: Cannot use:\n#{colored.yellow src}\nas an argument to #{stub}, since it's not an expression, it produces: #{repr arg_lua}", 0 - insert args, arg_lua - - if metadata and metadata.arg_orders - args = [args[p] for p in *metadata.arg_orders[stub]] - - lua = LuaValue(tree.source, "ACTIONS[#{repr stub}](") - for i, arg in ipairs args - lua\append arg - if i < #args then lua\append ", " - lua\append ")" - remove @compilestack - return lua - - when "Text" - lua = LuaValue(tree.source) - string_buffer = "" - for bit in *tree.value - 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 - line, src = bit.source\get_line!, bit.source\get_text! - error "#{line}: Cannot use #{colored.yellow 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 = LuaValue(bit.source, "stringify(",bit_lua,")") - lua\append bit_lua + return tree\as_lua(self) - 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 "IndexChain" - lua = @tree_to_lua tree.value[1] - unless lua.is_value - line, src = tree.value[1].source\get_line!, tree.value[1].source\get_text! - error "#{line}: Cannot index #{colored.yellow src}, since it's not an expression.", 0 - last_char = tostring(lua)\sub(1,1) - if last_char == "}" or last_char == '"' - lua\parenthesize! - - for i=2,#tree.value - key = tree.value[i] - if key.type == 'Text' and #key.value == 1 and type(key.value[1]) == 'string' and key.value[1]\match("^[a-zA-Z_][a-zA-Z0-9_]$") - lua\append ".#{key.value[1]}" - continue - key_lua = @tree_to_lua key - unless key_lua.is_value - line, src = key.source\get_line!, key.source\get_text! - error "#{line}: Cannot use #{colored.yellow src} as an index, since it's not an expression.", 0 - -- 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"] - if tostring(key_lua)\sub(1,1) == '[' - lua\append "[ ",key_lua,"]" - else - lua\append "[",key_lua,"]" - return lua - - when "List" - lua = LuaValue tree.source, "{" - line_length = 0 - for i, item in ipairs tree.value - item_lua = @tree_to_lua item - unless item_lua.is_value - line, src = item.source\get_line!, item.source\get_text! - error "#{line}: Cannot use #{colored.yellow src} as a list item, since it's not an expression.", 0 - lua\append item_lua - newlines, last_line = tostring(item_lua)\match("^(.-)([^\n]*)$") - if #newlines > 0 - line_length = #last_line - else - line_length += #last_line - if i < #tree.value - if line_length >= 80 - lua\append ",\n" - line_length = 0 - else - lua\append ", " - line_length += 2 - lua\append "}" - return lua - - when "Dict" - lua = LuaValue tree.source, "{" - line_length = 0 - for i, entry in ipairs tree.value - key_lua = @tree_to_lua entry.key - unless key_lua.is_value - line, src = key.source\get_line!, key.source\get_text! - error "#{line}: Cannot use #{colored.yellow src} as a dict key, since it's not an expression.", 0 - value_lua = @tree_to_lua entry.value - unless value_lua.is_value - line, src = value.source\get_line!, value.source\get_text! - error "#{line}: Cannot use #{colored.yellow src} as a dict value, since it's not an expression.", 0 - key_str = tostring(key_lua)\match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=]) - if key_str - lua\append 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\append "[ ",key_lua,"]=",value_lua - else - lua\append "[",key_lua,"]=",value_lua - - -- TODO: maybe make this more accurate? It's only a heuristic, so eh... - newlines, last_line = ("[#{key_lua}=#{value_lua}")\match("^(.-)([^\n]*)$") - if #newlines > 0 - line_length = #last_line - else - line_length += #last_line - if i < #tree.value - if line_length >= 80 - lua\append ",\n" - line_length = 0 - else - lua\append ", " - line_length += 2 - lua\append "}" - return lua - - when "Number" - return LuaValue(tree.source, tostring(tree.value)) - - when "Var" - return LuaValue(tree.source, @var_to_lua_identifier(tree.value)) - - else - error("Unknown/unimplemented thingy: #{tree.type}", 0) - walk_tree: (tree, depth=0)=> coroutine.yield(tree, depth) return unless Types.is_node(tree) switch tree.type - when "List", "File", "Block", "FunctionCall", "Text", "IndexChain" + when "List", "File", "Block", "Action", "Text", "IndexChain" for v in *tree.value @walk_tree(v, depth+1) when "Dict" @@ -919,7 +688,7 @@ class NomsuCompiler if replacement != nil return replacement switch tree.type - when "File", "Nomsu", "Block", "List", "FunctionCall", "Text", "IndexChain" + when "File", "Nomsu", "Block", "List", "Action", "Text", "IndexChain" new_values, is_changed = {}, false for i,old_value in ipairs(tree.value) new_value = type(old_value) != "string" and @tree_map(old_value, fn) or nil @@ -956,11 +725,11 @@ class NomsuCompiler return replacements[id] tree_to_stub: (tree)=> - if tree.type != "FunctionCall" then error "Tried to get stub from non-functioncall tree: #{tree.type}", 0 + if tree.type != "Action" then error "Tried to get stub from non-functioncall tree: #{tree.type}", 0 return concat([(t.type == "Word" and t.value or "%") for t in *tree.value], " ") tree_to_named_stub: (tree)=> - if tree.type != "FunctionCall" then error "Tried to get stub from non-functioncall tree: #{tree.type}", 0 + if tree.type != "Action" then error "Tried to get stub from non-functioncall tree: #{tree.type}", 0 return concat([(t.type == "Word" and t.value or "%#{t.value}") for t in *tree.value], " ") stub_defs = { @@ -1009,16 +778,17 @@ class NomsuCompiler get_line_no = -> "nomsu.moon:#{debug_getinfo(2).currentline}" nomsu = self @define_compile_action "immediately %block", get_line_no!, (_block)=> - lua = nomsu\tree_to_lua(_block)\as_statements! + lua = nomsu\tree_to_lua(_block) + lua\convert_to_statements! lua\declare_locals! nomsu\run_lua(lua) return Lua(@source, "if IMMEDIATE then\n", lua, "\nend") - @define_compile_action "Lua %code", get_line_no!, (_code)=> + @define_compile_action "Lua %source %code", get_line_no!, (_source, _code)=> if _code.type != "Text" - return LuaValue(@source, "Lua(", repr(_code.source),", ",nomsu\tree_to_lua(_code),")") + return Lua.Value(_source, "Lua(", repr(_code.source),", ",nomsu\tree_to_lua(_code),")") - lua = LuaValue(@source, "Lua(", repr(_code.source)) + lua = Lua.Value(_source, "Lua(", repr(_code.source)) for bit in *_code.value lua\append ", " if type(bit) == "string" @@ -1032,11 +802,11 @@ class NomsuCompiler lua\append ")" return lua - @define_compile_action "LuaValue %code", get_line_no!, (_code)=> + @define_compile_action "Lua value %source %code", get_line_no!, (_source, _code)=> if _code.type != "Text" - return LuaValue(@source, "LuaValue(", repr(_code.source),", ",nomsu\tree_to_lua(_code),")") + return Lua.Value(_source, "Lua.Value(", repr(_code.source),", ",nomsu\tree_to_lua(_code),")") - lua = LuaValue(@source, "LuaValue(", repr(_code.source)) + lua = Lua.Value(_source, "Lua.Value(", repr(_code.source)) for bit in *_code.value lua\append ", " if type(bit) == "string" @@ -1052,7 +822,7 @@ class NomsuCompiler @define_compile_action "lua> %code", get_line_no!, (_code)=> if _code.type != "Text" - return LuaValue(@source, "nomsu:run_lua(",nomsu\tree_to_lua(_code),")") + return Lua.Value(@source, "nomsu:run_lua(",nomsu\tree_to_lua(_code),")") lua = Lua(_code.source) for bit in *_code.value @@ -1068,9 +838,9 @@ class NomsuCompiler @define_compile_action "=lua %code", get_line_no!, (_code)=> if _code.type != "Text" - return LuaValue(@source, "nomsu:run_lua(",nomsu\tree_to_lua(_code),")") + return Lua.Value(@source, "nomsu:run_lua(",nomsu\tree_to_lua(_code),")") - lua = LuaValue(@source) + lua = Lua.Value(@source) for bit in *_code.value if type(bit) == "string" lua\append bit @@ -1083,7 +853,7 @@ class NomsuCompiler return lua @define_compile_action "!! code location !!", get_line_no!, => - return LuaValue(@source, repr(@source)) + return Lua.Value(@source, repr(tostring(@source))) @define_action "run file %filename", get_line_no!, (_filename)-> return nomsu\run_file(_filename) @@ -1091,7 +861,7 @@ class NomsuCompiler @define_compile_action "use %filename", get_line_no!, (_filename)=> filename = nomsu\tree_to_value(_filename) nomsu\use_file(filename) - return LuaValue(@source, "nomsu:use_file(#{repr filename})") + return Lua.Value(@source, "nomsu:use_file(#{repr filename})") -- 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 -- cgit v1.2.3