|
|
@@ -1,5 +1,12 @@ |
|
|
|
const OP = require('./op.js'); |
|
|
|
|
|
|
|
const PRECEDENCE = { |
|
|
|
"#": 0, // Precedence 0 should be ignored! |
|
|
|
"=": 1, |
|
|
|
"<":7, "<=":7, ">":7, ">=":7, "==":7, "!=":7, |
|
|
|
"+": 10, "-": 10, |
|
|
|
"*": 20, "/": 20 |
|
|
|
}; |
|
|
|
|
|
|
|
function TokenStream(input){ |
|
|
|
var pos = 0; |
|
|
@@ -50,11 +57,7 @@ var stream = null; |
|
|
|
|
|
|
|
function isTokenType(type, val){ |
|
|
|
let t = stream.peek(); |
|
|
|
if (t !== null && t.type === type){ |
|
|
|
if (!val || t.val === val) |
|
|
|
return true; |
|
|
|
} |
|
|
|
return false; |
|
|
|
return (t && t.type === type && (!val || t.val === val) && t); |
|
|
|
} |
|
|
|
|
|
|
|
function isPunc(ch){ |
|
|
@@ -79,15 +82,13 @@ function isOp(ch){ |
|
|
|
|
|
|
|
|
|
|
|
function skipPunc(ch){ |
|
|
|
if (isPunc(ch)){ |
|
|
|
stream.next(); |
|
|
|
} else { |
|
|
|
if (!isPunc(ch)) |
|
|
|
stream.die("Unexpected punctuation '" + ch + "'."); |
|
|
|
} |
|
|
|
stream.next(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
parseDelimited(s,e,d,parser){ |
|
|
|
function parseDelimited(s,e,d,parser){ |
|
|
|
let toEOL = (s === null || e === null); |
|
|
|
let a = []; |
|
|
|
let first = true; |
|
|
@@ -102,27 +103,34 @@ parseDelimited(s,e,d,parser){ |
|
|
|
return a; |
|
|
|
} |
|
|
|
|
|
|
|
function parseByteDirective(){ |
|
|
|
stream.next(); |
|
|
|
return { |
|
|
|
type: "directive", |
|
|
|
op: ".byte", |
|
|
|
args: parseDelimited(null, null, ",", parseExpression); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
parseOpCode(){ |
|
|
|
function parseOpCode(){ |
|
|
|
let val = stream.next(); |
|
|
|
let mode = 0; // Guess between absolute and zero page. |
|
|
|
let args = []; // TODO: Finish figuring out how to get the argument list! |
|
|
|
if (isOp("#")){ |
|
|
|
stream.next(); |
|
|
|
mode = 1; // Immediate |
|
|
|
} else if (isPunc("(")){ |
|
|
|
mode = 2; // Indirect |
|
|
|
// TODO: Use parseDelimited() |
|
|
|
} |
|
|
|
} |
|
|
|
return { |
|
|
|
type: "opcode", |
|
|
|
val: val, |
|
|
|
args: args, |
|
|
|
args: parseDelimited(null, null, ",", parseExpression), |
|
|
|
mode: mode |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
parseAtom(){ |
|
|
|
function parseAtom(){ |
|
|
|
if (isPunc("(")){ |
|
|
|
stream.next(); |
|
|
|
let exp = parseExpression(); |
|
|
@@ -132,18 +140,70 @@ parseAtom(){ |
|
|
|
} |
|
|
|
} else if (isOpCode()){ |
|
|
|
return parseOpCode(); |
|
|
|
} else if (isDirective(".byte")){ |
|
|
|
return parseByteDirective(); |
|
|
|
} |
|
|
|
|
|
|
|
let tok = stream.next(); |
|
|
|
if (tok.type === "number" || tok.type === "string" || tok.type === "label") |
|
|
|
return tok; |
|
|
|
stream.die("Unexpected token " + tok); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
parse(tokens){ |
|
|
|
function parseExpression(){ |
|
|
|
maybeCall(function(){ |
|
|
|
return maybeBinary(parseAtom, 0); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
function parseCall(func){ |
|
|
|
return { |
|
|
|
type: "call", |
|
|
|
func: func, |
|
|
|
args: parseDelimited("(", ")", ",", parseExpression); |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
function maybeCall(expr){ |
|
|
|
expr = expr(); |
|
|
|
return (isOp("(")) ? parseCall(expr) : expr; |
|
|
|
} |
|
|
|
|
|
|
|
function maybeBinary(left, pres){ |
|
|
|
let tok = isOp(); |
|
|
|
if (tok){ |
|
|
|
let cpres = PRECEDENCE[tok.val]; |
|
|
|
if (cpres > pres){ |
|
|
|
stream.next(); |
|
|
|
return maybeBinary({ |
|
|
|
type: (tok.val === "=") ? "assign" : "binary", |
|
|
|
op: tok.val, |
|
|
|
left: left, |
|
|
|
right: maybeBinary(parseAtom(), cpres) |
|
|
|
}, pres); |
|
|
|
} |
|
|
|
} |
|
|
|
return left; |
|
|
|
} |
|
|
|
|
|
|
|
function parse(tokens){ |
|
|
|
let p = { |
|
|
|
type: "prog", |
|
|
|
expressions: []; |
|
|
|
}; |
|
|
|
stream = TokenStream(tokens); |
|
|
|
while (!stream.eof()){ |
|
|
|
p.expressions.push(parseExpression()); |
|
|
|
let e = parseExpression(); |
|
|
|
if (e.type === "label"){ |
|
|
|
e = { |
|
|
|
type: "assign", |
|
|
|
op: "=", |
|
|
|
left: e, |
|
|
|
right: "*" // To designate "current program counter". |
|
|
|
} |
|
|
|
} |
|
|
|
p.expressions.push(e); |
|
|
|
} |
|
|
|
stream = null; |
|
|
|
return p; |