%lex %s comment %% <> { return "EOF"; } [\n] { this.popState(); return "NEWLINE"; } . { /* ignore anything else inside a comment */ } ";" { this.pushState('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 %left '+' '-' %left '*' '/' %left UMINUS %% program : lines END coda { return { start: 0, program: $lines }; } | lines END label coda { return { start: yy.getLabel($label), program: $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.step(); $$ = $op; } | label op { yy.setLine($label); yy.step(); $$ = $op; } | label EQU e { yy.setEqu($label, $e); $$ = null; } ; op : op_immediate_a { $$ = $1; } | op_immediate_b { $$ = $1; } | DAT dat_addr "," dat_addr { $$ = { opcode: $1, a: $dat_addr1, b: $dat_addr2 }; } | DAT dat_addr { $$ = { opcode: $1, a: $dat_addr1, b: { mode: 'immediate', value: 0 } }; } ; op_immediate_a : opcode_immediate_a address "," non_immediate_addr { $$ = { opcode: $1, a: $2, b: $4 }; } ; op_immediate_b : opcode_immediate_b non_immediate_addr "," address { $$ = { opcode: $1, a: $2, b: $4 }; } | opcode_immediate_b non_immediate_addr { $$ = { opcode: $1, a: $2, b: { mode: 'direct', value: 0 }, }; } ; opcode_immediate_a : MOV { $$ = $1; } | ADD { $$ = $1; } | SUB { $$ = $1; } | CMP { $$ = $1; } | SLT { $$ = $1; } ; opcode_immediate_b : JMP { $$ = $1; } | JMZ { $$ = $1; } | JMN { $$ = $1; } | DJN { $$ = $1; } | SPL { $$ = $1; } ; address : immediate_addr { $$ = $1; } | non_immediate_addr { $$ = $1; } ; non_immediate_addr : direct_addr { $$ = $1; } | indirect_addr { $$ = $1; } | predecrement_addr { $$ = $1; } ; dat_addr : immediate_addr { $$ = $1; } | predecrement_addr { $$ = $1; } ; immediate_addr : "#" e { $$ = { mode: 'immediate', value: $e }; } ; direct_addr : "$" e { $$ = { mode: 'direct', value: $e }; } | e { $$ = { mode: 'direct', value: $e }; } ; indirect_addr : "@" e { $$ = { mode: 'indirect', value: $e }; } ; predecrement_addr : "<" e { $$ = { mode: 'predecrement', value: $e }; } ; 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; } ;