%lex %s comment %% <> { console.log("EOF"); return "EOF"; } "\n" { this.popState(); console.log("NEWLINE"); return "NEWLINE"; } . { /* ignore anything else inside a comment */ } ";" { this.pushState('comment'); } [\n] { console.log("NEWLINE"); return "NEWLINE"; } \s { /* ignore whitespace */ } "MOV" { console.log("MOV"); return "MOV"; } "ADD" { console.log("ADD"); return "ADD"; } "SUB" { console.log("SUB"); return "SUB"; } "CMP" { console.log("CMP"); return "CMP"; } "SLT" { console.log("SLT"); return "SLT"; } "JMP" { console.log("JMP"); return "JMP"; } "JMZ" { console.log("JMZ"); return "JMZ"; } "JMN" { console.log("JMN"); return "JMN"; } "DJN" { console.log("DJN"); return "DJN"; } "SPL" { console.log("SPL"); return "SPL"; } "DAT" { console.log("DAT"); return "DAT"; } "EQU" { console.log("EQU"); return "EQU"; } "END" { console.log("END"); return "END"; } ":" { console.log(":"); return ":"; } "," { console.log(","); return ","; } "#" { console.log("#"); return "#"; } "@" { console.log("@"); return "@"; } "<" { console.log("<"); return "<"; } "$" { console.log("$"); return "$"; } "(" { console.log("("); return "("; } ")" { console.log(")"); return ")"; } "+" { console.log("+"); return "+"; } "-" { console.log("-"); return "-"; } "*" { console.log("*"); return "*"; } "/" { console.log("/"); return "/"; } [0-9]+ { console.log("NUMBER"); return "NUMBER"; } [A-Z][A-Z0-9_]+ { console.log("LABEL"); return "LABEL"; } /lex %left '+' '-' %left '*' '/' %left UMINUS %% program : lines END coda { yy.start = 0; console.log($lines); return $lines; } | lines END label coda { yy.start = yy.getLabel($label); console.log($lines); return $lines; } ; coda : newlines EOF | EOF ; newlines : newlines NEWLINE | NEWLINE ; lines : lines line { if ($line !== null) { $lines.push($line); } $$ = $lines; } | line { if ($line === null) { $$ = []; } else { $$ = [ $line ]; } } ; line : 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: $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 : 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; } ;