mirror of
https://github.com/notwa/lips
synced 2024-11-14 18:19:03 -08:00
reimplement variables and relative labels
This commit is contained in:
parent
91e028ef6a
commit
433d25a457
2 changed files with 146 additions and 108 deletions
|
@ -188,27 +188,32 @@ function Parser:tokenize(asm)
|
|||
end)
|
||||
end
|
||||
|
||||
-- the lexer guarantees an EOL and EOF for a blank file
|
||||
assert(#tokens > 0, 'Internal Error: no tokens after preprocessing')
|
||||
|
||||
local collector = Collector(self.options)
|
||||
local statements = collector:collect(tokens, self.main_fn)
|
||||
|
||||
--[[
|
||||
local preproc = Preproc(self.options)
|
||||
self.statements = preproc:process(statements)
|
||||
|
||||
-- the lexer guarantees an EOL and EOF for a blank file
|
||||
assert(#self.tokens > 0, 'Internal Error: no tokens after preprocessing')
|
||||
--]]
|
||||
self.statements = statements
|
||||
end
|
||||
|
||||
function Parser:parse(asm)
|
||||
self:tokenize(asm)
|
||||
|
||||
--[[ process:
|
||||
- inline constants
|
||||
- inline variables
|
||||
- inline labels? how do you know how far they are?
|
||||
i guess you can just offset on labels instead
|
||||
- assemble? dumper gets passed .org .base
|
||||
--]]
|
||||
|
||||
-- DEBUG
|
||||
for i, s in ipairs(self.statements) do
|
||||
local values = ''
|
||||
for j, v in ipairs(s) do
|
||||
values = values..'\t'..v.tt
|
||||
values = values..'\t'..v.tt..'('..v.tok..')'
|
||||
end
|
||||
values = values:sub(2)
|
||||
print(i, s.type, values)
|
||||
|
|
243
lips/Preproc.lua
243
lips/Preproc.lua
|
@ -2,8 +2,9 @@ local insert = table.insert
|
|||
|
||||
local path = string.gsub(..., "[^.]+$", "")
|
||||
local data = require(path.."data")
|
||||
local Muncher = require(path.."Muncher")
|
||||
local Base = require(path.."Base")
|
||||
local Token = require(path.."Token")
|
||||
local Statement = require(path.."Statement")
|
||||
|
||||
local abs = math.abs
|
||||
|
||||
|
@ -19,6 +20,16 @@ local function signs(s)
|
|||
end
|
||||
end
|
||||
|
||||
local function unsigns(n)
|
||||
if n > 0 then
|
||||
return string.rep('+', n)
|
||||
elseif n < 0 then
|
||||
return string.rep('-', -n)
|
||||
else
|
||||
return ''
|
||||
end
|
||||
end
|
||||
|
||||
local function RelativeLabel(index, name)
|
||||
return {
|
||||
index = index,
|
||||
|
@ -26,91 +37,58 @@ local function RelativeLabel(index, name)
|
|||
}
|
||||
end
|
||||
|
||||
local Preproc = Muncher:extend()
|
||||
local Preproc = Base:extend()
|
||||
function Preproc:init(options)
|
||||
self.options = options or {}
|
||||
end
|
||||
|
||||
function Preproc:process(tokens)
|
||||
self.tokens = tokens
|
||||
|
||||
local variables = {}
|
||||
local plus_labels = {} -- constructed forwards
|
||||
local minus_labels = {} -- constructed backwards
|
||||
|
||||
-- first pass: resolve unary ops, variables, and collect relative labels
|
||||
local new_tokens = {}
|
||||
self.i = 0
|
||||
while self.i < #self.tokens do
|
||||
local t = self:advance()
|
||||
local sign = 1
|
||||
if t.tt == 'UNARY' then
|
||||
sign = t.tok
|
||||
local peek = self.tokens[self.i + 1]
|
||||
if peek.tt == 'UNARY' then
|
||||
self:error('unary operators cannot be chained')
|
||||
elseif peek.tt == 'EOL' or peek.tt == 'SEP' then
|
||||
t.tt = 'RELLABELSYM'
|
||||
t.tok = sign == 1 and '+' or sign == -1 and '-'
|
||||
elseif peek.tt == 'VARSYM' then
|
||||
t = self:advance()
|
||||
else
|
||||
self:error('expected a symbolic constant after unary operator')
|
||||
end
|
||||
end
|
||||
if t.tt == nil then
|
||||
error('Internal Error: missing token')
|
||||
elseif t.tt == 'VAR' then
|
||||
local t2 = self:advance()
|
||||
if t2.tt ~= 'NUM' then
|
||||
self:error('expected number for variable')
|
||||
end
|
||||
variables[t.tok] = t2.tok
|
||||
elseif t.tt == 'VARSYM' then
|
||||
local tt = 'NUM'
|
||||
local tok = variables[t.tok]
|
||||
if tok == nil then
|
||||
self:error('undefined variable')
|
||||
end
|
||||
insert(new_tokens, self:token(tt, tok * sign))
|
||||
elseif t.tt == 'RELLABEL' then
|
||||
local label = t.tok or ''
|
||||
local rl = RelativeLabel(#new_tokens + 1, label:sub(2))
|
||||
if label:sub(1, 1) == '+' then
|
||||
insert(plus_labels, rl)
|
||||
elseif label:sub(1, 1) == '-' then
|
||||
insert(minus_labels, 1, rl)
|
||||
else
|
||||
error('Internal Error: unexpected token for relative label')
|
||||
end
|
||||
insert(new_tokens, t)
|
||||
else
|
||||
insert(new_tokens, t)
|
||||
function Preproc:error(msg, got)
|
||||
if got ~= nil then
|
||||
msg = msg..', got '..tostring(got)
|
||||
end
|
||||
error(('%s:%d: Error: %s'):format(self.fn, self.line, msg), 2)
|
||||
end
|
||||
|
||||
-- second pass: resolve relative labels
|
||||
for i, t in ipairs(new_tokens) do
|
||||
self.fn = t.fn
|
||||
self.line = t.line
|
||||
--[[
|
||||
function Preproc:advance()
|
||||
self.i = self.i + 1
|
||||
self.s = self.statements[self.i]
|
||||
self.fn = self.s.fn
|
||||
self.line = self.s.line
|
||||
return self.s
|
||||
end
|
||||
--]]
|
||||
|
||||
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
|
||||
if t.tt == 'RELLABEL' then
|
||||
t.tt = 'LABEL'
|
||||
-- exploits the fact that user labels can't begin with a number
|
||||
local name = t.tok:sub(2)
|
||||
t.tok = tostring(i)..name
|
||||
t.tok = tostring(self.i)..name
|
||||
elseif t.tt == 'RELLABELSYM' then
|
||||
local i = self.i
|
||||
t.tt = 'LABELSYM'
|
||||
|
||||
local rel = signs(t.tok)
|
||||
if rel == 0 then
|
||||
error('Internal Error: relative label without signs')
|
||||
end
|
||||
assert(rel ~= 0, 'Internal Error: relative label without signs')
|
||||
|
||||
local name = t.tok:sub(abs(rel) + 1)
|
||||
local seen = 0
|
||||
|
||||
-- TODO: don't iterate over *every* label, just the ones nearby
|
||||
-- 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)
|
||||
if rel > 0 then
|
||||
for _, rl in ipairs(plus_labels) do
|
||||
for _, rl in ipairs(self.plus_labels) do
|
||||
if rl.name == name and rl.index > i then
|
||||
seen = seen + 1
|
||||
if seen == rel then
|
||||
|
@ -120,7 +98,7 @@ function Preproc:process(tokens)
|
|||
end
|
||||
end
|
||||
else
|
||||
for _, rl in ipairs(minus_labels) do
|
||||
for _, rl in ipairs(self.minus_labels) do
|
||||
if rl.name == name and rl.index < i then
|
||||
seen = seen - 1
|
||||
if seen == rel then
|
||||
|
@ -132,50 +110,105 @@ function Preproc:process(tokens)
|
|||
end
|
||||
|
||||
if seen ~= rel then
|
||||
self:error('could not find appropriate relative label')
|
||||
self:error('could not find appropriate relative label', unsigns(rel))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.tokens = new_tokens
|
||||
new_tokens = {}
|
||||
|
||||
-- third pass: resolve specials
|
||||
self.i = 0
|
||||
while self.i < #self.tokens do
|
||||
local t = self:advance()
|
||||
if t.tt == 'SPECIAL' then
|
||||
local name, args = self:special()
|
||||
-- TODO: split to its own file, not unlike overrides.lua
|
||||
if name == 'hi' then
|
||||
if #args ~= 1 then
|
||||
self:error('%hi expected exactly one argument')
|
||||
end
|
||||
local tnew = self:token(args[1]):set('portion', 'upperoff')
|
||||
insert(new_tokens, tnew)
|
||||
elseif name == 'up' then
|
||||
if #args ~= 1 then
|
||||
self:error('%up expected exactly one argument')
|
||||
end
|
||||
local tnew = self:token(args[1]):set('portion', 'upper')
|
||||
insert(new_tokens, tnew)
|
||||
elseif name == 'lo' then
|
||||
if #args ~= 1 then
|
||||
self:error('%lo expected exactly one argument')
|
||||
end
|
||||
local tnew = self:token(args[1]):set('portion', 'lower')
|
||||
insert(new_tokens, tnew)
|
||||
else
|
||||
self:error('unknown special')
|
||||
end
|
||||
else
|
||||
insert(new_tokens, t)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
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)
|
||||
--[[
|
||||
local newtt, newtok = self:lookup(argtt, argtok)
|
||||
if newtt and newtok then
|
||||
argtt, argtok = newtt, newtok
|
||||
end
|
||||
--]]
|
||||
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
|
||||
end
|
||||
insert(new_statements, s)
|
||||
end
|
||||
else
|
||||
-- regular instruction
|
||||
for j, t in ipairs(s) do
|
||||
self:lookup(t)
|
||||
--[[
|
||||
local newtt, newtok = self:lookup(t.tt, t.tok)
|
||||
if newtt and newtok then
|
||||
else
|
||||
end
|
||||
--]]
|
||||
end
|
||||
insert(new_statements, s)
|
||||
end
|
||||
end
|
||||
|
||||
self.tokens = new_tokens
|
||||
-- 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 self.tokens
|
||||
self.statements = new_statements
|
||||
return self.statements
|
||||
end
|
||||
|
||||
return Preproc
|
||||
|
|
Loading…
Reference in a new issue