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