UNPKG

68kcounter

Version:
227 lines (196 loc) 5.24 kB
import json5 from "json5"; import { AddressingMode, aliases, Directive, isDirective, isMnemonic, isQualifier, Mnemonic, Qualifier, Qualifiers, } from "../syntax"; import lookupAddressingMode from "./operandMode"; import tokenize from "./tokenize"; export abstract class Node { type: string; loc: Location; text: string; constructor(start: number, text: string, type = "") { this.type = type; this.text = text; this.loc = { start, end: start + text.length, }; } } export interface Location { start: number; end: number; } export class LabelNode extends Node { name: string; local: boolean; macro: boolean; constructor(start: number, text: string) { super(start, text, "Label"); this.name = text; this.local = text.indexOf(".") === 0; this.macro = text.includes("@"); } } export class OpcodeNode extends Node { op: MnemonicNode | DirectiveNode | MacroNode; qualifier?: QualifierNode; constructor(start: number, text: string) { super(start, text, "Opcode"); let [op, qualifier] = text.toUpperCase().split("."); if (aliases[op]) op = aliases[op]; if (isMnemonic(op)) { this.op = new MnemonicNode(start, op); if (qualifier === Qualifiers.S) { qualifier = Qualifiers.B; } } else if (isDirective(op)) { this.op = new DirectiveNode(start, op); } else { this.op = new MacroNode(start, op); } if (isQualifier(qualifier)) { const sepPos = text.indexOf("."); this.qualifier = new QualifierNode(start + sepPos + 1, qualifier); } } } export class MnemonicNode extends Node { name: Mnemonic; constructor(start: number, name: Mnemonic) { super(start, name, "Mnemonic"); this.name = name; } } export class DirectiveNode extends Node { name: Directive; constructor(start: number, name: Directive) { super(start, name, "Directive"); this.name = name; } } export class MacroNode extends Node { name: string; constructor(start: number, name: string) { super(start, name, "Macro"); this.name = name; } } export class QualifierNode extends Node { name: Qualifier; constructor(start: number, name: Qualifier) { super(start, name, "Qualifier"); this.name = name; } } export class EffectiveAddressNode extends Node { mode: AddressingMode; constructor(start: number, text: string) { super(start, text, "EffectiveAddress"); this.mode = lookupAddressingMode(text); } } export class StringNode extends Node { value: string; constructor(start: number, text: string) { super(start, text, "String"); try { this.value = json5.parse(text); } catch (_) { this.value = ""; console.error("Unable to parse text", { text }); } } } export class MacroArgNode extends Node { index: number; constructor(start: number, text: string) { super(start, text, "MacroArg"); this.index = parseInt(text.substring(1), 10); } } export class MacroInvocationsNode extends Node { constructor(start: number, text: string) { super(start, text, "MacroInvocations"); } } export class CommentNode extends Node { constructor(start: number, text: string) { super(start, text, "Comment"); } } export class StatementNode extends Node { label?: LabelNode; opcode?: OpcodeNode; operands: Node[]; comment?: CommentNode; constructor(text: string) { super(0, text); this.operands = []; const tokens = tokenize(text); for (const i in tokens) { const [start, text] = tokens[i]; if (text[0] === ";" || (text[0] === "*" && text[1] !== "+")) { this.comment = new CommentNode(start, text); } else if (start === 0) { this.label = new LabelNode(start, text); } else if (!this.opcode) { this.opcode = new OpcodeNode(start, text); } else { // Operands if (["'", '"'].includes(text[0])) { this.operands.push(new StringNode(start, text)); } else if (text === "\\@") { this.operands.push(new MacroInvocationsNode(start, text)); } else if (text[0] === "\\") { this.operands.push(new MacroArgNode(start, text)); } else { this.operands.push(new EffectiveAddressNode(start, text)); } } } } isLabel(): this is LabelStatement { return !this.opcode && this.label !== undefined; } isInstruction(): this is InstructionStatement { return this.opcode !== undefined && this.opcode.op instanceof MnemonicNode; } isDirective(): this is DirectiveStatement { return this.opcode !== undefined && this.opcode.op instanceof DirectiveNode; } isMacro(): this is MacroStatement { return this.opcode !== undefined && this.opcode.op instanceof MacroNode; } } export interface InstructionStatement { opcode: { op: MnemonicNode; qualifier?: QualifierNode; }; operands: EffectiveAddressNode[]; } export interface DirectiveStatement { opcode: { op: DirectiveNode; qualifier?: QualifierNode; }; operands: Node[]; } export interface MacroStatement { opcode: { op: MacroNode; }; operands: Node[]; } export interface LabelStatement { label: LabelNode; }