1
0
Fork 0
mirror of https://github.com/notwa/mm synced 2024-11-05 04:39:03 -08:00

update lips

This commit is contained in:
Connor Olding 2016-11-27 22:02:14 -08:00
parent 8969788911
commit 3f318bd0db
5 changed files with 97 additions and 76 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -18,6 +18,7 @@ end
TokenIter.arg_types = {
NUM = true,
EXPR = true,
REG = true,
VARSYM = true,
LABELSYM = true,