| } | } | ||||
| function eol(){ | function eol(){ | ||||
| return (pos < input.length - 1) ? (input[pos].line !== input[pos+1].line) : true; | |||||
| return (pos > 0) ? (input[pos-1].line !== input[pos].line) : true; | |||||
| } | } | ||||
| function eof(){ | function eof(){ | ||||
| next: next, | next: next, | ||||
| line: line, | line: line, | ||||
| col: col, | col: col, | ||||
| eol: eol, | |||||
| eof, eof, | eof, eof, | ||||
| die: die | die: die | ||||
| }; | }; | ||||
| } | } | ||||
| function skipDirective(ch){ | |||||
| if (!isDirective(ch)) | |||||
| stream.die("Unexpected directive '" + ch + "'."); | |||||
| stream.next(); | |||||
| } | |||||
| function parseDelimited(s,e,d,parser){ | function parseDelimited(s,e,d,parser){ | ||||
| let toEOL = (s === null || e === null); | let toEOL = (s === null || e === null); | ||||
| let a = []; | let a = []; | ||||
| while (!stream.eof() && ((!toEOL && isPunc(e)) || (toEOL && !stream.eol()))){ | while (!stream.eof() && ((!toEOL && isPunc(e)) || (toEOL && !stream.eol()))){ | ||||
| if (first){ | if (first){ | ||||
| first = false; | first = false; | ||||
| } else {skipPunk(d);} | |||||
| } else {skipPunc(d);} | |||||
| a.push(parser()); | a.push(parser()); | ||||
| } | } | ||||
| if (!toEOL){skipPunc(e);} | if (!toEOL){skipPunc(e);} | ||||
| } | } | ||||
| function parseByteDirective(){ | function parseByteDirective(){ | ||||
| let line = stream.line(); | |||||
| let col = stream.col(); | |||||
| stream.next(); | stream.next(); | ||||
| return { | return { | ||||
| type: "directive", | type: "directive", | ||||
| op: ".byte", | op: ".byte", | ||||
| args: parseDelimited(null, null, ",", parseExpression); | |||||
| 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(){ | function parseOpCode(){ | ||||
| let line = stream.line(); | |||||
| let col = stream.col(); | |||||
| let val = stream.next(); | let val = stream.next(); | ||||
| let mode = 0; // Guess between absolute and zero page. | let mode = 0; // Guess between absolute and zero page. | ||||
| if (isOp("#")){ | if (isOp("#")){ | ||||
| } | } | ||||
| return { | return { | ||||
| type: "opcode", | type: "opcode", | ||||
| val: val, | |||||
| op: val, | |||||
| args: parseDelimited(null, null, ",", parseExpression), | args: parseDelimited(null, null, ",", parseExpression), | ||||
| mode: mode | |||||
| mode: mode, | |||||
| line: line, | |||||
| col: col | |||||
| }; | }; | ||||
| } | } | ||||
| } | } | ||||
| } else if (isOpCode()){ | } else if (isOpCode()){ | ||||
| return parseOpCode(); | return parseOpCode(); | ||||
| } else if (isDirective(".byte")){ | |||||
| } else if (isDirective(".if")){ | |||||
| return parseIfDirective(); | |||||
| } else if (isDirective(".bytes")){ | |||||
| return parseByteDirective(); | return parseByteDirective(); | ||||
| } | } | ||||
| let tok = stream.next(); | let tok = stream.next(); | ||||
| if (tok.type === "number" || tok.type === "string" || tok.type === "label") | if (tok.type === "number" || tok.type === "string" || tok.type === "label") | ||||
| return tok; | return tok; | ||||
| stream.die("Unexpected token " + tok); | |||||
| stream.die("Unexpected token {type:" + tok.type + ", val:'" + tok.val + "'}."); | |||||
| } | } | ||||
| function parseExpression(){ | function parseExpression(){ | ||||
| maybeCall(function(){ | |||||
| return maybeBinary(parseAtom, 0); | |||||
| return maybeCall(function(){ | |||||
| return maybeBinary(parseAtom(), 0); | |||||
| }); | }); | ||||
| } | } | ||||
| return { | return { | ||||
| type: "call", | type: "call", | ||||
| func: func, | func: func, | ||||
| args: parseDelimited("(", ")", ",", parseExpression); | |||||
| args: parseDelimited("(", ")", ",", parseExpression) | |||||
| }; | }; | ||||
| } | } | ||||
| type: (tok.val === "=") ? "assign" : "binary", | type: (tok.val === "=") ? "assign" : "binary", | ||||
| op: tok.val, | op: tok.val, | ||||
| left: left, | left: left, | ||||
| right: maybeBinary(parseAtom(), cpres) | |||||
| right: maybeBinary(parseAtom(), cpres), | |||||
| line: tok.line, | |||||
| col: tok.col | |||||
| }, pres); | }, pres); | ||||
| } | } | ||||
| } | } | ||||
| return left; | return left; | ||||
| } | } | ||||
| function parse(tokens){ | |||||
| let p = { | |||||
| type: "prog", | |||||
| expressions: []; | |||||
| }; | |||||
| stream = TokenStream(tokens); | |||||
| function parseBlock(bed){ | |||||
| let exp = []; | |||||
| let isBlockEnd = (t) => { | |||||
| return (bed && t.type === 'directive' && bed.indexOf(t.val) >= 0); | |||||
| } | |||||
| while (!stream.eof()){ | while (!stream.eof()){ | ||||
| if (isBlockEnd(stream.peek())) | |||||
| break; | |||||
| let e = parseExpression(); | let e = parseExpression(); | ||||
| if (e.type === "label"){ | if (e.type === "label"){ | ||||
| e = { | e = { | ||||
| type: "assign", | type: "assign", | ||||
| op: "=", | op: "=", | ||||
| left: e, | left: e, | ||||
| right: "*" // To designate "current program counter". | |||||
| right: "*", // To designate "current program counter". | |||||
| line: e.line, | |||||
| col: e.col | |||||
| } | } | ||||
| } | } | ||||
| p.expressions.push(e); | |||||
| 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; | stream = null; | ||||
| return p; | return p; | ||||
| } | } |