1
0
Fork 0
mirror of https://github.com/notwa/mm synced 2024-11-05 05:39:02 -08:00
mm/Lua/lib/lips/Preproc.lua

245 lines
6.7 KiB
Lua
Raw Normal View History

2016-01-13 11:20:28 -08:00
local insert = table.insert
2016-04-21 13:04:49 -07:00
local path = string.gsub(..., "[^.]+$", "")
local data = require(path.."data")
local overrides = require(path.."overrides")
local Statement = require(path.."Statement")
local Reader = require(path.."Reader")
2016-01-13 11:20:28 -08:00
2016-04-10 06:07:06 -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-21 13:04:49 -07:00
local Preproc = Reader:extend()
2016-01-13 11:20:28 -08:00
function Preproc:init(options)
self.options = options or {}
end
2016-04-21 13:04:49 -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)
2016-04-10 06:07:06 -07:00
end
2016-04-21 13:04:49 -07:00
elseif self.do_labels and t.tt == 'RELLABELSYM' or t.tt == 'RELLABEL' then
2016-01-13 11:20:28 -08:00
if t.tt == 'RELLABEL' then
t.tt = 'LABEL'
-- exploits the fact that user labels can't begin with a number
2016-04-10 06:07:06 -07:00
local name = t.tok:sub(2)
2016-04-21 13:04:49 -07:00
t.tok = tostring(self.i)..name
2016-01-13 11:20:28 -08:00
elseif t.tt == 'RELLABELSYM' then
2016-04-21 13:04:49 -07:00
local i = self.i
2016-01-13 11:20:28 -08:00
t.tt = 'LABELSYM'
2016-04-10 06:07:06 -07:00
local rel = signs(t.tok)
2016-04-21 13:04:49 -07:00
assert(rel ~= 0, 'Internal Error: relative label without signs')
2016-04-10 06:07:06 -07:00
local name = t.tok:sub(abs(rel) + 1)
2016-01-13 11:20:28 -08:00
local seen = 0
2016-04-10 06:07:06 -07:00
2016-04-21 13:04:49 -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-13 11:20:28 -08:00
if rel > 0 then
2016-04-21 13:04:49 -07:00
for _, rl in ipairs(self.plus_labels) do
2016-04-10 06:07:06 -07:00
if rl.name == name and rl.index > i then
2016-01-13 11:20:28 -08:00
seen = seen + 1
if seen == rel then
2016-04-10 06:07:06 -07:00
t.tok = tostring(rl.index)..name
2016-01-13 11:20:28 -08:00
break
end
end
end
else
2016-04-21 13:04:49 -07:00
for _, rl in ipairs(self.minus_labels) do
2016-04-10 06:07:06 -07:00
if rl.name == name and rl.index < i then
2016-01-13 11:20:28 -08:00
seen = seen - 1
if seen == rel then
2016-04-10 06:07:06 -07:00
t.tok = tostring(rl.index)..name
2016-01-13 11:20:28 -08:00
break
end
end
end
end
if seen ~= rel then
2016-04-21 13:04:49 -07:00
self:error('could not find appropriate relative label', t.tok)
2016-01-13 11:20:28 -08:00
end
end
2016-04-21 13:04:49 -07:00
else
return false
2016-01-13 11:20:28 -08:00
end
2016-04-21 13:04:49 -07:00
return true
end
2016-01-13 11:20:28 -08:00
2016-04-21 13:04:49 -07:00
function Preproc:check(s, i, tt)
s = s or self.s
i = i or self.i
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 = {
index = #new_statements + 1,
name = 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-13 11:20:28 -08:00
end
2016-04-21 13:04:49 -07:00
insert(new_statements, s)
2016-01-13 11:20:28 -08:00
else
2016-04-21 13:04:49 -07:00
insert(new_statements, s)
2016-01-13 11:20:28 -08:00
end
else
2016-04-21 13:04:49 -07:00
-- regular instruction
for j, t in ipairs(s) do
self:lookup(t)
end
insert(new_statements, s)
2016-01-13 11:20:28 -08:00
end
end
2016-04-21 13:04:49 -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
return new_statements
end
function Preproc:statement(...)
self.fn = self.s.fn
self.line = self.s.line
local s = Statement(self.fn, self.line, ...)
return s
end
function Preproc:push(s)
s:validate()
insert(self.statements, s)
end
function Preproc:push_new(...)
self:push(self:statement(...))
end
function Preproc:pop(kind)
local ret
if kind == nil then
-- noop
elseif kind == 'CPU' then
ret = self:register(data.registers)
elseif kind == 'DEREF' then
ret = self:deref()
elseif kind == 'CONST' then
ret = self:const()
elseif kind == 'REL' then
ret = self:const('REL')
elseif kind == 'END' then
if self.s[self.i + 1] ~= nil then
self:error('too many arguments')
end
return -- don't increment self.i past end of arguments
else
error('Internal Error: unknown kind, got '..tostring(kind))
end
self.i = self.i + 1
return ret
end
function Preproc:expand(statements)
-- third pass: expand pseudo-instructions
self.statements = {}
for i=1, #statements do
local s = statements[i]
self.s = s
self.fn = s.fn
self.line = s.line
if s.type:sub(1, 1) == '!' then
self:push(s)
else
local name = s.type
local h = data.instructions[name]
if h == nil then
error('Internal Error: unknown instruction')
end
if overrides[name] then
self.i = 1
overrides[name](self, name)
self:pop('END')
else
self:push(s)
end
end
end
2016-01-13 11:20:28 -08:00
2016-04-21 13:04:49 -07:00
return self.statements
2016-01-13 11:20:28 -08:00
end
return Preproc