// buildFunction compiles the given function. func (a *assembler) buildFunction(f *parser.Function) (err error) { nodes := f.Children() name := nodes[0].(*parser.Label) a.debug.SetFunctionStart(cpu.Word(len(a.code)), f.Line(), name.Data) for i := range nodes { switch tt := nodes[i].(type) { case *parser.Comment: /* ignore */ case *parser.Label: a.labels[tt.Data] = cpu.Word(len(a.code)) case *parser.Instruction: err = a.buildInstruction(tt.Children()) default: err = NewBuildError( a.ast.Files[tt.File()], tt.Line(), tt.Col(), "Unexpected node %T. Want Comment, Label, Instruction.", tt, ) } if err != nil { return } } a.debug.SetFunctionEnd(cpu.Word(len(a.code)), nodes[len(nodes)-1].Line()) return }
func parseFuncConst(ast *parser.AST, f *parser.Function) (err error) { list, err := parseConstants(ast, f.Children()) if err != nil { return } f.SetChildren(list) return }
func (sw *SourceWriter) writeFunction(n *parser.Function) { var name string chld := n.Children() switch tt := chld[0].(type) { case *parser.Name: name = tt.Data case *parser.Label: name = tt.Data } fmt.Fprintf(sw.w, "def %s\n", name) sw.writeList(chld[1:]) sw.w.Write([]byte("end")) }
// fixFunctionReturns finds 'return' instructions and replaces them // with appropriate `set pc, $__<name>_epilog` versions. func fixFunctionReturns(ast *parser.AST, f *parser.Function) { var instr *parser.Instruction var expr *parser.Expression var file, line, col int var code []parser.Node var ok bool name := f.Children()[0].(*parser.Name) exitLabel := "$__" + name.Data + "_epilog" list := f.Children()[1:] for i := range list { if instr, ok = list[i].(*parser.Instruction); !ok { continue } code = instr.Children() name = code[0].(*parser.Name) if name.Data != "return" { continue } file, line, col = instr.File(), instr.Line(), instr.Col() name.Data = "set" code = append(code, nil, nil) expr = parser.NewExpression(file, line, col) expr.SetChildren([]parser.Node{ parser.NewName(file, line, col, "pc"), }) code[1] = expr expr = parser.NewExpression(file, line, col) expr.SetChildren([]parser.Node{ parser.NewName(file, line, col, exitLabel), }) code[2] = expr instr.SetChildren(code) } }
// injectFunctionCode injects function prologs and epilogs. func injectFunctionCode(ast *parser.AST, f *parser.Function) { var tmp, code []parser.Node var instr *parser.Instruction var expr *parser.Expression var idx int name := f.Children()[0].(*parser.Name) exitLabel := "$__" + name.Data + "_epilog" file, line, col := name.File(), name.Line(), name.Col() // Find list of referenced protected registers. var regs []string findProtectedRegisters(f.Children()[1:], ®s) // Inject prolog and epilog code. // // For each protected register we found, we add appropriate // stack push/pop instructions to preserve their state. tmp = make([]parser.Node, len(f.Children())+(len(regs)*2)+2) tmp[idx] = parser.NewLabel(file, line, col, name.Data) idx++ // set push, $reg for n := range regs { instr = parser.NewInstruction(file, line, col) code = make([]parser.Node, 3) code[0] = parser.NewName(file, line, col, "set") expr = parser.NewExpression(file, line, col) expr.SetChildren([]parser.Node{ parser.NewName(file, line, col, "push"), }) code[1] = expr expr = parser.NewExpression(file, line, col) expr.SetChildren([]parser.Node{ parser.NewName(file, line, col, regs[n]), }) code[2] = expr instr.SetChildren(code) tmp[idx] = instr idx++ } // Regular code goes here. copy(tmp[idx:], f.Children()[1:]) idx += len(f.Children()) - 1 // Label denoting the start of the epilog. line, col = tmp[idx-1].Line()+1, 1 tmp[idx] = parser.NewLabel(file, line, col, exitLabel) idx++ // set $reg, pop for n := range regs { instr = parser.NewInstruction(file, line, col) code = make([]parser.Node, 3) code[0] = parser.NewName(file, line, col, "set") expr = parser.NewExpression(file, line, col) expr.SetChildren([]parser.Node{ parser.NewName(file, line, col, regs[n]), }) code[1] = expr expr = parser.NewExpression(file, line, col) expr.SetChildren([]parser.Node{ parser.NewName(file, line, col, "pop"), }) code[2] = expr instr.SetChildren(code) tmp[idx] = instr idx++ } // set pc, pop instr = parser.NewInstruction(file, line, col) code = make([]parser.Node, 3) code[0] = parser.NewName(file, line, col, "set") expr = parser.NewExpression(file, line, col) expr.SetChildren([]parser.Node{ parser.NewName(file, line, col, "pc"), }) code[1] = expr expr = parser.NewExpression(file, line, col) expr.SetChildren([]parser.Node{ parser.NewName(file, line, col, "pop"), }) code[2] = expr instr.SetChildren(code) tmp[idx] = instr f.SetChildren(tmp) }