Add some initial analyzer tests for units

This commit is contained in:
Sasha Koshka 2024-02-14 23:20:01 -05:00
parent afe3683c2a
commit 78fa0f703d
1 changed files with 81 additions and 37 deletions

View File

@ -5,6 +5,7 @@ import "fmt"
import "errors"
import "os/exec"
import "path/filepath"
import "github.com/google/uuid"
import "git.tebibyte.media/sashakoshka/fspl/llvm"
import "git.tebibyte.media/sashakoshka/fspl/lexer"
import "git.tebibyte.media/sashakoshka/fspl/entity"
@ -21,21 +22,8 @@ type Compiler struct {
}
func (this *Compiler) CompileUnit (address entity.Address) error {
_, isFile := address.SourceFile()
_, isModule := address.Module()
if !isFile && !isModule {
return errors.New(fmt.Sprintf (
"%v is not a module, nor a source file",
address))
}
var syntaxTree fsplParser.Tree
var err error
if isModule {
syntaxTree, err = this.ParseModule(address)
} else {
syntaxTree, err = this.ParseSourceFile(address)
}
_, err := this.ParseUnit(&syntaxTree, address, false)
if err != nil { return err }
// analyze syntax tree
@ -88,28 +76,59 @@ func (this *Compiler) CompileUnit (address entity.Address) error {
}
}
func (this *Compiler) ParseModule (address entity.Address) (fsplParser.Tree, error) {
func (this *Compiler) ParseUnit (
syntaxTree *fsplParser.Tree,
address entity.Address,
skim bool,
) (
*fsplParser.UnitInfo,
error,
) {
_, isFile := address.SourceFile()
_, isModule := address.Module()
if !isFile && !isModule {
return nil, errors.New(fmt.Sprintf (
"%v is not a module, nor a source file",
address))
}
if isModule {
return this.ParseModule(syntaxTree, address, skim)
} else {
return this.ParseSourceFile(syntaxTree, address, skim)
}
}
func (this *Compiler) ParseModule (
syntaxTree *fsplParser.Tree,
address entity.Address,
skim bool,
) (
*fsplParser.UnitInfo,
error,
) {
modulePath, isModule := address.Module()
var syntaxTree fsplParser.Tree
if !isModule {
return syntaxTree, errors.New(fmt.Sprintf("%v is not a module", address))
return nil, errors.New(fmt.Sprintf("%v is not a module", address))
}
// parse module metadata file
var metaTree metaParser.Tree
lx, err := lexer.LexFile(filepath.Join(modulePath, "fspl.mod"))
if err != nil { return syntaxTree, err }
if err != nil { return nil, err }
err = metaTree.Parse(lx)
if err != nil { return syntaxTree, err }
if err != nil { return nil, err }
// ensure metadata is well formed
// FIXME: sort this map alphabetically so the result of the compiler is
// deterministic
dependencies := make(map[string] *entity.Dependency)
for _, dependency := range metaTree.Dependencies {
nickname := dependency.Nickname
if nickname == "" {
newNickname, ok := dependency.Address.Nickname()
if !ok {
return syntaxTree, ferrors.Errorf (
return nil, ferrors.Errorf (
dependency.Position,
"cannot generate nickname for %v, " +
"please add one after the address",
@ -118,7 +137,7 @@ func (this *Compiler) ParseModule (address entity.Address) (fsplParser.Tree, err
nickname = newNickname
}
if previous, exists := dependencies[nickname]; exists {
return syntaxTree, ferrors.Errorf (
return nil, ferrors.Errorf (
dependency.Position,
"unit with nickname %v already listed at %v",
nickname, previous.Position)
@ -126,36 +145,61 @@ func (this *Compiler) ParseModule (address entity.Address) (fsplParser.Tree, err
dependencies[nickname] = dependency
}
// parse dependency units
unitInfo := fsplParser.UnitInfo {
UUID: metaTree.UUID,
Dependencies: make(map[string] uuid.UUID),
}
for nickname, dependency := range dependencies {
dependencyInfo, err := this.ParseUnit(syntaxTree, dependency.Address, true)
if err != nil { return nil, err }
unitInfo.Dependencies[nickname] = dependencyInfo.UUID
}
// parse all files in the module
entries, err := os.ReadDir(modulePath)
if err != nil { return syntaxTree, err }
if err != nil { return nil, err }
for _, entry := range entries {
lx, err := lexer.LexFile(filepath.Join (
modulePath,
entry.Name()))
if err != nil { return syntaxTree, err }
err = syntaxTree.Parse(lx)
if err != nil { return syntaxTree, err }
if err != nil { return nil, err }
if skim {
err = syntaxTree.ParseDependency(unitInfo, lx)
} else {
err = syntaxTree.Parse(unitInfo, lx)
}
if err != nil { return nil, err }
}
// TODO
// have a ParseDependency method on fsplParser.Tree that parses
// in a dependency, and parse in each dependency. Would need
// extra fields in top-level entities for a module namespace.
return syntaxTree, nil
return &unitInfo, nil
}
func (this *Compiler) ParseSourceFile (address entity.Address) (fsplParser.Tree, error) {
func (this *Compiler) ParseSourceFile (
syntaxTree *fsplParser.Tree,
address entity.Address,
skim bool,
) (
*fsplParser.UnitInfo,
error,
) {
filePath, isFile := address.Module()
var syntaxTree fsplParser.Tree
if !isFile {
return syntaxTree, errors.New(fmt.Sprintf("%v is not a source file", address))
return nil, errors.New(fmt.Sprintf("%v is not a source file", address))
}
unitInfo := fsplParser.UnitInfo {
UUID: address.UUID(),
}
lx, err := lexer.LexFile(filePath)
if err != nil { return syntaxTree, err }
err = syntaxTree.Parse(lx)
if err != nil { return syntaxTree, err }
return syntaxTree, nil
if err != nil { return nil, err }
if skim {
err = syntaxTree.ParseDependency(unitInfo, lx)
} else {
err = syntaxTree.Parse(unitInfo, lx)
}
if err != nil { return nil, err }
return &unitInfo, nil
}
func (this *Compiler) CompileIRModule (module *llvm.Module, filetype string) error {