aboutsummaryrefslogtreecommitdiffgithub
diff options
context:
space:
mode:
authorAustin Adams <git@austinjadams.com>2019-01-04 13:19:30 -0600
committerAustin Adams <git@austinjadams.com>2019-01-04 13:19:30 -0600
commit185cf6e9d45816cd6387a5b90e1d7ae8513531f5 (patch)
tree800791145b084ae0f1201c47da3dffd5b8c5fb53
parent6b17f6581802ebaea95be877f905d0c0d88a0daa (diff)
downloadnovice-185cf6e9d45816cd6387a5b90e1d7ae8513531f5.tar.gz
novice-185cf6e9d45816cd6387a5b90e1d7ae8513531f5.tar.xz
Handle LC-3 labels properly
-rw-r--r--novice/assembler/assembler.test.ts38
-rw-r--r--novice/assembler/assembler.ts7
-rw-r--r--novice/assembler/index.ts11
-rw-r--r--novice/assembler/isa/lc3.ts297
-rw-r--r--novice/assembler/isa/state.ts13
-rw-r--r--novice/assembler/parsers/complx.ts103
-rw-r--r--novice/assembler/parsers/grammars/complx.ts8
-rw-r--r--novice/assembler/parsers/parser.ts9
-rw-r--r--novice/assembler/parsers/tables/complx.ts4
-rw-r--r--novice/cli.test.ts22
-rw-r--r--novice/cli.ts16
11 files changed, 465 insertions, 63 deletions
diff --git a/novice/assembler/assembler.test.ts b/novice/assembler/assembler.test.ts
index 4961309..24172ab 100644
--- a/novice/assembler/assembler.test.ts
+++ b/novice/assembler/assembler.test.ts
@@ -1,5 +1,5 @@
import { Readable } from 'stream';
-import { getParser, Assembler } from '.';
+import { getParser, getIsa, Assembler } from '.';
describe('assembler', () => {
describe('parse(fp)', () => {
@@ -8,20 +8,20 @@ describe('assembler', () => {
beforeEach(() => {
fp = new Readable();
- // Test the complx parser for now
- assembler = new Assembler(getParser("complx"));
+ // Test the complx parser and lc3 isa for now
+ assembler = new Assembler(getParser("complx"), getIsa("lc3"));
});
it('parses trivial program', () => {
fp.push('.orig x3000\n')
- fp.push('halt\n')
+ fp.push('add\n')
fp.push('.end\n')
fp.push(null)
return expect(assembler.parse(fp)).resolves.toEqual({
sections: [
{startAddr: 0x3000, instructions: [
- {kind: 'instr', op: 'halt', operands: []},
+ {kind: 'instr', op: 'add', operands: []},
]},
],
labels: {},
@@ -30,7 +30,7 @@ describe('assembler', () => {
it('parses label on its own line', () => {
fp.push('.orig x3000\n')
- fp.push('fun:\n')
+ fp.push('fun\n')
fp.push('br fun\n')
fp.push('.end\n')
fp.push(null)
@@ -54,7 +54,7 @@ describe('assembler', () => {
fp.push('halt\n')
fp.push('\n');
fp.push('\n');
- fp.push('mystring: .stringz "hello world!"\n')
+ fp.push('mystring .stringz "hello world!"\n')
fp.push('.end\n')
fp.push(null)
@@ -80,12 +80,12 @@ describe('assembler', () => {
it('parses multiple sections', () => {
fp.push('.orig x3000\n')
- fp.push('halt: halt\n')
+ fp.push('haltme halt\n')
fp.push('.end\n')
fp.push('\n')
fp.push('.orig x4000\n')
fp.push('and r0, r0, -3\n')
- fp.push('halt2: halt\n')
+ fp.push('halt2 halt\n')
fp.push('.end\n')
fp.push(null)
@@ -104,17 +104,17 @@ describe('assembler', () => {
]},
],
labels: {
- 'halt': [0, 0],
- 'halt2': [1, 1],
+ 'haltme': [0, 0],
+ 'halt2': [1, 1],
},
});
});
it('preserves label case', () => {
fp.push('.orig x3000\n')
- fp.push('mYlAbeL: halt\n')
- fp.push('another-label: .blkw 1\n')
- fp.push('LOUD_LABEL: .blkw 1\n')
+ fp.push('mYlAbeL halt\n')
+ fp.push('another-label .blkw 1\n')
+ fp.push('LOUD_LABEL .blkw 1\n')
fp.push('.end\n')
fp.push(null)
@@ -170,8 +170,8 @@ describe('assembler', () => {
it('errors on duplicate labels', () => {
fp.push('.orig x3000\n')
- fp.push('mylabel: .blkw 1\n')
- fp.push('mylabel: .blkw 1\n')
+ fp.push('mylabel .blkw 1\n')
+ fp.push('mylabel .blkw 1\n')
fp.push('.end\n')
fp.push(null)
@@ -190,7 +190,7 @@ describe('assembler', () => {
it('errors on .end label', () => {
fp.push('.orig x3000\n')
fp.push('halt\n')
- fp.push('duh: .end\n')
+ fp.push('duh .end\n')
fp.push(null)
return expect(assembler.parse(fp)).rejects.toThrow('should not have a label');
@@ -206,7 +206,7 @@ describe('assembler', () => {
});
it('errors on .orig label', () => {
- fp.push('duhhhh: .orig x3000\n')
+ fp.push('duhhhh .orig x3000\n')
fp.push('halt\n')
fp.push('.end\n')
fp.push(null)
@@ -240,7 +240,7 @@ describe('assembler', () => {
});
it('errors on stray label', () => {
- fp.push('doh:\n')
+ fp.push('doh\n')
fp.push(null)
return expect(assembler.parse(fp)).rejects.toThrow('stray label');
diff --git a/novice/assembler/assembler.ts b/novice/assembler/assembler.ts
index 7a4e27f..7075b68 100644
--- a/novice/assembler/assembler.ts
+++ b/novice/assembler/assembler.ts
@@ -1,18 +1,21 @@
import { Readable } from 'stream';
+import { Isa } from './isa';
import { ParsedAssembly, Parser } from './parsers';
import { Scanner } from './scanner';
class Assembler {
private scanner: Scanner;
private parser: Parser;
+ private isa: Isa;
- public constructor(parser: Parser) {
+ public constructor(parser: Parser, isa: Isa) {
this.scanner = new Scanner();
this.parser = parser;
+ this.isa = isa;
}
public async parse(fp: Readable): Promise<ParsedAssembly> {
- return this.parser.parse(await this.scanner.scan(fp));
+ return this.parser.parse(this.isa, await this.scanner.scan(fp));
}
}
diff --git a/novice/assembler/index.ts b/novice/assembler/index.ts
index 96300d8..3a04dc2 100644
--- a/novice/assembler/index.ts
+++ b/novice/assembler/index.ts
@@ -1,4 +1,5 @@
import { Assembler } from './assembler';
+import { Isa, isas } from './isa';
import { Parser, parsers } from './parsers';
function getParser(parserName: string): Parser {
@@ -9,4 +10,12 @@ function getParser(parserName: string): Parser {
return new parsers[parserName]();
}
-export { Assembler, getParser };
+function getIsa(isaName: string): Isa {
+ if (!isas.hasOwnProperty(isaName)) {
+ throw new Error(`no such isa \`${isaName}'\n`);
+ }
+
+ return isas[isaName];
+}
+
+export { Assembler, getParser, getIsa };
diff --git a/novice/assembler/isa/lc3.ts b/novice/assembler/isa/lc3.ts
index cfca296..f943ef2 100644
--- a/novice/assembler/isa/lc3.ts
+++ b/novice/assembler/isa/lc3.ts
@@ -2,8 +2,11 @@ import { Fields, Isa } from './isa';
import { MachineState, MachineStateUpdate } from './state';
function calcCC(val: number): number {
- const fixed = val & 0xffff | -1 << 16;
- return (fixed < 0) ? 0b100 : (fixed > 0) ? 0b001 : 0b010;
+ const fixed = val & 0xffff;
+ return (fixed === 0) ? 0b010 :
+ // MSB is set
+ (fixed & 1 << 15) ? 0b100 :
+ 0b001;
}
const Lc3Isa: Isa = {
@@ -42,6 +45,296 @@ const Lc3Isa: Isa = {
{kind: 'reg', reg: 'cc', val: calcCC(sum)},
];
}},
+
+ {op: 'and', fields: [
+ {kind: 'const', bits: [15, 12], val: 0b0101},
+ {kind: 'reg', bits: [11, 9], prefix: 'r', name: 'dr'},
+ {kind: 'reg', bits: [ 8, 6], prefix: 'r', name: 'sr1'},
+ {kind: 'const', bits: [ 5, 3], val: 0b000},
+ {kind: 'reg', bits: [ 2, 0], prefix: 'r', name: 'sr2'},
+ ],
+ sim: (state: MachineState, ir: Fields) => {
+ const res = state.reg(ir.regs.sr1) & state.reg(ir.regs.sr2);
+ return [
+ {kind: 'reg', reg: ir.regs.dr, val: res},
+ {kind: 'reg', reg: 'cc', val: calcCC(res)},
+ ];
+ }},
+
+ {op: 'and', fields: [
+ {kind: 'const', bits: [15, 12], val: 0b0101},
+ {kind: 'reg', bits: [11, 9], prefix: 'r', name: 'dr'},
+ {kind: 'reg', bits: [ 8, 6], prefix: 'r', name: 'sr1'},
+ {kind: 'const', bits: [ 5, 5], val: 0b1},
+ {kind: 'imm', bits: [ 4, 0], sext: true, label: false,
+ name: 'imm5'},
+ ],
+ sim: (state: MachineState, ir: Fields) => {
+ const res = state.reg(ir.regs.sr1) & ir.imms.imm5;
+ return [
+ {kind: 'reg', reg: ir.regs.dr, val: res},
+ {kind: 'reg', reg: 'cc', val: calcCC(res)},
+ ];
+ }},
+
+ {op: 'nop', fields: [
+ {kind: 'const', bits: [15, 0], val: 0x0000},
+ ],
+ sim: (state: MachineState, ir: Fields) => [],
+ },
+
+ {op: 'br', fields: [
+ {kind: 'const', bits: [15, 9], val: 0b0000111},
+ {kind: 'imm', bits: [ 8, 0], sext: true, label: true,
+ name: 'pcoffset9'},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ [{kind: 'pc', where: state.pc + 1 + ir.imms.pcoffset9}],
+ },
+
+ {op: 'brnzp', fields: [
+ {kind: 'const', bits: [15, 9], val: 0b0000111},
+ {kind: 'imm', bits: [ 8, 0], sext: true, label: true,
+ name: 'pcoffset9'},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ [{kind: 'pc', where: state.pc + 1 + ir.imms.pcoffset9}],
+ },
+
+ {op: 'brp', fields: [
+ {kind: 'const', bits: [15, 9], val: 0b0000001},
+ {kind: 'imm', bits: [ 8, 0], sext: true, label: true,
+ name: 'pcoffset9'},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ (state.reg('cc') & 0b001)
+ ? [{kind: 'pc', where: state.pc + 1 + ir.imms.pcoffset9}]
+ : [],
+ },
+
+ {op: 'brz', fields: [
+ {kind: 'const', bits: [15, 9], val: 0b0000001},
+ {kind: 'imm', bits: [ 8, 0], sext: true, label: true,
+ name: 'pcoffset9'},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ (state.reg('cc') & 0b010)
+ ? [{kind: 'pc', where: state.pc + 1 + ir.imms.pcoffset9}]
+ : [],
+ },
+
+ {op: 'brzp', fields: [
+ {kind: 'const', bits: [15, 9], val: 0b0000001},
+ {kind: 'imm', bits: [ 8, 0], sext: true, label: true,
+ name: 'pcoffset9'},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ (state.reg('cc') & 0b011)
+ ? [{kind: 'pc', where: state.pc + 1 + ir.imms.pcoffset9}]
+ : [],
+ },
+
+ {op: 'brn', fields: [
+ {kind: 'const', bits: [15, 9], val: 0b0000001},
+ {kind: 'imm', bits: [ 8, 0], sext: true, label: true,
+ name: 'pcoffset9'},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ (state.reg('cc') & 0b100)
+ ? [{kind: 'pc', where: state.pc + 1 + ir.imms.pcoffset9}]
+ : [],
+ },
+
+ {op: 'brnp', fields: [
+ {kind: 'const', bits: [15, 9], val: 0b0000001},
+ {kind: 'imm', bits: [ 8, 0], sext: true, label: true,
+ name: 'pcoffset9'},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ (state.reg('cc') & 0b101)
+ ? [{kind: 'pc', where: state.pc + 1 + ir.imms.pcoffset9}]
+ : [],
+ },
+
+ {op: 'brnz', fields: [
+ {kind: 'const', bits: [15, 9], val: 0b0000001},
+ {kind: 'imm', bits: [ 8, 0], sext: true, label: true,
+ name: 'pcoffset9'},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ (state.reg('cc') & 0b110)
+ ? [{kind: 'pc', where: state.pc + 1 + ir.imms.pcoffset9}]
+ : [],
+ },
+
+ {op: 'jmp', fields: [
+ {kind: 'const', bits: [15, 9], val: 0b1100000},
+ {kind: 'reg', bits: [ 8, 6], prefix: 'r', name: 'baser'},
+ {kind: 'const', bits: [ 5, 0], val: 0b000000},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ [{kind: 'pc', where: state.reg(ir.regs.baser)}],
+ },
+
+ {op: 'ret', fields: [
+ {kind: 'const', bits: [15, 0], val: 0b1100000111000000},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ [{kind: 'pc', where: state.reg(['r', 7])}],
+ },
+
+ {op: 'jsr', fields: [
+ {kind: 'const', bits: [15, 11], val: 0b01000},
+ {kind: 'imm', bits: [10, 0], sext: true, label: true,
+ name: 'pcoffset11'},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ [{kind: 'pc', where: state.pc + 1 + ir.imms.pcoffset11},
+ {kind: 'reg', reg: ['r', 7], val: state.pc + 1}],
+ },
+
+ {op: 'jsrr', fields: [
+ {kind: 'const', bits: [15, 9], val: 0b1000000},
+ {kind: 'reg', bits: [ 8, 6], prefix: 'r', name: 'baser'},
+ {kind: 'const', bits: [ 5, 0], val: 0b000000},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ [{kind: 'pc', where: state.reg(ir.regs.baser)},
+ {kind: 'reg', reg: ['r', 7], val: state.pc + 1}],
+ },
+
+ {op: 'ld', fields: [
+ {kind: 'const', bits: [15, 12], val: 0b0010},
+ {kind: 'reg', bits: [11, 9], prefix: 'r', name: 'dr'},
+ {kind: 'imm', bits: [ 8, 0], sext: true, label: true,
+ name: 'pcoffset9'},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ [{kind: 'reg', reg: ir.regs.dr,
+ val: state.load(state.pc + 1 + ir.imms.pcoffset9)}],
+ },
+
+ {op: 'ldi', fields: [
+ {kind: 'const', bits: [15, 12], val: 0b1010},
+ {kind: 'reg', bits: [11, 9], prefix: 'r', name: 'dr'},
+ {kind: 'imm', bits: [ 8, 0], sext: true, label: true,
+ name: 'pcoffset9'},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ [{kind: 'reg', reg: ir.regs.dr,
+ val: state.load(state.pc + 1 + ir.imms.pcoffset9)}],
+ },
+
+ {op: 'ldr', fields: [
+ {kind: 'const', bits: [15, 12], val: 0b0110},
+ {kind: 'reg', bits: [11, 9], prefix: 'r', name: 'dr'},
+ {kind: 'reg', bits: [ 8, 6], prefix: 'r', name: 'baser'},
+ {kind: 'imm', bits: [ 5, 0], sext: true, label: false,
+ name: 'offset6'},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ [{kind: 'reg', reg: ir.regs.dr,
+ val: state.load(state.reg(ir.regs.baser) + 1 +
+ ir.imms.offset6)}],
+ },
+
+ {op: 'lea', fields: [
+ {kind: 'const', bits: [15, 12], val: 0b1110},
+ {kind: 'reg', bits: [11, 9], prefix: 'r', name: 'dr'},
+ {kind: 'imm', bits: [ 8, 0], sext: true, label: true,
+ name: 'pcoffset9'},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ [{kind: 'reg', reg: ir.regs.dr,
+ val: state.pc + 1 + ir.imms.pcoffset9}],
+ },
+
+ {op: 'not', fields: [
+ {kind: 'const', bits: [15, 12], val: 0b1001},
+ {kind: 'reg', bits: [11, 9], prefix: 'r', name: 'dr'},
+ {kind: 'reg', bits: [ 8, 6], prefix: 'r', name: 'sr'},
+ {kind: 'const', bits: [ 5, 0], val: 0b111111},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ [{kind: 'reg', reg: ir.regs.dr, val: ~state.reg(ir.regs.sr)}],
+ },
+
+ {op: 'st', fields: [
+ {kind: 'const', bits: [15, 12], val: 0b0011},
+ {kind: 'reg', bits: [11, 9], prefix: 'r', name: 'sr'},
+ {kind: 'imm', bits: [ 8, 0], sext: true, label: true,
+ name: 'pcoffset9'},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ [{kind: 'mem', addr: state.pc + 1 + ir.imms.pcoffset9,
+ val: state.reg(ir.regs.sr)}],
+ },
+
+ {op: 'sti', fields: [
+ {kind: 'const', bits: [15, 12], val: 0b1011},
+ {kind: 'reg', bits: [11, 9], prefix: 'r', name: 'sr'},
+ {kind: 'imm', bits: [ 8, 0], sext: true, label: true,
+ name: 'pcoffset9'},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ [{kind: 'mem',
+ addr: state.load(state.pc + 1 + ir.imms.pcoffset9),
+ val: state.reg(ir.regs.sr)}],
+ },
+
+ {op: 'str', fields: [
+ {kind: 'const', bits: [15, 12], val: 0b0111},
+ {kind: 'reg', bits: [11, 9], prefix: 'r', name: 'sr'},
+ {kind: 'reg', bits: [ 8, 6], prefix: 'r', name: 'baser'},
+ {kind: 'imm', bits: [ 5, 0], sext: true, label: false,
+ name: 'offset6'},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ [{kind: 'mem',
+ addr: state.reg(ir.regs.baser) + ir.imms.offset6,
+ val: state.reg(ir.regs.sr)}],
+ },
+
+ {op: 'trap', fields: [
+ {kind: 'const', bits: [15, 8], val: 0b11110000},
+ {kind: 'imm', bits: [ 7, 0], sext: false, label: false,
+ name: 'trapvect8'},
+ ],
+ sim: (state: MachineState, ir: Fields) =>
+ [{kind: 'reg', reg: ['r', 7], val: state.pc + 1},
+ {kind: 'pc', where: state.load(ir.imms.trapvect8)}],
+ },
+
+ // TODO: implement these
+ {op: 'getc', fields: [
+ {kind: 'const', bits: [15, 0], val: 0b1111000000100000},
+ ],
+ sim: (state: MachineState, ir: Fields) => [],
+ },
+
+ {op: 'out', fields: [
+ {kind: 'const', bits: [15, 0], val: 0b1111000000100001},
+ ],
+ sim: (state: MachineState, ir: Fields) => [],
+ },
+
+ {op: 'puts', fields: [
+ {kind: 'const', bits: [15, 0], val: 0b1111000000100010},
+ ],
+ sim: (state: MachineState, ir: Fields) => [],
+ },
+
+ {op: 'in', fields: [
+ {kind: 'const', bits: [15, 0], val: 0b1111000000100011},
+ ],
+ sim: (state: MachineState, ir: Fields) => [],
+ },
+
+ {op: 'halt', fields: [
+ {kind: 'const', bits: [15, 0], val: 0b1111000000100101},
+ ],
+ sim: (state: MachineState, ir: Fields) => [],
+ },
],
};
diff --git a/novice/assembler/isa/state.ts b/novice/assembler/isa/state.ts
index 4f643ee..1105d53 100644
--- a/novice/assembler/isa/state.ts
+++ b/novice/assembler/isa/state.ts
@@ -4,6 +4,10 @@ type RegIdentifier = string|[string, number];
interface MachineState {
// Auto sign-extends and everything
reg: (reg: RegIdentifier) => number;
+ // Address of current instruction. NOT incremented!
+ pc: number;
+ // memory accesses
+ load: (addr: number) => number;
}
interface MachineStateRegUpdate {
@@ -18,6 +22,13 @@ interface MachineStateMemUpdate {
val: number;
}
-type MachineStateUpdate = MachineStateRegUpdate|MachineStateMemUpdate;
+interface MachineStatePcUpdate {
+ kind: 'pc';
+ where: number;
+}
+
+type MachineStateUpdate = MachineStateRegUpdate|
+ MachineStateMemUpdate|
+ MachineStatePcUpdate;
export { RegIdentifier, MachineState, MachineStateUpdate };
diff --git a/novice/assembler/parsers/complx.ts b/novice/assembler/parsers/complx.ts
index 331855b..a158379 100644
--- a/novice/assembler/parsers/complx.ts
+++ b/novice/assembler/parsers/complx.ts
@@ -1,4 +1,5 @@
// Parser for complx syntax
+import { Isa } from '../isa';
import { ParseTable, ParseTree } from '../lr1';
import { Grammar } from './grammar';
import { grammar, NT, T } from './grammars/complx';
@@ -8,6 +9,7 @@ import { AbstractParser, Instruction, IntegerOperand, LabelOperand, Line,
import table from './tables/complx';
interface ParseContext {
+ isa: Isa;
currentSection: Section|null;
labels: string[];
assembly: ParsedAssembly;
@@ -22,8 +24,8 @@ class ComplxParser extends AbstractParser<ParseContext, NT> {
return grammar;
}
- protected initCtx(): ParseContext {
- return {currentSection: null, labels: [],
+ protected initCtx(isa: Isa): ParseContext {
+ return {isa, currentSection: null, labels: [],
assembly: {sections: [], labels: {}}};
}
@@ -33,23 +35,63 @@ class ComplxParser extends AbstractParser<ParseContext, NT> {
const op = parseTree.children[0];
switch (op.token) {
- case 'label':
+ case 'word':
+ // Two cases for a word on its own:
+ // 1. it's an instruction, ex:
+ // halt
+ // 2. it's a label, ex:
+ // cleanup
+ // We can't handle these two cases in the ISA-agnostic
+ // grammar unambiguously, so handle them here when
+ // inspecting the parse tree
+ const isInstr = this.isInstruction(ctx, op);
+
if (!ctx.currentSection) {
- throw new Error(`stray label on line ${line.num}`);
+ const what = isInstr ? 'instruction' : 'label';
+ throw new Error(`stray ${what} on line ${line.num}`);
+ }
+
+ if (isInstr) {
+ ctx.currentSection.instructions.push(
+ {kind: 'instr', op: this.parseLabel(op).toLowerCase(),
+ operands: []});
+ this.applyLabels(ctx);
+ } else {
+ this.pushLabel(ctx, this.parseLabel(op), line);
}
- this.pushLabel(ctx, this.parseLabel(op), line);
break;
case 'instr-line':
if (!ctx.currentSection) {
throw new Error(`stray instruction on line ${line.num}`);
}
- const instrLabel = this.parseLineLabel(op);
+
+ // Another ambiguity which plagues LC-3 assembly:
+ // Which of these two cases is the following:
+ //
+ // word word
+ //
+ // 1. A labelled solo instruction, ex:
+ // myhaltlabel halt
+ // 2. An instruction with one label operand, like
+ // jsr myfunc
+ //
+ // To prevent being an ambiguous grammar, the grammar
+ // permits only #2 (that is, the parse trees we receive
+ // will contain only #2), so we need to handle case #1
+ // here.
+ let [instr, instrLabel] = this.handleLabelledSoloInstr(ctx, op);
+
+ if (!instr) {
+ instrLabel = this.parseLineLabel(op);
+ instr = this.parseInstrLine(op);
+ }
+
if (instrLabel) {
this.pushLabel(ctx, instrLabel, line);
}
- ctx.currentSection.instructions.push(this.parseInstrLine(op));
+ ctx.currentSection.instructions.push(instr);
this.applyLabels(ctx);
break;
@@ -104,19 +146,23 @@ class ComplxParser extends AbstractParser<ParseContext, NT> {
return ctx.assembly;
}
+ private isInstruction(ctx: ParseContext, op: ParseTree<NT, T>) {
+ const wordVal = this.parseLabel(op).toLowerCase();
+ return ctx.isa.instructions.some(
+ instr => instr.op.toLowerCase() === wordVal);
+ }
+
// Takes either an insr-line or a pseudoop-line, returns its label
// or null
private parseLineLabel(line: ParseTree<NT, T>): string|null {
- if (line.children[0].token === 'label') {
- const label = line.children[0];
- return this.parseLabel(label);
+ if (line.children[0].token === 'word') {
+ return this.parseLabel(line.children[0]);
} else {
return null;
}
}
- private parseLabel(label: ParseTree<NT, T>): string {
- const word = label.children[0];
+ private parseLabel(word: ParseTree<NT, T>): string {
return word.val as string;
}
@@ -134,6 +180,39 @@ class ComplxParser extends AbstractParser<ParseContext, NT> {
return {kind: 'pseudoop', op, operand};
}
+ private handleLabelledSoloInstr(ctx: ParseContext,
+ instrLine: ParseTree<NT, T>):
+ [Instruction|null, string|null] {
+ // Intended to handle this parse tree:
+ //
+ // instr-line
+ // |
+ // instr
+ // / \
+ // word instr-operands
+ // |
+ // operand
+ // |
+ // word
+
+ if (instrLine.children.length === 1 &&
+ instrLine.children[0].children[1].children.length === 1 &&
+ instrLine.children[0].children[1].children[0].children[0].token === 'word' &&
+ !this.isInstruction(ctx, instrLine.children[0].children[0])) {
+ const label = this.parseLabel(instrLine.children[0].children[0]);
+ const instr: Instruction = {
+ kind: 'instr',
+ op: this.parseLabel(instrLine.children[0]
+ .children[1]
+ .children[0]
+ .children[0]).toLowerCase(),
+ operands: []};
+ return [instr, label];
+ } else {
+ return [null, null];
+ }
+ }
+
private parseInstrLine(instrLine: ParseTree<NT, T>): Instruction {
const instruction: Instruction = {kind: 'instr', op: '', operands: []};
const instr = instrLine.children[instrLine.children.length - 1];
diff --git a/novice/assembler/parsers/grammars/complx.ts b/novice/assembler/parsers/grammars/complx.ts
index a5892e4..674edea 100644
--- a/novice/assembler/parsers/grammars/complx.ts
+++ b/novice/assembler/parsers/grammars/complx.ts
@@ -16,13 +16,11 @@ type NT = keyof typeof NTsObj;
const NTs = new Set(Object.keys(NTsObj) as NT[]);
const productions: Production<NT, T>[] = [
- {lhs: 'line', rhs: ['label']},
+ {lhs: 'line', rhs: ['word']},
{lhs: 'line', rhs: ['instr-line']},
{lhs: 'line', rhs: ['pseudoop-line']},
- {lhs: 'label', rhs: ['word', ':']},
- {lhs: 'instr-line', rhs: ['label', 'instr']},
+ {lhs: 'instr-line', rhs: ['word', 'instr']},
{lhs: 'instr-line', rhs: ['instr']},
- {lhs: 'instr', rhs: ['word']},
{lhs: 'instr', rhs: ['word', 'instr-operands']},
{lhs: 'instr-operands', rhs: ['operand']},
{lhs: 'instr-operands', rhs: ['instr-operands', ',', 'operand']},
@@ -31,7 +29,7 @@ const productions: Production<NT, T>[] = [
{lhs: 'operand', rhs: ['int-decimal']},
{lhs: 'operand', rhs: ['int-hex']},
{lhs: 'operand', rhs: ['reg']},
- {lhs: 'pseudoop-line', rhs: ['label', 'pseudoop-call']},
+ {lhs: 'pseudoop-line', rhs: ['word', 'pseudoop-call']},
{lhs: 'pseudoop-line', rhs: ['pseudoop-call']},
{lhs: 'pseudoop-call', rhs: ['pseudoop']},
{lhs: 'pseudoop-call', rhs: ['pseudoop', 'pseudoop-operand']},
diff --git a/novice/assembler/parsers/parser.ts b/novice/assembler/parsers/parser.ts
index 93e4f4b..7ea928d 100644
--- a/novice/assembler/parsers/parser.ts
+++ b/novice/assembler/parsers/parser.ts
@@ -1,4 +1,5 @@
import { Readable } from 'stream';
+import { Isa } from '../isa';
import { Parser as LR1Parser, ParseTable, ParseTree,
TableGenerator } from '../lr1';
import { Line, Scanner, Token } from '../scanner';
@@ -47,7 +48,7 @@ interface ParsedAssembly {
}
interface Parser {
- parse(line: Line<T>[]): ParsedAssembly;
+ parse(isa: Isa, line: Line<T>[]): ParsedAssembly;
// Pass back an object because higher levels of abstraction don't
// care about what exactly is in here, it's just a blob of JSON
genTable(): object;
@@ -61,8 +62,8 @@ abstract class AbstractParser<Ctx, NT> implements Parser {
this.parser = new LR1Parser<NT, T>(this.getTable());
}
- public parse(lines: Line<T>[]): ParsedAssembly {
- const ctx = this.initCtx();
+ public parse(isa: Isa, lines: Line<T>[]): ParsedAssembly {
+ const ctx = this.initCtx(isa);
for (const line of lines) {
const parseTree = this.parser.parse(line);
@@ -80,7 +81,7 @@ abstract class AbstractParser<Ctx, NT> implements Parser {
protected abstract getTable(): ParseTable<NT, T>;
protected abstract getGrammar(): Grammar<NT>;
- protected abstract initCtx(): Ctx;
+ protected abstract initCtx(isa: Isa): Ctx;
protected abstract parseLine(ctx: Ctx, parseTree: ParseTree<NT, T>,
line: Line<T>): void;
protected abstract finish(ctx: Ctx): ParsedAssembly;
diff --git a/novice/assembler/parsers/tables/complx.ts b/novice/assembler/parsers/tables/complx.ts
index 02d45bd..7de34b2 100644
--- a/novice/assembler/parsers/tables/complx.ts
+++ b/novice/assembler/parsers/tables/complx.ts
@@ -1,7 +1,7 @@
/* tslint:disable */
-// WARNING: GENERATED CODE by bootstrap-parse-table.sh
+// WARNING: GENERATED CODE by generate-parse-table.sh
import { ParseTable } from '../../lr1';
import { T } from '../grammar';
import { NT } from '../grammars/complx';
-const table: ParseTable<NT, T> = {"positions":{"(":0,")":1,",":2,":":3,"char":4,"eof":5,"int-decimal":6,"int-hex":7,"pseudoop":8,"reg":9,"string":10,"word":11,"instr":0,"instr-line":1,"instr-operands":2,"label":3,"line":4,"operand":5,"pseudoop-call":6,"pseudoop-line":7,"pseudoop-operand":8},"actionTable":[[null,null,null,null,null,null,null,null,{"action":"shift","newState":7},null,null,{"action":"shift","newState":4}],[null,null,null,null,null,{"action":"accept","production":{"lhs":"line","rhs":["label"]}},null,null,{"action":"shift","newState":7},null,null,{"action":"shift","newState":32}],[null,null,null,null,null,{"action":"accept","production":{"lhs":"line","rhs":["instr-line"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"accept","production":{"lhs":"line","rhs":["pseudoop-line"]}},null,null,null,null,null,null],[null,null,null,{"action":"shift","newState":14},null,{"action":"reduce","production":{"lhs":"instr","rhs":["word"]}},{"action":"shift","newState":18},{"action":"shift","newState":19},null,{"action":"shift","newState":20},null,{"action":"shift","newState":17}],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"instr-line","rhs":["instr"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"pseudoop-line","rhs":["pseudoop-call"]}},null,null,null,null,null,null],[null,null,null,null,{"action":"shift","newState":12},{"action":"reduce","production":{"lhs":"pseudoop-call","rhs":["pseudoop"]}},{"action":"shift","newState":10},{"action":"shift","newState":11},null,null,{"action":"shift","newState":13},{"action":"shift","newState":9}],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"pseudoop-call","rhs":["pseudoop","pseudoop-operand"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"pseudoop-operand","rhs":["word"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"pseudoop-operand","rhs":["int-decimal"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"pseudoop-operand","rhs":["int-hex"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"pseudoop-operand","rhs":["char"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"pseudoop-operand","rhs":["string"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"label","rhs":["word",":"]}},null,null,{"action":"reduce","production":{"lhs":"label","rhs":["word",":"]}},null,null,{"action":"reduce","production":{"lhs":"label","rhs":["word",":"]}}],[{"action":"shift","newState":22},null,{"action":"shift","newState":21},null,null,{"action":"reduce","production":{"lhs":"instr","rhs":["word","instr-operands"]}},null,null,null,null,null,null],[{"action":"reduce","production":{"lhs":"instr-operands","rhs":["operand"]}},null,{"action":"reduce","production":{"lhs":"instr-operands","rhs":["operand"]}},null,null,{"action":"reduce","production":{"lhs":"instr-operands","rhs":["operand"]}},null,null,null,null,null,null],[{"action":"reduce","production":{"lhs":"operand","rhs":["word"]}},null,{"action":"reduce","production":{"lhs":"operand","rhs":["word"]}},null,null,{"action":"reduce","production":{"lhs":"operand","rhs":["word"]}},null,null,null,null,null,null],[{"action":"reduce","production":{"lhs":"operand","rhs":["int-decimal"]}},null,{"action":"reduce","production":{"lhs":"operand","rhs":["int-decimal"]}},null,null,{"action":"reduce","production":{"lhs":"operand","rhs":["int-decimal"]}},null,null,null,null,null,null],[{"action":"reduce","production":{"lhs":"operand","rhs":["int-hex"]}},null,{"action":"reduce","production":{"lhs":"operand","rhs":["int-hex"]}},null,null,{"action":"reduce","production":{"lhs":"operand","rhs":["int-hex"]}},null,null,null,null,null,null],[{"action":"reduce","production":{"lhs":"operand","rhs":["reg"]}},null,{"action":"reduce","production":{"lhs":"operand","rhs":["reg"]}},null,null,{"action":"reduce","production":{"lhs":"operand","rhs":["reg"]}},null,null,null,null,null,null],[null,null,null,null,null,null,{"action":"shift","newState":18},{"action":"shift","newState":19},null,{"action":"shift","newState":20},null,{"action":"shift","newState":17}],[null,null,null,null,null,null,{"action":"shift","newState":25},{"action":"shift","newState":26},null,{"action":"shift","newState":27},null,{"action":"shift","newState":24}],[null,{"action":"shift","newState":28},null,null,null,null,null,null,null,null,null,null],[null,{"action":"reduce","production":{"lhs":"operand","rhs":["word"]}},null,null,null,null,null,null,null,null,null,null],[null,{"action":"reduce","production":{"lhs":"operand","rhs":["int-decimal"]}},null,null,null,null,null,null,null,null,null,null],[null,{"action":"reduce","production":{"lhs":"operand","rhs":["int-hex"]}},null,null,null,null,null,null,null,null,null,null],[null,{"action":"reduce","production":{"lhs":"operand","rhs":["reg"]}},null,null,null,null,null,null,null,null,null,null],[{"action":"reduce","production":{"lhs":"instr-operands","rhs":["instr-operands","(","operand",")"]}},null,{"action":"reduce","production":{"lhs":"instr-operands","rhs":["instr-operands","(","operand",")"]}},null,null,{"action":"reduce","production":{"lhs":"instr-operands","rhs":["instr-operands","(","operand",")"]}},null,null,null,null,null,null],[{"action":"reduce","production":{"lhs":"instr-operands","rhs":["instr-operands",",","operand"]}},null,{"action":"reduce","production":{"lhs":"instr-operands","rhs":["instr-operands",",","operand"]}},null,null,{"action":"reduce","production":{"lhs":"instr-operands","rhs":["instr-operands",",","operand"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"instr-line","rhs":["label","instr"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"pseudoop-line","rhs":["label","pseudoop-call"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"instr","rhs":["word"]}},{"action":"shift","newState":18},{"action":"shift","newState":19},null,{"action":"shift","newState":20},null,{"action":"shift","newState":17}]],"gotoTable":[[5,2,null,1,null,null,6,3,null],[30,null,null,null,null,null,31,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,15,null,null,16,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,8],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,29,null,null,null],[null,null,null,null,null,23,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,15,null,null,16,null,null,null]]};
+const table: ParseTable<NT, T> = {"positions":{"(":0,")":1,",":2,":":3,"char":4,"eof":5,"int-decimal":6,"int-hex":7,"pseudoop":8,"reg":9,"string":10,"word":11,"instr":0,"instr-line":1,"instr-operands":2,"label":3,"line":4,"operand":5,"pseudoop-call":6,"pseudoop-line":7,"pseudoop-operand":8},"actionTable":[[null,null,null,null,null,null,null,null,{"action":"shift","newState":6},null,null,{"action":"shift","newState":1}],[null,null,null,null,null,{"action":"accept","production":{"lhs":"line","rhs":["word"]}},{"action":"shift","newState":18},{"action":"shift","newState":19},{"action":"shift","newState":6},{"action":"shift","newState":20},null,{"action":"shift","newState":16}],[null,null,null,null,null,{"action":"accept","production":{"lhs":"line","rhs":["instr-line"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"accept","production":{"lhs":"line","rhs":["pseudoop-line"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"instr-line","rhs":["instr"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"pseudoop-line","rhs":["pseudoop-call"]}},null,null,null,null,null,null],[null,null,null,null,{"action":"shift","newState":11},{"action":"reduce","production":{"lhs":"pseudoop-call","rhs":["pseudoop"]}},{"action":"shift","newState":9},{"action":"shift","newState":10},null,null,{"action":"shift","newState":12},{"action":"shift","newState":8}],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"pseudoop-call","rhs":["pseudoop","pseudoop-operand"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"pseudoop-operand","rhs":["word"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"pseudoop-operand","rhs":["int-decimal"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"pseudoop-operand","rhs":["int-hex"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"pseudoop-operand","rhs":["char"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"pseudoop-operand","rhs":["string"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"instr-line","rhs":["word","instr"]}},null,null,null,null,null,null],[null,null,null,null,null,{"action":"reduce","production":{"lhs":"pseudoop-line","rhs":["word","pseudoop-call"]}},null,null,null,null,null,null],[{"action":"shift","newState":23},null,{"action":"shift","newState":22},null,null,{"action":"reduce","production":{"lhs":"instr","rhs":["word","instr-operands"]}},null,null,null,null,null,null],[{"action":"reduce","production":{"lhs":"operand","rhs":["word"]}},null,{"action":"reduce","production":{"lhs":"operand","rhs":["word"]}},null,null,{"action":"reduce","production":{"lhs":"operand","rhs":["word"]}},{"action":"shift","newState":18},{"action":"shift","newState":19},null,{"action":"shift","newState":20},null,{"action":"shift","newState":21}],[{"action":"reduce","production":{"lhs":"instr-operands","rhs":["operand"]}},null,{"action":"reduce","production":{"lhs":"instr-operands","rhs":["operand"]}},null,null,{"action":"reduce","production":{"lhs":"instr-operands","rhs":["operand"]}},null,null,null,null,null,null],[{"action":"reduce","production":{"lhs":"operand","rhs":["int-decimal"]}},null,{"action":"reduce","production":{"lhs":"operand","rhs":["int-decimal"]}},null,null,{"action":"reduce","production":{"lhs":"operand","rhs":["int-decimal"]}},null,null,null,null,null,null],[{"action":"reduce","production":{"lhs":"operand","rhs":["int-hex"]}},null,{"action":"reduce","production":{"lhs":"operand","rhs":["int-hex"]}},null,null,{"action":"reduce","production":{"lhs":"operand","rhs":["int-hex"]}},null,null,null,null,null,null],[{"action":"reduce","production":{"lhs":"operand","rhs":["reg"]}},null,{"action":"reduce","production":{"lhs":"operand","rhs":["reg"]}},null,null,{"action":"reduce","production":{"lhs":"operand","rhs":["reg"]}},null,null,null,null,null,null],[{"action":"reduce","production":{"lhs":"operand","rhs":["word"]}},null,{"action":"reduce","production":{"lhs":"operand","rhs":["word"]}},null,null,{"action":"reduce","production":{"lhs":"operand","rhs":["word"]}},null,null,null,null,null,null],[null,null,null,null,null,null,{"action":"shift","newState":18},{"action":"shift","newState":19},null,{"action":"shift","newState":20},null,{"action":"shift","newState":21}],[null,null,null,null,null,null,{"action":"shift","newState":26},{"action":"shift","newState":27},null,{"action":"shift","newState":28},null,{"action":"shift","newState":25}],[null,{"action":"shift","newState":29},null,null,null,null,null,null,null,null,null,null],[null,{"action":"reduce","production":{"lhs":"operand","rhs":["word"]}},null,null,null,null,null,null,null,null,null,null],[null,{"action":"reduce","production":{"lhs":"operand","rhs":["int-decimal"]}},null,null,null,null,null,null,null,null,null,null],[null,{"action":"reduce","production":{"lhs":"operand","rhs":["int-hex"]}},null,null,null,null,null,null,null,null,null,null],[null,{"action":"reduce","production":{"lhs":"operand","rhs":["reg"]}},null,null,null,null,null,null,null,null,null,null],[{"action":"reduce","production":{"lhs":"instr-operands","rhs":["instr-operands","(","operand",")"]}},null,{"action":"reduce","production":{"lhs":"instr-operands","rhs":["instr-operands","(","operand",")"]}},null,null,{"action":"reduce","production":{"lhs":"instr-operands","rhs":["instr-operands","(","operand",")"]}},null,null,null,null,null,null],[{"action":"reduce","production":{"lhs":"instr-operands","rhs":["instr-operands",",","operand"]}},null,{"action":"reduce","production":{"lhs":"instr-operands","rhs":["instr-operands",",","operand"]}},null,null,{"action":"reduce","production":{"lhs":"instr-operands","rhs":["instr-operands",",","operand"]}},null,null,null,null,null,null]],"gotoTable":[[4,2,null,null,null,null,5,3,null],[13,null,15,null,null,17,14,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,7],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,15,null,null,17,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,30,null,null,null],[null,null,null,null,null,24,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null,null]]};
export default table;
diff --git a/novice/cli.test.ts b/novice/cli.test.ts
index c1fdd89..154a321 100644
--- a/novice/cli.test.ts
+++ b/novice/cli.test.ts
@@ -6,7 +6,7 @@ import { Parser } from './assembler/parsers';
jest.mock('fs');
import * as fs from 'fs';
jest.mock('./assembler');
-import { Assembler, getParser } from './assembler';
+import { Assembler, getParser, getIsa } from './assembler';
describe('cli', () => {
let stdout: Writable, stderr: Writable;
@@ -34,6 +34,8 @@ describe('cli', () => {
Assembler.mockReset();
// @ts-ignore
getParser.mockReset();
+ // @ts-ignore
+ getIsa.mockReset();
});
it('prints usage with no args', () => {
@@ -73,18 +75,22 @@ describe('cli', () => {
// @ts-ignore
let mockParser: Parser = {thanku: 'next'};
// @ts-ignore
- getParser.mockImplementation((parserName: string) => {
- return mockParser;
- });
+ getParser.mockReturnValue(mockParser);
+ // @ts-ignore
+ let mockIsa: Isa = {its: 'gucci'};
+ // @ts-ignore
+ getIsa.mockReturnValue(mockIsa);
- return main(['asm-pass1', 'pasta', 'patrick.asm'],
+ return main(['asm-pass1', 'pizza', 'roll', 'patrick.asm'],
stdout, stderr).then(exitCode => {
// @ts-ignore
expect(fs.createReadStream.mock.calls).toEqual([['patrick.asm']]);
// @ts-ignore
- expect(getParser.mock.calls).toEqual([['pasta']]);
+ expect(getParser.mock.calls).toEqual([['pizza']]);
+ // @ts-ignore
+ expect(getIsa.mock.calls).toEqual([['roll']]);
// @ts-ignore
- expect(Assembler.mock.calls).toEqual([[mockParser]]);
+ expect(Assembler.mock.calls).toEqual([[mockParser, mockIsa]]);
expect(mockParse.mock.calls).toEqual([[mockFp]]);
expect(exitCode).toEqual(0);
@@ -104,7 +110,7 @@ describe('cli', () => {
// @ts-ignore
fs.createReadStream.mockReturnValue(mockFp);
- return main(['asm-pass1', 'pizza', 'sanjay.asm'],
+ return main(['asm-pass1', 'michael', 'lin', 'sanjay.asm'],
stdout, stderr).then(exitCode => {
// @ts-ignore
expect(fs.createReadStream.mock.calls).toEqual([['sanjay.asm']]);
diff --git a/novice/cli.ts b/novice/cli.ts
index 6d43a8f..d070fac 100644
--- a/novice/cli.ts
+++ b/novice/cli.ts
@@ -1,6 +1,6 @@
import * as fs from 'fs';
import { Writable } from 'stream';
-import { Assembler, getParser } from './assembler';
+import { Assembler, getIsa, getParser } from './assembler';
async function main(args: string[], stdout: Writable, stderr: Writable):
Promise<number> {
@@ -8,10 +8,11 @@ async function main(args: string[], stdout: Writable, stderr: Writable):
switch (subcommand) {
case 'asm-pass1':
- if (args.length === 3) {
+ if (args.length === 4) {
const parser = args[1];
- const path = args[2];
- return await asmPass1(parser, path, stdout, stderr);
+ const isa = args[2];
+ const path = args[3];
+ return await asmPass1(parser, isa, path, stdout, stderr);
} else {
return usage(stderr);
}
@@ -28,21 +29,22 @@ async function main(args: string[], stdout: Writable, stderr: Writable):
}
function usage(stderr: Writable): number {
- stderr.write('usage: novice asm-pass1 <parser> <file>\n' +
+ stderr.write('usage: novice asm-pass1 <parser> <isa> <file>\n' +
' novice tablegen <parser>\n');
return 1;
}
-async function asmPass1(parserName: string, path: string, stdout: Writable,
+async function asmPass1(parserName: string, isaName: string, path: string, stdout: Writable,
stderr: Writable): Promise<number> {
try {
const parser = getParser(parserName);
+ const isa = getIsa(isaName);
const fp = fs.createReadStream(path);
await new Promise((resolve, reject) => {
fp.on('readable', resolve);
fp.on('error', reject);
});
- const assembly = await new Assembler(parser).parse(fp);
+ const assembly = await new Assembler(parser, isa).parse(fp);
stdout.write(JSON.stringify(assembly));
return 0;
} catch (err) {