1/* 2 * gomacro - A Go interpreter with Lisp-like macros 3 * 4 * Copyright (C) 2017-2019 Massimiliano Ghilardi 5 * 6 * This Source Code Form is subject to the terms of the Mozilla Public 7 * License, v. 2.0. If a copy of the MPL was not distributed with this 8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * 11 * compile.go 12 * 13 * Created on Apr 01, 2017 14 * Author Massimiliano Ghilardi 15 */ 16 17package fast 18 19import ( 20 "go/ast" 21 "go/token" 22 r "reflect" 23 24 . "github.com/cosmos72/gomacro/ast2" 25 . "github.com/cosmos72/gomacro/base" 26 "github.com/cosmos72/gomacro/base/dep" 27 "github.com/cosmos72/gomacro/gls" 28) 29 30func NewComp(outer *Comp, code *Code) *Comp { 31 if outer == nil { 32 return &Comp{UpCost: 1} 33 } 34 c := Comp{ 35 UpCost: 1, 36 Depth: outer.Depth + 1, 37 Outer: outer, 38 CompGlobals: outer.CompGlobals, 39 } 40 // Debugf("NewComp(%p->%p) %s", outer, &c, debug.Stack()) 41 if code != nil { 42 c.Code = *code 43 } 44 return &c 45} 46 47func (c *Comp) TopComp() *Comp { 48 for ; c != nil; c = c.Outer { 49 if c.Outer == nil { 50 break 51 } 52 } 53 return c 54} 55 56func (c *Comp) FileComp() *Comp { 57 for ; c != nil; c = c.Outer { 58 outer := c.Outer 59 if outer == nil || outer.Outer == nil { 60 break 61 } 62 } 63 return c 64} 65 66func NewIrGlobals() *IrGlobals { 67 return &IrGlobals{ 68 gls: make(map[uintptr]*Run), 69 Globals: *NewGlobals(), 70 } 71} 72 73func (g *IrGlobals) glsGet(goid uintptr) *Run { 74 g.lock.Lock() 75 ret := g.gls[goid] 76 g.lock.Unlock() 77 return ret 78} 79 80func (run *Run) getRun4Goid(goid uintptr) *Run { 81 g := run.IrGlobals 82 ret := g.glsGet(goid) 83 if ret == nil { 84 ret = run.new(goid) 85 ret.glsStore() 86 } 87 return ret 88} 89 90func (tg *Run) glsStore() { 91 g := tg.IrGlobals 92 goid := tg.goid 93 g.lock.Lock() 94 g.gls[goid] = tg 95 g.lock.Unlock() 96} 97 98func (tg *Run) glsDel() { 99 g := tg.IrGlobals 100 goid := tg.goid 101 g.lock.Lock() 102 delete(g.gls, goid) 103 g.lock.Unlock() 104} 105 106func (run *Run) new(goid uintptr) *Run { 107 return &Run{ 108 IrGlobals: run.IrGlobals, 109 goid: goid, 110 // Interrupt, Signal, PoolSize and Pool are zero-initialized, fine with that 111 } 112} 113 114// common part between NewEnv() and newEnv4Func() 115func newEnv(run *Run, outer *Env, nbind int, nintbind int) *Env { 116 pool := &run.Pool // pool is an array, do NOT copy it! 117 index := run.PoolSize - 1 118 var env *Env 119 if index >= 0 { 120 run.PoolSize = index 121 env = pool[index] 122 pool[index] = nil 123 } else { 124 env = &Env{} 125 } 126 if cap(env.Vals) >= nbind { 127 env.Vals = env.Vals[0:nbind] 128 } else { 129 env.Vals = make([]r.Value, nbind) 130 } 131 if cap(env.Ints) >= nintbind { 132 env.Ints = env.Ints[0:nintbind] 133 } else { 134 env.Ints = make([]uint64, nintbind) 135 } 136 env.Outer = outer 137 env.Run = run 138 env.FileEnv = outer.FileEnv 139 return env 140} 141 142// return a new, nested Env with given number of binds and intbinds 143func NewEnv(outer *Env, nbind int, nintbind int) *Env { 144 run := outer.Run 145 146 // manually inline 147 // env := newEnv(run, outer, nbind, nintbind) 148 var env *Env 149 { 150 pool := &run.Pool // pool is an array, do NOT copy it! 151 index := run.PoolSize - 1 152 if index >= 0 { 153 run.PoolSize = index 154 env = pool[index] 155 pool[index] = nil 156 } else { 157 env = &Env{} 158 } 159 if cap(env.Vals) >= nbind { 160 env.Vals = env.Vals[0:nbind] 161 } else { 162 env.Vals = make([]r.Value, nbind) 163 } 164 if cap(env.Ints) >= nintbind { 165 env.Ints = env.Ints[0:nintbind] 166 } else { 167 env.Ints = make([]uint64, nintbind) 168 } 169 env.Outer = outer 170 env.Run = run 171 env.FileEnv = outer.FileEnv 172 } 173 env.IP = outer.IP 174 env.Code = outer.Code 175 env.DebugPos = outer.DebugPos 176 env.CallDepth = outer.CallDepth 177 // this is a nested *Env, not a function body: to obtain the caller function, 178 // follow env.Outer.Outer... chain until you find an *Env with non-nil Caller 179 // env.Caller = nil 180 // DebugCallStack Debugf("NewEnv(%p->%p) nbind=%d nintbind=%d calldepth: %d->%d", outer, env, nbind, nintbind, outer.CallDepth, env.CallDepth) 181 run.CurrEnv = env 182 return env 183} 184 185func newEnv4Func(outer *Env, nbind int, nintbind int, debugComp *Comp) *Env { 186 goid := gls.GoID() 187 run := outer.Run 188 if run.goid != goid { 189 // no luck... get the correct ThreadGlobals for goid 190 run = run.getRun4Goid(goid) 191 } 192 // manually inline 193 // env := newEnv(run, outer, nbind, nintbind) 194 var env *Env 195 { 196 pool := &run.Pool // pool is an array, do NOT copy it! 197 index := run.PoolSize - 1 198 if index >= 0 { 199 run.PoolSize = index 200 env = pool[index] 201 pool[index] = nil 202 } else { 203 env = &Env{} 204 } 205 if cap(env.Vals) >= nbind { 206 env.Vals = env.Vals[0:nbind] 207 } else { 208 env.Vals = make([]r.Value, nbind) 209 } 210 if cap(env.Ints) >= nintbind { 211 env.Ints = env.Ints[0:nintbind] 212 } else { 213 env.Ints = make([]uint64, nintbind) 214 } 215 env.Outer = outer 216 env.Run = run 217 env.FileEnv = outer.FileEnv 218 } 219 env.DebugComp = debugComp 220 caller := run.CurrEnv 221 env.Caller = caller 222 if caller == nil { 223 env.CallDepth = 1 224 } else { 225 env.CallDepth = caller.CallDepth + 1 226 } 227 // DebugCallStack Debugf("newEnv4Func(%p->%p) nbind=%d nintbind=%d calldepth: %d->%d", caller, env, nbind, nintbind, env.CallDepth-1, env.CallDepth) 228 run.CurrEnv = env 229 return env 230} 231 232func (env *Env) MarkUsedByClosure() { 233 for ; env != nil && !env.UsedByClosure; env = env.Outer { 234 env.UsedByClosure = true 235 } 236} 237 238// FreeEnv tells the interpreter that given nested *Env is no longer needed. 239func (env *Env) FreeEnv() { 240 run := env.Run 241 run.CurrEnv = env.Outer 242 env.freeEnv(run) 243} 244 245// freeEnv4Func tells the interpreter that given function body *Env is no longer needed. 246func (env *Env) freeEnv4Func() { 247 run := env.Run 248 run.CurrEnv = env.Caller 249 env.freeEnv(run) 250} 251 252func (env *Env) freeEnv(run *Run) { 253 // DebugCallStack Debugf("FreeEnv(%p->%p), calldepth: %d->%d", env, caller, env.CallDepth, caller.CallDepth) 254 if env.UsedByClosure { 255 // output.Debugf("freeEnv: used by closure, cannot reuse: %p %+v", env, env) 256 return 257 } 258 n := run.PoolSize 259 if n >= poolCapacity { 260 return 261 } 262 if env.IntAddressTaken { 263 env.Ints = nil 264 env.IntAddressTaken = false 265 } 266 env.Outer = nil 267 env.Code = nil 268 env.DebugPos = nil 269 env.DebugComp = nil 270 env.Caller = nil 271 env.Run = nil 272 env.FileEnv = nil 273 run.Pool[n] = env // pool is an array, be careful NOT to copy it! 274 run.PoolSize = n + 1 275} 276 277func (env *Env) Top() *Env { 278 if env == nil { 279 return nil 280 } 281 if file := env.FileEnv; file != nil { 282 if top := file.Outer; top != nil && top.Outer == nil { 283 return top 284 } 285 } 286 for o := env.Outer; o != nil; o = o.Outer { 287 env = o 288 } 289 return env 290} 291 292func (env *Env) Up(n int) *Env { 293 for ; n >= 3; n -= 3 { 294 env = env.Outer.Outer.Outer 295 } 296 switch n { 297 case 2: 298 env = env.Outer 299 fallthrough 300 case 1: 301 env = env.Outer 302 } 303 return env 304} 305 306// combined Parse + MacroExpandCodeWalk 307func (c *Comp) Parse(src string) Ast { 308 // do NOT set c.Globals.Line = 0 309 // caller can do it manually if needed 310 nodes := c.ParseBytes([]byte(src)) 311 forms := anyToAst(nodes, "Parse") 312 313 forms, _ = c.MacroExpandCodewalk(forms) 314 if c.Options&OptShowMacroExpand != 0 { 315 c.Debugf("after macroexpansion: %v", forms.Interface()) 316 } 317 return forms 318} 319 320// compile code. support out-of-order declarations 321func (c *Comp) Compile(in Ast) *Expr { 322 if in == nil { 323 return nil 324 } 325 switch node := in.Interface().(type) { 326 case *ast.File, ast.Decl, *ast.ValueSpec: 327 // complicated, use general technique below 328 case ast.Node: 329 // shortcut 330 return c.compileNode(node, dep.Unknown) 331 } 332 // order declarations by topological sort on their dependencies 333 sorter := dep.NewSorter() 334 sorter.LoadAst(in) 335 336 decls := sorter.All() 337 338 switch n := len(decls); n { 339 case 0: 340 return nil 341 case 1: 342 return c.compileDecl(decls[0]) 343 default: 344 exprs := make([]*Expr, 0, n) 345 for _, decl := range decls { 346 e := c.compileDecl(decl) 347 if e != nil { 348 exprs = append(exprs, e) 349 } 350 } 351 return exprList(exprs, c.CompileOptions()) 352 } 353 return nil 354} 355 356// compile code. support out-of-order declarations too 357func (c *Comp) CompileNode(node ast.Node) *Expr { 358 return c.Compile(ToAst(node)) 359} 360 361func (c *Comp) compileDecl(decl *dep.Decl) *Expr { 362 if decl == nil { 363 return nil 364 } 365 if extra := decl.Extra; extra != nil { 366 // decl.Node may declare multiple constants or variables: 367 // do not use it! 368 // instead get the single const or var declaration from Extra 369 switch decl.Kind { 370 case dep.Const: 371 // see Comp.GenDecl() in declaration.go for a discussion 372 // on the scope where to declare iota, and what to do 373 // with any previous declaration of iota in the same scope 374 top := c.TopComp() 375 defer top.endIota(top.beginIota()) 376 top.setIota(extra.Iota) 377 378 c.DeclConsts(extra.Spec(), nil, nil) 379 return c.Code.AsExpr() 380 case dep.Var: 381 c.DeclVars(extra.Spec()) 382 return c.Code.AsExpr() 383 } 384 } 385 if node := decl.Node; node != nil { 386 return c.compileNode(node, decl.Kind) 387 } 388 // may happen for second and later variables in VarMulti, 389 // which CANNOT be declared individually 390 return nil 391} 392 393// compileExpr is a wrapper for Compile 394// that guarantees Code does not get clobbered/cleared. 395// Used by Comp.Quasiquote 396func (c *Comp) compileExpr(in Ast) *Expr { 397 cf := NewComp(c, nil) 398 cf.UpCost = 0 399 cf.Depth-- 400 return cf.Compile(in) 401} 402 403// common backend for Compile, CompileNode, File, compileDecl. 404// does NOT support out-of-order declarations 405func (c *Comp) compileNode(node ast.Node, kind dep.Kind) *Expr { 406 if n := c.Code.Len(); n != 0 { 407 c.Warnf("Compile: discarding %d previously compiled statements from code buffer", n) 408 } 409 if node == nil { 410 return nil 411 } 412 c.Code.Clear() 413 c.Loop = nil 414 c.Func = nil 415 c.Labels = nil 416 c.FuncMaker = nil 417 c.Pos = node.Pos() 418 switch node := node.(type) { 419 case ast.Decl: 420 c.Decl(node) 421 case ast.Expr: 422 return c.Expr(node, nil) 423 case *ast.ImportSpec: 424 // dep.Sorter.Some() returns naked *ast.ImportSpec, 425 // instead of *ast.GenDecl containing one or more *ast.ImportSpec as parser does 426 c.Import(node) 427 case *ast.TypeSpec: 428 // dep.Sorter.Some() returns naked *ast.TypeSpec, 429 // instead of *ast.GenDecl containing one or more *ast.TypeSpec as parser does 430 if kind == dep.TypeFwd { 431 // forward type declaration 432 c.DeclNamedType(node.Name.Name) 433 } else { 434 c.DeclType(node) 435 } 436 case *ast.ExprStmt: 437 // special case of statement 438 return c.Expr(node.X, nil) 439 case ast.Stmt: 440 c.Stmt(node) 441 case *ast.File: 442 // not c.File(node): unnecessary and risks an infinite recursion 443 for _, decl := range node.Decls { 444 c.Decl(decl) 445 } 446 default: 447 c.Errorf("unsupported node type, expecting <ast.Decl>, <ast.Expr>, <ast.Stmt> or <*ast.File>, found %v <%v>", node, r.TypeOf(node)) 448 return nil 449 } 450 return c.Code.AsExpr() 451} 452 453// compile file. support out-of-order declarations too 454func (c *Comp) File(node *ast.File) { 455 if node != nil { 456 c.Name = node.Name.Name 457 c.Compile(File{node}) 458 } 459} 460 461func (c *Comp) Append(stmt Stmt, pos token.Pos) { 462 c.Code.Append(stmt, pos) 463} 464 465func (c *Comp) append(stmt Stmt) { 466 c.Code.Append(stmt, c.Pos) 467} 468