A "high-level" language for the Gameboy
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

80 lines
2.0 KiB

import { convertASM } from "./asm"
import { convertIR, prettyPrintIR } from "./ir"
import { parse } from "./parser"
import type { Attr, Decl, Stmt, Type, VarDecl } from "./ast"
type SymbolDefn = {
attrs: Array<Attr>,
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 = convertIR(stmts)
console.log("=== IR === ")
console.log(ir.map(prettyPrintIR).join("\n"))
// 5. Generate code
const insns = convertASM(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<Decl>): SymbolMap => decls.reduce(processDecl, {})
export const processDecl = (symbols: SymbolMap, decl: Decl): SymbolMap => {
const symbol = processVarDecl(symbols, decl)
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,
}
}