1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
{:insert, :remove, :concat} = table
immutable = require 'immutable'
local Lua, Location
export LINE_STARTS
Location = immutable {"filename","start","stop"}, {
name:"Location"
__new: (filename, start, stop)=>
--assert(type(filename) == 'string' and type(start) == 'number' and type(stop) == 'number')
return filename, start, stop or start
__tostring: => "Location(\"#{@filename}\", #{@start}, #{@stop})"
__lt: (other)=>
assert(@filename == other.filename, "Cannot compare sources from different files")
return if @start == other.start
@stop < other.stop
else @start < other.start
__le: (other)=>
assert(@filename == other.filename, "Cannot compare sources from different files")
return if @start == other.start
@stop <= other.stop
else @start <= other.start
get_text: => FILE_CACHE[@filename]\sub(@start,@stop)
get_line_number: =>
-- TODO: do a binary search if this is actually slow, which I doubt
src = FILE_CACHE[@filename]
line_starts = LINE_STARTS[src]
start_line = 1
while (line_starts[start_line+1] or math.huge) <= @start
start_line += 1
stop_line = start_line
while (line_starts[stop_line+1] or math.huge) <= @stop
stop_line += 1
return start_line, stop_line
get_line: => "#{@filename}:#{@get_line_number!}"
get_line_range: =>
start_line, stop_line = @get_line_number!
return if stop_line == start_line
"#{@filename}:#{start_line}"
else "#{@filename}:#{start_line}-#{stop_line}"
}
class Lua
new: (@source, ...)=>
if type(@source) == 'string'
filename,start,stop = @source\match("^(.-)[(%d+):(%d+)]$")
@source = Location(filename, tonumber(start), tonumber(stop))
@bits = {...}
@free_vars = {}
@is_value = false
@Value = (...)->
lua = Lua(...)
lua.is_value = true
return lua
clone: =>
copy = Lua(@source, {unpack(@bits)})
copy.is_value = @is_value
for k,v in pairs @free_vars
copy.free_vars[k] = v
return copy
add_free_vars: (...)=>
seen = {[v]:true for v in *@free_vars}
for i=1,select("#",...)
var = select(i, ...)
unless seen[var]
@free_vars[#@free_vars+1] = var
seen[var] = true
convert_to_statements: (prefix="", suffix=";")=>
unless @is_value
return
if prefix != ""
@prepend prefix
if suffix != ""
@append suffix
declare_locals: (skip={})=>
if next(skip) == 1
skip = {[s]:true for s in *skip}
if #@free_vars > 0
@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 i,b in ipairs @bits
buff[#buff+1] = tostring(b)
if i < #@bits and type(b) != 'string' and not b.is_value
buff[#buff+1] = "\n"
ret = concat(buff, "")
return ret
__len: =>
len = 0
for b in *@bits
len += #b
return len
__add: (other)=>
Lua(nil, self, other)
__concat: (other)=>
Lua(nil, self, other)
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.filename
lua_filename:lua_chunkname, lua_file:lua_str
lua_to_nomsu: {}, nomsu_to_lua: {}
}
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.source, Location(lua_chunkname, 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
parenthesize: =>
if @is_value
@prepend "("
@append ")"
else
error "Cannot parenthesize lua statements"
return {:Lua, :Location}
|