@ -0,0 +1,44 @@ | |||
export type Program = Array<Decl | Stmt> | |||
// Declarations | |||
export type Decl = VarDecl | |||
export type VarDecl = ADecl<"var", { | |||
type: Type, | |||
}> | |||
type ADecl<Ty, Args> = { | |||
type: "decl", | |||
decl_type: Ty, | |||
attrs: Array<Attr>, | |||
name: string, | |||
args: Args, | |||
} | |||
// Statements | |||
export type Stmt = AssignStmt | |||
export type AssignStmt = AStmt<"assign", { | |||
name: string, | |||
expr: Expr, | |||
}> | |||
type AStmt<Ty, Args> = { | |||
type: "stmt", | |||
stmt_type: Ty, | |||
args: Args, | |||
} | |||
// Expressions | |||
export type Expr = number | |||
// Attributes | |||
export type Attr = { | |||
name: string, | |||
args: Array<string | number>, | |||
} | |||
// Types | |||
export type Type = PrimitiveType | |||
export type PrimitiveType = "s8" | "u8" | "u16" |
@ -1,9 +1,60 @@ | |||
import { parse } from "./parser" | |||
import { convertIR } from "./ir" | |||
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 = (source: string): string => { | |||
// 1. Parse | |||
const ast = parse(source) | |||
console.log(ast) | |||
// 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") | |||
console.log("Declarations", decls) | |||
console.log("Statements", stmts) | |||
// 3. Create top-level symbol map | |||
const symbols = processDecls(decls) | |||
console.log("Symbols", symbols) | |||
// TODO: Some form of type-checking | |||
// 4. Generate IR | |||
const ir = convertIR(stmts) | |||
console.log("IR", ir) | |||
return "" | |||
} | |||
export const processDecls = (decls: Array<Decl>): SymbolMap => decls.reduce((symbols, decl) => { | |||
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, | |||
} | |||
} |
@ -0,0 +1,46 @@ | |||
import type { AssignStmt, Expr, Stmt } from "./ast" | |||
// dest <- op(x, y) | |||
type Operand = string | number | |||
type ASSA<Data> = { dest: string } & Data | |||
type SSA = ASSA<{ source: Operand }> | |||
| ASSA<{ source: Operand, op: string }> | |||
| ASSA<{ source1: Operand, op: string, source2: Operand }> | |||
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> => { | |||
const dest = stmt.args.name | |||
const [source, expr_stmts] = convertExpr(state, stmt.args.expr) | |||
expr_stmts.push({ | |||
dest, | |||
source, | |||
}) | |||
return expr_stmts | |||
} | |||
export const convertExpr = (state: IRState, expr: Expr): [string, Array<SSA>] => { | |||
const name = `temp${state.nextID++}` | |||
return [name, [{ dest: name, source: expr }]] | |||
} |
@ -1,3 +1,5 @@ | |||
export const SyntaxError: any; | |||
import type { Program } from "./ast" | |||
export function parse(input: string): any; | |||
export const SyntaxError: any | |||
export function parse(input: string): Program |