1
0
Fork 0
mirror of https://github.com/notwa/lips synced 2024-05-03 10:03:23 -07:00

reimplement variables and relative labels

This commit is contained in:
Connor Olding 2016-04-20 17:47:58 -07:00
parent 91e028ef6a
commit 433d25a457
2 changed files with 146 additions and 108 deletions

View File

@ -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)

View File

@ -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)
end
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
else
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.tokens = new_tokens
new_tokens = {}
self.fn = t.fn
self.line = t.line
-- 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')
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
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')
insert(new_statements, s)
end
else
insert(new_tokens, t)
-- 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