diff --git a/lips/Parser.lua b/lips/Parser.lua index 2a64ddb..62eb16e 100644 --- a/lips/Parser.lua +++ b/lips/Parser.lua @@ -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) diff --git a/lips/Preproc.lua b/lips/Preproc.lua index 540b1f6..44cb842 100644 --- a/lips/Preproc.lua +++ b/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) - 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