{:insert, :remove, :concat} = table immutable = require 'immutable' local Lua, LuaValue, Location export LINE_STARTS Location = immutable {"text_name","text","start","stop"}, { name:"Location" __new: (text_name, text, start, stop)=> text_name, text, start, stop or start __tostring: => "#{@text_name}[#{@start}:#{@stop}]" __lt: (other)=> assert(@text == other.text, "Cannot compare sources from different texts") return if @start == other.start @stop < other.stop else @start < other.start __le: (other)=> assert(@text == other.text, "Cannot compare sources from different texts") return if @start == other.start @stop <= other.stop else @start <= other.start get_text: => @text\sub(@start,@stop) get_line_number: => -- TODO: do a binary search if this is actually slow, which I doubt line_starts = LINE_STARTS[@text] start_line = 1 while (line_starts[start_line+1] or (#src+1)) <= @start start_line += 1 stop_line = start_line while (line_starts[stop_line+1] or (#src+1)) <= @stop stop_line += 1 return start_line, stop_line get_line: => "#{@text_name}:#{@get_line_number}" get_line_range: => start_line, stop_line = @get_line_number return if stop_line == start_line "#{text_name}:#{start_line}" else "#{text_name}:#{start_line}-#{stop_line}" } class Lua is_statement: true is_value: false new: (@source, ...)=> @bits = {...} @free_vars = {} add_free_vars: (free_vars)=> seen = {[v]:true for v in *@free_vars} for var in *free_vars unless seen[var] @free_vars[#@free_vars+1] = var seen[var] = true as_statements: => self declare_locals: (skip={})=> if next(skip) == 1 skip = {[s]:true for s in *skip} @prepend "local #{concat @free_vars, ", "};\n" for var in *@free_vars do skip[var] = true for bit in *@bits if type(bit) == Lua bit\declare_locals(skip) __tostring: => buff = {} for b in *@bits buff[#buff+1] = tostring(b) return concat(buff, "") __len: => len = 0 for b in *@bits len += #b return len append: (...)=> n = select("#",...) bits = @bits for i=1,n bits[#bits+1] = select(i, ...) prepend: (...)=> n = select("#",...) bits = @bits for i=#bits+n,n+1,-1 bits[i] = bits[i-n] for i=1,n bits[i] = select(i, ...) make_offset_table: (lua_chunkname)=> -- Return a mapping from output (lua) character number to input (nomsu) character number lua_str = tostring(self) metadata = { nomsu_filename:@source.text_name, nomsu_file:@source.text, lua_filename:lua_chunkname, lua_file:lua_str lua_to_nomsu: {}, nomsu_to_lua: {} } metadata, lua_offset = {}, 1 lua_offset = 1 walk = (lua)-> if type(lua) == 'string' lua_offset += #lua else lua_start = lua_offset for b in *lua.bits walk b lua_stop = lua_offset nomsu_src, lua_src = lua.souce, Location(lua_chunkname, lua_str, lua_start, lua_stop) metadata.lua_to_nomsu[lua_src] = nomsu_src metadata.nomsu_to_lua[nomsu_src] = lua_src walk self return lua_str, metadata class LuaValue extends Lua is_statement: false is_value: true new: (@source, ...)=> @bits = {...} as_statements: => bits = {unpack @bits} bits[#bits+1] = ";" return Lua(@source, bits) parenthesize: => @prepend "(" @append ")" return {:Lua, :LuaValue, :Location}