import { BasicBlock } from "./ir/block" import { convertIR } from "./ir/convert" import { prettyPrintBlock } from "./ir/pretty" import { parse } from "./parser" import { allocateRegisters } from "./regalloc" import { realizeBlock } from "./sm83/realize" import { R8 } from "./sm83/cpu" import { allocate } from "./codegen/allocate" import type { Attr, Decl, Stmt, Type, VarDecl } from "./ast" type SymbolDefn = { attrs: Array, name: string, type: Type } type SymbolMap = { [name: string]: SymbolDefn } // TODO: Support more than one TU export const compile = (fileName: string, source: string): string => { // 1. Parse const ast = parse(source) // 2. Partition declarations and statements const decls = ast.filter((x): x is Decl => x.type == "decl") const stmts = ast.filter((x): x is Stmt => x.type == "stmt") // 3. Create top-level symbol map const symbols = processDecls(decls) // TODO: Support declaring types other than U8/S8 const asmDecls = Object.values(symbols).map(symbol => `${symbol.name}:: DB`) // TODO: Some form of type-checking // 4. Generate IR const ir = new BasicBlock(convertIR(stmts)) console.log("=== IR === ") console.log(prettyPrintBlock(ir)) // 5. Generate code const alloc = allocateRegisters(ir, Object.values(R8)) const insns = realizeBlock(allocate(alloc, ir)) let output = '' if (asmDecls.length > 0) { output += `SECTION "${fileName} Data", WRAM0\n\n` output += asmDecls.join("\n") output += "\n\n" } if (insns.length > 0) { output += `SECTION "${fileName} Code", ROM0\n\n` output += insns.join("\n") } console.log("=== ASM ===") return output } export const processDecls = (decls: Array): SymbolMap => decls.reduce(processDecl, {}) export const processDecl = (symbols: SymbolMap, decl: Decl): SymbolMap => { const symbol = processVarDecl(symbols, decl) // Don't declare extern symbols if (hasAttr(symbol, { name: 'extern', args: [] })) { return symbols } return { ...symbols, [symbol.name]: symbol, } } export const processVarDecl = (symbols: SymbolMap, decl: VarDecl): SymbolDefn => { if (typeof symbols[decl.name] !== 'undefined') { throw new Error(`a variable named \`${decl.name}' is already defined`) } return { attrs: decl.attrs, name: decl.name, type: decl.args.type, } } export const hasAttr = (symbol: SymbolDefn, attr: Attr) => { let has = false for (let i = 0; i < symbol.attrs.length; ++i) { has = attrEquals(symbol.attrs[i], attr) if (has) { break } } return has } const attrEquals = (attr1: Attr, attr2: Attr): boolean => { if (attr1.name !== attr2.name || attr1.args.length !== attr2.args.length) { return false } for (let i = 0; i < attr1.args.length; ++i) { if (attr1.args[i] !== attr2.args[i]) { return false } } return true }