~mna/snow

76e9ce96bbc31f51204fb18e56a41efdaacaf476 — Martin Angers 1 year, 8 months ago ae3f184
pkg/codegen: almost works, but not quite
M pkg/codegen/codegen.go => pkg/codegen/codegen.go +1 -1
@@ 84,7 84,7 @@ func ExecUnit(outDir string, unit *semantic.Unit) ([]string, error) {
	// 4. build the Go AST tree from the Snow-associated nodes so that relative
	//    position of nodes respects the snow source
	tb := &treebuilder{
		nodeToGoAST: t.nodeToGoAST,
		nodeToGenerated: t.nodeToGenerated,
	}
	semantic.Walk(tb, unit)
	gofiles := tb.gofiles

M pkg/codegen/testdata/fn_generic_identity.snow.want => pkg/codegen/testdata/fn_generic_identity.snow.want +6 -6
@@ 2,18 2,18 @@ package main

import "fmt"

func _اidا1(
	v int64,
) int64 {
	return v
}

func _اidا0(
	v string,
) string {
	return v
}

func _اidا1(
	v int64,
) int64 {
	return v
}

func main() {
	var s string = _اidا0("a")
	var i int64 = _اidا1(int64(1))

M pkg/codegen/testdata/fn_nested_struct_method.snow.want => pkg/codegen/testdata/fn_nested_struct_method.snow.want +21 -21
@@ 2,27 2,20 @@ package main

import "fmt"

type _ا4اS struct {
	_اx string
}

func _ا4اSاnew(
	s _ا4اS,
	m map[string]bool,
) _ا4اS {
	var r _ا4اS
	if m["x"] {
		r._اx = s._اx
	}
	return r
}

func (
	self *_ا4اS,
) _اinit() {
	self._اx = "hello"
}

func (
	self *_ا5اT,
) _اappend(
	s string,
) {
	self._اy = self._اy + s
}

type _ا5اT struct {
	_اy string
}


@@ 38,12 31,19 @@ func _ا5اTاnew(
	return r
}

func (
	self *_ا5اT,
) _اappend(
	s string,
) {
	self._اy = self._اy + s
type _ا4اS struct {
	_اx string
}

func _ا4اSاnew(
	s _ا4اS,
	m map[string]bool,
) _ا4اS {
	var r _ا4اS
	if m["x"] {
		r._اx = s._اx
	}
	return r
}

func main() {

M pkg/codegen/testdata/fn_return_nested_tuple_reconcile.snow.want => pkg/codegen/testdata/fn_return_nested_tuple_reconcile.snow.want +21 -21
@@ 41,27 41,6 @@ func _ااconvert_0(
	}{from._ا0, _ااconvert_1(from._ا1)}
}

func _اget_tuple(
	x struct {
		_ا0 int8
		_ا1 string
	},
) struct {
	_ا0 bool
	_ا1 struct {
		_ا0 int16
		_ا1 string
	}
} {
	return _ااconvert_0(struct {
		_ا0 bool
		_ا1 struct {
			_ا0 int8
			_ا1 string
		}
	}{true, x})
}

func _ااconvert_3(
	from struct {
		_ا0 int16


@@ 140,6 119,27 @@ func _ااconvert_4(
	}{from._ا0, _ااconvert_5(from._ا1)}
}

func _اget_tuple(
	x struct {
		_ا0 int8
		_ا1 string
	},
) struct {
	_ا0 bool
	_ا1 struct {
		_ا0 int16
		_ا1 string
	}
} {
	return _ااconvert_0(struct {
		_ا0 bool
		_ا1 struct {
			_ا0 int8
			_ا1 string
		}
	}{true, x})
}

func main() {
	var i int8
	var t struct {

M pkg/codegen/testdata/fn_struct_method.snow.want => pkg/codegen/testdata/fn_struct_method.snow.want +14 -14
@@ 2,10 2,16 @@ package main

import "fmt"

func _اprintln(
	x int,
) {
	fmt.Println(x)
func (
	self *_ا5اS,
) _اinc() {
	self._اx = self._اx + 1
}

func (
	self *_ا5اS,
) _اrefinc() {
	self._اx = self._اx + 1
}

type _ا5اS struct {


@@ 23,16 29,10 @@ func _ا5اSاnew(
	return r
}

func (
	self *_ا5اS,
) _اinc() {
	self._اx = self._اx + 1
}

func (
	self *_ا5اS,
) _اrefinc() {
	self._اx = self._اx + 1
func _اprintln(
	x int,
) {
	fmt.Println(x)
}

func main() {

M pkg/codegen/translate.go => pkg/codegen/translate.go +33 -10
@@ 30,13 30,14 @@ type translator struct {
	// set of already rendered generic instantiations, based on the mangled name
	renderedGenInsts map[string]bool

	// map of semantic.Node to []goast.Node, in the order they must appear when
	// 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.
	nodeToGoAST map[semantic.Node][]goast.Node
	nodeToGenerated map[semantic.Node][]interface{}

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



@@ 44,10 45,16 @@ type convertFnKey struct {
	from, to semantic.Type
}

type topLevelNode goast.Node
type topLevelNode struct {
	goast.Node
}

func (t *translator) append(n semantic.Node, gonodes ...goast.Node) {
	t.nodeToGoAST[n] = append(t.nodeToGoAST[n], gonodes...)
	ns := t.nodeToGenerated[n]
	for _, gon := range gonodes {
		ns = append(ns, gon)
	}
	t.nodeToGenerated[n] = ns
}

// merges the new generic instantiation replacement types into a cloned lookup map,


@@ 70,7 77,7 @@ 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.nodeToGoAST = make(map[semantic.Node][]goast.Node)
		t.nodeToGenerated = make(map[semantic.Node][]interface{})
		t.convertFns = make(map[convertFnKey]string)
		t.renderedGenInsts = make(map[string]bool)
		for _, f := range n.Files {


@@ 369,7 376,7 @@ func (t *translator) createFn(nm string, fn *semantic.Fn) []goast.Node {
		if str := fn.MethodOf; str != nil {
			t.convertFuncToMethod(fd, str, fn.IsRef)
		}
		return []goast.Node{topLevelNode(fd)}
		return []goast.Node{topLevelNode{fd}}
	}
	return createFuncLit(nm, ft, body)
}


@@ 383,7 390,7 @@ func (t *translator) createStruct(nm string, str *semantic.Struct) []goast.Node 
	propInit := make(map[*semantic.Var][]goast.Expr)
	for i, prop := range str.Vars {
		semantic.Walk(t, prop)
		propDecls := t.nodeToGoAST[prop]
		propDecls := t.nodeToGenerated[prop]
		vs := propDecls[len(propDecls)-1].(*goast.GenDecl).Specs[0].(*goast.ValueSpec)
		fields.List[i] = &goast.Field{
			Names: vs.Names,


@@ 412,7 419,7 @@ func (t *translator) createStruct(nm string, str *semantic.Struct) []goast.Node 
		semantic.Walk(t, str)
	}

	return []goast.Node{topLevelNode(decl), topLevelNode(strInit)}
	return []goast.Node{topLevelNode{decl}, topLevelNode{strInit}}
}

func (t *translator) addFileImports(f *semantic.File, gof *goast.File) {


@@ 717,9 724,21 @@ 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
	decl := gi.GenericDecl.Ref

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

	decl := gi.GenericDecl.Ref
	gd := decl.(semantic.GenericDecl)
	_, insts := gd.GenInsts()
	types := insts[gi]


@@ 735,6 754,10 @@ func (t *translator) renderGenInst(gi *semantic.GenericInst, name string) {
	default:
		panic(fmt.Sprintf("invalid generic type to render: %T", decl))
	}

	// reset the nodeToGenerated map and record the sub-map with the generic declaration
	parentMap[decl] = append(parentMap[decl], t.nodeToGenerated)
	t.nodeToGenerated = parentMap
}

func createFuncLit(name string, ft *goast.FuncType, body *goast.BlockStmt) []goast.Node {

M pkg/codegen/treebuild.go => pkg/codegen/treebuild.go +139 -38
@@ 10,12 10,57 @@ import (
// The treebuilder builds the Go AST from the nodes associated with the
// abstract semantic graph in the translator phase.
type treebuilder struct {
	curGoFile   *goast.File
	gofiles     []*goast.File
	nodeToGoAST map[semantic.Node][]goast.Node
	curGoFile       *goast.File
	gofiles         []*goast.File
	nodeToGenerated map[semantic.Node][]interface{}
}

func (tb *treebuilder) appendDecl(list *[]goast.Decl, n semantic.Node, vs []interface{}) {
	for _, v := range vs {
		if tln, ok := v.(topLevelNode); ok {
			v = tln.Node
		}

		switch v := v.(type) {
		case goast.Node:
			*list = append(*list, v.(goast.Decl))

		case map[semantic.Node][]interface{}:
			// inside that nested map, the Go AST node for that node is the one to append
			tb.appendDecl(list, n, v[n])

		default:
			panic(fmt.Sprintf("invalid value type in nodeToGenerated map: %T", v))
		}
	}
}

func (tb *treebuilder) appendStmt(list *[]goast.Stmt, n semantic.Node, vs []interface{}) {
	for _, v := range vs {
		switch v := v.(type) {
		case goast.Node:
			if tln, ok := v.(topLevelNode); ok {
				tb.curGoFile.Decls = append(tb.curGoFile.Decls, tln.Node.(goast.Decl))
			} else {
				if gd, ok := v.(*goast.GenDecl); ok {
					v = &goast.DeclStmt{Decl: gd}
				}
				*list = append(*list, v.(goast.Stmt))
			}

		case map[semantic.Node][]interface{}:
			// inside that nested map, the Go AST node for stmt is the one to append
			tb.appendStmt(list, n, v[n])

		default:
			panic(fmt.Sprintf("invalid value type in nodeToGenerated map: %T", v))
		}
	}
}

	// single statement generated by visiting e.g. a Block, If or Guard.
	stmt goast.Stmt
func (tb *treebuilder) generatedStmt(n semantic.Node) goast.Stmt {
	ns := tb.nodeToGenerated[n]
	return ns[0].(goast.Stmt)
}

func (tb *treebuilder) Visit(n semantic.Node) semantic.Visitor {


@@ 29,11 74,9 @@ func (tb *treebuilder) Visit(n semantic.Node) semantic.Visitor {
	case *semantic.File:
		// file node should have its goast.File as first node, rest should be added
		// to the file's declarations.
		ns := tb.nodeToGoAST[n]
		ns := tb.nodeToGenerated[n]
		tb.curGoFile = ns[0].(*goast.File)
		for i := 1; i < len(ns); i++ {
			tb.curGoFile.Decls = append(tb.curGoFile.Decls, ns[i].(goast.Decl))
		}
		tb.appendDecl(&tb.curGoFile.Decls, n, ns[1:])

		decls := make([]semantic.Decl, 0, len(n.Vars)+len(n.Structs)+len(n.Fns))
		for _, v := range n.Vars {


@@ 49,18 92,84 @@ func (tb *treebuilder) Visit(n semantic.Node) semantic.Visitor {
			decls = append(decls, fn)
		}
		for _, decl := range decls {
			for _, gons := range tb.nodeToGoAST[decl] {
				tb.curGoFile.Decls = append(tb.curGoFile.Decls, gons.(goast.Decl))
			}
			tb.appendDecl(&tb.curGoFile.Decls, decl, tb.nodeToGenerated[decl])
		}

	case *semantic.Fn:
		if n.Body == nil {
			break
		}

		for _, v := range tb.nodeToGenerated[n] {
			if tln, ok := v.(topLevelNode); ok {
				v = tln.Node
			}

			switch v := v.(type) {
			case *goast.FuncDecl:
				semantic.Walk(tb, n.Body)
				v.Body = tb.generatedStmt(n.Body).(*goast.BlockStmt)

			case *goast.AssignStmt:
				semantic.Walk(tb, n.Body)
				fnLit := v.Rhs[0].(*goast.FuncLit)
				fnLit.Body = tb.generatedStmt(n.Body).(*goast.BlockStmt)

			case map[semantic.Node][]interface{}:
				prev := tb.nodeToGenerated
				tb.nodeToGenerated = v
				semantic.Walk(tb, n)
				tb.nodeToGenerated = prev

			case *goast.DeclStmt:
				// nothing to do, always paired with an AssignStmt
			default:
				panic(fmt.Sprintf("invalid generated node type for function: %T", v))
			}
		}

	case *semantic.Struct:
		for _, v := range tb.nodeToGenerated[n] {
			if tln, ok := v.(topLevelNode); ok {
				v = tln.Node
			}

			switch v := v.(type) {
			case map[semantic.Node][]interface{}:
				prev := tb.nodeToGenerated
				tb.nodeToGenerated = v
				for _, fn := range n.Fns {
					semantic.Walk(tb, fn)
					tb.appendDecl(&tb.curGoFile.Decls, fn, tb.nodeToGenerated[fn])
				}
				for _, str := range n.Structs {
					semantic.Walk(tb, str)
					tb.appendDecl(&tb.curGoFile.Decls, str, tb.nodeToGenerated[str])
				}
				tb.nodeToGenerated = prev

			case *goast.GenDecl:
				// the struct declaration
				for _, fn := range n.Fns {
					semantic.Walk(tb, fn)
					tb.appendDecl(&tb.curGoFile.Decls, fn, tb.nodeToGenerated[fn])
				}
				for _, str := range n.Structs {
					semantic.Walk(tb, str)
					tb.appendDecl(&tb.curGoFile.Decls, str, tb.nodeToGenerated[str])
				}

			case *goast.FuncDecl:
				// the struct init, always paired with a GenDecl

			default:
				panic(fmt.Sprintf("invalid generated node type for struct: %T", v))
			}
		}

	case *semantic.Block:
		// block may have multiple associated statements (e.g. in a generic fn)
		ns := tb.nodeToGoAST[n]
		// block should have a single associated statement
		ns := tb.nodeToGenerated[n]
		if len(ns) != 1 {
			panic(fmt.Sprintf("block statement should have 1 associated Go node, got %d", len(ns)))
		}


@@ 68,56 177,48 @@ func (tb *treebuilder) Visit(n semantic.Node) semantic.Visitor {

		for _, stmt := range n.Stmts {
			semantic.Walk(tb, stmt)
			gons := tb.nodeToGoAST[stmt]
			for _, gon := range gons {
				if _, ok := gon.(topLevelNode); ok {
					tb.curGoFile.Decls = append(tb.curGoFile.Decls, gon.(goast.Decl))
				} else {
					block.List = append(block.List, gon.(goast.Stmt))
				}
			}
			tb.appendStmt(&block.List, stmt, tb.nodeToGenerated[stmt])
		}
		tb.stmt = block

	case *semantic.If:
		// if should have a single associated statement
		ns := tb.nodeToGoAST[n]
		ns := tb.nodeToGenerated[n]
		if len(ns) != 1 {
			panic(fmt.Sprintf("if statement should have 1 associated Go node, got %d", len(ns)))
		}
		ifs := ns[0].(*goast.IfStmt)

		semantic.Walk(tb, n.Body)
		ifs.Body = tb.stmt.(*goast.BlockStmt)
		ifs.Body = tb.generatedStmt(n.Body).(*goast.BlockStmt)
		if n.Else != nil {
			semantic.Walk(tb, n.Else)
			ifs.Else = tb.stmt
			ifs.Else = tb.generatedStmt(n.Else)
		}
		tb.stmt = ifs

	case *semantic.Guard:
		// guard should have a single associated statement
		ns := tb.nodeToGoAST[n]
		ns := tb.nodeToGenerated[n]
		if len(ns) != 1 {
			panic(fmt.Sprintf("guard statement should have 1 associated Go node, got %d", len(ns)))
		}
		ifs := ns[0].(*goast.IfStmt)
		semantic.Walk(tb, n.Else)
		ifs.Else = tb.stmt
		tb.stmt = ifs
		ifs.Else = tb.generatedStmt(n.Else)

	case *semantic.Var, *semantic.Return, *semantic.Assign, *semantic.ExprStmt:
		ns := tb.nodeToGoAST[n]
		ns := tb.nodeToGenerated[n]
		if len(ns) != 1 {
			panic(fmt.Sprintf("statement should have 1 associated Go node, got %d", len(ns)))
		}
		var stmt goast.Stmt
		if gd, ok := ns[0].(*goast.GenDecl); ok {
			stmt = &goast.DeclStmt{Decl: gd}
		} else {
			stmt = ns[0].(goast.Stmt)
		}
		tb.stmt = stmt
		/*
			var stmt goast.Stmt
			if gd, ok := ns[0].(*goast.GenDecl); ok {
				stmt = &goast.DeclStmt{Decl: gd}
			} else {
				stmt = ns[0].(goast.Stmt)
			}
			tb.stmt = stmt
		*/
	}
	return nil
}