From 3f318bd0dbc84e67e5ab838c3d5a7504977464bd Mon Sep 17 00:00:00 2001 From: Connor Olding Date: Sun, 27 Nov 2016 22:02:14 -0800 Subject: [PATCH] update lips --- Lua/lib/lips/Expander.lua | 2 +- Lua/lib/lips/Expression.lua | 18 ++++- Lua/lib/lips/Lexer.lua | 14 ++++ Lua/lib/lips/Preproc.lua | 138 +++++++++++++++++------------------- Lua/lib/lips/TokenIter.lua | 1 + 5 files changed, 97 insertions(+), 76 deletions(-) diff --git a/Lua/lib/lips/Expander.lua b/Lua/lib/lips/Expander.lua index 6664654..08e885f 100644 --- a/Lua/lib/lips/Expander.lua +++ b/Lua/lib/lips/Expander.lua @@ -48,7 +48,7 @@ function Expander:pop(kind) end function Expander:expand(statements) - -- fourth pass: expand pseudo-instructions and register arguments + -- third pass: expand pseudo-instructions and register arguments self.statements = {} for i, s in ipairs(statements) do self.s = s diff --git a/Lua/lib/lips/Expression.lua b/Lua/lib/lips/Expression.lua index 4d4cfcb..022c765 100644 --- a/Lua/lib/lips/Expression.lua +++ b/Lua/lib/lips/Expression.lua @@ -4,6 +4,9 @@ local path = string.gsub(..., "[^.]+$", "") local Base = require(path.."Base") local Expression = Base:extend() +function Expression:init(variables) + self.variables = variables or {} +end Expression.precedence = { -- python-ish precedence @@ -80,6 +83,7 @@ Expression.binary_ops = { local operators = {} local operators_maxlen = 0 do + -- reorder operators so we can match the longest strings first for k, v in pairs(Expression.precedence) do if operators[#k] == nil then operators[#k] = {} @@ -150,9 +154,12 @@ function Expression:lex1(str, tokens) num = tonumber(considered, 2) elseif consider('0x[0-9A-Fa-f]+') then num = tonumber(considered, 16) - elseif consider('0[0-7]+') then + elseif consider('0[0-9]+') then + if considered:match('[89]') then + return "bad octal number: "..considered..here + end num = tonumber(considered, 8) - elseif consider('[1-9][0-9]*') then + elseif consider('[0-9]*') then num = tonumber(considered) end if num == nil then @@ -169,6 +176,13 @@ function Expression:lex1(str, tokens) elseif consider_operator() then insert(tokens, {type='operator', value=considered}) consume(#considered) + elseif consider('%w+') then + local num = self.variables[considered] + if num == nil then + return 'undefined variable "'..considered..'"' + end + insert(tokens, {type='number', value=num}) + consume(#considered) else local chr = rest:sub(1, 1) return "unexpected character '"..chr.."'"..here diff --git a/Lua/lib/lips/Lexer.lua b/Lua/lib/lips/Lexer.lua index 4194ade..ff7318c 100644 --- a/Lua/lib/lips/Lexer.lua +++ b/Lua/lib/lips/Lexer.lua @@ -329,6 +329,7 @@ function Lexer:lex_include_binary(_yield) local data = util.readfile(fn, true) _yield('DIR', 'BIN', fn, 0) _yield('STRING', data, fn, 0) + _yield('EOF', self.EOF, self.fn, self.line) end function Lexer:lex_expression(yield) @@ -392,6 +393,9 @@ function Lexer:lex(_yield) yield('SEP', ',') elseif self.chr == '[' then self:nextc() + if self.chr:find('%d') then + self:error('variable names cannot begin with a number') + end local buff = self:read_chars('[%w_]') if self.chr ~= ']' then self:error('invalid variable name') @@ -402,6 +406,13 @@ function Lexer:lex(_yield) end self:nextc() yield('VAR', buff) + self:read_spaces() + if self.chr == '@' then + -- old syntax; nothing to do here + else + buff = self:read_chars('[^;\n]') + yield('EXPR', buff) + end elseif self.chr == ']' then self:error('unmatched closing bracket') elseif self.chr == '(' then @@ -433,6 +444,9 @@ function Lexer:lex(_yield) self:lex_string(yield) elseif self.chr == '@' then self:nextc() + if self.chr:find('%d') then + self:error('variable names cannot begin with a number') + end local buff = self:read_chars('[%w_]') yield('VARSYM', buff) elseif self.chr == '%' then diff --git a/Lua/lib/lips/Preproc.lua b/Lua/lib/lips/Preproc.lua index a4a9137..8c4c515 100644 --- a/Lua/lib/lips/Preproc.lua +++ b/Lua/lib/lips/Preproc.lua @@ -1,4 +1,5 @@ local abs = math.abs +local format = string.format local insert = table.insert local path = string.gsub(..., "[^.]+$", "") @@ -13,6 +14,13 @@ function Preproc:init(options) self.options = options or {} end +function Preproc:error(msg, got) + if got ~= nil then + msg = msg..', got '..tostring(got) + end + error(format('%s:%d: Error: %s', self.fn, self.line, msg), 2) +end + function Preproc:iter(statements) assert(statements) local i = 0 @@ -36,74 +44,64 @@ function Preproc:lookup(t) 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(self.i)..name - elseif t.tt == 'RELLABELSYM' then - local i = self.i - t.tt = 'LABELSYM' + end +end - local rel = signs(t.tok) - assert(rel ~= 0, 'Internal Error: relative label without signs') +function Preproc:resolve(t) + 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(self.i)..name + elseif t.tt == 'RELLABELSYM' then + local i = self.i + t.tt = 'LABELSYM' - local name = t.tok:sub(abs(rel) + 1) - local seen = 0 + local rel = signs(t.tok) + assert(rel ~= 0, 'Internal Error: relative label without signs') - -- 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(self.plus_labels) do - if rl.name == name and rl.index > i then - seen = seen + 1 - if seen == rel then - t.tok = tostring(rl.index)..name - break - end - end - end - else - for _, rl in ipairs(self.minus_labels) do - if rl.name == name and rl.index < i then - seen = seen - 1 - if seen == rel then - t.tok = tostring(rl.index)..name - break - end + local name = t.tok:sub(abs(rel) + 1) + local seen = 0 + + -- 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(self.plus_labels) do + if rl.name == name and rl.index > i then + seen = seen + 1 + if seen == rel then + t.tok = tostring(rl.index)..name + break end end end - - if seen ~= rel then - self:error('could not find appropriate relative label', t.tok) + else + for _, rl in ipairs(self.minus_labels) do + if rl.name == name and rl.index < i then + seen = seen - 1 + if seen == rel then + t.tok = tostring(rl.index)..name + break + end + end end end - else - return false + + if seen ~= rel then + self:error('could not find appropriate relative label', t.tok) + end end - return true end function Preproc:check(s, i, tt) - s = s or self.s - i = i or self.i local t = s[i] if t == nil then local err = ("expected another argument for %s at position %i"):format(self.s.type, self.i) self:error(err) end - self.fn = t.fn - self.line = t.line - - if t.tt ~= tt then - self:lookup(t) - 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) @@ -111,16 +109,30 @@ function Preproc:check(s, i, tt) return t.tok end +function Preproc:evaluate(t) + if t.tt == 'EXPR' then + local result, err = self.expr:eval(t.tok) + if err then + self:error('failed to evaulate ('..t.tok..')', err) + end + t.tt = 'NUM' + t.tok = result + end + self:lookup(t) +end + function Preproc:process(statements) self.variables = {} self.plus_labels = {} -- constructed forwards self.minus_labels = {} -- constructed backwards - self.do_labels = false + self.expr = Expression(self.variables) -- first pass: resolve variables and collect relative labels local new_statements = {} for s in self:iter(statements) do - -- directive, label, etc. + for j, t in ipairs(s) do + self:evaluate(t) + end if s.type == '!VAR' then local a = self:check(s, 1, 'VAR') local b = self:check(s, 2, 'NUM') @@ -143,34 +155,14 @@ function Preproc:process(statements) end insert(new_statements, s) else - -- regular instruction - for j, t in ipairs(s) do - self:lookup(t) - end insert(new_statements, s) end end -- second pass: resolve relative labels - self.do_labels = true for s in self:iter(new_statements) do for j, t in ipairs(s) do - self:lookup(t) - end - end - - -- third pass: evaluate constant expressions - for s in self:iter(new_statements) do - for j, t in ipairs(s) do - if t.tt == 'EXPR' then - local expr = Expression() - local result, err = expr:eval(t.tok) - if err then - self:error('failed to evaulate ('..t.tok..')', err) - end - t.tt = 'NUM' - t.tok = result - end + self:resolve(t) end end diff --git a/Lua/lib/lips/TokenIter.lua b/Lua/lib/lips/TokenIter.lua index 3ad5173..a63032f 100644 --- a/Lua/lib/lips/TokenIter.lua +++ b/Lua/lib/lips/TokenIter.lua @@ -18,6 +18,7 @@ end TokenIter.arg_types = { NUM = true, + EXPR = true, REG = true, VARSYM = true, LABELSYM = true,