~mna/snow

9da316f0d17d13317543c1e48324d693fff297fa — Martin Angers 1 year, 8 months ago b73673a
pkg/codegen: start move to linked-list-based approach
1 files changed, 81 insertions(+), 58 deletions(-)

M pkg/codegen/translate.go
M pkg/codegen/translate.go => pkg/codegen/translate.go +81 -58
@@ 1,6 1,7 @@
package codegen

import (
	"container/list"
	"fmt"
	goast "go/ast"
	gotoken "go/token"


@@ 30,37 31,48 @@ type translator struct {
	// set of already rendered generic instantiations, based on the mangled name
	renderedGenInsts map[string]bool

	// map of semantic.Node to slice of either goast.Node or a nested
	// map[semantic.Node]interface{}, in the order they must appear when
	// processed by snow source order. Only statements, declarations and other
	// non-expression nodes (such as Files) are recorded here.
	nodeToGenerated map[semantic.Node][]interface{}

	// expressions are generated and returned to the parent node, and are not
	// recorded in nodeToGenerated.
	// expressions are generated and returned to the parent node
	generatedExpr goast.Expr

	// TODO: maybe something like...
	//topLevel        *list.List // file-level
	//parentContainer *list.List // stack-like for current container, e.g. body block
	filesList *list.List
	// the following two lists represent the current topLevel list of nodes (the file),
	// and the current container list.
	topLevel        *list.List
	parentContainer *list.List
	// map associating placeholder marks in their container where generic
	// instantiations should be inserted. The associated Element is a container
	// with no associated Go nodes.
	genDeclMarks map[semantic.Decl]*list.Element
}

type convertFnKey struct {
	from, to semantic.Type
}

// file, block, if, guard, struct and fn are containers
type container struct {
	nodes    []goast.Node
	children *list.List
}

func newContainer(ns ...goast.Node) *container {
	return &container{
		nodes:    ns,
		children: list.New(),
	}
}

type topLevelNode struct {
	goast.Node
}

func (t *translator) append(n semantic.Node, gonodes ...goast.Node) {
	ns := t.nodeToGenerated[n]
	for _, gon := range gonodes {
		ns = append(ns, gon)
	}
	t.nodeToGenerated[n] = ns
func (t *translator) withContainer(c *container, fn func()) {
	old := t.parentContainer
	defer func() { t.parentContainer = old }()
	fn()
}

// TODO: do a with genInst like withContainer for this?
// merges the new generic instantiation replacement types into a cloned lookup map,
// sets it as the current replacement lookup map and returns the previous map, that
// should be set back in place once done with this generic instantiation.


@@ 81,9 93,10 @@ func (t *translator) cloneGenInst(clause *semantic.GenericClause, types []semant
func (t *translator) Visit(n semantic.Node) semantic.Visitor {
	switch n := n.(type) {
	case *semantic.Unit:
		t.nodeToGenerated = make(map[semantic.Node][]interface{})
		t.convertFns = make(map[convertFnKey]string)
		t.renderedGenInsts = make(map[string]bool)
		t.genDeclMarks = make(map[semantic.Decl]*list.Element)
		t.filesList = list.New()
		for _, f := range n.Files {
			t.curFile = f
			semantic.Walk(t, f)


@@ 93,35 106,47 @@ func (t *translator) Visit(n semantic.Node) semantic.Visitor {
		// create package clause, imports if any
		gof := &goast.File{Name: &goast.Ident{Name: "main"}}
		t.addFileImports(n, gof)
		t.append(n, gof)
		cont := newContainer(gof)
		t.filesList.PushBack(cont)
		t.topLevel = cont.children

		for _, v := range n.Vars {
			semantic.Walk(t, v)
		}
		for _, str := range n.Structs {
			semantic.Walk(t, str)
		}
		for _, fn := range n.Fns {
			semantic.Walk(t, fn)
		}
		t.withContainer(cont, func() {
			for _, v := range n.Vars {
				semantic.Walk(t, v)
			}
			for _, str := range n.Structs {
				semantic.Walk(t, str)
			}
			for _, fn := range n.Fns {
				semantic.Walk(t, fn)
			}
		})

		// ************** DECLARATIONS *****************

	case *semantic.Fn:
		if n.IsGeneric() {
			// go nodes will be generated only on instantiation
			// go nodes will be generated only on instantiation, create an empty container for now
			mark := newContainer()
			t.genDeclMarks[n] = t.parentContainer.PushBack(mark)
			break
		}
		name := t.resolver.NameForDecl(n)
		t.append(n, t.createFn(name, n)...)
		// TODO: createFn should happen inside that container as parent...
		cont := newContainer(t.createFn(name, n)...)
		t.parentContainer.PushBack(cont)

	case *semantic.Struct:
		if n.IsGeneric() {
			// go nodes will be generated only on instantiation
			// go nodes will be generated only on instantiation, create an empty container for now
			mark := newContainer()
			t.genDeclMarks[n] = t.parentContainer.PushBack(mark)
			break
		}
		name := t.resolver.NameForDecl(n)
		t.append(n, t.createStruct(name, semantic.AsStructType(n.Type()))...)
		// TODO: createStruct should happen inside that container as parent...
		cont := newContainer(t.createStruct(name, semantic.AsStructType(n.Type()))...)
		t.parentContainer.PushBack(cont)

	case *semantic.Var:
		name := t.resolver.NameForDecl(n)


@@ 134,7 159,7 @@ func (t *translator) Visit(n semantic.Node) semantic.Visitor {
			expr := t.generatedExpr
			vs.Values = []goast.Expr{expr}
		}
		t.append(n, &goast.GenDecl{
		t.parentContainer.PushBack(&goast.GenDecl{
			Tok:   gotoken.VAR,
			Specs: []goast.Spec{vs},
		})


@@ 145,10 170,13 @@ func (t *translator) Visit(n semantic.Node) semantic.Visitor {
		bs := &goast.BlockStmt{
			List: make([]goast.Stmt, 0, len(n.Stmts)),
		}
		for _, stmt := range n.Stmts {
			semantic.Walk(t, stmt)
		}
		t.append(n, bs)
		cont := newContainer(bs)
		t.parentContainer.PushBack(cont)
		t.withContainer(cont, func() {
			for _, stmt := range n.Stmts {
				semantic.Walk(t, stmt)
			}
		})

	case *semantic.Return:
		rs := &goast.ReturnStmt{}


@@ 156,7 184,7 @@ func (t *translator) Visit(n semantic.Node) semantic.Visitor {
			semantic.Walk(t, n.Value)
			rs.Results = []goast.Expr{t.generatedExpr}
		}
		t.append(n, rs)
		t.parentContainer.PushBack(rs)

	case *semantic.Assign:
		semantic.Walk(t, n.Left)


@@ 166,11 194,11 @@ func (t *translator) Visit(n semantic.Node) semantic.Visitor {
		}
		semantic.Walk(t, n.Right)
		as.Rhs = []goast.Expr{t.generatedExpr}
		t.append(n, as)
		t.parentContainer.PushBack(as)

	case *semantic.ExprStmt:
		semantic.Walk(t, n.Value)
		t.append(n, &goast.ExprStmt{X: t.generatedExpr})
		t.parentContainer.PushBack(&goast.ExprStmt{X: t.generatedExpr})

	case *semantic.If:
		is := &goast.IfStmt{}


@@ 188,11 216,14 @@ func (t *translator) Visit(n semantic.Node) semantic.Visitor {
			is.Cond = and
		}

		semantic.Walk(t, n.Body)
		if n.Else != nil {
			semantic.Walk(t, n.Else)
		}
		t.append(n, is)
		cont := newContainer(is)
		t.parentContainer.PushBack(cont)
		t.withContainer(cont, func() {
			semantic.Walk(t, n.Body)
			if n.Else != nil {
				semantic.Walk(t, n.Else)
			}
		})

	case *semantic.Guard:
		// use an empty if and an else


@@ 213,8 244,11 @@ func (t *translator) Visit(n semantic.Node) semantic.Visitor {

		// empty "if" body
		is.Body = &goast.BlockStmt{}
		semantic.Walk(t, n.Else)
		t.append(n, is)
		cont := newContainer(is)
		t.parentContainer.PushBack(cont)
		t.withContainer(cont, func() {
			semantic.Walk(t, n.Else)
		})

		// ************** EXPRESSIONS *****************



@@ 260,8 294,6 @@ func (t *translator) Visit(n semantic.Node) semantic.Visitor {
		semantic.Walk(t, n.Fun)

		if n.InitOf != nil {
			// TODO : the name for the initialized struct should be the instantiated one
			// if n.Fun triggers a generic inst...??? Maybe use the type to get the name?
			t.generatedExpr = t.callStructInit(n)
			break
			// TODO: once modules exist, the n.Fun might be a qualified struct type, and the initializer


@@ 732,15 764,6 @@ func (t *translator) renderGenInst(gi *semantic.GenericInst, name string) {
		return
	}

	// generic instantiations are handled differently, because if we used the same nodeToGenerated
	// map, it would be tricky to generate the final Go AST (the same return statement, for example,
	// could have 3 associated nodes, which one goes with which generic instantiation would be a pain
	// to manage).
	//
	// Instead, what we do when instantiating a generic is that we create a new map, store it temporarily
	// in t.nodeToGenerated so that all its body scope is stored in that map, and once the body is fully
	// rendered, associate that map with the generic declaration node in the parent map, and set the parent
	// map back in t.nodeToGenerated (stack-like, new map pushed during body generation, popped on exit).
	t.renderedGenInsts[name] = true

	parentMap, newMap := t.nodeToGenerated, make(map[semantic.Node][]interface{})