mirror of
https://github.com/notwa/lips
synced 2024-11-14 09:39:03 -08:00
split out token-handling and preprocessing
This commit is contained in:
parent
1eed93f2e7
commit
61cd69004f
3 changed files with 197 additions and 176 deletions
94
lips/Muncher.lua
Normal file
94
lips/Muncher.lua
Normal file
|
@ -0,0 +1,94 @@
|
|||
local format = string.format
|
||||
|
||||
local data = require "lips.data"
|
||||
local Muncher = require("lips.Class")()
|
||||
|
||||
function Muncher:error(msg)
|
||||
error(format('%s:%d: Error: %s', self.fn, self.line, msg), 2)
|
||||
end
|
||||
|
||||
function Muncher:advance()
|
||||
self.i = self.i + 1
|
||||
local t = self.tokens[self.i]
|
||||
self.tt = t.tt
|
||||
self.tok = t.tok
|
||||
self.fn = t.fn
|
||||
self.line = t.line
|
||||
return t.tt, t.tok
|
||||
end
|
||||
|
||||
function Muncher:is_EOL()
|
||||
return self.tt == 'EOL' or self.tt == 'EOF'
|
||||
end
|
||||
|
||||
function Muncher:expect_EOL()
|
||||
if self:is_EOL() then
|
||||
self:advance()
|
||||
return
|
||||
end
|
||||
self:error('expected end of line')
|
||||
end
|
||||
|
||||
function Muncher:optional_comma()
|
||||
if self.tt == 'SEP' and self.tok == ',' then
|
||||
self:advance()
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function Muncher:number()
|
||||
if self.tt ~= 'NUM' then
|
||||
self:error('expected number')
|
||||
end
|
||||
local value = self.tok
|
||||
self:advance()
|
||||
return value
|
||||
end
|
||||
|
||||
function Muncher:string()
|
||||
if self.tt ~= 'STRING' then
|
||||
self:error('expected string')
|
||||
end
|
||||
local value = self.tok
|
||||
self:advance()
|
||||
return value
|
||||
end
|
||||
|
||||
function Muncher:register(t)
|
||||
t = t or data.registers
|
||||
if self.tt ~= 'REG' then
|
||||
self:error('expected register')
|
||||
end
|
||||
local reg = self.tok
|
||||
if not t[reg] then
|
||||
self:error('wrong type of register')
|
||||
end
|
||||
self:advance()
|
||||
return reg
|
||||
end
|
||||
|
||||
function Muncher:deref()
|
||||
if self.tt ~= 'DEREF' then
|
||||
self:error('expected register to dereference')
|
||||
end
|
||||
local reg = self.tok
|
||||
self:advance()
|
||||
return reg
|
||||
end
|
||||
|
||||
function Muncher:const(relative, no_label)
|
||||
if self.tt ~= 'NUM' and self.tt ~= 'LABELSYM' then
|
||||
self:error('expected constant')
|
||||
end
|
||||
if no_label and self.tt == 'LABELSYM' then
|
||||
self:error('labels are not allowed here')
|
||||
end
|
||||
if relative and self.tt == 'LABELSYM' then
|
||||
self.tt = 'LABELREL'
|
||||
end
|
||||
local t = {self.tt, self.tok}
|
||||
self:advance()
|
||||
return t
|
||||
end
|
||||
|
||||
return Muncher
|
189
lips/Parser.lua
189
lips/Parser.lua
|
@ -1,69 +1,18 @@
|
|||
local format = string.format
|
||||
local insert = table.insert
|
||||
|
||||
local data = require "lips.data"
|
||||
local overrides = require "lips.overrides"
|
||||
local Dumper = require "lips.Dumper"
|
||||
local Lexer = require "lips.Lexer"
|
||||
local Dumper = require "lips.Dumper"
|
||||
local Muncher = require "lips.Muncher"
|
||||
local Preproc = require "lips.Preproc"
|
||||
|
||||
local Parser = require("lips.Class")()
|
||||
local Parser = require("lips.Class")(Muncher)
|
||||
function Parser:init(writer, fn, options)
|
||||
self.fn = fn or '(string)'
|
||||
self.main_fn = self.fn
|
||||
self.options = options or {}
|
||||
self.dumper = Dumper(writer, fn, options)
|
||||
self.defines = {}
|
||||
end
|
||||
|
||||
function Parser:error(msg)
|
||||
error(format('%s:%d: Error: %s', self.fn, self.line, msg), 2)
|
||||
end
|
||||
|
||||
function Parser:advance()
|
||||
self.i = self.i + 1
|
||||
local t = self.tokens[self.i]
|
||||
self.tt = t.tt
|
||||
self.tok = t.tok
|
||||
self.fn = t.fn
|
||||
self.line = t.line
|
||||
return t.tt, t.tok
|
||||
end
|
||||
|
||||
function Parser:is_EOL()
|
||||
return self.tt == 'EOL' or self.tt == 'EOF'
|
||||
end
|
||||
|
||||
function Parser:expect_EOL()
|
||||
if self:is_EOL() then
|
||||
self:advance()
|
||||
return
|
||||
end
|
||||
self:error('expected end of line')
|
||||
end
|
||||
|
||||
function Parser:optional_comma()
|
||||
if self.tt == 'SEP' and self.tok == ',' then
|
||||
self:advance()
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function Parser:number()
|
||||
if self.tt ~= 'NUM' then
|
||||
self:error('expected number')
|
||||
end
|
||||
local value = self.tok
|
||||
self:advance()
|
||||
return value
|
||||
end
|
||||
|
||||
function Parser:string()
|
||||
if self.tt ~= 'STRING' then
|
||||
self:error('expected string')
|
||||
end
|
||||
local value = self.tok
|
||||
self:advance()
|
||||
return value
|
||||
end
|
||||
|
||||
function Parser:directive()
|
||||
|
@ -123,43 +72,6 @@ function Parser:directive()
|
|||
end
|
||||
end
|
||||
|
||||
function Parser:register(t)
|
||||
t = t or data.registers
|
||||
if self.tt ~= 'REG' then
|
||||
self:error('expected register')
|
||||
end
|
||||
local reg = self.tok
|
||||
if not t[reg] then
|
||||
self:error('wrong type of register')
|
||||
end
|
||||
self:advance()
|
||||
return reg
|
||||
end
|
||||
|
||||
function Parser:deref()
|
||||
if self.tt ~= 'DEREF' then
|
||||
self:error('expected register to dereference')
|
||||
end
|
||||
local reg = self.tok
|
||||
self:advance()
|
||||
return reg
|
||||
end
|
||||
|
||||
function Parser:const(relative, no_label)
|
||||
if self.tt ~= 'NUM' and self.tt ~= 'LABELSYM' then
|
||||
self:error('expected constant')
|
||||
end
|
||||
if no_label and self.tt == 'LABELSYM' then
|
||||
self:error('labels are not allowed here')
|
||||
end
|
||||
if relative and self.tt == 'LABELSYM' then
|
||||
self.tt = 'LABELREL'
|
||||
end
|
||||
local t = {self.tt, self.tok}
|
||||
self:advance()
|
||||
return t
|
||||
end
|
||||
|
||||
function Parser:format_in(informat)
|
||||
local args = {}
|
||||
for i=1,#informat do
|
||||
|
@ -304,7 +216,6 @@ function Parser:instruction()
|
|||
end
|
||||
|
||||
function Parser:tokenize(asm)
|
||||
self.tokens = {}
|
||||
self.i = 0
|
||||
|
||||
local routine = coroutine.create(function()
|
||||
|
@ -312,103 +223,29 @@ function Parser:tokenize(asm)
|
|||
lexer:lex(coroutine.yield)
|
||||
end)
|
||||
|
||||
local function lex()
|
||||
local tokens = {}
|
||||
while true do
|
||||
local ok, a, b, c, d = coroutine.resume(routine)
|
||||
if not ok then
|
||||
a = a or 'Internal Error: lexer coroutine has stopped'
|
||||
error(a)
|
||||
end
|
||||
assert(a, 'Internal Error: missing token')
|
||||
|
||||
local t = {}
|
||||
t.tt = a
|
||||
t.tok = b
|
||||
t.fn = c
|
||||
t.line = d
|
||||
insert(self.tokens, t)
|
||||
return t.tt, t.tok, t.fn, t.line
|
||||
end
|
||||
insert(tokens, t)
|
||||
|
||||
-- first pass: collect tokens, constants, and relative labels.
|
||||
-- can't do more because instruction size can depend on a constant's size
|
||||
-- and labels depend on instruction size.
|
||||
-- note however, instruction size does not depend on label size.
|
||||
-- this would cause a recursive problem to solve,
|
||||
-- which is too much for our simple assembler.
|
||||
local plus_labels = {} -- constructed forwards
|
||||
local minus_labels = {} -- constructed backwards
|
||||
while true do
|
||||
local tt, tok, fn, line = lex()
|
||||
self.fn = fn
|
||||
self.line = line
|
||||
if tt == 'DEF' then
|
||||
local tt2, tok2 = lex()
|
||||
if tt2 ~= 'NUM' then
|
||||
self:error('expected number for define')
|
||||
end
|
||||
self.defines[tok] = tok2
|
||||
elseif tt == 'RELLABEL' then
|
||||
if tok == '+' then
|
||||
insert(plus_labels, #self.tokens)
|
||||
elseif tok == '-' then
|
||||
insert(minus_labels, 1, #self.tokens)
|
||||
else
|
||||
error('Internal Error: unexpected token for relative label', 1)
|
||||
end
|
||||
elseif tt == 'EOL' then
|
||||
-- noop
|
||||
elseif tt == 'EOF' then
|
||||
if fn == self.main_fn then
|
||||
break
|
||||
end
|
||||
elseif tt == nil then
|
||||
error('Internal Error: missing token', 1)
|
||||
if t.tt == 'EOF' and t.fn == self.main_fn then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- resolve defines and relative labels
|
||||
for i, t in ipairs(self.tokens) do
|
||||
self.fn = t.fn
|
||||
self.line = t.line
|
||||
if t.tt == 'DEFSYM' then
|
||||
t.tt = 'NUM'
|
||||
t.tok = self.defines[t.tok]
|
||||
if t.tok == nil then
|
||||
self:error('undefined define') -- uhhh nice wording
|
||||
end
|
||||
elseif t.tt == 'RELLABEL' then
|
||||
t.tt = 'LABEL'
|
||||
-- exploits the fact that user labels can't begin with a number
|
||||
t.tok = tostring(i)
|
||||
elseif t.tt == 'RELLABELSYM' then
|
||||
t.tt = 'LABELSYM'
|
||||
local rel = t.tok
|
||||
local seen = 0
|
||||
-- TODO: don't iterate over *every* label, just the ones nearby
|
||||
if rel > 0 then
|
||||
for _, label_i in ipairs(plus_labels) do
|
||||
if label_i > i then
|
||||
seen = seen + 1
|
||||
if seen == rel then
|
||||
t.tok = tostring(label_i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
for _, label_i in ipairs(minus_labels) do
|
||||
if label_i < i then
|
||||
seen = seen - 1
|
||||
if seen == rel then
|
||||
t.tok = tostring(label_i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if seen ~= rel then
|
||||
self:error('could not find appropriate relative label')
|
||||
end
|
||||
end
|
||||
end
|
||||
local preproc = Preproc(self.options)
|
||||
self.tokens = preproc:process(tokens)
|
||||
end
|
||||
|
||||
function Parser:parse(asm)
|
||||
|
|
90
lips/Preproc.lua
Normal file
90
lips/Preproc.lua
Normal file
|
@ -0,0 +1,90 @@
|
|||
local insert = table.insert
|
||||
|
||||
local Muncher = require "lips.Muncher"
|
||||
|
||||
local Preproc = require("lips.Class")(Muncher)
|
||||
function Preproc:init(options)
|
||||
self.options = options or {}
|
||||
end
|
||||
|
||||
function Preproc:process(tokens)
|
||||
self.tokens = tokens
|
||||
|
||||
local defines = {}
|
||||
local plus_labels = {} -- constructed forwards
|
||||
local minus_labels = {} -- constructed backwards
|
||||
|
||||
-- first pass: collect tokens, constants, and relative labels.
|
||||
self.i = 0
|
||||
while self.i < #self.tokens do
|
||||
local tt, tok = self:advance()
|
||||
if tt == 'DEF' then
|
||||
local tt2, tok2 = self:advance()
|
||||
if tt2 ~= 'NUM' then
|
||||
self:error('expected number for define')
|
||||
end
|
||||
defines[tok] = tok2
|
||||
elseif tt == 'RELLABEL' then
|
||||
if tok == '+' then
|
||||
insert(plus_labels, self.i)
|
||||
elseif tok == '-' then
|
||||
insert(minus_labels, 1, self.i)
|
||||
else
|
||||
error('Internal Error: unexpected token for relative label')
|
||||
end
|
||||
elseif tt == nil then
|
||||
error('Internal Error: missing token')
|
||||
end
|
||||
end
|
||||
|
||||
-- resolve defines and relative labels
|
||||
for i, t in ipairs(self.tokens) do
|
||||
self.fn = t.fn
|
||||
self.line = t.line
|
||||
if t.tt == 'DEFSYM' then
|
||||
t.tt = 'NUM'
|
||||
t.tok = defines[t.tok]
|
||||
if t.tok == nil then
|
||||
self:error('undefined define') -- uhhh nice wording
|
||||
end
|
||||
elseif t.tt == 'RELLABEL' then
|
||||
t.tt = 'LABEL'
|
||||
-- exploits the fact that user labels can't begin with a number
|
||||
t.tok = tostring(i)
|
||||
elseif t.tt == 'RELLABELSYM' then
|
||||
t.tt = 'LABELSYM'
|
||||
|
||||
local rel = t.tok
|
||||
local seen = 0
|
||||
-- TODO: don't iterate over *every* label, just the ones nearby
|
||||
if rel > 0 then
|
||||
for _, label_i in ipairs(plus_labels) do
|
||||
if label_i > i then
|
||||
seen = seen + 1
|
||||
if seen == rel then
|
||||
t.tok = tostring(label_i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
for _, label_i in ipairs(minus_labels) do
|
||||
if label_i < i then
|
||||
seen = seen - 1
|
||||
if seen == rel then
|
||||
t.tok = tostring(label_i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if seen ~= rel then
|
||||
self:error('could not find appropriate relative label')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self.tokens
|
||||
end
|
||||
|
||||
return Preproc
|
Loading…
Reference in a new issue