mirror of
				https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
				synced 2025-10-31 23:47:01 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			170 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			170 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { Ty, tyToString } from "./ty.ts";
 | |
| import * as ast from "./ast.ts";
 | |
| 
 | |
| export type Fn = {
 | |
|     stmt: ast.Stmt;
 | |
|     locals: Local[];
 | |
|     paramLocals: Map<number, Local>;
 | |
|     returnLocal: Local;
 | |
| 
 | |
|     blocks: Block[];
 | |
|     entry: Block;
 | |
|     exit: Block;
 | |
| };
 | |
| 
 | |
| export type Block = {
 | |
|     id: number;
 | |
|     stmts: Stmt[];
 | |
|     ter: Ter;
 | |
| };
 | |
| 
 | |
| export type Local = {
 | |
|     id: number;
 | |
|     ty: Ty;
 | |
|     ident?: string;
 | |
|     stmt?: ast.Stmt;
 | |
| };
 | |
| 
 | |
| export type Stmt = {
 | |
|     kind: StmtKind;
 | |
| };
 | |
| 
 | |
| export const Stmt = (kind: StmtKind): Stmt => ({ kind });
 | |
| 
 | |
| export type StmtKind =
 | |
|     | { tag: "error" }
 | |
|     | { tag: "push"; val: Val; ty: Ty }
 | |
|     | { tag: "pop" }
 | |
|     | { tag: "load"; local: Local }
 | |
|     | { tag: "store"; local: Local }
 | |
|     | { tag: "call"; args: number }
 | |
|     | { tag: BinaryOp };
 | |
| 
 | |
| export type BinaryOp =
 | |
|     | "lt"
 | |
|     | "gt"
 | |
|     | "le"
 | |
|     | "ge"
 | |
|     | "eq"
 | |
|     | "ne"
 | |
|     | "add"
 | |
|     | "sub"
 | |
|     | "mul"
 | |
|     | "div"
 | |
|     | "mod";
 | |
| 
 | |
| export type Ter = {
 | |
|     kind: TerKind;
 | |
| };
 | |
| export const Ter = (kind: TerKind): Ter => ({ kind });
 | |
| 
 | |
| export type TerKind =
 | |
|     | { tag: "error" }
 | |
|     | { tag: "unset" }
 | |
|     | { tag: "return" }
 | |
|     | { tag: "goto"; target: Block }
 | |
|     | {
 | |
|         tag: "if";
 | |
|         truthy: Block;
 | |
|         falsy: Block;
 | |
|     };
 | |
| 
 | |
| export type Val =
 | |
|     | { tag: "int"; val: number }
 | |
|     | { tag: "str"; val: string }
 | |
|     | { tag: "fn"; stmt: ast.Stmt };
 | |
| 
 | |
| export class FnStringifyer {
 | |
|     public constructor(
 | |
|         private fn: Fn,
 | |
|     ) {}
 | |
| 
 | |
|     public stringify(): string {
 | |
|         const kind = this.fn.stmt.kind;
 | |
|         if (kind.tag !== "fn") {
 | |
|             throw new Error();
 | |
|         }
 | |
|         return `${kind.ident}:\n${
 | |
|             this.fn.locals
 | |
|                 .map((local) =>
 | |
|                     `    %${local.id} :: ${tyToString(local.ty)}${
 | |
|                         local.ident ? ` '${local.ident}'` : ""
 | |
|                     }\n`
 | |
|                 )
 | |
|                 .join("")
 | |
|         }${
 | |
|             this.fn.blocks
 | |
|                 .map((block) =>
 | |
|                     `    .b${block.id}:\n${
 | |
|                         block.stmts
 | |
|                             .map((stmt) => `        ${this.stmt(stmt)}\n`)
 | |
|                             .join("")
 | |
|                     }        ${this.ter(block.ter)}\n`
 | |
|                 )
 | |
|                 .join("")
 | |
|         }`;
 | |
|     }
 | |
| 
 | |
|     private stmt(stmt: Stmt): string {
 | |
|         const k = stmt.kind;
 | |
|         switch (k.tag) {
 | |
|             case "error":
 | |
|                 return "<error>";
 | |
|             case "push":
 | |
|                 return `push (${tyToString(k.ty, { short: true })}) ${
 | |
|                     this.val(k.val)
 | |
|                 }`;
 | |
|             case "pop":
 | |
|                 return "pop";
 | |
|             case "load":
 | |
|                 return `load %${k.local.id}`;
 | |
|             case "store":
 | |
|                 return `store %${k.local.id}`;
 | |
|             case "call":
 | |
|                 return `call ${k.args}`;
 | |
|             case "lt":
 | |
|             case "gt":
 | |
|             case "le":
 | |
|             case "ge":
 | |
|             case "eq":
 | |
|             case "ne":
 | |
|             case "add":
 | |
|             case "sub":
 | |
|             case "mul":
 | |
|             case "div":
 | |
|             case "mod":
 | |
|                 return k.tag;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private ter(ter: Ter): string {
 | |
|         const k = ter.kind;
 | |
|         switch (k.tag) {
 | |
|             case "error":
 | |
|                 return "<error>";
 | |
|             case "unset":
 | |
|                 return "<unset>";
 | |
|             case "return":
 | |
|                 return "return";
 | |
|             case "goto":
 | |
|                 return `goto .b${k.target.id}`;
 | |
|             case "if":
 | |
|                 return `goto .b${k.truthy.id}, .b${k.falsy.id}`;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private val(val: Val): string {
 | |
|         switch (val.tag) {
 | |
|             case "str":
 | |
|                 return JSON.stringify(val.val);
 | |
|             case "int":
 | |
|                 return `${val.val}`;
 | |
|             case "fn":
 | |
|                 if (val.stmt.kind.tag !== "fn") {
 | |
|                     throw new Error();
 | |
|                 }
 | |
|                 return val.stmt.kind.ident;
 | |
|         }
 | |
|     }
 | |
| }
 |