@@ -1,96 +0,0 @@ | |||
// 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; | |||
} | |||
}); | |||
@@ -1,304 +0,0 @@ | |||
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 | |||
}); | |||
@@ -0,0 +1,338 @@ | |||
//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; | |||
@@ -1,4 +1,4 @@ | |||
const OP = require('./op.js'); | |||
function GetTextStream(input){ | |||
var pos = 0; | |||
@@ -23,6 +23,9 @@ function GetTextStream(input){ | |||
return (input.charAt(pos) === ""); | |||
} | |||
function getPos(){return pos;} | |||
function getLength(){return input.length;} | |||
function getLine(){return line;} | |||
function getCol(){return col;} | |||
@@ -36,6 +39,8 @@ function GetTextStream(input){ | |||
eof: eof, | |||
line: getLine, | |||
col: getCol, | |||
pos: getPos, | |||
length: getLength, | |||
die: die | |||
}; | |||
} | |||
@@ -92,9 +97,22 @@ function isOp(c){ | |||
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){ | |||
return { | |||
@@ -170,7 +188,7 @@ class Tokenizer{ | |||
readLabel(){ | |||
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('label', str); | |||
} | |||
@@ -205,25 +223,27 @@ class Tokenizer{ | |||
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; | |||