mirror of
				https://git.sfja.dk/Mikkel/slige.git
				synced 2025-10-31 20:28:15 +00:00 
			
		
		
		
	fix lowerer
This commit is contained in:
		
							parent
							
								
									2dad35c21f
								
							
						
					
					
						commit
						916c2376a0
					
				| @ -1,113 +1,19 @@ | ||||
| import { Ops, opToString } from "./arch.ts"; | ||||
| 
 | ||||
| export type Line = { | ||||
|     labels?: number[]; | ||||
| } & LineKind; | ||||
| export type Line = { labels?: string[]; ins: Ins }; | ||||
| 
 | ||||
| export type Label = { label: number }; | ||||
| export type Ins = Lit[]; | ||||
| 
 | ||||
| export type LineKind = | ||||
|     | { type: "op"; op: number } | ||||
|     | { type: "lit"; val: number } | ||||
|     | { type: "ref"; label: number }; | ||||
| 
 | ||||
| export class Assembler { | ||||
|     private lines: Line[] = []; | ||||
|     private labelCounter = 0; | ||||
| 
 | ||||
|     public push(...values: number[]) { | ||||
|         for (const value of values) { | ||||
|             this.addLit(value); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public addOp(op: number): Assembler { | ||||
|         this.lines.push({ type: "op", op }); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public addLit(val: number): Assembler { | ||||
|         this.lines.push({ type: "lit", val }); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public addRef({ label }: Label): Assembler { | ||||
|         this.lines.push({ type: "ref", label }); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public setLabel({ label }: Label): Assembler { | ||||
|         const line = this.lines.at(-1); | ||||
|         if (line === undefined) { | ||||
|             return this; | ||||
|         } | ||||
|         if (line.labels === undefined) { | ||||
|             line.labels = []; | ||||
|         } | ||||
|         line.labels.push(label); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public makeLabel(): Label { | ||||
|         const label = this.labelCounter; | ||||
|         this.labelCounter += 1; | ||||
|         return { label }; | ||||
|     } | ||||
| 
 | ||||
|     public concat(assembler: Assembler) { | ||||
|         this.lines.push(...assembler.lines); | ||||
|     } | ||||
| 
 | ||||
|     public assemble(): number[] { | ||||
|         let ip = 0; | ||||
|         const output: number[] = []; | ||||
|         const locs: { [key: number]: number } = {}; | ||||
|         const refs: { [key: number]: number } = {}; | ||||
|         for (const line of this.lines) { | ||||
|             switch (line.type) { | ||||
|                 case "op": | ||||
|                     output.push(line.op); | ||||
|                     ip += 1; | ||||
|                     break; | ||||
|                 case "lit": | ||||
|                     output.push(line.val); | ||||
|                     ip += 1; | ||||
|                     break; | ||||
|                 case "ref": | ||||
|                     output.push(0); | ||||
|                     refs[ip] = line.label; | ||||
|                     ip += 1; | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|         for (let i = 0; i < output.length; ++i) { | ||||
|             if (!(i in refs)) { | ||||
|                 continue; | ||||
|             } | ||||
|             if (!(refs[i] in locs)) { | ||||
|                 console.error( | ||||
|                     `Assembler: label '${refs[i]}' used at ${i} not defined`, | ||||
|                 ); | ||||
|                 continue; | ||||
|             } | ||||
|             output[i] = locs[refs[i]]; | ||||
|         } | ||||
|         return output; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export type Line2 = { labels?: number[]; ins: Ins2 }; | ||||
| 
 | ||||
| export type Ins2 = [Ops, ...Lit[]]; | ||||
| export type Label = { label: string }; | ||||
| 
 | ||||
| export type Lit = number | string | boolean | Label; | ||||
| 
 | ||||
| export class Assembler2 { | ||||
|     private lines: Line2[] = []; | ||||
|     private addedLabels: number[] = []; | ||||
| export class Assembler { | ||||
|     private lines: Line[] = []; | ||||
|     private addedLabels: string[] = []; | ||||
|     private labelCounter = 0; | ||||
| 
 | ||||
|     public add(ins: Ins2): Assembler2 { | ||||
|     public add(...ins: Ins): Assembler { | ||||
|         if (this.addedLabels.length > 0) { | ||||
|             this.lines.push({ ins, labels: this.addedLabels }); | ||||
|             this.addedLabels = []; | ||||
| @ -117,8 +23,19 @@ export class Assembler2 { | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public concat(assembler: Assembler) { | ||||
|         if (assembler.lines.length < 0) { | ||||
|             return; | ||||
|         } | ||||
|         if (assembler.lines[0].labels !== undefined) { | ||||
|             this.addedLabels.push(...assembler.lines[0].labels); | ||||
|         } | ||||
|         this.add(...assembler.lines[0].ins); | ||||
|         this.lines.push(...assembler.lines.slice(1)); | ||||
|     } | ||||
| 
 | ||||
|     public makeLabel(): Label { | ||||
|         return { label: this.labelCounter++ }; | ||||
|         return { label: `.L${(this.labelCounter++).toString()}` }; | ||||
|     } | ||||
| 
 | ||||
|     public setLabel({ label }: Label) { | ||||
| @ -128,9 +45,12 @@ export class Assembler2 { | ||||
|     public assemble(): number[] { | ||||
|         let ip = 0; | ||||
|         const output: number[] = []; | ||||
|         const locs: { [key: number]: number } = {}; | ||||
|         const refs: { [key: number]: number } = {}; | ||||
|         const locs: { [key: string]: number } = {}; | ||||
|         const refs: { [key: number]: string } = {}; | ||||
|         for (const line of this.lines) { | ||||
|             for (const label of line.labels ?? []) { | ||||
|                 locs[label] = ip; | ||||
|             } | ||||
|             for (const lit of line.ins as Lit[]) { | ||||
|                 if (typeof lit === "number") { | ||||
|                     output.push(lit); | ||||
| @ -168,12 +88,10 @@ export class Assembler2 { | ||||
|     public printProgram() { | ||||
|         for (const line of this.lines) { | ||||
|             for (const label of line.labels ?? []) { | ||||
|                 console.log(`.L${label}:`); | ||||
|                 console.log(`${label}:`); | ||||
|             } | ||||
|             const op = opToString(line.ins[0] as unknown as number).padEnd( | ||||
|                 13, | ||||
|                 " ", | ||||
|             ); | ||||
|             const op = opToString(line.ins[0] as number) | ||||
|                 .padEnd(13, " "); | ||||
|             const args = (line.ins.slice(1) as Lit[]).map((lit) => { | ||||
|                 if (typeof lit === "number") { | ||||
|                     return lit; | ||||
| @ -186,7 +104,7 @@ export class Assembler2 { | ||||
|                             .replaceAll("\r", "\\r") + | ||||
|                         '"'; | ||||
|                 } else { | ||||
|                     return `.L${lit.label}`; | ||||
|                     return lit.label; | ||||
|                 } | ||||
|             }).join(", "); | ||||
|             console.log(`    ${op} ${args}`); | ||||
|  | ||||
| @ -8,7 +8,7 @@ import { VType, vtypeToString } from "./vtype.ts"; | ||||
| export class Lowerer { | ||||
|     private program = new Assembler(); | ||||
|     private locals: Locals = new LocalsFnRoot(); | ||||
|     private fnStmtIdAddrMap: { [key: number]: number } = {}; | ||||
|     private fnStmtIdLabelMap: { [key: number]: string } = {}; | ||||
|     private breakStack: Label[] = []; | ||||
| 
 | ||||
|     public lower(stmts: Stmt[]) { | ||||
| @ -51,7 +51,7 @@ export class Lowerer { | ||||
|                 return this.lowerAssignStmt(stmt); | ||||
|             case "expr": | ||||
|                 this.lowerExpr(stmt.kind.expr); | ||||
|                 this.program.push(Ops.Pop); | ||||
|                 this.program.add(Ops.Pop); | ||||
|                 return; | ||||
|         } | ||||
|         throw new Error(`unhandled stmt '${stmt.kind.type}'`); | ||||
| @ -65,21 +65,19 @@ export class Lowerer { | ||||
|         switch (stmt.kind.subject.kind.type) { | ||||
|             case "field": { | ||||
|                 this.lowerExpr(stmt.kind.subject.kind.subject); | ||||
|                 this.addPushStringOp(stmt.kind.subject.kind.value); | ||||
|                 this.program.addOp(Ops.Builtin); | ||||
|                 this.program.addLit(Builtins.StructSet); | ||||
|                 this.program.add(Ops.PushString, stmt.kind.subject.kind.value); | ||||
|                 this.program.add(Ops.Builtin, Builtins.StructSet); | ||||
|                 return; | ||||
|             } | ||||
|             case "index": { | ||||
|                 this.lowerExpr(stmt.kind.subject.kind.subject); | ||||
|                 this.lowerExpr(stmt.kind.subject.kind.value); | ||||
|                 this.program.addOp(Ops.Builtin); | ||||
|                 this.program.addLit(Builtins.ArraySet); | ||||
|                 this.program.add(Ops.Builtin, Builtins.ArraySet); | ||||
|                 return; | ||||
|             } | ||||
|             case "sym": { | ||||
|                 this.program.addOp(Ops.StoreLocal); | ||||
|                 this.program.addLit( | ||||
|                 this.program.add( | ||||
|                     Ops.StoreLocal, | ||||
|                     this.locals.symId(stmt.kind.subject.kind.sym.ident), | ||||
|                 ); | ||||
|                 return; | ||||
| @ -96,14 +94,17 @@ export class Lowerer { | ||||
|         if (stmt.kind.expr) { | ||||
|             this.lowerExpr(stmt.kind.expr); | ||||
|         } | ||||
|         this.program.addOp(Ops.Jump); | ||||
|         this.program.addRef(this.breakStack.at(-1)!); | ||||
|         this.program.add(Ops.Jump, this.breakStack.at(-1)!); | ||||
|     } | ||||
| 
 | ||||
|     private lowerFnStmt(stmt: Stmt) { | ||||
|         if (stmt.kind.type !== "fn") { | ||||
|             throw new Error(); | ||||
|         } | ||||
|         const label = `${stmt.kind.ident}_${stmt.id}`; | ||||
|         this.fnStmtIdLabelMap[stmt.id] = label; | ||||
|         this.program.setLabel({ label }); | ||||
| 
 | ||||
|         const outerLocals = this.locals; | ||||
|         this.locals = new LocalsFnRoot(outerLocals); | ||||
|         const outerProgram = this.program; | ||||
| @ -111,13 +112,13 @@ export class Lowerer { | ||||
| 
 | ||||
|         for (const { ident } of stmt.kind.params) { | ||||
|             this.locals.allocSym(ident); | ||||
|             this.program.push( | ||||
|             this.program.add( | ||||
|                 Ops.StoreLocal, | ||||
|                 this.locals.symId(ident), | ||||
|             ); | ||||
|         } | ||||
|         this.lowerExpr(stmt.kind.body); | ||||
|         this.program.push(Ops.Return); | ||||
|         this.program.add(Ops.Return); | ||||
| 
 | ||||
|         this.locals = outerLocals; | ||||
|         outerProgram.concat(this.program); | ||||
| @ -129,9 +130,11 @@ export class Lowerer { | ||||
|             throw new Error(); | ||||
|         } | ||||
|         this.lowerExpr(stmt.kind.value); | ||||
|         this.locals.allocSym(stmt.kind.param.ident), | ||||
|             this.program.push(Ops.StoreLocal); | ||||
|         this.program.push(this.locals.symId(stmt.kind.param.ident)); | ||||
|         this.locals.allocSym(stmt.kind.param.ident); | ||||
|         this.program.add( | ||||
|             Ops.StoreLocal, | ||||
|             this.locals.symId(stmt.kind.param.ident), | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     private lowerExpr(expr: Expr) { | ||||
| @ -177,22 +180,22 @@ export class Lowerer { | ||||
|             throw new Error(); | ||||
|         } | ||||
|         if (expr.kind.sym.type === "let") { | ||||
|             this.program.push( | ||||
|             this.program.add( | ||||
|                 Ops.LoadLocal, | ||||
|                 this.locals.symId(expr.kind.ident), | ||||
|             ); | ||||
|             return; | ||||
|         } | ||||
|         if (expr.kind.sym.type === "fn_param") { | ||||
|             this.program.push( | ||||
|             this.program.add( | ||||
|                 Ops.LoadLocal, | ||||
|                 this.locals.symId(expr.kind.ident), | ||||
|             ); | ||||
|             return; | ||||
|         } | ||||
|         if (expr.kind.sym.type === "fn") { | ||||
|             const addr = this.fnStmtIdAddrMap[expr.kind.sym.stmt.id]; | ||||
|             this.program.push(Ops.PushPtr, addr); | ||||
|             const label = this.fnStmtIdLabelMap[expr.kind.sym.stmt.id]; | ||||
|             this.program.add(Ops.PushPtr, label); | ||||
|             return; | ||||
|         } | ||||
|         throw new Error(`unhandled sym type '${expr.kind.sym.type}'`); | ||||
| @ -202,21 +205,14 @@ export class Lowerer { | ||||
|         if (expr.kind.type !== "int") { | ||||
|             throw new Error(); | ||||
|         } | ||||
|         this.program.push(Ops.PushInt, expr.kind.value); | ||||
|         this.program.add(Ops.PushInt, expr.kind.value); | ||||
|     } | ||||
| 
 | ||||
|     private lowerStringExpr(expr: Expr) { | ||||
|         if (expr.kind.type !== "string") { | ||||
|             throw new Error(); | ||||
|         } | ||||
|         this.addPushStringOp(expr.kind.value); | ||||
|     } | ||||
| 
 | ||||
|     private addPushStringOp(value: string) { | ||||
|         this.program.addOp(Ops.PushString); | ||||
|         for (let i = 0; i < value.length; ++i) { | ||||
|             this.program.addLit(value.charCodeAt(i)); | ||||
|         } | ||||
|         this.program.add(Ops.PushString, expr.kind.value); | ||||
|     } | ||||
| 
 | ||||
|     private lowerBinaryExpr(expr: Expr) { | ||||
| @ -229,32 +225,33 @@ export class Lowerer { | ||||
|         if (vtype.type === "int") { | ||||
|             switch (expr.kind.binaryType) { | ||||
|                 case "+": | ||||
|                     this.program.addOp(Ops.Add); | ||||
|                     this.program.add(Ops.Add); | ||||
|                     return; | ||||
|                 case "*": | ||||
|                     this.program.addOp(Ops.Multiply); | ||||
|                     this.program.add(Ops.Multiply); | ||||
|                     return; | ||||
|                 case "==": | ||||
|                     this.program.addOp(Ops.Equal); | ||||
|                     this.program.add(Ops.Equal); | ||||
|                     return; | ||||
|                 case ">=": | ||||
|                     this.program.addOp(Ops.LessThan); | ||||
|                     this.program.addOp(Ops.Not); | ||||
|                     this.program.add(Ops.LessThan); | ||||
|                     this.program.add(Ops.Not); | ||||
|                     return; | ||||
|                 default: | ||||
|             } | ||||
|         } | ||||
|         if (vtype.type === "string") { | ||||
|             if (expr.kind.binaryType === "+") { | ||||
|                 this.program.push(Ops.Builtin, Builtins.StringConcat); | ||||
|                 this.program.add(Ops.Builtin, Builtins.StringConcat); | ||||
|                 return; | ||||
|             } | ||||
|             if (expr.kind.binaryType === "==") { | ||||
|                 this.program.push(Ops.Builtin, Builtins.StringEqual); | ||||
|                 this.program.add(Ops.Builtin, Builtins.StringEqual); | ||||
|                 return; | ||||
|             } | ||||
|             if (expr.kind.binaryType === "!=") { | ||||
|                 this.program.push(Ops.Builtin, Builtins.StringEqual, Ops.Not); | ||||
|                 this.program.add(Ops.Builtin, Builtins.StringEqual); | ||||
|                 this.program.add(Ops.Not); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
| @ -287,13 +284,12 @@ export class Lowerer { | ||||
| 
 | ||||
|         this.lowerExpr(expr.kind.cond); | ||||
| 
 | ||||
|         this.program.push(Ops.Not, Ops.JumpIfTrue); | ||||
|         this.program.addRef(falseLabel); | ||||
|         this.program.add(Ops.Not); | ||||
|         this.program.add(Ops.JumpIfTrue, falseLabel); | ||||
| 
 | ||||
|         this.lowerExpr(expr.kind.truthy); | ||||
| 
 | ||||
|         this.program.push(Ops.Jump); | ||||
|         this.program.addRef(doneLabel); | ||||
|         this.program.add(Ops.Jump, doneLabel); | ||||
| 
 | ||||
|         this.program.setLabel(falseLabel); | ||||
| 
 | ||||
| @ -315,11 +311,10 @@ export class Lowerer { | ||||
| 
 | ||||
|         this.program.setLabel(contineLabel); | ||||
|         this.lowerExpr(expr.kind.body); | ||||
|         this.program.addOp(Ops.Jump); | ||||
|         this.program.addRef(breakLabel); | ||||
|         this.program.add(Ops.Jump, breakLabel); | ||||
|         this.program.setLabel(breakLabel); | ||||
|         if (expr.vtype!.type === "null") { | ||||
|             this.program.addOp(Ops.PushNull); | ||||
|             this.program.add(Ops.PushNull); | ||||
|         } | ||||
| 
 | ||||
|         this.breakStack.pop(); | ||||
| @ -337,7 +332,7 @@ export class Lowerer { | ||||
|         if (expr.kind.expr) { | ||||
|             this.lowerExpr(expr.kind.expr); | ||||
|         } else { | ||||
|             this.program.addOp(Ops.PushNull); | ||||
|             this.program.add(Ops.PushNull); | ||||
|         } | ||||
|         this.locals = outerLocals; | ||||
|     } | ||||
|  | ||||
| @ -17,4 +17,5 @@ const lowerer = new Lowerer(); | ||||
| lowerer.lower(ast); | ||||
| lowerer.printProgram(); | ||||
| const program = lowerer.finish(); | ||||
| console.log(JSON.stringify(program, null, 4)); | ||||
| //console.log(JSON.stringify(program, null, 4));
 | ||||
| console.log(JSON.stringify(program)); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user