From 07c68b6229141e13029f3db757417ea5a5a960f0 Mon Sep 17 00:00:00 2001 From: Connor Olding Date: Fri, 15 Jan 2016 20:25:41 -0800 Subject: [PATCH] implement basic specials; more refactoring --- lips/Class.lua | 0 lips/Dumper.lua | 43 +++++++++++++++-------------- lips/Muncher.lua | 51 +++++++++++++++++++++-------------- lips/Parser.lua | 67 ++++++++++++++++++++++------------------------ lips/Preproc.lua | 14 ++++------ lips/Token.lua | 60 +++++++++++++++++++++++++++++++++++++++++ lips/overrides.lua | 38 +++++++++++++++----------- lips/util.lua | 29 -------------------- 8 files changed, 171 insertions(+), 131 deletions(-) delete mode 100644 lips/Class.lua create mode 100644 lips/Token.lua diff --git a/lips/Class.lua b/lips/Class.lua deleted file mode 100644 index e69de29..0000000 diff --git a/lips/Dumper.lua b/lips/Dumper.lua index 337fa53..fb54c93 100644 --- a/lips/Dumper.lua +++ b/lips/Dumper.lua @@ -127,20 +127,20 @@ function Dumper:add_directive(fn, line, name, a, b) end end -function Dumper:desym(tok) - if type(tok[2]) == 'number' then - return tok[2] - elseif tok[1] == 'REG' then - assert(data.all_registers[tok[2]], 'Internal Error: unknown register') - return data.registers[tok[2]] or data.fpu_registers[tok[2]] or data.sys_registers[tok[2]] - elseif tok[1] == 'LABELSYM' then - local label = self.labels[tok[2]] +function Dumper:desym(t) + if type(t.tok) == 'number' then + return t.tok + elseif t.tt == 'REG' then + assert(data.all_registers[t.tok], 'Internal Error: unknown register') + return data.registers[t.tok] or data.fpu_registers[t.tok] or data.sys_registers[t.tok] + elseif t.tt == 'LABELSYM' then + local label = self.labels[t.tok] if label == nil then self:error('undefined label') end return label - elseif tok[1] == 'LABELREL' then - local label = self.labels[tok[2]] + elseif t.tt == 'LABELREL' then + local label = self.labels[t.tok] if label == nil then self:error('undefined label') end @@ -155,31 +155,30 @@ function Dumper:desym(tok) error('Internal Error: failed to desym') end -function Dumper:toval(tok) - assert(type(tok) == 'table', 'Internal Error: invalid value') - assert(#tok == 2, 'Internal Error: invalid token') +function Dumper:toval(t) + assert(type(t) == 'table', 'Internal Error: invalid value') - local val = self:desym(tok) + local val = self:desym(t) - if tok.index then + if t.index then val = val % 0x80000000 val = floor(val/4) end - if tok.negate then + if t.negate then val = -val end - if tok.negate or tok.signed then + if t.negate or t.signed then if val >= 0x10000 or val < -0x8000 then self:error('value out of range') end val = val % 0x10000 end - if tok.portion == 'upper' then + if t.portion == 'upper' then val = bitrange(val, 16, 31) - elseif tok.portion == 'lower' then + elseif t.portion == 'lower' then val = bitrange(val, 0, 15) - elseif tok.portion == 'upperoff' then + elseif t.portion == 'upperoff' then local upper = bitrange(val, 16, 31) local lower = bitrange(val, 0, 15) if lower >= 0x8000 then @@ -202,8 +201,8 @@ function Dumper:validate(n, bits) end end -function Dumper:valvar(tok, bits) - local val = self:toval(tok) +function Dumper:valvar(t, bits) + local val = self:toval(t) self:validate(val, bits) return val end diff --git a/lips/Muncher.lua b/lips/Muncher.lua index 8266d78..42cefd4 100644 --- a/lips/Muncher.lua +++ b/lips/Muncher.lua @@ -3,6 +3,7 @@ local insert = table.insert local data = require "lips.data" local util = require "lips.util" +local Token = require "lips.Token" local arg_types = { NUM = true, @@ -19,14 +20,25 @@ function Muncher:error(msg) error(format('%s:%d: Error: %s', self.fn, self.line, msg), 2) end +function Muncher:token(t, val) + -- note: call Token directly if you want to specify fn and line manually + if type(t) == 'table' then + t.fn = self.fn + t.line = self.line + return Token(t) + else + return Token(self.fn, self.line, t, val) + end +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 + self.t = self.tokens[self.i] + self.tt = self.t.tt + self.tok = self.t.tok + self.fn = self.t.fn + self.line = self.t.line + return self.t end function Muncher:is_EOL() @@ -52,31 +64,31 @@ function Muncher:number() if self.tt ~= 'NUM' then self:error('expected number') end - local value = self.tok + local t = self.t self:advance() - return value + return self:token(t) end function Muncher:string() if self.tt ~= 'STRING' then self:error('expected string') end - local value = self.tok + local t = self.t self:advance() - return value + return self:token(t) end -function Muncher:register(t) - t = t or data.registers +function Muncher:register(registers) + registers = registers or data.registers if self.tt ~= 'REG' then self:error('expected register') end - local reg = self.tok - if not t[reg] then + local t = self.t + if not registers[t.tok] then self:error('wrong type of register') end self:advance() - return reg + return self:token(t) end function Muncher:deref() @@ -87,13 +99,13 @@ function Muncher:deref() if self.tt ~= 'REG' then self:error('expected register to dereference') end - local reg = self.tok + local t = self.t self:advance() if self.tt ~= 'CLOSE' then self:error('expected closing parenthesis for dereferencing') end self:advance() - return reg + return self:token(t) end function Muncher:const(relative, no_label) @@ -106,9 +118,9 @@ function Muncher:const(relative, no_label) if relative and self.tt == 'LABELSYM' then self.tt = 'LABELREL' end - local t = {self.tt, self.tok} + local t = self.t self:advance() - return t + return self:token(t) end function Muncher:special() @@ -133,7 +145,6 @@ function Muncher:special() insert(args, arg) elseif self.tt == 'CLOSE' then insert(args, arg) - self:advance() break else self:error('unexpected token in argument list') diff --git a/lips/Parser.lua b/lips/Parser.lua index 206872f..f68faac 100644 --- a/lips/Parser.lua +++ b/lips/Parser.lua @@ -3,14 +3,12 @@ local insert = table.insert local data = require "lips.data" local util = require "lips.util" local overrides = require "lips.overrides" +local Token = require "lips.Token" local Lexer = require "lips.Lexer" local Dumper = require "lips.Dumper" local Muncher = require "lips.Muncher" local Preproc = require "lips.Preproc" -local construct = util.construct -local withflag = util.withflag - local Parser = util.Class(Muncher) function Parser:init(writer, fn, options) self.fn = fn or '(string)' @@ -26,7 +24,7 @@ function Parser:directive() self.dumper:add_directive(self.fn, self.line, ...) end if name == 'ORG' then - add(name, self:number()) + add(name, self:number().tok) elseif name == 'ALIGN' or name == 'SKIP' then if self:is_EOL() and name == 'ALIGN' then add(name, 0) @@ -36,24 +34,24 @@ function Parser:directive() add(name, size) else self:optional_comma() - add(name, size, self:number()) + add(name, size, self:number().tok) end self:expect_EOL() end elseif name == 'BYTE' or name == 'HALFWORD' then - add(name, self:number()) + add(name, self:number().tok) while not self:is_EOL() do self:advance() self:optional_comma() - add(name, self:number()) + add(name, self:number().tok) end self:expect_EOL() elseif name == 'WORD' then -- allow labels in word directives - add(name, self:const()[2]) + add(name, self:const().tok) while not self:is_EOL() do self:advance() self:optional_comma() - add(name, self:const()[2]) + add(name, self:const().tok) end self:expect_EOL() elseif name == 'INC' then @@ -100,17 +98,17 @@ function Parser:format_in(informat) elseif c == 'Z' and not args.rt then args.rt = self:register(data.sys_registers) elseif c == 'o' and not args.offset then - args.offset = withflag(self:const(), 'signed') + args.offset = Token(self:const()):set('signed') elseif c == 'r' and not args.offset then - args.offset = withflag(self:const('relative'), 'signed') + args.offset = Token(self:const('relative')):set('signed') elseif c == 'i' and not args.immediate then args.immediate = self:const(nil, 'no label') elseif c == 'I' and not args.index then - args.index = withflag(self:const(), 'index') + args.index = Token(self:const()):set('index') elseif c == 'k' and not args.immediate then - args.immediate = withflag(self:const(nil, 'no label'), 'negate') + args.immediate = Token(self:const(nil, 'no label')):set('negate') elseif c == 'K' and not args.immediate then - args.immediate = withflag(self:const(nil, 'no label'), 'signed') + args.immediate = Token(self:const(nil, 'no label')):set('signed') elseif c == 'b' and not args.base then args.base = self:deref() else @@ -133,31 +131,31 @@ function Parser:format_out_raw(outformat, first, args, const, formatconst) for i=1,#outformat do local c = outformat:sub(i, i) if c == 'd' then - out[#out+1] = construct(args.rd) + out[#out+1] = self:token(args.rd) elseif c == 's' then - out[#out+1] = construct(args.rs) + out[#out+1] = self:token(args.rs) elseif c == 't' then - out[#out+1] = construct(args.rt) + out[#out+1] = self:token(args.rt) elseif c == 'D' then - out[#out+1] = construct(args.fd) + out[#out+1] = self:token(args.fd) elseif c == 'S' then - out[#out+1] = construct(args.fs) + out[#out+1] = self:token(args.fs) elseif c == 'T' then - out[#out+1] = construct(args.ft) + out[#out+1] = self:token(args.ft) elseif c == 'o' then - out[#out+1] = construct(args.offset) + out[#out+1] = self:token(args.offset) elseif c == 'i' then - out[#out+1] = construct(args.immediate) + out[#out+1] = self:token(args.immediate) elseif c == 'I' then - out[#out+1] = construct(args.index) + out[#out+1] = self:token(args.index) elseif c == 'b' then - out[#out+1] = construct(args.base) + out[#out+1] = self:token(args.base) elseif c == '0' then - out[#out+1] = construct(0) + out[#out+1] = self:token(0) elseif c == 'C' then - out[#out+1] = construct(const) + out[#out+1] = self:token(const) elseif c == 'F' then - out[#out+1] = construct(formatconst) + out[#out+1] = self:token(formatconst) end end local f = lookup[#outformat] @@ -193,9 +191,12 @@ function Parser:instruction() local lui_args = {} local addu_args = {} local o = self:const() - args.offset = withflag({o[1], o[2]}, 'portion', 'lower') - if o[1] == 'LABELSYM' or o[2] >= 0x80000000 then - lui_args.immediate = withflag({o[1], o[2]}, 'portion', 'upperoff') + args.offset = self:token(o) + if not o.portion then + args.offset:set('portion', 'lower') + end + if not o.portion and (o.tt == 'LABELSYM' or o.tok >= 0x80000000) then + lui_args.immediate = Token(o):set('portion', 'upperoff') lui_args.rt = 'AT' self:format_out(lui, lui_args) if not self:is_EOL() then @@ -236,11 +237,7 @@ function Parser:tokenize(asm) end assert(a, 'Internal Error: missing token') - local t = {} - t.tt = a - t.tok = b - t.fn = c - t.line = d + local t = Token(c, d, a, b) insert(tokens, t) if t.tt == 'EOF' and t.fn == self.main_fn then diff --git a/lips/Preproc.lua b/lips/Preproc.lua index 58e3528..07f0e72 100644 --- a/lips/Preproc.lua +++ b/lips/Preproc.lua @@ -3,6 +3,7 @@ local insert = table.insert local data = require "lips.data" local util = require "lips.util" local Muncher = require "lips.Muncher" +local Token = require "lips.Token" local Preproc = util.Class(Muncher) function Preproc:init(options) @@ -35,7 +36,7 @@ function Preproc:process(tokens) if tok == nil then self:error('undefined define') -- uhhh nice wording end - insert(new_tokens, {fn=t.fn, line=t.line, tt=tt, tok=tok}) + insert(new_tokens, self:token(tt, tok)) elseif t.tt == 'RELLABEL' then if t.tok == '+' then insert(plus_labels, #new_tokens + 1) @@ -99,8 +100,6 @@ function Preproc:process(tokens) self.i = 0 while self.i < #self.tokens do local t = self:advance() - self.fn = t.fn - self.line = t.line if t.tt == 'SPECIAL' then local name, args = self:special() -- TODO: split to its own file, not unlike overrides.lua @@ -108,22 +107,19 @@ function Preproc:process(tokens) if #args ~= 1 then self:error('%hi expected exactly one argument') end - --local tnew = {fn=t.fn, line=t.line, tt='UPPEROFF', tok=args[1]} - self:error('unimplemented special') + 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 = {fn=t.fn, line=t.line, tt='UPPER', tok=args[1]} - self:error('unimplemented special') + 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 - self:error('unimplemented special') - --local tnew = {fn=t.fn, line=t.line, tt='LOWER', tok=args[1]} + local tnew = self:token(args[1]):set('portion', 'lower') insert(new_tokens, tnew) else self:error('unknown special') diff --git a/lips/Token.lua b/lips/Token.lua new file mode 100644 index 0000000..7978fec --- /dev/null +++ b/lips/Token.lua @@ -0,0 +1,60 @@ +local util = require "lips.util" + +local Token = util.Class() +function Token:init(...) + local args = {...} + if #args == 1 then + local t = args[1] + if type(t) == 'table' then + for k, v in pairs(t) do + self[k] = v + end + end + elseif #args == 3 then + self.fn = args[1] + self.line = args[2] + local t = args[3] + if type(t) == 'table' then + self.tt = t[1] + self.tok = t[2] + elseif type(t) == 'string' then + self.tt = 'REG' + self.tok = t + elseif type(t) == 'number' then + self.tt = 'NUM' + self.tok = t + else + error('Internal Error: unknown type to construct', 3) + end + elseif #args == 4 then + self.fn = args[1] + self.line = args[2] + self.tt = args[3] + self.tok = args[4] + else + error('Internal Error: init takes 1, 3 or 4 arguments', 3) + end + if not self.fn then + error('Internal Error: tokens require a filename', 3) + end + if not self.line then + error('Internal Error: tokens require a line number', 3) + end + if not self.tt then + error('Internal Error: token is missing a type', 3) + end + if not self.tok then + error('Internal Error: token is missing a value', 3) + end + return self +end + +function Token:set(key, value) + if value == nil then + value = true + end + self[key] = value + return self +end + +return Token diff --git a/lips/overrides.lua b/lips/overrides.lua index 0d1c17a..6893414 100644 --- a/lips/overrides.lua +++ b/lips/overrides.lua @@ -4,7 +4,6 @@ local data = require "lips.data" local util = require "lips.util" local instructions = data.instructions -local withflag = util.withflag local overrides = {} -- note: "self" is an instance of Parser @@ -20,26 +19,33 @@ function overrides.LI(self, name) -- for us, this is just semantics. for a "real" assembler, -- LA could add appropriate RELO LUI/ADDIU directives. - if im[1] == 'LABELSYM' then + if im.tt == 'LABELSYM' then self:error('use LA for labels') end - im[2] = im[2] % 0x100000000 - if im[2] >= 0x10000 and im[2] <= 0xFFFF8000 then + if im.portion then + args.rs = 'R0' + args.immediate = im + self:format_out(addiu, args) + return + end + + im.tok = im.tok % 0x100000000 + if im.tok >= 0x10000 and im.tok <= 0xFFFF8000 then args.rs = args.rt - args.immediate = withflag(im, 'portion', 'upper') + args.immediate = self:token(im):set('portion', 'upper') self:format_out(lui, args) - if im[2] % 0x10000 ~= 0 then - args.immediate = withflag(im, 'portion', 'lower') + if im.tok % 0x10000 ~= 0 then + args.immediate = self:token(im):set('portion', 'lower') self:format_out(ori, args) end - elseif im[2] >= 0x8000 and im[2] < 0x10000 then + elseif im.tok >= 0x8000 and im.tok < 0x10000 then args.rs = 'R0' - args.immediate = withflag(im, 'portion', 'lower') + args.immediate = self:token(im):set('portion', 'lower') self:format_out(ori, args) else args.rs = 'R0' - args.immediate = withflag(im, 'portion', 'lower') + args.immediate = self:token(im):set('portion', 'lower') self:format_out(addiu, args) end end @@ -53,9 +59,9 @@ function overrides.LA(self, name) local im = self:const() args.rs = args.rt - args.immediate = withflag(im, 'portion', 'upperoff') + args.immediate = self:token(im):set('portion', 'upperoff') self:format_out(lui, args) - args.immediate = withflag(im, 'portion', 'lower') + args.immediate = self:token(im):set('portion', 'lower') self:format_out(addiu, args) end @@ -87,7 +93,7 @@ function overrides.PUSH(self, name) if name == 'PUSH' then args.rt = 'SP' args.rs = 'SP' - args.immediate = withflag(#stack*4, 'negate') + args.immediate = self:token(#stack*4):set('negate') self:format_out(addi, args) end args.base = 'SP' @@ -241,7 +247,7 @@ function overrides.BEQI(self, name) self:optional_comma() args.immediate = self:const() self:optional_comma() - args.offset = withflag(self:const('relative'), 'signed') + args.offset = self:token(self:const('relative')):set('signed') if reg == 'AT' then self:error('register cannot be AT in this pseudo-instruction') @@ -264,7 +270,7 @@ function overrides.BLTI(self, name) self:optional_comma() args.immediate = self:const() self:optional_comma() - args.offset = withflag(self:const('relative'), 'signed') + args.offset = self:token(self:const('relative')):set('signed') if args.rs == 'AT' then self:error('register cannot be AT in this pseudo-instruction') @@ -290,7 +296,7 @@ function overrides.BLEI(self, name) self:optional_comma() args.immediate = self:const() self:optional_comma() - local offset = withflag(self:const('relative'), 'signed') + local offset = self:token(self:const('relative')):set('signed') if reg == 'AT' then self:error('register cannot be AT in this pseudo-instruction') diff --git a/lips/util.lua b/lips/util.lua index bc1af31..e545561 100644 --- a/lips/util.lua +++ b/lips/util.lua @@ -16,33 +16,6 @@ local function Class(inherit) return setmetatable(class, mt_class) end -local function construct(t) - if type(t) == 'table' then - return t - elseif type(t) == 'string' then - return {'REG', t} - elseif type(t) == 'number' then - return {'NUM', t} - else - error('Internal Error: unknown type to construct') - end -end - -local function withflag(t, key, value) - if type(t) == 'table' then - t = {t[1], t[2]} - else - t = construct(t) - end - if value == nil then - value = true - end - if key ~= nil then - t[key] = value - end - return t -end - local function readfile(fn) local f = open(fn, 'r') if not f then @@ -59,8 +32,6 @@ end return { Class = Class, - construct = construct, - withflag = withflag, readfile = readfile, bitrange = bitrange, }