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.

143 lines
3.4 KiB

  1. import type { AssignStmt, Expr, Stmt, UnaryOp, BinaryOp } from "./ast"
  2. type LocationType = "register" | "temporary" | "variable"
  3. type Location = {
  4. type: LocationType,
  5. name: string,
  6. }
  7. type Operand = Location | number
  8. type ASSA<Data> = { dest: Location, source: Operand } & Data
  9. export type SSACopy = ASSA<{}> // dest = source
  10. export type SSAUnary = ASSA<{ op: UnaryOp }> // dest = op(source)
  11. export type SSABinary = ASSA<{ op: BinaryOp, source1: Operand }> // dest = op(source, source1)
  12. export type SSA = SSACopy | SSAUnary | SSABinary
  13. type IRState = {
  14. nextID: number
  15. ssa_stmts: Array<SSA>
  16. }
  17. const temp = (state: IRState): Location => ({
  18. type: "temporary",
  19. name: `t${state.nextID++}`,
  20. })
  21. const vari = (name: string): Location => ({
  22. type: "variable",
  23. name,
  24. })
  25. const reg = (name: string): Location => ({
  26. type: "register",
  27. name,
  28. })
  29. export const convertIR = (stmts: Array<Stmt>): Array<SSA> => {
  30. const state: IRState = {
  31. nextID: 0,
  32. ssa_stmts: [],
  33. }
  34. stmts.forEach(stmt => {
  35. const ssa_stmts = convertAssignStmt(state, stmt)
  36. state.ssa_stmts.push(...ssa_stmts)
  37. })
  38. return state.ssa_stmts
  39. }
  40. export const convertAssignStmt = (state: IRState, stmt: AssignStmt): Array<SSA> => {
  41. return convertExpr(state, vari(stmt.args.name), stmt.args.expr)
  42. }
  43. export const convertExpr = (state: IRState, dest: Location, expr: Expr): Array<SSA> => {
  44. let expr_stmts = []
  45. if (typeof expr === "number") {
  46. expr_stmts = [{ dest, source: expr }]
  47. } else if (typeof expr === "string") {
  48. expr_stmts = [{ dest, source: vari(expr) }]
  49. } else if (expr.type === "unary") {
  50. const [source, stmts] = getSource(state, expr.arg)
  51. stmts.push({
  52. dest,
  53. op: expr.op,
  54. source,
  55. })
  56. expr_stmts = stmts
  57. } else {
  58. const [left_source, left_stmts] = getSource(state, expr.left)
  59. const [right_source, right_stmts] = getSource(state, expr.right)
  60. const stmts = [...left_stmts, ...right_stmts]
  61. stmts.push({
  62. dest,
  63. op: expr.op,
  64. source: left_source,
  65. source1: right_source,
  66. })
  67. expr_stmts = stmts
  68. }
  69. return expr_stmts
  70. }
  71. const getSource = (state: IRState, expr: Expr): [Operand, Array<SSA>] => {
  72. if (typeof expr === "number") {
  73. return [expr, []]
  74. } else if (typeof expr === "string") {
  75. return [vari(expr), []]
  76. }
  77. const source = temp(state)
  78. const stmts = convertExpr(state, source, expr)
  79. return [source, stmts]
  80. }
  81. export const prettyPrintIR = (ssa: SSA): string => {
  82. let output = ""
  83. if ("source1" in ssa) {
  84. output = `${ppLoc(ssa.dest)} = ${ssa.op}(${ppOp(ssa.source)}, ${ppOp(ssa.source1)})`
  85. } else if ("op" in ssa) {
  86. output = `${ppLoc(ssa.dest)} = ${ssa.op}(${ppOp(ssa.source)})`
  87. } else {
  88. output = `${ppLoc(ssa.dest)} = ${ppOp(ssa.source)}`
  89. }
  90. return output
  91. }
  92. const ppOp = (op: Operand): string =>
  93. typeof op === "number"
  94. ? op.toString()
  95. : ppLoc(op)
  96. const ppLoc = (loc: Location): string => {
  97. let output = ''
  98. switch (loc.type) {
  99. case "register":
  100. output = `%${loc.name}`
  101. break
  102. case "temporary":
  103. output = `#${loc.name}`
  104. break
  105. case "variable":
  106. output = loc.name
  107. break
  108. }
  109. return output
  110. }