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.

66 lines
1.6 KiB

  1. import { convertASM } from "./asm"
  2. import { convertIR, prettyPrintIR } from "./ir"
  3. import { parse } from "./parser"
  4. import type { Attr, Decl, Stmt, Type, VarDecl } from "./ast"
  5. type SymbolDefn = {
  6. attrs: Array<Attr>,
  7. name: string,
  8. type: Type
  9. }
  10. type SymbolMap = {
  11. [name: string]: SymbolDefn
  12. }
  13. // TODO: Support more than one TU
  14. export const compile = (source: string): string => {
  15. // 1. Parse
  16. const ast = parse(source)
  17. // 2. Partition declarations and statements
  18. const decls = ast.filter((x): x is Decl => x.type == "decl")
  19. const stmts = ast.filter((x): x is Stmt => x.type == "stmt")
  20. // 3. Create top-level symbol map
  21. const symbols = processDecls(decls)
  22. // TODO: Some form of type-checking
  23. // 4. Generate IR
  24. const ir = convertIR(stmts)
  25. console.log("=== IR === ")
  26. console.log(ir.map(prettyPrintIR).join("\n"))
  27. // 5. Generate code
  28. const insns = convertASM(ir)
  29. const code = insns.join("\n")
  30. console.log("=== ASM === ")
  31. return code
  32. }
  33. export const processDecls = (decls: Array<Decl>): SymbolMap => decls.reduce(processDecl, {})
  34. export const processDecl = (symbols: SymbolMap, decl: Decl): SymbolMap => {
  35. const symbol = processVarDecl(symbols, decl)
  36. return {
  37. ...symbols,
  38. [symbol.name]: symbol,
  39. }
  40. }
  41. export const processVarDecl = (symbols: SymbolMap, decl: VarDecl): SymbolDefn => {
  42. if (typeof symbols[decl.name] !== 'undefined') {
  43. throw new Error(`a variable named \`${decl.name}' is already defined`)
  44. }
  45. return {
  46. attrs: decl.attrs,
  47. name: decl.name,
  48. type: decl.args.type,
  49. }
  50. }