|
-
-
- 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(off){
- off = off || 0;
- return (pos+off < input.length) ? input[pos+off] : 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.__PCLabel = "__PC__";
- this.parseOpCode = pof(this);
- }
-
- set tokens(t){
- this.__stream = new TokenStream(t);
- this.__output = null;
- this.__SKIPEOL = false;
- }
- get stream(){return this.__stream;}
-
- get PCLabel(){return this.__PCLabel;}
- set PCLabel(pcl){
- if (typeof(pcl) !== 'string' || pcl === "")
- throw new TypeError("PC Label must be a non-zero-length string.");
- this.__PCLabel = pcl;
- }
-
-
- 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){this.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()){
- let ct = this.stream.peek();
- if (ct.type === "opcode" && t.col === t.val.length){
- this.__SKIPEOL = true;
- return {
- type: "assign",
- op: "=",
- left: t,
- right: this.__PCLabel, // To designate "current program counter".
- line: t.line,
- col: t.col
- }
- }
- }
- return t;
- }
-
- parseTempLabel(){
- let isEOL = this.stream.eol();
- this.stream.next();
- if (isEOL){
- this.__SKIPEOL = true;
- return {
- type:"temp-label",
- val:-1, // Value of -1 means "set new temp label"
- line:this.stream.line(),
- col:this.stream.col()
- };
- }
- let t = this.stream.next();
- if (t.type === "number"){
- t.type = "temp-label";
- return t;
- }
- this.stream.die("Temp label missing index. New temp labels must be defined at the start of the line.");
- }
-
- parseAtom(){
- if (this.isPunc("(")){
- this.stream.next();
- let exp = this.parseExpression();
- if (this.isPunc(")")){
- this.stream.next();
- return exp;
- }
- } else if (this.isPunc(":")){
- return this.parseTempLabel();
- } 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"){
- return tok;
- } else if (tok.type === "label"){
- return this.parseLabel(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.parseBlock();
- return this.__output;
- }
- return null;
- }
- }
-
-
- module.exports = Parser;
-
-
|