// Each array orders op codes as... | |||||
// [<immediate>, <zero page>, <zero page X/Y>, <absolute>, <absolute X>, <absolute Y>, <Indirect X>, <Indirect Y>, <Indirect>, <accumulator>] | |||||
// If an opcode does not support a particular format, null will be in that space. | |||||
// NOTE: Opcode that do not have arguments will store their opcode value directly (no array). | |||||
const CODES = { | |||||
"ADC":[0x69, 0x65, 0x75, 0x6D, 0x7D, 0x79, 0x61, 0x71, null, null], | |||||
"AND":[0x29, 0x25, 0x35, 0x2D, 0x3D, 0x39, 0x21, 0x31, null, null], | |||||
"ASL":[null, 0x06, 0x16, 0x0E, 0x1E, null, null, null, null, 0x0A], | |||||
"BCC":0x90, | |||||
"BCS":0xB0, | |||||
"BEQ":0xF0, | |||||
"BIT":[null, 0x24, null, 0x2C, null, null, null, null, null, null], | |||||
"BMI":0x30, | |||||
"BNE":0xD0, | |||||
"BPL":0x10, | |||||
"BRK":0x00, | |||||
"BVC":0x50, | |||||
"BVS":0x70, | |||||
"CLC":0x18, | |||||
"CLD":0xD8, | |||||
"CLI":0x58, | |||||
"CLV":0xB8, | |||||
"CMP":[0xC9, 0xC5, 0xD5, 0xCD, 0xDD, 0xD9, 0xC1, 0xD1, null, null], | |||||
"CPX":[0xE0, 0xE4, null, 0xEC, null, null, null, null, null, null], | |||||
"CPY":[0xC0, 0xC4, null, 0xCC, null, null, null, null, null, null], | |||||
"DEC":[null, 0xC6, 0xD6, 0xCE, 0xDE, null, null, null, null, null], | |||||
"DEX":0xCA, | |||||
"DEY":0x88, | |||||
"EOR":[0x49, 0x45, 0x55, 0x4D, 0x5D, 0x59, 0x41, 0x51, null, null], | |||||
"INC":[null, 0xE6, 0xF6, 0xEE, 0xFE, null, null, null, null, null], | |||||
"INX":0xE8, | |||||
"INY":0xC8, | |||||
"JMP":[null, null, null, 0x4C, null, null, null, null, 0x6C, null], | |||||
"JSR":[null, null, null, 0x20, null, null, null, null, null, null], | |||||
"LDA":[0xA9, 0xA5, 0xB5, 0xAD, 0xBD, 0xB9, 0xA1, 0xB1, null, null], | |||||
"LDX":[0xA2, 0xA6, 0xB6, 0xAE, 0xBE, null, null, null, null, null], | |||||
"LDY":[0xA0, 0xA4, 0xB4, 0xAC, 0xBC, null, null, null, null, null], | |||||
"LSR":[null, 0x46, 0x56, 0x4E, 0x5E, null, null, null, null, 0x4A], | |||||
"NOP":0xEA, | |||||
"ORA":[0x09, 0x05, 0x15, 0x0D, 0x1D, 0x19, 0x01, 0x11, null, null], | |||||
"PHA":0x48, | |||||
"PHP":0x08, | |||||
"PLA":0x68, | |||||
"PLP":0x28, | |||||
"ROL":[null, 0x26, 0x36, 0x2E, 0x3E, null, null, null, null, 0x2A], | |||||
"ROR":[null, 0x66, 0x76, 0x6E, 0x7E, null, null, null, null, 0x6A], | |||||
"RTI":0x40, | |||||
"RTS":0x60, | |||||
"SBC":[0xE9, 0xE5, 0xF5, 0xED, 0xFD, 0xF9, 0xE1, 0xF1, null, null], | |||||
"SEC":0x38, | |||||
"SED":0xF8, | |||||
"SEI":0x78, | |||||
"STA":[null, 0x85, 0x95, 0x8D, 0x9D, 0x99, 0x81, 0x91, null, null], | |||||
"STX":[null, 0x86, 0x96, 0x8E, null, null, null, null, null, null], | |||||
"STY":[null, 0x84, 0x94, 0x8C, null, null, null, null, null, null], | |||||
"TAX":0xAA, | |||||
"TAY":0xA8, | |||||
"TSX":0xBA, | |||||
"TXA":0x8A, | |||||
"TXS":0x9A, | |||||
"TYA":0x98 | |||||
}; | |||||
var NAMES = Object.keys(CODES); | |||||
module.exports = Object.freeze({ | |||||
MODES:{ | |||||
IMMEDIATE: 0, | |||||
ZEROPAGE: 1, | |||||
ZEROPAGEXY: 2, | |||||
ABSOLUTE: 3, | |||||
ABSOLUTEX: 4, | |||||
ABSOLUTEY: 5, | |||||
INDIRECTX: 6, | |||||
INDIRECTY: 7, | |||||
INDIRECT: 8, | |||||
ACCUMULATOR: 9 | |||||
}, | |||||
CODES: CODES, | |||||
NAMES: NAMES, | |||||
isCode:function(op){return (NAMES.indexOf(op) >= 0);}, | |||||
getCode:function(op, mode){ | |||||
if (NAMES.indexOf(op) >= 0){ | |||||
if (typeof(CODES[op]) === 'number') | |||||
return CODES[op]; | |||||
return (!(mode >= 0 && mode <= 9)) ? CODES[op][mode] : null; | |||||
} | |||||
return null; | |||||
} | |||||
}); | |||||
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; | |||||
function peek(){ | |||||
return (pos < input.length) ? input[pos] : null; | |||||
} | |||||
function next(){ | |||||
if (pos < input.length){ | |||||
let v = input[pos]; | |||||
pos += 1; | |||||
return v; | |||||
} | |||||
return null; | |||||
} | |||||
function line(){ | |||||
return (pos < input.length) ? input[pos].line : -1; | |||||
} | |||||
function col(){ | |||||
return (pos < input.length) ? input[pos].col : -1; | |||||
} | |||||
function eol(){ | |||||
return (pos > 0) ? (input[pos-1].line !== input[pos].line) : true; | |||||
} | |||||
function eof(){ | |||||
return (pos >= input.length); | |||||
} | |||||
function die(msg){ | |||||
throw new Error(msg + " Line: " + input[pos].line + ", Col: " + input[pos].col); | |||||
} | |||||
return { | |||||
peek: peek, | |||||
next: next, | |||||
line: line, | |||||
col: col, | |||||
eol: eol, | |||||
eof, eof, | |||||
die: die | |||||
}; | |||||
} | |||||
var stream = null; | |||||
function isTokenType(type, val){ | |||||
let t = stream.peek(); | |||||
return (t && t.type === type && (!val || t.val === val) && t); | |||||
} | |||||
function isPunc(ch){ | |||||
return isTokenType("punc", ch); | |||||
} | |||||
function isOpCode(ch){ | |||||
return isTokenType("opcode", ch); | |||||
} | |||||
function isLabel(ch){ | |||||
return isTokenType("label", ch); | |||||
} | |||||
function isDirective(ch){ | |||||
return isTokenType("directive", ch); | |||||
} | |||||
function isOp(ch){ | |||||
return isTokenType("op", ch); | |||||
} | |||||
function skipPunc(ch){ | |||||
if (!isPunc(ch)) | |||||
stream.die("Unexpected punctuation '" + ch + "'."); | |||||
stream.next(); | |||||
} | |||||
function skipDirective(ch){ | |||||
if (!isDirective(ch)) | |||||
stream.die("Unexpected directive '" + ch + "'."); | |||||
stream.next(); | |||||
} | |||||
function parseDelimited(s,e,d,parser){ | |||||
let toEOL = (s === null || e === null); | |||||
let a = []; | |||||
let first = true; | |||||
if (!toEOL){skipPunc(s);} | |||||
while (!stream.eof() && ((!toEOL && isPunc(e)) || (toEOL && !stream.eol()))){ | |||||
if (first){ | |||||
first = false; | |||||
} else {skipPunc(d);} | |||||
a.push(parser()); | |||||
} | |||||
if (!toEOL){skipPunc(e);} | |||||
return a; | |||||
} | |||||
function parseByteDirective(){ | |||||
let line = stream.line(); | |||||
let col = stream.col(); | |||||
stream.next(); | |||||
return { | |||||
type: "directive", | |||||
op: ".byte", | |||||
args: parseDelimited(null, null, ",", parseExpression), | |||||
line: line, | |||||
col: col | |||||
} | |||||
} | |||||
function parseElseIfDirective(){ | |||||
skipDirective(".elseif"); | |||||
let cond = parseExpression(); | |||||
skipDirective(".then"); | |||||
let then = parseBlock([".else", ".elif", ".endif"]); | |||||
let f = { | |||||
type: "directive", | |||||
op: "if", | |||||
cond: cond, | |||||
then: then | |||||
} | |||||
if (isDirective(".elif")){ | |||||
f["else"] = parseElseIfDirective(); | |||||
} else if (isDirective(".else")){ | |||||
f["else"] = parseBlock([".endif"]); | |||||
} else if(!isDirective(".endif")){ | |||||
stream.die("Expected '.endif' Directive."); | |||||
} | |||||
return f; | |||||
} | |||||
function parseIfDirective(){ | |||||
skipDirective(".if"); | |||||
let cond = parseExpression(); | |||||
skipDirective(".then"); | |||||
let then = parseExpression(); | |||||
let f = { | |||||
type: "directive", | |||||
op: "if", | |||||
cond: cond, | |||||
then: then | |||||
}; | |||||
if (isDirective(".elif")){ | |||||
f["else"] = parseElseIfDirective(); | |||||
} else if (isDirective(".else")){ | |||||
f["else"] = parseBlock([".endif"]); | |||||
} | |||||
skipDirective(".endif"); | |||||
return f; | |||||
} | |||||
function parseOpCode(){ | |||||
let line = stream.line(); | |||||
let col = stream.col(); | |||||
let val = stream.next(); | |||||
let mode = 0; // Guess between absolute and zero page. | |||||
if (isOp("#")){ | |||||
stream.next(); | |||||
mode = 1; // Immediate | |||||
} else if (isPunc("(")){ | |||||
mode = 2; // Indirect | |||||
} | |||||
return { | |||||
type: "opcode", | |||||
op: val, | |||||
args: parseDelimited(null, null, ",", parseExpression), | |||||
mode: mode, | |||||
line: line, | |||||
col: col | |||||
}; | |||||
} | |||||
function parseAtom(){ | |||||
if (isPunc("(")){ | |||||
stream.next(); | |||||
let exp = parseExpression(); | |||||
if (isPunc(")")){ | |||||
stream.next(); | |||||
return exp; | |||||
} | |||||
} else if (isOpCode()){ | |||||
return parseOpCode(); | |||||
} else if (isDirective(".if")){ | |||||
return parseIfDirective(); | |||||
} else if (isDirective(".bytes")){ | |||||
return parseByteDirective(); | |||||
} | |||||
let tok = stream.next(); | |||||
if (tok.type === "number" || tok.type === "string" || tok.type === "label") | |||||
return tok; | |||||
stream.die("Unexpected token {type:" + tok.type + ", val:'" + tok.val + "'}."); | |||||
} | |||||
function parseExpression(){ | |||||
return 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), | |||||
line: tok.line, | |||||
col: tok.col | |||||
}, pres); | |||||
} | |||||
} | |||||
return left; | |||||
} | |||||
function parseBlock(bed){ | |||||
let exp = []; | |||||
let isBlockEnd = (t) => { | |||||
return (bed && t.type === 'directive' && bed.indexOf(t.val) >= 0); | |||||
} | |||||
while (!stream.eof()){ | |||||
if (isBlockEnd(stream.peek())) | |||||
break; | |||||
let e = parseExpression(); | |||||
if (e.type === "label"){ | |||||
e = { | |||||
type: "assign", | |||||
op: "=", | |||||
left: e, | |||||
right: "*", // To designate "current program counter". | |||||
line: e.line, | |||||
col: e.col | |||||
} | |||||
} | |||||
exp.push(e); | |||||
if (!stream.eol()) | |||||
stream.die("Expected End of Line."); | |||||
} | |||||
return exp; | |||||
} | |||||
function parseProg(ed){ | |||||
let line = stream.line(); | |||||
let col = stream.col(); | |||||
return { | |||||
type: "prog", | |||||
expressions: parseBlock(), | |||||
line: line, | |||||
col: col | |||||
}; | |||||
} | |||||
function parse(tokens){ | |||||
stream = TokenStream(tokens); | |||||
let p = parseProg(); | |||||
stream = null; | |||||
return p; | |||||
} | |||||
module.exports = Object.freeze({ | |||||
parse: parse | |||||
}); | |||||
//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; | |||||
function peek(){ | |||||
return (pos < input.length) ? input[pos] : null; | |||||
} | |||||
function next(){ | |||||
if (pos < input.length){ | |||||
let v = input[pos]; | |||||
pos += 1; | |||||
return v; | |||||
} | |||||
return null; | |||||
} | |||||
function line(){ | |||||
return (pos < input.length) ? input[pos].line : -1; | |||||
} | |||||
function col(){ | |||||
return (pos < input.length) ? input[pos].col : -1; | |||||
} | |||||
function eol(){ | |||||
return (pos > 0 && pos < input.length) ? (input[pos-1].line !== input[pos].line) : true; | |||||
} | |||||
function eof(){ | |||||
return (pos >= input.length); | |||||
} | |||||
function die(msg){ | |||||
throw new Error(msg + " Line: " + input[pos].line + ", Col: " + input[pos].col); | |||||
} | |||||
function getPos(){return pos;} | |||||
return { | |||||
peek: peek, | |||||
next: next, | |||||
line: line, | |||||
col: col, | |||||
pos: getPos, | |||||
eol: eol, | |||||
eof, eof, | |||||
die: die | |||||
}; | |||||
} | |||||
// var stream = null; | |||||
// var SKIPEOL = false; | |||||
function isTokenType(stream, type, val){ | |||||
let t = stream.peek(); | |||||
return (t && t.type === type && (!val || t.val === val) && t); | |||||
} | |||||
class Parser{ | |||||
constructor(pof){ | |||||
if (typeof(pof) !== 'function') | |||||
throw new TypeError("Expected op code parsing function."); | |||||
this.__stream = null; | |||||
this.__SKIPEOL = false; | |||||
this.__output = null; | |||||
this.parseOpCode = pof(this); | |||||
} | |||||
set tokens(t){ | |||||
this.__stream = new TokenStream(t); | |||||
this.__output = null; | |||||
this.__SKIPEOL = false; | |||||
} | |||||
get stream(){return this.__stream;} | |||||
isPunc(ch){ | |||||
return isTokenType(this.stream, "punc", ch); | |||||
} | |||||
isOpCode(ch){ | |||||
return isTokenType(this.stream, "opcode", ch); | |||||
} | |||||
isLabel(ch){ | |||||
return isTokenType(this.stream, "label", ch); | |||||
} | |||||
isDirective(ch){ | |||||
return isTokenType(this.stream, "directive", ch); | |||||
} | |||||
isOp(ch){ | |||||
return isTokenType(this.stream, "op", ch); | |||||
} | |||||
skipPunc(ch){ | |||||
if (!this.isPunc(ch)) | |||||
this.stream.die("Unexpected punctuation '" + ch + "'."); | |||||
this.stream.next(); | |||||
} | |||||
skipDirective(ch){ | |||||
if (!this.isDirective(ch)) | |||||
this.stream.die("Unexpected directive '" + ch + "'."); | |||||
this.stream.next(); | |||||
} | |||||
parseDelimited(s,e,d,parser){ | |||||
let toEOL = (s === null || e === null); | |||||
let a = []; | |||||
let first = true; | |||||
if (!toEOL){this.skipPunc(s);} | |||||
while (!this.stream.eof() && ((!toEOL && this.isPunc(e)) || (toEOL && !this.stream.eol()))){ | |||||
if (first){ | |||||
first = false; | |||||
} else {this.skipPunc(d);} | |||||
a.push(parser()); | |||||
} | |||||
if (!toEOL){skipPunc(e);} | |||||
return a; | |||||
} | |||||
parseByteDirective(){ | |||||
let line = this.stream.line(); | |||||
let col = this.stream.col(); | |||||
this.stream.next(); | |||||
return { | |||||
type: "directive", | |||||
op: ".byte", | |||||
args: this.parseDelimited(null, null, | |||||
",", | |||||
this.parseExpression.bind(this) | |||||
), | |||||
line: line, | |||||
col: col | |||||
} | |||||
} | |||||
parseElseIfDirective(){ | |||||
this.skipDirective(".elif"); | |||||
let cond = this.parseExpression(); | |||||
this.skipDirective(".then"); | |||||
let then = this.parseBlock([".else", ".elif", ".endif"]); | |||||
let f = { | |||||
type: "directive", | |||||
op: "if", | |||||
cond: cond, | |||||
then: then | |||||
} | |||||
if (this.isDirective(".elif")){ | |||||
f["else"] = this.parseElseIfDirective(); | |||||
} else if (this.isDirective(".else")){ | |||||
this.skipDirective(".else"); | |||||
f["else"] = this.parseBlock([".endif"]); | |||||
} else if(!this.isDirective(".endif")){ | |||||
this.stream.die("Expected '.endif' Directive."); | |||||
} | |||||
return f; | |||||
} | |||||
parseIfDirective(){ | |||||
this.skipDirective(".if"); | |||||
let cond = this.parseExpression(); | |||||
this.skipDirective(".then"); | |||||
let then = this.parseBlock([".elif", ".else", ".endif"]); | |||||
let f = { | |||||
type: "directive", | |||||
op: "if", | |||||
cond: cond, | |||||
then: then | |||||
}; | |||||
if (this.isDirective(".elif")){ | |||||
f["else"] = this.parseElseIfDirective(); | |||||
} else if (this.isDirective(".else")){ | |||||
this.skipDirective(".else"); | |||||
f["else"] = this.parseBlock([".endif"]); | |||||
} | |||||
this.skipDirective(".endif"); | |||||
return f; | |||||
} | |||||
parseLabel(t){ | |||||
if (!this.stream.eof()){ | |||||
if (this.stream.peek().type === "opcode"){ | |||||
this.__SKIPEOL = true; | |||||
return { | |||||
type: "assign", | |||||
op: "=", | |||||
left: t, | |||||
right: "*", // To designate "current program counter". | |||||
line: t.line, | |||||
col: t.col | |||||
} | |||||
} | |||||
} | |||||
return t; | |||||
} | |||||
parseAtom(){ | |||||
if (this.isPunc("(")){ | |||||
this.stream.next(); | |||||
let exp = this.parseExpression(); | |||||
if (this.isPunc(")")){ | |||||
this.stream.next(); | |||||
return exp; | |||||
} | |||||
} else if (this.isOpCode()){ | |||||
return this.parseOpCode(); | |||||
} else if (this.isDirective(".if")){ | |||||
return this.parseIfDirective(); | |||||
} else if (this.isDirective(".bytes")){ | |||||
return this.parseByteDirective(); | |||||
} | |||||
let tok = this.stream.next(); | |||||
if (tok.type === "number" || tok.type === "string" || tok.type === "label"){ | |||||
if (tok.type === "label") | |||||
return this.parseLabel(tok); | |||||
return tok; | |||||
} | |||||
this.stream.die("Unexpected token {type:" + tok.type + ", val:'" + tok.val + "'}."); | |||||
} | |||||
parseExpression(){ | |||||
return this.maybeCall((function(){ | |||||
return this.maybeBinary(this.parseAtom(), 0); | |||||
}).bind(this)); | |||||
} | |||||
parseCall(func){ | |||||
return { | |||||
type: "call", | |||||
func: func, | |||||
args: this.parseDelimited( | |||||
"(", ")", | |||||
",", | |||||
this.parseExpression.bind(this) | |||||
) | |||||
}; | |||||
} | |||||
maybeCall(expr){ | |||||
expr = expr(); | |||||
return (this.isOp("(")) ? this.parseCall(expr) : expr; | |||||
} | |||||
maybeBinary(left, pres){ | |||||
let tok = this.isOp(); | |||||
if (tok){ | |||||
let cpres = PRECEDENCE[tok.val]; | |||||
if (cpres > pres){ | |||||
this.stream.next(); | |||||
return this.maybeBinary({ | |||||
type: (tok.val === "=") ? "assign" : "binary", | |||||
op: tok.val, | |||||
left: left, | |||||
right: this.maybeBinary(this.parseAtom(), cpres), | |||||
line: tok.line, | |||||
col: tok.col | |||||
}, pres); | |||||
} | |||||
} | |||||
return left; | |||||
} | |||||
parseBlock(bed){ | |||||
let line = this.stream.line(); | |||||
let col = this.stream.col(); | |||||
let exp = []; | |||||
let isBlockEnd = (t) => { | |||||
return (bed && t.type === 'directive' && bed.indexOf(t.val) >= 0); | |||||
} | |||||
while (!this.stream.eof()){ | |||||
if (isBlockEnd(this.stream.peek())) | |||||
break; | |||||
exp.push(this.parseExpression()); | |||||
if (!this.stream.eol()){ | |||||
if (!this.__SKIPEOL) | |||||
this.stream.die("Expected End of Line."); | |||||
this.__SKIPEOL = false; | |||||
} | |||||
} | |||||
return { | |||||
type: "block", | |||||
expressions: exp, | |||||
line: line, | |||||
col: col | |||||
}; | |||||
} | |||||
parseProg(){ | |||||
let line = this.stream.line(); | |||||
let col = this.stream.col(); | |||||
return { | |||||
type: "prog", | |||||
block: this.parseBlock(), | |||||
line: line, | |||||
col: col | |||||
}; | |||||
} | |||||
parse(tokens){ | |||||
if (tokens) | |||||
this.tokens = tokens; | |||||
if (this.__stream !== null){ | |||||
if (this.__output === null && this.__stream.pos() === 0) | |||||
this.__output = this.parseProg(); | |||||
return this.__output; | |||||
} | |||||
return null; | |||||
} | |||||
} | |||||
module.exports = Parser; | |||||
const OP = require('./op.js'); | |||||
function GetTextStream(input){ | function GetTextStream(input){ | ||||
var pos = 0; | var pos = 0; | ||||
return (input.charAt(pos) === ""); | return (input.charAt(pos) === ""); | ||||
} | } | ||||
function getPos(){return pos;} | |||||
function getLength(){return input.length;} | |||||
function getLine(){return line;} | function getLine(){return line;} | ||||
function getCol(){return col;} | function getCol(){return col;} | ||||
eof: eof, | eof: eof, | ||||
line: getLine, | line: getLine, | ||||
col: getCol, | col: getCol, | ||||
pos: getPos, | |||||
length: getLength, | |||||
die: die | die: die | ||||
}; | }; | ||||
} | } | ||||
class Tokenizer{ | class Tokenizer{ | ||||
constructor(input){ | |||||
this.__stream = GetTextStream(input); | |||||
constructor(op, input){ | |||||
if (!(op.hasOwnProperty("isCode") && op.hasOwnProperty("getCode"))) | |||||
throw new Error("OP Code definition object missing required method(s)."); | |||||
this.__stream = (input) ? GetTextStream(input) : GetTextStream(""); | |||||
this.__output = null; | |||||
this.__op = op; | |||||
} | |||||
set input(i){ | |||||
if (typeof(i) === 'string'){ | |||||
this.__stream = GetTextStream(i); | |||||
this.__output = null; | |||||
} | |||||
} | } | ||||
get stream(){return this.__stream;} | |||||
genTokenObject(type, val, line, col){ | genTokenObject(type, val, line, col){ | ||||
return { | return { | ||||
readLabel(){ | readLabel(){ | ||||
var str = this.readWhile(isLabel); | var str = this.readWhile(isLabel); | ||||
if (OP.isCode(str.toUpperCase())) | |||||
if (this.__op.isCode(str.toUpperCase())) | |||||
return this.genTokenObject('opcode', str.toUpperCase()); | return this.genTokenObject('opcode', str.toUpperCase()); | ||||
return this.genTokenObject('label', str); | return this.genTokenObject('label', str); | ||||
} | } | ||||
this.__stream.die("Unable to process character '" + c + "'."); | this.__stream.die("Unable to process character '" + c + "'."); | ||||
} | } | ||||
} | |||||
tokenize(input){ | |||||
if (input) | |||||
this.input = input; | |||||
function tokenize(input){ | |||||
var tokenizer = new Tokenizer(input); | |||||
var tokens = []; | |||||
var t = tokenizer.nextToken(); | |||||
while (t !== null){ | |||||
tokens.push(t); | |||||
t = tokenizer.nextToken(); | |||||
if (this.__stream.length() > 0){ | |||||
if (this.__output === null && this.__stream.pos() === 0){ | |||||
this.__output = []; | |||||
let t = this.nextToken(); | |||||
while (t !== null){ | |||||
this.__output.push(t); | |||||
t = this.nextToken(); | |||||
} | |||||
} | |||||
return this.__output; | |||||
} | |||||
return []; | |||||
} | } | ||||
return tokens; | |||||
} | } | ||||
module.exports = Object.freeze({ | |||||
Tokenizer:Tokenizer, | |||||
tokenize:tokenize | |||||
}); | |||||
module.exports = Tokenizer; | |||||