aboutsummaryrefslogtreecommitdiff
path: root/nomsu_tree.moon
diff options
context:
space:
mode:
Diffstat (limited to 'nomsu_tree.moon')
-rw-r--r--nomsu_tree.moon269
1 files changed, 269 insertions, 0 deletions
diff --git a/nomsu_tree.moon b/nomsu_tree.moon
new file mode 100644
index 0000000..3162bf8
--- /dev/null
+++ b/nomsu_tree.moon
@@ -0,0 +1,269 @@
+-- This file contains the datastructures used to represent parsed Nomsu syntax trees,
+-- as well as the logic for converting them to Lua code.
+utils = require 'utils'
+re = require 're'
+{:repr, :stringify, :min, :max, :equivalent, :set, :is_list, :sum} = utils
+immutable = require 'immutable'
+{:insert, :remove, :concat} = table
+{:Lua, :Location} = require "lua_obj"
+
+
+common_methods = {
+ __tostring: =>
+ "#{@name}(#{repr(@value)})"
+ with_value: (value)=> getmetatable(self)(value, @source)
+}
+fields = {"value","source"}
+
+Types = {}
+Types.DictEntry = immutable({"key","value"}, {name:"DictEntry"})
+Types.is_node = (n)->
+ type(n) == 'userdata' and getmetatable(n) and Types[n.type] == getmetatable(n)
+
+-- Helper method:
+Tree = (name, methods)->
+ with methods
+ .__tostring = => "#{@name}(#{repr(@value)})"
+ .with_value = (value)=> getmetatable(self)(value, @source)
+ .type = name
+ .name = name
+
+ Types[name] = immutable {"value","source"}, methods
+
+
+Tree "File",
+ as_lua: (nomsu)=>
+ if #@value == 1
+ return @value[1]\as_lua(nomsu)
+ declared_locals = {}
+ lua = Lua(@source)
+ for i, line in ipairs @value
+ line_lua = line\as_lua(nomsu)
+ if not line_lua
+ error("No lua produced by #{repr line}", 0)
+ if i < #@value
+ lua\append "\n"
+ lua\convert_to_statements!
+ lua\append line_lua
+ lua\declare_locals!
+ return lua
+
+Tree "Nomsu",
+ as_lua: (nomsu)=>
+ Lua.Value(@source, "nomsu:parse(",repr(@source\get_text!),", ",repr(@source.filename),")")
+
+Tree "Block",
+ as_lua: (nomsu)=>
+ if #@value == 1
+ return @value[1]\as_lua(nomsu)
+ lua = Lua(@source)
+ for i,line in ipairs @value
+ line_lua = line\as_lua(nomsu)
+ if i < #@value
+ lua\append "\n"
+ line_lua\convert_to_statements!
+ lua\append line_lua
+ return lua
+
+math_patt = re.compile [[ "%" (" " [*/^+-] " %")+ ]]
+Tree "Action",
+ as_lua: (nomsu)=>
+ stub = @get_stub!
+ action = rawget(nomsu.environment.ACTIONS, stub)
+ metadata = nomsu.action_metadata[action]
+ if metadata and metadata.compile_time
+ args = [arg for arg in *@value when arg.type != "Word"]
+ -- Force all compile-time actions to take a tree location
+ if metadata.arg_orders
+ new_args = [args[p-1] for p in *metadata.arg_orders[stub]]
+ args = new_args
+ return action(Lua(@source), unpack(args))
+
+ lua = Lua.Value(@source)
+ if 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.
+ for i,tok in ipairs @value
+ if tok.type == "Word"
+ lua\append tok.value
+ else
+ tok_lua = tok\as_lua(nomsu)
+ 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 < #@value
+ lua\append " "
+ lua\parenthesize!
+ return lua
+
+ args = {}
+ for i, tok in ipairs @value
+ if tok.type == "Word" then continue
+ arg_lua = tok\as_lua(nomsu)
+ 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]]
+
+ -- 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
+
+ get_stub: (include_names=false)=>
+ bits = if include_names
+ [(t.type == "Word" and t.value or "%#{t.value}") for t in *@value]
+ else [(t.type == "Word" and t.value or "%") for t in *@value]
+ return concat(bits, " ")
+
+
+Tree "Text",
+ as_lua: (nomsu)=>
+ lua = Lua.Value(@source)
+ string_buffer = ""
+ for bit in *@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 = bit\as_lua(nomsu)
+ 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 = Lua.Value(bit.source, "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
+
+Tree "List",
+ as_lua: (nomsu)=>
+ lua = Lua.Value @source, "{"
+ line_length = 0
+ for i, item in ipairs @value
+ item_lua = item\as_lua(nomsu)
+ 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 < #@value
+ if line_length >= 80
+ lua\append ",\n"
+ line_length = 0
+ else
+ lua\append ", "
+ line_length += 2
+ lua\append "}"
+ return lua
+
+Tree "Dict",
+ as_lua: (nomsu)=>
+ lua = Lua.Value @source, "{"
+ line_length = 0
+ for i, entry in ipairs @value
+ key_lua = entry.key\as_lua(nomsu)
+ 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 = entry.value\as_lua(nomsu)
+ 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 < #@value
+ if line_length >= 80
+ lua\append ",\n"
+ line_length = 0
+ else
+ lua\append ", "
+ line_length += 2
+ lua\append "}"
+ return lua
+
+Tree "IndexChain",
+ as_lua: (nomsu)=>
+ lua = @value[1]\as_lua(nomsu)
+ unless lua.is_value
+ line, src = @value[1].source\get_line!, @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 == '"' or last_char == "]"
+ lua\parenthesize!
+
+ for i=2,#@value
+ key = @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 = key\as_lua(nomsu)
+ 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
+
+Tree "Number",
+ as_lua: (nomsu)=>
+ Lua.Value(@source, tostring(@value))
+
+Tree "Var",
+ as_lua: (nomsu)=>
+ lua_id = "_"..(@value\gsub "%W", (verboten)->
+ if verboten == "_" then "__" else ("_%x")\format(verboten\byte!))
+ Lua.Value(@source, lua_id)
+
+Tree "Word",
+ as_lua: (nomsu)=>
+ error("Attempt to convert Word to lua")
+
+Tree "Comment",
+ as_lua: (nomsu)=>
+ Lua(@source, "--"..@value\gsub("\n","\n--").."\n")
+
+return Types