A Golang runtime and compilation backend for Delta Interaction Nets.
at main 243 lines 7.4 kB view raw
1package compiler 2 3import ( 4 "fmt" 5 "strings" 6 7 "github.com/vic/godnet/pkg/lambda" 8) 9 10// CodeGenerator translates lambda AST to Go code. 11type CodeGenerator struct { 12 SourceFile string 13 SourceText string 14 buf strings.Builder 15 nodeCount int 16 vars map[string]*varInfo 17} 18 19type varInfo struct { 20 nodeName string 21 port int // Next available aux port (for replicators) 22 level int 23 uses int // Number of times variable has been used 24} 25 26// Generate produces Go source code from a lambda term. 27func (g *CodeGenerator) Generate(term lambda.Term) string { 28 g.vars = make(map[string]*varInfo) 29 30 g.writeHeader() 31 g.writeBuildNetFunction(term) 32 g.writeMainFunction() 33 34 return g.buf.String() 35} 36 37func (g *CodeGenerator) writeHeader() { 38 g.writeLine("package main") 39 g.writeLine("") 40 g.writeLine("// Generated by godnet compiler") 41 g.writeLine("// Source: %s", g.SourceFile) 42 g.writeLine("") 43 g.writeLine("import (") 44 g.writeLine("\t\"fmt\"") 45 g.writeLine("\t\"os\"") 46 g.writeLine("\t\"time\"") 47 g.writeLine("\t\"github.com/vic/godnet/pkg/deltanet\"") 48 g.writeLine("\t\"github.com/vic/godnet/pkg/lambda\"") 49 g.writeLine(")") 50 g.writeLine("") 51} 52 53func (g *CodeGenerator) writeBuildNetFunction(term lambda.Term) { 54 g.writeLine("func buildNet(net *deltanet.Network) (deltanet.Node, int, map[uint64]string) {") 55 g.writeLine("\tvarNames := make(map[uint64]string)") 56 g.writeLine("") 57 58 // Translate the term 59 rootNode, rootPort := g.translateTerm(term, 0, 0) 60 61 g.writeLine("") 62 g.writeLine("\treturn %s, %d, varNames", rootNode, rootPort) 63 g.writeLine("}") 64 g.writeLine("") 65} 66 67func (g *CodeGenerator) writeMainFunction() { 68 g.writeLine("func main() {") 69 g.writeLine("\tnet := deltanet.NewNetwork()") 70 g.writeLine("\troot, port, varNames := buildNet(net)") 71 g.writeLine("") 72 g.writeLine("\toutput := net.NewVar()") 73 g.writeLine("\tnet.Link(root, port, output, 0)") 74 g.writeLine("") 75 g.writeLine("\tstart := time.Now()") 76 g.writeLine("\tnet.ReduceAll()") 77 g.writeLine("\telapsed := time.Since(start)") 78 g.writeLine("") 79 g.writeLine("\tresultNode, resultPort := net.GetLink(output, 0)") 80 g.writeLine("\tresult := lambda.FromDeltaNet(net, resultNode, resultPort, varNames)") 81 g.writeLine("\tfmt.Println(result)") 82 g.writeLine("") 83 g.writeLine("\tstats := net.GetStats()") 84 g.writeLine("\tseconds := elapsed.Seconds()") 85 g.writeLine("\tfmt.Fprintf(os.Stderr, \"\\nStats:\\n\")") 86 g.writeLine("\tfmt.Fprintf(os.Stderr, \"Time: %%v\\n\", elapsed)") 87 g.writeLine("\tfmt.Fprintf(os.Stderr, \"Total Reductions: %%d\", stats.TotalReductions)") 88 g.writeLine("\tif seconds > 0 {") 89 g.writeLine("\t\tfmt.Fprintf(os.Stderr, \" (%%.2f ops/sec)\", float64(stats.TotalReductions)/seconds)") 90 g.writeLine("\t}") 91 g.writeLine("\tfmt.Fprintf(os.Stderr, \"\\n\")") 92 g.writeLine("}") 93} 94 95func (g *CodeGenerator) translateTerm(term lambda.Term, level int, depth uint64) (string, int) { 96 switch t := term.(type) { 97 case lambda.Var: 98 return g.genVar(t, level, depth) 99 case lambda.Abs: 100 return g.genAbs(t, level, depth) 101 case lambda.App: 102 return g.genApp(t, level, depth) 103 case lambda.Let: 104 // Desugar: let x = v in b → (λx. b) v 105 desugared := lambda.App{ 106 Fun: lambda.Abs{Arg: t.Name, Body: t.Body}, 107 Arg: t.Val, 108 } 109 return g.translateTerm(desugared, level, depth) 110 default: 111 panic(fmt.Sprintf("unknown term type: %T", term)) 112 } 113} 114 115func (g *CodeGenerator) genVar(v lambda.Var, level int, depth uint64) (string, int) { 116 if info, ok := g.vars[v.Name]; ok { 117 // Bound variable - expand replicator 118 g.writeComment("Variable: %s (bound)", v.Name) 119 120 if strings.HasPrefix(info.nodeName, "rep_") { 121 // Already a replicator, expand it 122 oldRepName := info.nodeName 123 newRepName := g.nextNode("rep") 124 delta := level - (info.level + 1) 125 nextPort := info.uses + 1 // Next aux port index (1-based, since port 0 is principal) 126 127 g.writeLine("\t// Expand replicator for variable '%s' (use #%d)", v.Name, info.uses+1) 128 g.writeLine("\t%s := net.NewReplicator(%d, append(%s.Deltas(), %d))", 129 newRepName, info.level, oldRepName, delta) 130 131 // Move principal connection 132 g.writeLine("\tsourceNode, sourcePort := net.GetLink(%s, 0)", oldRepName) 133 g.writeLine("\tnet.LinkAt(%s, 0, sourceNode, sourcePort, %d)", newRepName, depth) 134 135 // Move existing aux ports 136 g.writeLine("\tfor i := 0; i < len(%s.Deltas()); i++ {", oldRepName) 137 g.writeLine("\t\tdestNode, destPort := net.GetLink(%s, i+1)", oldRepName) 138 g.writeLine("\t\tif destNode != nil {") 139 g.writeLine("\t\t\tnet.LinkAt(%s, i+1, destNode, destPort, %d)", newRepName, depth) 140 g.writeLine("\t\t}") 141 g.writeLine("\t}") 142 143 info.nodeName = newRepName 144 info.uses++ 145 return newRepName, nextPort 146 } else { 147 // First use after eraser - create replicator 148 g.writeLine("\t// First use of variable '%s'", v.Name) 149 repName := g.nextNode("rep") 150 delta := level - (info.level + 1) 151 repLevel := info.level + 1 152 153 g.writeLine("\t%s := net.NewReplicator(%d, []int{%d})", repName, repLevel, delta) 154 g.writeLine("\tnet.LinkAt(%s, 0, %s, %d, %d)", repName, info.nodeName, info.port, depth) 155 156 info.nodeName = repName 157 info.uses = 1 158 return repName, 1 159 } 160 } else { 161 // Free variable 162 g.writeComment("Variable: %s (free)", v.Name) 163 varName := g.nextNode("var") 164 repName := g.nextNode("rep") 165 166 g.writeLine("\t%s := net.NewVar()", varName) 167 g.writeLine("\tvarNames[%s.ID()] = \"%s\"", varName, v.Name) 168 g.writeLine("\t%s := net.NewReplicator(0, []int{%d})", repName, level-1) 169 g.writeLine("\tnet.LinkAt(%s, 0, %s, 0, %d)", repName, varName, depth) 170 171 g.vars[v.Name] = &varInfo{ 172 nodeName: repName, 173 port: 0, 174 level: 0, 175 } 176 177 return repName, 1 178 } 179} 180 181func (g *CodeGenerator) genAbs(abs lambda.Abs, level int, depth uint64) (string, int) { 182 g.writeComment("Abstraction: λ%s. ...", abs.Arg) 183 184 fanName := g.nextNode("fan") 185 eraName := g.nextNode("era") 186 187 g.writeLine("\t%s := net.NewFan()", fanName) 188 g.writeLine("\t%s := net.NewEraser()", eraName) 189 g.writeLine("\tnet.LinkAt(%s, 0, %s, 2, %d)", eraName, fanName, depth) 190 191 // Save old binding if shadowing 192 oldVar := g.vars[abs.Arg] 193 g.vars[abs.Arg] = &varInfo{ 194 nodeName: fanName, 195 port: 2, 196 level: level, 197 } 198 199 // Generate body 200 bodyNode, bodyPort := g.translateTerm(abs.Body, level, depth) 201 g.writeLine("\tnet.LinkAt(%s, 1, %s, %d, %d)", fanName, bodyNode, bodyPort, depth) 202 203 // Restore old binding 204 if oldVar != nil { 205 g.vars[abs.Arg] = oldVar 206 } else { 207 delete(g.vars, abs.Arg) 208 } 209 210 return fanName, 0 211} 212 213func (g *CodeGenerator) genApp(app lambda.App, level int, depth uint64) (string, int) { 214 g.writeComment("Application") 215 216 fanName := g.nextNode("fan") 217 g.writeLine("\t%s := net.NewFan()", fanName) 218 219 // Generate function 220 funNode, funPort := g.translateTerm(app.Fun, level, depth) 221 g.writeLine("\tnet.LinkAt(%s, 0, %s, %d, %d)", fanName, funNode, funPort, depth) 222 223 // Generate argument (level + 1) 224 argNode, argPort := g.translateTerm(app.Arg, level+1, depth+1) 225 g.writeLine("\tnet.LinkAt(%s, 2, %s, %d, %d)", fanName, argNode, argPort, depth+1) 226 227 return fanName, 1 228} 229 230func (g *CodeGenerator) nextNode(prefix string) string { 231 g.nodeCount++ 232 return fmt.Sprintf("%s_%d", prefix, g.nodeCount) 233} 234 235func (g *CodeGenerator) writeLine(format string, args ...interface{}) { 236 g.buf.WriteString(fmt.Sprintf(format, args...)) 237 g.buf.WriteString("\n") 238} 239 240func (g *CodeGenerator) writeComment(format string, args ...interface{}) { 241 comment := "\t// " + fmt.Sprintf(format, args...) 242 g.writeLine("%s", comment) 243}