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.
 
 

92 lines
2.4 KiB

import type { AssignStmt, Expr, Stmt } from "./ast"
type Operand = string | number
type ASSA<Data> = { dest: string, source: Operand } & Data
export type SSACopy = ASSA<{ source: Operand }> // dest = source
export type SSAUnary = ASSA<{ op: string }> // dest = op(source)
export type SSABinary = ASSA<{ op: string, source1: Operand }> // dest = op(source, source1)
export type SSA = SSACopy | SSAUnary | SSABinary
type IRState = {
nextID: number
ssa_stmts: Array<SSA>
}
export const convertIR = (stmts: Array<Stmt>): Array<SSA> => {
const state: IRState = {
nextID: 0,
ssa_stmts: [],
}
stmts.forEach(stmt => {
const ssa_stmts = convertAssignStmt(state, stmt)
state.ssa_stmts.push(...ssa_stmts)
})
return state.ssa_stmts
}
export const convertAssignStmt = (state: IRState, stmt: AssignStmt): Array<SSA> => {
return convertExpr(state, stmt.args.name, stmt.args.expr)
}
export const convertExpr = (state: IRState, dest: string, expr: Expr): Array<SSA> => {
let expr_stmts = []
if (typeof expr === "number" || typeof expr === "string") {
expr_stmts = [{ dest, source: expr }]
} else if (expr.type === "unary") {
const [source, stmts] = getSource(state, expr.arg)
stmts.push({
dest,
op: expr.op,
source,
})
expr_stmts = stmts
} else {
const [left_source, left_stmts] = getSource(state, expr.left)
const [right_source, right_stmts] = getSource(state, expr.right)
const stmts = [...left_stmts, ...right_stmts]
stmts.push({
dest,
op: expr.op,
source: left_source,
source1: right_source,
})
expr_stmts = stmts
}
return expr_stmts
}
const getSource = (state: IRState, expr: Expr): [string | number, Array<SSA>] => {
if (typeof expr === "number" || typeof expr === "string") {
return [expr, []]
}
const source = `temp${state.nextID++}`
const stmts = convertExpr(state, source, expr)
return [source, stmts]
}
export const prettyPrintIR = (ssa: SSA): string => {
let output = ""
if ("source1" in ssa) {
output = `${ssa.dest} = ${ssa.op}(${ssa.source}, ${ssa.source1})`
} else if ("op" in ssa) {
output = `${ssa.dest} = ${ssa.op}(${ssa.source})`
} else {
output = `${ssa.dest} = ${ssa.source}`
}
return output
}