Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(parser): make line count header optional #143

Merged
merged 10 commits into from
Apr 2, 2024
171 changes: 87 additions & 84 deletions src/core/Common/CodeParser.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,41 @@
import {
type Token,
TokenError,
type TokenPosition,
alt_sc,
apply,
buildLexer,
expectEOF,
expectSingleResult,
opt_sc,
rep_sc,
seq,
tok,
opt_sc,
Token,
TokenError,
alt_sc,
fail,
TokenPosition,
} from "typescript-parsec";
import { OpcodesNames, opcodeToFormat } from "./Opcodes";
import { Formats, FormatsNames } from "./InstructionFormats";
import { Instruction } from "./Instruction";
import { Formats, FormatsNames } from "./InstructionFormats";
import { OpcodesNames, opcodeToFormat } from "./Opcodes";

enum Tokens {
Inmediate,
RegFP,
RegGP,
Id,
Label,
BraketOpen,
BraketClose,
Number,
Comma,
Space,
NewLine,
Comment,
Inmediate = 0,
RegFP = 1,
RegGP = 2,
Id = 3,
Label = 4,
BraketOpen = 5,
BraketClose = 6,
Number = 7,
Comma = 8,
Space = 9,
NewLine = 10,
Comment = 11,
oxcabe marked this conversation as resolved.
Show resolved Hide resolved
}

enum RegType {
FP,
GP,
FP = 0,
GP = 1,
oxcabe marked this conversation as resolved.
Show resolved Hide resolved
}
let RegTypeNames: string[] = ["FP", "GP"];
const RegTypeNames: string[] = ["FP", "GP"];

interface Reg {
type: RegType;
Expand Down Expand Up @@ -74,14 +73,14 @@ const inmParser = apply(
tok(Tokens.Inmediate),
(num: Token<Tokens.Inmediate>) => {
return +num.text.slice(1);
}
},
);

const regParser = apply(
alt_sc(tok(Tokens.RegFP), tok(Tokens.RegGP)),
(reg: Token<Tokens.RegFP> | Token<Tokens.RegGP>) => {
let type = RegType.GP;
if (reg.kind == Tokens.RegFP) {
if (reg.kind === Tokens.RegFP) {
type = RegType.FP;
}
return {
Expand All @@ -90,44 +89,43 @@ const regParser = apply(
num: +reg.text.slice(1),
text: reg.text,
};
}
},
);

const addressParser = apply(
seq(
opt_sc(tok(Tokens.Number)),
tok(Tokens.BraketOpen),
regParser,
tok(Tokens.BraketClose)
tok(Tokens.BraketClose),
),
(
address: [
Token<Tokens.Number>,
Token<Tokens.BraketOpen>,
Reg,
Token<Tokens.BraketClose>
]
Token<Tokens.BraketClose>,
],
) => {
if (address[2].type == RegType.FP) {
if (address[2].type === RegType.FP) {
throw new TokenError(
address[2].pos,
"Address register cannot be FP register"
"Address register cannot be FP register",
);
}
return { address: address[0] ? +address[0].text : 0, reg: address[2] };
}
},
);

const opcodeParser = apply(
tok(Tokens.Id),
(opcodeTok: Token<Tokens.Id>): OpcodeToken => {
let opcode: number = OpcodesNames.indexOf(opcodeTok.text);
const opcode: number = OpcodesNames.indexOf(opcodeTok.text);
if (opcode !== -1) {
return { opcode: opcode, pos: opcodeTok.pos };
} else {
throw new TokenError(opcodeTok.pos, `Unknown opcode ${opcodeTok.text}`);
}
}
throw new TokenError(opcodeTok.pos, `Unknown opcode ${opcodeTok.text}`);
},
);

export class CodeParser {
Expand Down Expand Up @@ -156,22 +154,22 @@ export class CodeParser {
}

private parse(code: string) {
let result = expectSingleResult(
expectEOF(this.genCodeParser().parse(tokenizer.parse(code)))
const result = expectSingleResult(
expectEOF(this.genCodeParser().parse(tokenizer.parse(code))),
);

// Create labels and instructions
let pos = 0;
for (let i = 0; i < result[1].length; i++) {
let line = result[1][i];
if ("kind" in line && line.kind == Tokens.Label) {
let name = line.text.slice(0, -1);
const line = result[1][i];
if ("kind" in line && line.kind === Tokens.Label) {
const name = line.text.slice(0, -1);
if (name in this._labels) {
throw new Error(
`Error at instruction ${pos}, label ${line.text.slice(
0,
-1
)} already exists`
-1,
)} already exists`,
);
}
this._labels[name] = pos;
Expand All @@ -186,8 +184,8 @@ export class CodeParser {

private genCodeParser() {
return seq(
tok(Tokens.Number),
rep_sc(alt_sc(this.genOperationParser(), tok(Tokens.Label)))
opt_sc(tok(Tokens.Number)),
rep_sc(alt_sc(this.genOperationParser(), tok(Tokens.Label))),
);
}

Expand All @@ -198,22 +196,22 @@ export class CodeParser {
seq(opcodeParser, regParser, regParser, inmParser),
seq(opcodeParser, regParser, regParser, tok(Tokens.Id)),
seq(opcodeParser, regParser, addressParser),
opcodeParser
opcodeParser,
), // The order is important, the first succesfull match is the one that is returned
(
operation:
| OpcodeToken
| [OpcodeToken, Reg, Reg, Reg]
| [OpcodeToken, Reg, Reg, number]
| [OpcodeToken, Reg, Address]
| [OpcodeToken, Reg, Reg, Token<Tokens.Id>]
| [OpcodeToken, Reg, Reg, Token<Tokens.Id>],
) => {
var type: Formats;
var instruction: Instruction = new Instruction();
var pos: TokenPosition;
let type: Formats;
const instruction: Instruction = new Instruction();
let pos: TokenPosition;

// set the opcode and get the current position
if (operation instanceof Array) {
if (Array.isArray(operation)) {
instruction.opcode = operation[0].opcode;
pos = operation[0].pos;
} else {
Expand All @@ -222,31 +220,32 @@ export class CodeParser {
}

// Check the recived instruction format and set the operands
if (!(operation instanceof Array)) {
if (!Array.isArray(operation)) {
type = Formats.Noop;
} else if ("num" in operation[2] && operation.length == 4) {
} else if ("num" in operation[2] && operation.length === 4) {
if (operation[2].type !== operation[1].type) {
throw new TokenError(
operation[2].pos,
`Second operand register type(${
RegTypeNames[operation[2].type]
}) mistmatch. Expected ${RegTypeNames[operation[1].type]}`
}) mistmatch. Expected ${RegTypeNames[operation[1].type]}`,
);
}

if (typeof operation[3] == "number") {
if (typeof operation[3] === "number") {
type = Formats.GeneralRegisterAndInmediate;

// Check that the registers are in bounds
if (operation[1].num > this._generalRegisters) {
throw new TokenError(
operation[1].pos,
`Destiny register number out of bounds`
"Destiny register number out of bounds",
);
} else if (operation[2].num > this._generalRegisters) {
}
if (operation[2].num > this._generalRegisters) {
throw new TokenError(
operation[2].pos,
`Operand 1 register number out of bounds`
"Operand 1 register number out of bounds",
);
}

Expand All @@ -255,12 +254,12 @@ export class CodeParser {
instruction.setOperand(
2,
operation[3],
"#" + operation[3].toString()
`#${operation[3].toString()}`,
);
if (operation[1].type == RegType.FP) {
if (operation[1].type === RegType.FP) {
throw new TokenError(
operation[1].pos,
`Inmediate operand not allowed for floating point registers`
"Inmediate operand not allowed for floating point registers",
);
}
} else if ("num" in operation[3]) {
Expand All @@ -269,11 +268,11 @@ export class CodeParser {
operation[3].pos,
`Third operand register type(${
RegTypeNames[operation[3].type]
}) mistmatch. Expected ${RegTypeNames[operation[1].type]}`
}) mistmatch. Expected ${RegTypeNames[operation[1].type]}`,
);
}

if (operation[1].type == RegType.FP) {
if (operation[1].type === RegType.FP) {
type = Formats.TwoFloatingRegisters;
} else {
type = Formats.TwoGeneralRegisters;
Expand All @@ -283,17 +282,19 @@ export class CodeParser {
if (operation[1].num > this._generalRegisters) {
throw new TokenError(
operation[1].pos,
`Destiny register number out of bounds`
"Destiny register number out of bounds",
);
} else if (operation[2].num > this._generalRegisters) {
}
if (operation[2].num > this._generalRegisters) {
throw new TokenError(
operation[2].pos,
`Operand 1 register number out of bounds`
"Operand 1 register number out of bounds",
);
} else if (operation[3].num > this._generalRegisters) {
}
if (operation[3].num > this._generalRegisters) {
throw new TokenError(
operation[3].pos,
`Operand 2 register number out of bounds`
"Operand 2 register number out of bounds",
);
}

Expand All @@ -307,21 +308,22 @@ export class CodeParser {
if (operation[1].num > this._generalRegisters) {
throw new TokenError(
operation[1].pos,
`Operand 1 register number out of bounds`
"Operand 1 register number out of bounds",
);
} else if (operation[2].num > this._generalRegisters) {
}
if (operation[2].num > this._generalRegisters) {
throw new TokenError(
operation[2].pos,
`Operand 2 register number out of bounds`
"Operand 2 register number out of bounds",
);
}

instruction.setOperand(0, operation[1].num, operation[1].text);
instruction.setOperand(1, operation[2].num, operation[2].text);
instruction.setOperand(2, undefined, operation[3].text);
}
} else if (operation.length == 3) {
if (operation[1].type == RegType.FP) {
} else if (operation.length === 3) {
if (operation[1].type === RegType.FP) {
type = Formats.FloatingLoadStore;
} else {
type = Formats.GeneralLoadStore;
Expand All @@ -331,48 +333,49 @@ export class CodeParser {
if (operation[1].num > this._generalRegisters) {
throw new TokenError(
operation[1].pos,
`Destiny register number out of bounds`
"Destiny register number out of bounds",
);
} else if (operation[2].reg.num > this._generalRegisters) {
}
if (operation[2].reg.num > this._generalRegisters) {
throw new TokenError(
operation[2].reg.pos,
`Adress register number out of bounds`
"Adress register number out of bounds",
);
} else if (operation[2].address > this._memorySize) {
}
if (operation[2].address > this._memorySize) {
throw new TokenError(
operation[2].reg.pos,
`Memory address out of bounds`
"Memory address out of bounds",
);
}

instruction.setOperand(0, operation[1].num, operation[1].text);
instruction.setOperand(
1,
operation[2].address,
operation[2].address.toString()
operation[2].address.toString(),
);
instruction.setOperand(
2,
operation[2].reg.num,
"(" + operation[2].reg.text + ")"
`(${operation[2].reg.text})`,
);
}

let expectedType = opcodeToFormat(instruction.opcode);
const expectedType = opcodeToFormat(instruction.opcode);
if (type !== expectedType) {
//return fail(`Invalid instruction format for ${OpcodesNames[instruction.opcode]}. Expected ${FormatsNames[expectedType]} format, got ${FormatsNames[type]} format or similar`);
throw new TokenError(
pos,
`Invalid instruction format for ${
OpcodesNames[instruction.opcode]
}. Expected ${FormatsNames[expectedType]} format, got ${
FormatsNames[type]
} format or similar`
} format or similar`,
);
}

return instruction;
}
},
);
}
}
Loading