2016-01-14 16:01:49 -08:00
|
|
|
local insert = table.insert
|
|
|
|
|
2016-04-14 07:33:33 -07:00
|
|
|
local path = string.gsub(..., "[^.]+$", "")
|
|
|
|
local data = require(path.."data")
|
2016-04-20 17:47:58 -07:00
|
|
|
local Base = require(path.."Base")
|
2016-04-14 07:33:33 -07:00
|
|
|
local Token = require(path.."Token")
|
2016-04-20 17:47:58 -07:00
|
|
|
local Statement = require(path.."Statement")
|
2016-01-14 16:01:49 -08:00
|
|
|
|
2016-04-10 05:08:41 -07:00
|
|
|
local abs = math.abs
|
|
|
|
|
|
|
|
local function signs(s)
|
|
|
|
local start, end_ = s:find('[+-]+')
|
|
|
|
if start ~= 1 then
|
|
|
|
return 0
|
|
|
|
end
|
|
|
|
if s:sub(1, 1) == '+' then
|
|
|
|
return end_
|
|
|
|
elseif s:sub(1, 1) == '-' then
|
|
|
|
return -end_
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-04-20 17:47:58 -07:00
|
|
|
local function unsigns(n)
|
|
|
|
if n > 0 then
|
|
|
|
return string.rep('+', n)
|
|
|
|
elseif n < 0 then
|
|
|
|
return string.rep('-', -n)
|
|
|
|
else
|
|
|
|
return ''
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-04-10 05:08:41 -07:00
|
|
|
local function RelativeLabel(index, name)
|
|
|
|
return {
|
|
|
|
index = index,
|
|
|
|
name = name,
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2016-04-20 17:47:58 -07:00
|
|
|
local Preproc = Base:extend()
|
2016-01-14 16:01:49 -08:00
|
|
|
function Preproc:init(options)
|
|
|
|
self.options = options or {}
|
|
|
|
end
|
|
|
|
|
2016-04-20 17:47:58 -07:00
|
|
|
function Preproc:error(msg, got)
|
2016-04-20 20:51:26 -07:00
|
|
|
if got ~= nil then
|
2016-04-20 17:47:58 -07:00
|
|
|
msg = msg..', got '..tostring(got)
|
2016-01-14 16:01:49 -08:00
|
|
|
end
|
2016-04-20 17:47:58 -07:00
|
|
|
error(('%s:%d: Error: %s'):format(self.fn, self.line, msg), 2)
|
|
|
|
end
|
2016-01-14 16:01:49 -08:00
|
|
|
|
2016-04-20 17:47:58 -07:00
|
|
|
function Preproc:lookup(t)
|
|
|
|
if t.tt == 'VARSYM' then
|
|
|
|
local name = t.tok
|
|
|
|
t.tt = 'NUM'
|
|
|
|
t.tok = self.variables[name]
|
|
|
|
if t.tok == nil then
|
|
|
|
self:error('undefined variable', name)
|
|
|
|
end
|
|
|
|
elseif self.do_labels and t.tt == 'RELLABELSYM' or t.tt == 'RELLABEL' then
|
2016-01-14 17:08:56 -08:00
|
|
|
if t.tt == 'RELLABEL' then
|
2016-01-14 16:01:49 -08:00
|
|
|
t.tt = 'LABEL'
|
|
|
|
-- exploits the fact that user labels can't begin with a number
|
2016-04-10 05:08:41 -07:00
|
|
|
local name = t.tok:sub(2)
|
2016-04-20 17:47:58 -07:00
|
|
|
t.tok = tostring(self.i)..name
|
2016-01-14 16:01:49 -08:00
|
|
|
elseif t.tt == 'RELLABELSYM' then
|
2016-04-20 17:47:58 -07:00
|
|
|
local i = self.i
|
2016-01-14 16:01:49 -08:00
|
|
|
t.tt = 'LABELSYM'
|
|
|
|
|
2016-04-10 05:08:41 -07:00
|
|
|
local rel = signs(t.tok)
|
2016-04-20 17:47:58 -07:00
|
|
|
assert(rel ~= 0, 'Internal Error: relative label without signs')
|
|
|
|
|
2016-04-10 05:08:41 -07:00
|
|
|
local name = t.tok:sub(abs(rel) + 1)
|
2016-01-14 16:01:49 -08:00
|
|
|
local seen = 0
|
2016-04-10 05:08:41 -07:00
|
|
|
|
2016-04-20 17:47:58 -07:00
|
|
|
-- TODO: don't iterate over *every* label, just the ones nearby.
|
|
|
|
-- we could do this by popping labels as we pass over them.
|
|
|
|
-- (would need to iterate once forwards and once backwards
|
|
|
|
-- for plus and minus labels respectively)
|
2016-01-14 16:01:49 -08:00
|
|
|
if rel > 0 then
|
2016-04-20 17:47:58 -07:00
|
|
|
for _, rl in ipairs(self.plus_labels) do
|
2016-04-10 05:08:41 -07:00
|
|
|
if rl.name == name and rl.index > i then
|
2016-01-14 16:01:49 -08:00
|
|
|
seen = seen + 1
|
|
|
|
if seen == rel then
|
2016-04-10 05:08:41 -07:00
|
|
|
t.tok = tostring(rl.index)..name
|
2016-01-14 16:01:49 -08:00
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
else
|
2016-04-20 17:47:58 -07:00
|
|
|
for _, rl in ipairs(self.minus_labels) do
|
2016-04-10 05:08:41 -07:00
|
|
|
if rl.name == name and rl.index < i then
|
2016-01-14 16:01:49 -08:00
|
|
|
seen = seen - 1
|
|
|
|
if seen == rel then
|
2016-04-10 05:08:41 -07:00
|
|
|
t.tok = tostring(rl.index)..name
|
2016-01-14 16:01:49 -08:00
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2016-01-14 17:08:56 -08:00
|
|
|
|
2016-01-14 16:01:49 -08:00
|
|
|
if seen ~= rel then
|
2016-04-20 17:47:58 -07:00
|
|
|
self:error('could not find appropriate relative label', unsigns(rel))
|
2016-01-14 16:01:49 -08:00
|
|
|
end
|
|
|
|
end
|
2016-04-20 17:47:58 -07:00
|
|
|
else
|
|
|
|
return false
|
2016-01-14 16:01:49 -08:00
|
|
|
end
|
2016-04-20 17:47:58 -07:00
|
|
|
return true
|
|
|
|
end
|
2016-01-14 16:01:49 -08:00
|
|
|
|
2016-04-20 17:47:58 -07:00
|
|
|
function Preproc:check(s, i, tt)
|
|
|
|
local t = s[i]
|
|
|
|
if t == nil then
|
|
|
|
self:error("expected another argument")
|
|
|
|
end
|
|
|
|
|
|
|
|
self.fn = t.fn
|
|
|
|
self.line = t.line
|
|
|
|
|
|
|
|
if t.tt ~= tt then
|
|
|
|
self:lookup(t)
|
|
|
|
end
|
|
|
|
|
|
|
|
if t.tt ~= tt then
|
|
|
|
local err = ("argument %i of %s expected type %s"):format(i, s.type, tt)
|
|
|
|
self:error(err, t.tt)
|
|
|
|
end
|
|
|
|
return t.tok
|
|
|
|
end
|
|
|
|
|
|
|
|
function Preproc:process(statements)
|
|
|
|
self.statements = statements
|
|
|
|
|
|
|
|
self.variables = {}
|
|
|
|
self.plus_labels = {} -- constructed forwards
|
|
|
|
self.minus_labels = {} -- constructed backwards
|
|
|
|
self.do_labels = false
|
|
|
|
|
|
|
|
-- first pass: resolve variables and collect relative labels
|
|
|
|
local new_statements = {}
|
|
|
|
for i=1, #self.statements do
|
|
|
|
local s = self.statements[i]
|
|
|
|
self.fn = s.fn
|
|
|
|
self.line = s.line
|
|
|
|
if s.type:sub(1, 1) == '!' then
|
|
|
|
-- directive, label, etc.
|
|
|
|
if s.type == '!VAR' then
|
|
|
|
local a = self:check(s, 1, 'VAR')
|
|
|
|
local b = self:check(s, 2, 'NUM')
|
|
|
|
self.variables[a] = b
|
|
|
|
elseif s.type == '!LABEL' then
|
|
|
|
if s[1].tt == 'RELLABEL' then
|
|
|
|
local label = s[1].tok
|
|
|
|
local rl = RelativeLabel(#new_statements + 1, label:sub(2))
|
|
|
|
local c = label:sub(1, 1)
|
|
|
|
if c == '+' then
|
|
|
|
insert(self.plus_labels, rl)
|
|
|
|
elseif c == '-' then
|
|
|
|
insert(self.minus_labels, 1, rl) -- remember, it's backwards
|
|
|
|
else
|
|
|
|
error('Internal Error: unexpected token for relative label')
|
|
|
|
end
|
2016-01-15 11:15:02 -08:00
|
|
|
end
|
2016-04-20 17:47:58 -07:00
|
|
|
insert(new_statements, s)
|
2016-04-20 20:51:26 -07:00
|
|
|
else
|
|
|
|
insert(new_statements, s)
|
2016-01-15 11:15:02 -08:00
|
|
|
end
|
|
|
|
else
|
2016-04-20 17:47:58 -07:00
|
|
|
-- regular instruction
|
|
|
|
for j, t in ipairs(s) do
|
|
|
|
self:lookup(t)
|
|
|
|
end
|
|
|
|
insert(new_statements, s)
|
2016-01-15 11:15:02 -08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-04-20 17:47:58 -07:00
|
|
|
-- second pass: resolve relative labels
|
|
|
|
self.do_labels = true
|
|
|
|
for i=1, #new_statements do
|
|
|
|
self.i = i -- make visible to :lookup
|
|
|
|
local s = new_statements[i]
|
|
|
|
self.fn = s.fn
|
|
|
|
self.line = s.line
|
|
|
|
for j, t in ipairs(s) do
|
|
|
|
self:lookup(t)
|
|
|
|
end
|
|
|
|
end
|
2016-01-14 17:08:56 -08:00
|
|
|
|
2016-04-20 17:47:58 -07:00
|
|
|
self.statements = new_statements
|
|
|
|
return self.statements
|
2016-01-14 16:01:49 -08:00
|
|
|
end
|
|
|
|
|
|
|
|
return Preproc
|