diff options
author | sanine <sanine.not@pm.me> | 2023-05-22 13:08:56 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2023-05-22 13:08:56 -0500 |
commit | 003640b43862e66256dd8e21711246d909431d98 (patch) | |
tree | 501f12162f43d296dea44c15ce240b959b2e6641 | |
parent | 51eef06ddfb82bedab4f5d70932132e28ba4249a (diff) |
update grammar
-rw-r--r-- | src/parser/grammar.jison | 97 | ||||
-rw-r--r-- | src/parser/grammar.jison.bak | 113 | ||||
-rw-r--r-- | src/parser/grammar.js | 762 | ||||
-rw-r--r-- | src/parser/test.red | 3 |
4 files changed, 954 insertions, 21 deletions
diff --git a/src/parser/grammar.jison b/src/parser/grammar.jison index 4a17d2b..ae8793b 100644 --- a/src/parser/grammar.jison +++ b/src/parser/grammar.jison @@ -1,15 +1,18 @@ %lex +%s comment + %% -";" { this.pushState('comment'); return "COMMENT"; } +<<EOF>> { return "EOF"; } <comment>"\n" { this.popState(); return "NEWLINE"; } <comment>. { /* ignore anything else inside a comment */ } +";" { this.pushState('comment'); } -"\n" { return "NEWLINE"; } -\s+ { /* ignore whitespace */ } +[\n] { console.log("new!"); return "NEWLINE"; } +\s { /* ignore whitespace */ } -"MOV" { return "MOV"; } +"MOV" { console.log("mov"); return "MOV"; } "ADD" { return "ADD"; } "SUB" { return "SUB"; } "CMP" { return "CMP"; } @@ -24,6 +27,7 @@ "EQU" { return "EQU"; } "END" { return "END"; } ":" { return ":"; } +"," { return ","; } "#" { return "#"; } "@" { return "@"; } @@ -41,28 +45,73 @@ /lex -%% +%left '+' '-' +%left '*' '/' +%left UMINUS +%% program - : lines { return $lines; } + : lines NEWLINE END coda { yy.start = 0; return $lines; } + | lines NEWLINE END label coda { yy.start = yy.getLabel($label); return $lines; } ; +coda + : newlines EOF + | EOF + ; + +newlines + : newlines NEWLINE + | NEWLINE + ; lines - : lines line { $lines.push($line); $$ = $lines; } - | line { $$ = [ $line ]; } + : lines line + { + if ($line !== null) { + $lines.push($line); + } + $$ = $lines; + } + | line + { + if ($line === null) { + $$ = []; + } else { + $$ = [ $line ]; + } + } ; line - : op NEWLINE { $$ = $op; } - | op EOF { $$ = $op; } + : row NEWLINE { $$ = $row; } + | NEWLINE { $$ = null; } + ; + +row + : op + { + yy.pc += 1; + $$ = $op; + } + | label ":" op + { + yy.setLabel($label, yy.pc); + yy.pc += 1; + $$ = $op; + } + | label EQU e + { + yy.setLabel($label, $e); + $$ = null; + } ; op - : opcode address address + : opcode address "," address { $$ = { opcode: $opcode, a: $address1, b: $address2 }; } ; @@ -82,7 +131,7 @@ opcode ; -address: +address : address_mode e { $$ = { mode: $1, value: $2 }; } | e { $$ = { mode: 'direct', value: $1 }; } ; @@ -90,22 +139,28 @@ address: address_mode : "#" { $$ = 'immediate'; } - | "@" { $$ = 'indirect'; } + | "@" { $$ = 'indirect'; } | "<" { $$ = 'predecrement'; } | "$" { $$ = 'direct'; } ; + e - : NUMBER { $$ = Math.floor(Number(yytext)); } - | LABEL { - $$ = yy.getLabel(yytext); - if ($$ === null) { - YYABORT; - } - } - | e '+' e { $$ = Math.floor($e1 + $e2); } + : e '+' e { $$ = Math.floor($e1 + $e2); } | e '-' e { $$ = Math.floor($e1 - $e2); } | e '*' e { $$ = Math.floor($e1 * $e2); } | e '/' e { $$ = Math.floor($e1 / $e2); } | '(' e ')' { $$ = Math.floor($e); } + | '-' e %prec UMINUS { $$ = - $e; } + | NUMBER { $$ = Math.floor(Number(yytext)); } + | label { + $$ = yy.getLabel($label); + } + ; + + +label + : LABEL { $$ = yytext; } ; + + diff --git a/src/parser/grammar.jison.bak b/src/parser/grammar.jison.bak new file mode 100644 index 0000000..6ac55df --- /dev/null +++ b/src/parser/grammar.jison.bak @@ -0,0 +1,113 @@ +%lex + +%x comment + +%% + +";" { this.pushState('comment'); return "COMMENT"; } +<comment>"\n" { this.popState(); return "NEWLINE"; } +<comment>. { /* ignore anything else inside a comment */ } + +"\n" { return "NEWLINE"; } +\s+ { /* ignore whitespace */ } + +"MOV" { return "MOV"; } +"ADD" { return "ADD"; } +"SUB" { return "SUB"; } +"CMP" { return "CMP"; } +"SLT" { return "SLT"; } +"JMP" { return "JMP"; } +"JMZ" { return "JMZ"; } +"JMN" { return "JMN"; } +"DJN" { return "DJN"; } +"SPL" { return "SPL"; } +"DAT" { return "DAT"; } + +"EQU" { return "EQU"; } +"END" { return "END"; } +":" { return ":"; } + +"#" { return "#"; } +"@" { return "@"; } +"<" { return "<"; } +"$" { return "$"; } + +"(" { return "("; } +")" { return ")"; } +"+" { return "+"; } +"-" { return "-"; } +"*" { return "*"; } +"/" { return "/"; } +[0-9]+ { return "NUMBER"; } +[A-Z][A-Z0-9_]+ { return "LABEL"; } + +/lex + +%% + + +program + : lines { return $lines; } + ; + + + +lines + : lines line { $lines.push($line); $$ = $lines; } + | line { $$ = [ $line ]; } + ; + +line + : op NEWLINE { $$ = $op; } + | op EOF { $$ = $op; } + ; + + +op + : opcode address address + { $$ = { opcode: $opcode, a: $address1, b: $address2 }; } + ; + + +opcode + : MOV { $$ = $1; } + | ADD { $$ = $1; } + | SUB { $$ = $1; } + | CMP { $$ = $1; } + | SLT { $$ = $1; } + | JMP { $$ = $1; } + | JMZ { $$ = $1; } + | JMN { $$ = $1; } + | DJN { $$ = $1; } + | SPL { $$ = $1; } + | DAT { $$ = $1; } + ; + + +address: + : address_mode e { $$ = { mode: $1, value: $2 }; } + | e { $$ = { mode: 'direct', value: $1 }; } + ; + + +address_mode + : "#" { $$ = 'immediate'; } + | "@" { $$ = 'indirect'; } + | "<" { $$ = 'predecrement'; } + | "$" { $$ = 'direct'; } + ; + +e + : NUMBER { $$ = Math.floor(Number(yytext)); } + | LABEL { + $$ = yy.getLabel(yytext); + if ($$ === null) { + YYABORT; + } + } + | e '+' e { $$ = Math.floor($e1 + $e2); } + | e '-' e { $$ = Math.floor($e1 - $e2); } + | e '*' e { $$ = Math.floor($e1 * $e2); } + | e '/' e { $$ = Math.floor($e1 / $e2); } + | '(' e ')' { $$ = Math.floor($e); } + ; diff --git a/src/parser/grammar.js b/src/parser/grammar.js new file mode 100644 index 0000000..73505c3 --- /dev/null +++ b/src/parser/grammar.js @@ -0,0 +1,762 @@ +/* parser generated by jison 0.4.18 */ +/* + Returns a Parser object of the following structure: + + Parser: { + yy: {} + } + + Parser.prototype: { + yy: {}, + trace: function(), + symbols_: {associative list: name ==> number}, + terminals_: {associative list: number ==> name}, + productions_: [...], + performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$), + table: [...], + defaultActions: {...}, + parseError: function(str, hash), + parse: function(input), + + lexer: { + EOF: 1, + parseError: function(str, hash), + setInput: function(input), + input: function(), + unput: function(str), + more: function(), + less: function(n), + pastInput: function(), + upcomingInput: function(), + showPosition: function(), + test_match: function(regex_match_array, rule_index), + next: function(), + lex: function(), + begin: function(condition), + popState: function(), + _currentRules: function(), + topState: function(), + pushState: function(condition), + + options: { + ranges: boolean (optional: true ==> token location info will include a .range[] member) + flex: boolean (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match) + backtrack_lexer: boolean (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code) + }, + + performAction: function(yy, yy_, $avoiding_name_collisions, YY_START), + rules: [...], + conditions: {associative list: name ==> set}, + } + } + + + token location info (@$, _$, etc.): { + first_line: n, + last_line: n, + first_column: n, + last_column: n, + range: [start_number, end_number] (where the numbers are indexes into the input string, regular zero-based) + } + + + the parseError function receives a 'hash' object with these members for lexer and parser errors: { + text: (matched text) + token: (the produced terminal token, if any) + line: (yylineno) + } + while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: { + loc: (yylloc) + expected: (string describing the set of expected tokens) + recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error) + } +*/ +var grammar = (function(){ +var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[1,10],$V1=[1,11],$V2=[1,12],$V3=[1,13],$V4=[1,14],$V5=[1,15],$V6=[1,16],$V7=[1,17],$V8=[1,18],$V9=[1,19],$Va=[1,20],$Vb=[1,9],$Vc=[5,20,21,22,23,24,25,26,27,28,29,30,43],$Vd=[2,10],$Ve=[1,29],$Vf=[1,30],$Vg=[1,31],$Vh=[1,32],$Vi=[1,34],$Vj=[1,33],$Vk=[1,35],$Vl=[32,33,34,35,37,40,42,43],$Vm=[5,19],$Vn=[1,42],$Vo=[1,43],$Vp=[1,44],$Vq=[1,45],$Vr=[37,40,42,43],$Vs=[5,19,36,37,38,39,41],$Vt=[1,52],$Vu=[1,51],$Vv=[5,10],$Vw=[5,19,36,37,41]; +var parser = {trace: function trace () { }, +yy: {}, +symbols_: {"error":2,"program":3,"lines":4,"NEWLINE":5,"END":6,"coda":7,"label":8,"newlines":9,"EOF":10,"line":11,"row":12,"op":13,":":14,"EQU":15,"e":16,"opcode":17,"address":18,",":19,"MOV":20,"ADD":21,"SUB":22,"CMP":23,"SLT":24,"JMP":25,"JMZ":26,"JMN":27,"DJN":28,"SPL":29,"DAT":30,"address_mode":31,"#":32,"@":33,"<":34,"$":35,"+":36,"-":37,"*":38,"/":39,"(":40,")":41,"NUMBER":42,"LABEL":43,"$accept":0,"$end":1}, +terminals_: {2:"error",5:"NEWLINE",6:"END",10:"EOF",14:":",15:"EQU",19:",",20:"MOV",21:"ADD",22:"SUB",23:"CMP",24:"SLT",25:"JMP",26:"JMZ",27:"JMN",28:"DJN",29:"SPL",30:"DAT",32:"#",33:"@",34:"<",35:"$",36:"+",37:"-",38:"*",39:"/",40:"(",41:")",42:"NUMBER",43:"LABEL"}, +productions_: [0,[3,4],[3,5],[7,2],[7,1],[9,2],[9,1],[4,2],[4,1],[11,2],[11,1],[12,1],[12,3],[12,3],[13,4],[17,1],[17,1],[17,1],[17,1],[17,1],[17,1],[17,1],[17,1],[17,1],[17,1],[17,1],[18,2],[18,1],[31,1],[31,1],[31,1],[31,1],[16,3],[16,3],[16,3],[16,3],[16,3],[16,2],[16,1],[16,1],[8,1]], +performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) { +/* this == yyval */ + +var $0 = $$.length - 1; +switch (yystate) { +case 1: + yy.start = 0; return $$[$0-3]; +break; +case 2: + yy.start = yy.getLabel($$[$0-1]); return $$[$0-4]; +break; +case 7: + + if ($$[$0] !== null) { + $$[$0-1].push($$[$0]); + } + this.$ = $$[$0-1]; + +break; +case 8: + + if ($$[$0] === null) { + this.$ = []; + } else { + this.$ = [ $$[$0] ]; + } + +break; +case 9: + this.$ = $$[$0-1]; +break; +case 10: + this.$ = null; +break; +case 11: + + yy.pc += 1; + this.$ = $$[$0]; + +break; +case 12: + + yy.setLabel($$[$0-2], yy.pc); + yy.pc += 1; + this.$ = $$[$0]; + +break; +case 13: + + yy.setLabel($$[$0-2], $$[$0]); + this.$ = null; + +break; +case 14: + this.$ = { opcode: $$[$0-3], a: $$[$0-2], b: $$[$0] }; +break; +case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 25: + this.$ = $$[$0]; +break; +case 26: + this.$ = { mode: $$[$0-1], value: $$[$0] }; +break; +case 27: + this.$ = { mode: 'direct', value: $$[$0] }; +break; +case 28: + this.$ = 'immediate'; +break; +case 29: + this.$ = 'indirect'; +break; +case 30: + this.$ = 'predecrement'; +break; +case 31: + this.$ = 'direct'; +break; +case 32: + this.$ = Math.floor($$[$0-2] + $$[$0]); +break; +case 33: + this.$ = Math.floor($$[$0-2] - $$[$0]); +break; +case 34: + this.$ = Math.floor($$[$0-2] * $$[$0]); +break; +case 35: + this.$ = Math.floor($$[$0-2] / $$[$0]); +break; +case 36: + this.$ = Math.floor($$[$0-1]); +break; +case 37: + this.$ = - $$[$0]; +break; +case 38: + this.$ = Math.floor(Number(yytext)); +break; +case 39: + + this.$ = yy.getLabel($$[$0]); + +break; +case 40: + this.$ = yytext; +break; +} +}, +table: [{3:1,4:2,5:[1,5],8:7,11:3,12:4,13:6,17:8,20:$V0,21:$V1,22:$V2,23:$V3,24:$V4,25:$V5,26:$V6,27:$V7,28:$V8,29:$V9,30:$Va,43:$Vb},{1:[3]},{5:[1,21],8:7,11:22,12:4,13:6,17:8,20:$V0,21:$V1,22:$V2,23:$V3,24:$V4,25:$V5,26:$V6,27:$V7,28:$V8,29:$V9,30:$Va,43:$Vb},o($Vc,[2,8]),{5:[1,23]},o($Vc,$Vd),{5:[2,11]},{14:[1,24],15:[1,25]},{8:36,16:28,18:26,31:27,32:$Ve,33:$Vf,34:$Vg,35:$Vh,37:$Vi,40:$Vj,42:$Vk,43:$Vb},o([5,10,14,15,19,36,37,38,39,41],[2,40]),o($Vl,[2,15]),o($Vl,[2,16]),o($Vl,[2,17]),o($Vl,[2,18]),o($Vl,[2,19]),o($Vl,[2,20]),o($Vl,[2,21]),o($Vl,[2,22]),o($Vl,[2,23]),o($Vl,[2,24]),o($Vl,[2,25]),o($Vc,$Vd,{6:[1,37]}),o($Vc,[2,7]),o($Vc,[2,9]),{13:38,17:8,20:$V0,21:$V1,22:$V2,23:$V3,24:$V4,25:$V5,26:$V6,27:$V7,28:$V8,29:$V9,30:$Va},{8:36,16:39,37:$Vi,40:$Vj,42:$Vk,43:$Vb},{19:[1,40]},{8:36,16:41,37:$Vi,40:$Vj,42:$Vk,43:$Vb},o($Vm,[2,27],{36:$Vn,37:$Vo,38:$Vp,39:$Vq}),o($Vr,[2,28]),o($Vr,[2,29]),o($Vr,[2,30]),o($Vr,[2,31]),{8:36,16:46,37:$Vi,40:$Vj,42:$Vk,43:$Vb},{8:36,16:47,37:$Vi,40:$Vj,42:$Vk,43:$Vb},o($Vs,[2,38]),o($Vs,[2,39]),{5:$Vt,7:48,8:49,9:50,10:$Vu,43:$Vb},{5:[2,12]},{5:[2,13],36:$Vn,37:$Vo,38:$Vp,39:$Vq},{8:36,16:28,18:53,31:27,32:$Ve,33:$Vf,34:$Vg,35:$Vh,37:$Vi,40:$Vj,42:$Vk,43:$Vb},o($Vm,[2,26],{36:$Vn,37:$Vo,38:$Vp,39:$Vq}),{8:36,16:54,37:$Vi,40:$Vj,42:$Vk,43:$Vb},{8:36,16:55,37:$Vi,40:$Vj,42:$Vk,43:$Vb},{8:36,16:56,37:$Vi,40:$Vj,42:$Vk,43:$Vb},{8:36,16:57,37:$Vi,40:$Vj,42:$Vk,43:$Vb},{36:$Vn,37:$Vo,38:$Vp,39:$Vq,41:[1,58]},o($Vs,[2,37]),{1:[2,1]},{5:$Vt,7:59,9:50,10:$Vu},{5:[1,61],10:[1,60]},{1:[2,4]},o($Vv,[2,6]),{5:[2,14]},o($Vw,[2,32],{38:$Vp,39:$Vq}),o($Vw,[2,33],{38:$Vp,39:$Vq}),o($Vs,[2,34]),o($Vs,[2,35]),o($Vs,[2,36]),{1:[2,2]},{1:[2,3]},o($Vv,[2,5])], +defaultActions: {6:[2,11],38:[2,12],48:[2,1],51:[2,4],53:[2,14],59:[2,2],60:[2,3]}, +parseError: function parseError (str, hash) { + if (hash.recoverable) { + this.trace(str); + } else { + var error = new Error(str); + error.hash = hash; + throw error; + } +}, +parse: function parse(input) { + var self = this, stack = [0], tstack = [], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1; + var args = lstack.slice.call(arguments, 1); + var lexer = Object.create(this.lexer); + var sharedState = { yy: {} }; + for (var k in this.yy) { + if (Object.prototype.hasOwnProperty.call(this.yy, k)) { + sharedState.yy[k] = this.yy[k]; + } + } + lexer.setInput(input, sharedState.yy); + sharedState.yy.lexer = lexer; + sharedState.yy.parser = this; + if (typeof lexer.yylloc == 'undefined') { + lexer.yylloc = {}; + } + var yyloc = lexer.yylloc; + lstack.push(yyloc); + var ranges = lexer.options && lexer.options.ranges; + if (typeof sharedState.yy.parseError === 'function') { + this.parseError = sharedState.yy.parseError; + } else { + this.parseError = Object.getPrototypeOf(this).parseError; + } + function popStack(n) { + stack.length = stack.length - 2 * n; + vstack.length = vstack.length - n; + lstack.length = lstack.length - n; + } + _token_stack: + var lex = function () { + var token; + token = lexer.lex() || EOF; + if (typeof token !== 'number') { + token = self.symbols_[token] || token; + } + return token; + }; + var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected; + while (true) { + state = stack[stack.length - 1]; + if (this.defaultActions[state]) { + action = this.defaultActions[state]; + } else { + if (symbol === null || typeof symbol == 'undefined') { + symbol = lex(); + } + action = table[state] && table[state][symbol]; + } + if (typeof action === 'undefined' || !action.length || !action[0]) { + var errStr = ''; + expected = []; + for (p in table[state]) { + if (this.terminals_[p] && p > TERROR) { + expected.push('\'' + this.terminals_[p] + '\''); + } + } + if (lexer.showPosition) { + errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\''; + } else { + errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\''); + } + this.parseError(errStr, { + text: lexer.match, + token: this.terminals_[symbol] || symbol, + line: lexer.yylineno, + loc: yyloc, + expected: expected + }); + } + if (action[0] instanceof Array && action.length > 1) { + throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol); + } + switch (action[0]) { + case 1: + stack.push(symbol); + vstack.push(lexer.yytext); + lstack.push(lexer.yylloc); + stack.push(action[1]); + symbol = null; + if (!preErrorSymbol) { + yyleng = lexer.yyleng; + yytext = lexer.yytext; + yylineno = lexer.yylineno; + yyloc = lexer.yylloc; + if (recovering > 0) { + recovering--; + } + } else { + symbol = preErrorSymbol; + preErrorSymbol = null; + } + break; + case 2: + len = this.productions_[action[1]][1]; + yyval.$ = vstack[vstack.length - len]; + yyval._$ = { + first_line: lstack[lstack.length - (len || 1)].first_line, + last_line: lstack[lstack.length - 1].last_line, + first_column: lstack[lstack.length - (len || 1)].first_column, + last_column: lstack[lstack.length - 1].last_column + }; + if (ranges) { + yyval._$.range = [ + lstack[lstack.length - (len || 1)].range[0], + lstack[lstack.length - 1].range[1] + ]; + } + r = this.performAction.apply(yyval, [ + yytext, + yyleng, + yylineno, + sharedState.yy, + action[1], + vstack, + lstack + ].concat(args)); + if (typeof r !== 'undefined') { + return r; + } + if (len) { + stack = stack.slice(0, -1 * len * 2); + vstack = vstack.slice(0, -1 * len); + lstack = lstack.slice(0, -1 * len); + } + stack.push(this.productions_[action[1]][0]); + vstack.push(yyval.$); + lstack.push(yyval._$); + newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; + stack.push(newState); + break; + case 3: + return true; + } + } + return true; +}}; +/* generated by jison-lex 0.3.4 */ +var lexer = (function(){ +var lexer = ({ + +EOF:1, + +parseError:function parseError(str, hash) { + if (this.yy.parser) { + this.yy.parser.parseError(str, hash); + } else { + throw new Error(str); + } + }, + +// resets the lexer, sets new input +setInput:function (input, yy) { + this.yy = yy || this.yy || {}; + this._input = input; + this._more = this._backtrack = this.done = false; + this.yylineno = this.yyleng = 0; + this.yytext = this.matched = this.match = ''; + this.conditionStack = ['INITIAL']; + this.yylloc = { + first_line: 1, + first_column: 0, + last_line: 1, + last_column: 0 + }; + if (this.options.ranges) { + this.yylloc.range = [0,0]; + } + this.offset = 0; + return this; + }, + +// consumes and returns one char from the input +input:function () { + var ch = this._input[0]; + this.yytext += ch; + this.yyleng++; + this.offset++; + this.match += ch; + this.matched += ch; + var lines = ch.match(/(?:\r\n?|\n).*/g); + if (lines) { + this.yylineno++; + this.yylloc.last_line++; + } else { + this.yylloc.last_column++; + } + if (this.options.ranges) { + this.yylloc.range[1]++; + } + + this._input = this._input.slice(1); + return ch; + }, + +// unshifts one char (or a string) into the input +unput:function (ch) { + var len = ch.length; + var lines = ch.split(/(?:\r\n?|\n)/g); + + this._input = ch + this._input; + this.yytext = this.yytext.substr(0, this.yytext.length - len); + //this.yyleng -= len; + this.offset -= len; + var oldLines = this.match.split(/(?:\r\n?|\n)/g); + this.match = this.match.substr(0, this.match.length - 1); + this.matched = this.matched.substr(0, this.matched.length - 1); + + if (lines.length - 1) { + this.yylineno -= lines.length - 1; + } + var r = this.yylloc.range; + + this.yylloc = { + first_line: this.yylloc.first_line, + last_line: this.yylineno + 1, + first_column: this.yylloc.first_column, + last_column: lines ? + (lines.length === oldLines.length ? this.yylloc.first_column : 0) + + oldLines[oldLines.length - lines.length].length - lines[0].length : + this.yylloc.first_column - len + }; + + if (this.options.ranges) { + this.yylloc.range = [r[0], r[0] + this.yyleng - len]; + } + this.yyleng = this.yytext.length; + return this; + }, + +// When called from action, caches matched text and appends it on next action +more:function () { + this._more = true; + return this; + }, + +// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead. +reject:function () { + if (this.options.backtrack_lexer) { + this._backtrack = true; + } else { + return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), { + text: "", + token: null, + line: this.yylineno + }); + + } + return this; + }, + +// retain first n characters of the match +less:function (n) { + this.unput(this.match.slice(n)); + }, + +// displays already matched input, i.e. for error messages +pastInput:function () { + var past = this.matched.substr(0, this.matched.length - this.match.length); + return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); + }, + +// displays upcoming input, i.e. for error messages +upcomingInput:function () { + var next = this.match; + if (next.length < 20) { + next += this._input.substr(0, 20-next.length); + } + return (next.substr(0,20) + (next.length > 20 ? '...' : '')).replace(/\n/g, ""); + }, + +// displays the character position where the lexing error occurred, i.e. for error messages +showPosition:function () { + var pre = this.pastInput(); + var c = new Array(pre.length + 1).join("-"); + return pre + this.upcomingInput() + "\n" + c + "^"; + }, + +// test the lexed token: return FALSE when not a match, otherwise return token +test_match:function(match, indexed_rule) { + var token, + lines, + backup; + + if (this.options.backtrack_lexer) { + // save context + backup = { + yylineno: this.yylineno, + yylloc: { + first_line: this.yylloc.first_line, + last_line: this.last_line, + first_column: this.yylloc.first_column, + last_column: this.yylloc.last_column + }, + yytext: this.yytext, + match: this.match, + matches: this.matches, + matched: this.matched, + yyleng: this.yyleng, + offset: this.offset, + _more: this._more, + _input: this._input, + yy: this.yy, + conditionStack: this.conditionStack.slice(0), + done: this.done + }; + if (this.options.ranges) { + backup.yylloc.range = this.yylloc.range.slice(0); + } + } + + lines = match[0].match(/(?:\r\n?|\n).*/g); + if (lines) { + this.yylineno += lines.length; + } + this.yylloc = { + first_line: this.yylloc.last_line, + last_line: this.yylineno + 1, + first_column: this.yylloc.last_column, + last_column: lines ? + lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length : + this.yylloc.last_column + match[0].length + }; + this.yytext += match[0]; + this.match += match[0]; + this.matches = match; + this.yyleng = this.yytext.length; + if (this.options.ranges) { + this.yylloc.range = [this.offset, this.offset += this.yyleng]; + } + this._more = false; + this._backtrack = false; + this._input = this._input.slice(match[0].length); + this.matched += match[0]; + token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]); + if (this.done && this._input) { + this.done = false; + } + if (token) { + return token; + } else if (this._backtrack) { + // recover context + for (var k in backup) { + this[k] = backup[k]; + } + return false; // rule action called reject() implying the next rule should be tested instead. + } + return false; + }, + +// return next match in input +next:function () { + if (this.done) { + return this.EOF; + } + if (!this._input) { + this.done = true; + } + + var token, + match, + tempMatch, + index; + if (!this._more) { + this.yytext = ''; + this.match = ''; + } + var rules = this._currentRules(); + for (var i = 0; i < rules.length; i++) { + tempMatch = this._input.match(this.rules[rules[i]]); + if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { + match = tempMatch; + index = i; + if (this.options.backtrack_lexer) { + token = this.test_match(tempMatch, rules[i]); + if (token !== false) { + return token; + } else if (this._backtrack) { + match = false; + continue; // rule action called reject() implying a rule MISmatch. + } else { + // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) + return false; + } + } else if (!this.options.flex) { + break; + } + } + } + if (match) { + token = this.test_match(match, rules[index]); + if (token !== false) { + return token; + } + // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) + return false; + } + if (this._input === "") { + return this.EOF; + } else { + return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), { + text: "", + token: null, + line: this.yylineno + }); + } + }, + +// return next match that has a token +lex:function lex () { + var r = this.next(); + if (r) { + return r; + } else { + return this.lex(); + } + }, + +// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack) +begin:function begin (condition) { + this.conditionStack.push(condition); + }, + +// pop the previously active lexer condition state off the condition stack +popState:function popState () { + var n = this.conditionStack.length - 1; + if (n > 0) { + return this.conditionStack.pop(); + } else { + return this.conditionStack[0]; + } + }, + +// produce the lexer rule set which is active for the currently active lexer condition state +_currentRules:function _currentRules () { + if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) { + return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules; + } else { + return this.conditions["INITIAL"].rules; + } + }, + +// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available +topState:function topState (n) { + n = this.conditionStack.length - 1 - Math.abs(n || 0); + if (n >= 0) { + return this.conditionStack[n]; + } else { + return "INITIAL"; + } + }, + +// alias for begin(condition) +pushState:function pushState (condition) { + this.begin(condition); + }, + +// return the number of states currently on the stack +stateStackSize:function stateStackSize() { + return this.conditionStack.length; + }, +options: {}, +performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { +var YYSTATE=YY_START; +switch($avoiding_name_collisions) { +case 0: return "EOF"; +break; +case 1: this.popState(); return "NEWLINE"; +break; +case 2: /* ignore anything else inside a comment */ +break; +case 3: this.pushState('comment'); +break; +case 4: console.log("new!"); return "NEWLINE"; +break; +case 5: /* ignore whitespace */ +break; +case 6: console.log("mov"); return "MOV"; +break; +case 7: return "ADD"; +break; +case 8: return "SUB"; +break; +case 9: return "CMP"; +break; +case 10: return "SLT"; +break; +case 11: return "JMP"; +break; +case 12: return "JMZ"; +break; +case 13: return "JMN"; +break; +case 14: return "DJN"; +break; +case 15: return "SPL"; +break; +case 16: return "DAT"; +break; +case 17: return "EQU"; +break; +case 18: return "END"; +break; +case 19: return ":"; +break; +case 20: return ","; +break; +case 21: return "#"; +break; +case 22: return "@"; +break; +case 23: return "<"; +break; +case 24: return "$"; +break; +case 25: return "("; +break; +case 26: return ")"; +break; +case 27: return "+"; +break; +case 28: return "-"; +break; +case 29: return "*"; +break; +case 30: return "/"; +break; +case 31: return "NUMBER"; +break; +case 32: return "LABEL"; +break; +} +}, +rules: [/^(?:$)/,/^(?:\\n)/,/^(?:.)/,/^(?:;)/,/^(?:[\n])/,/^(?:\s)/,/^(?:MOV\b)/,/^(?:ADD\b)/,/^(?:SUB\b)/,/^(?:CMP\b)/,/^(?:SLT\b)/,/^(?:JMP\b)/,/^(?:JMZ\b)/,/^(?:JMN\b)/,/^(?:DJN\b)/,/^(?:SPL\b)/,/^(?:DAT\b)/,/^(?:EQU\b)/,/^(?:END\b)/,/^(?::)/,/^(?:,)/,/^(?:#)/,/^(?:@)/,/^(?:<)/,/^(?:\$)/,/^(?:\()/,/^(?:\))/,/^(?:\+)/,/^(?:-)/,/^(?:\*)/,/^(?:\/)/,/^(?:[0-9]+)/,/^(?:[A-Z][A-Z0-9_]+)/], +conditions: {"comment":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32],"inclusive":true},"INITIAL":{"rules":[0,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32],"inclusive":true}} +}); +return lexer; +})(); +parser.lexer = lexer; +function Parser () { + this.yy = {}; +} +Parser.prototype = parser;parser.Parser = Parser; +return new Parser; +})(); + + +if (typeof require !== 'undefined' && typeof exports !== 'undefined') { +exports.parser = grammar; +exports.Parser = grammar.Parser; +exports.parse = function () { return grammar.parse.apply(grammar, arguments); }; +exports.main = function commonjsMain (args) { + if (!args[1]) { + console.log('Usage: '+args[0]+' FILE'); + process.exit(1); + } + var source = require('fs').readFileSync(require('path').normalize(args[1]), "utf8"); + return exports.parser.parse(source); +}; +if (typeof module !== 'undefined' && require.main === module) { + exports.main(process.argv.slice(1)); +} +}
\ No newline at end of file diff --git a/src/parser/test.red b/src/parser/test.red new file mode 100644 index 0000000..54e3453 --- /dev/null +++ b/src/parser/test.red @@ -0,0 +1,3 @@ +MOV 0, 0 + +END |