~mna/snow unlisted

snow/pkg/semantic/runner.go -rw-r--r-- 1.9 KiB
424066c5Martin Angers doc: v0.0.5 1 year, 7 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package semantic

import (
	"go/scanner"

	"git.sr.ht/~mna/snow/pkg/ast"
	"git.sr.ht/~mna/snow/pkg/parser"
	"git.sr.ht/~mna/snow/pkg/token"
)

const (
	TranslatePass = iota
	TypeAssignPass
	TypeCheckPass
	AnalysisPass
	AllPasses
)

// Run 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 Run(maxPass int, files ...string) (*Unit, error) {
	if len(files) == 0 {
		return nil, nil
	}

	fset, fs, err := parser.ParseFiles(files...)
	if err != nil {
		return nil, err
	}
	return RunAST(maxPass, fset, fs...)
}

// RunAST typechecks already parsed code for the provided AST files. It returns the
// typechecked *Unit and an error. The error, if non-nil, is guaranteed to be
// a scanner.ErrorList.
func RunAST(maxPass int, fset *token.FileSet, files ...*ast.File) (*Unit, error) {
	var errs scanner.ErrorList
	errh := func(pos token.Pos, msg string) {
		errs.Add(fset.Position(pos), msg)
	}

	// TODO: would probably be safer to stop as soon as a pass generates errors.

	var unit *Unit
	if maxPass >= TranslatePass {
		// step 1: translate to semantic tree and generate scopes
		unit = translate(files, errh)
		unit.FileSet = fset
	}

	if maxPass >= TypeAssignPass {
		// step 2: assign types to expressions and declarations
		typeassign(unit, errh)
	}

	if maxPass >= TypeCheckPass {
		// step 3: type-check the program
		typecheck(unit, errh)
	}

	if maxPass >= AnalysisPass {
		// step 4: static analysis of the program
		analysis(unit, errh)
	}

	return unit, removeDuplicateErrs(errs).Err()
}

func removeDuplicateErrs(el scanner.ErrorList) scanner.ErrorList {
	var noDupEl scanner.ErrorList
	set := make(map[scanner.Error]bool, len(el))
	for _, err := range el {
		set[*err] = true
	}
	for err := range set {
		err := err
		noDupEl = append(noDupEl, &err)
	}
	noDupEl.Sort()
	return noDupEl
}