~mna/snow

f708500e4e110a7e17ade47b4246ae66138e36f5 — Martin Angers 1 year, 11 months ago 4a393cd
pkg/typecheck: refactor passes as array of visitors
2 files changed, 33 insertions(+), 23 deletions(-)

M pkg/typecheck/checker.go
M pkg/typecheck/scope_test.go
M pkg/typecheck/checker.go => pkg/typecheck/checker.go +32 -22
@@ 7,10 7,17 @@ import (
	"git.sr.ht/~mna/snow/pkg/token"
)

const (
	AllPasses = iota - 1
	ScopePass
	TypeAssignPass
	TypeCheckPass
)

// CheckFiles is a helper function that parses and typechecks code for the
// provided source files. It returns the typechecked *Unit and an error.
// The error, if non-nil, is guaranteed to be a scanner.ErrorList.
func CheckFiles(files ...string) (*Unit, error) {
func CheckFiles(maxPass int, files ...string) (*Unit, error) {
	if len(files) == 0 {
		return nil, nil
	}


@@ 20,7 27,7 @@ func CheckFiles(files ...string) (*Unit, error) {
		return nil, err
	}

	var c checker
	c := checker{maxPass: maxPass}
	for _, f := range fs {
		c.checkFile(fset, f)
	}


@@ 28,34 35,37 @@ func CheckFiles(files ...string) (*Unit, error) {
}

type checker struct {
	fset   *token.FileSet
	unit   *Unit
	errors scanner.ErrorList
	maxPass int
	fset    *token.FileSet
	unit    *Unit
	errors  scanner.ErrorList
	passes  []ast.Visitor
}

func (c *checker) checkFile(fset *token.FileSet, file *ast.File) {
	if c.unit == nil {
		c.unit = &Unit{Universe: newUniverseScope()}
		c.unit = &Unit{
			Universe: newUniverseScope(),
			Types:    make(map[ast.Expr]Type),
		}
	}
	c.fset = fset

	// Step 1: collect all symbols and their respective scope
	sp := &scopePass{
		errHandler:      c.error,
		currentScope:    c.unit.Universe,
		preventNewScope: false,
	if len(c.passes) == 0 {
		c.passes = []ast.Visitor{
			// Step 0: collect all symbols and their respective scope
			&scopePass{errHandler: c.error, currentScope: c.unit.Universe, preventNewScope: false},
			// Step 1: determine type of each expression
			&typePass{scope: c.unit.Universe, types: c.unit.Types},
			// Step 2: typecheck expressions so that each value is of a valid type
		}
	}
	ast.Walk(sp, file)
	c.fset = fset

	// Step 2: determine type of each expression
	tp := &typePass{
		scope: c.unit.Universe,
		types: make(map[ast.Expr]Type),
	for i, p := range c.passes {
		ast.Walk(p, file)
		if i == c.maxPass {
			break
		}
	}
	ast.Walk(tp, file)
	c.unit.Types = tp.types

	// Step 3: typecheck expressions so that each value is of a valid type
}

func (c *checker) error(pos token.Pos, msg string) {

M pkg/typecheck/scope_test.go => pkg/typecheck/scope_test.go +1 -1
@@ 30,7 30,7 @@ func TestScopes(t *testing.T) {
		}

		t.Run(fi.Name(), func(t *testing.T) {
			unit, err := CheckFiles(filepath.Join(baseDir, fi.Name()))
			unit, err := CheckFiles(ScopePass, filepath.Join(baseDir, fi.Name()))
			if unit == nil && err != nil {
				t.Fatal(err)
			}