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
}