From dd731240a0104d4c21adc6da826256c0dc2b7525 Mon Sep 17 00:00:00 2001 From: Emil Valeev Date: Sun, 27 Oct 2024 06:16:33 +0500 Subject: [PATCH] feat(analyzer): (attempt to) improve compiler errors --- cmd/lsp/server/get_file_view.go | 6 +- examples/compare_values/main.neva | 7 +- internal/builder/builder.go | 11 +- internal/compiler/analyzer/analyzer.go | 52 ++++--- internal/compiler/analyzer/component.go | 9 +- internal/compiler/analyzer/const.go | 40 +++--- internal/compiler/analyzer/interface.go | 12 +- internal/compiler/analyzer/main_component.go | 45 +++--- internal/compiler/analyzer/main_pkg.go | 42 +++--- internal/compiler/analyzer/network.go | 142 +++++++++---------- internal/compiler/analyzer/nodes.go | 43 +++--- internal/compiler/analyzer/semver.go | 10 +- internal/compiler/analyzer/type.go | 10 +- internal/compiler/compiler.go | 2 +- internal/compiler/desugarer/component.go | 5 - internal/compiler/desugarer/const_sender.go | 12 +- internal/compiler/desugarer/desugarer.go | 18 +-- internal/compiler/desugarer/network.go | 14 +- internal/compiler/desugarer/node.go | 2 +- internal/compiler/error.go | 57 +++----- internal/compiler/irgen/func.go | 4 +- internal/compiler/irgen/irgen.go | 24 ++-- internal/compiler/irgen/message.go | 8 +- internal/compiler/parser/err_handler.go | 4 +- internal/compiler/parser/listener_helpers.go | 66 ++++----- internal/compiler/parser/net.go | 47 +++--- internal/compiler/parser/parser.go | 8 +- internal/compiler/sourcecode/scope.go | 52 +++---- internal/compiler/sourcecode/sourcecode.go | 33 +++-- 29 files changed, 363 insertions(+), 422 deletions(-) diff --git a/cmd/lsp/server/get_file_view.go b/cmd/lsp/server/get_file_view.go index cb260825..b4279477 100644 --- a/cmd/lsp/server/get_file_view.go +++ b/cmd/lsp/server/get_file_view.go @@ -47,9 +47,9 @@ func (s *Server) GetFileView(glspCtx *glsp.Context, req GetFileViewRequest) (Get scope := src.Scope{ Location: src.Location{ - ModRef: s.index.EntryModRef, - PkgName: pkgName, - FileName: fileName, + Module: s.index.EntryModRef, + Package: pkgName, + Filename: fileName, }, Build: *s.index, } diff --git a/examples/compare_values/main.neva b/examples/compare_values/main.neva index e60e5555..179d8a2b 100644 --- a/examples/compare_values/main.neva +++ b/examples/compare_values/main.neva @@ -1,12 +1,9 @@ flow Main(start) (stop) { - Eq, Ternary, Println + Eq, Println --- :start -> [ { 2 -> eq:actual }, { 2 -> eq:compared } ] - eq -> ternary:if - 'They match' -> ternary:then - 'They do not match' -> ternary:else - ternary -> println -> :stop + (eq ? 'They match' : 'They do not match') -> println -> :stop } diff --git a/internal/builder/builder.go b/internal/builder/builder.go index cad3eed5..2f14b42c 100644 --- a/internal/builder/builder.go +++ b/internal/builder/builder.go @@ -2,7 +2,6 @@ package builder import ( "context" - "fmt" "io/fs" "os" "path/filepath" @@ -31,7 +30,7 @@ func (b Builder) Build( entryMod, entryModRootPath, err := b.LoadModuleByPath(ctx, wd) if err != nil { return compiler.RawBuild{}, "", &compiler.Error{ - Err: fmt.Errorf("build entry mod: %w", err), + Message: "build entry mod: " + err.Error(), } } @@ -51,7 +50,7 @@ func (b Builder) Build( stdMod, _, err := b.LoadModuleByPath(ctx, b.stdLibPath) if err != nil { return compiler.RawBuild{}, "", &compiler.Error{ - Err: fmt.Errorf("build stdlib mod: %w", err), + Message: "build stdlib mod: " + err.Error(), } } @@ -61,7 +60,7 @@ func (b Builder) Build( release, err := acquireLockFile() if err != nil { return compiler.RawBuild{}, "", &compiler.Error{ - Err: fmt.Errorf("failed to acquire lock file: %w", err), + Message: "failed to acquire lock file: " + err.Error(), } } defer release() @@ -78,14 +77,14 @@ func (b Builder) Build( depWD, _, err := b.downloadDep(depModRef) if err != nil { return compiler.RawBuild{}, "", &compiler.Error{ - Err: fmt.Errorf("download dep: %w", err), + Message: "download dep: " + err.Error(), } } depMod, _, err := b.LoadModuleByPath(ctx, depWD) if err != nil { return compiler.RawBuild{}, "", &compiler.Error{ - Err: fmt.Errorf("build dep mod: %w", err), + Message: "build dep mod: " + err.Error(), } } diff --git a/internal/compiler/analyzer/analyzer.go b/internal/compiler/analyzer/analyzer.go index 172a9fea..cb244629 100644 --- a/internal/compiler/analyzer/analyzer.go +++ b/internal/compiler/analyzer/analyzer.go @@ -25,21 +25,21 @@ type Analyzer struct { func (a Analyzer) AnalyzeExecutableBuild(build src.Build, mainPkgName string) (src.Build, *compiler.Error) { location := src.Location{ - ModRef: build.EntryModRef, - PkgName: mainPkgName, + Module: build.EntryModRef, + Package: mainPkgName, } entryMod, ok := build.Modules[build.EntryModRef] if !ok { return src.Build{}, &compiler.Error{ - Err: fmt.Errorf("entry module not found: %s", build.EntryModRef), + Message: fmt.Sprintf("entry module not found: %s", build.EntryModRef), Location: &location, } } if _, ok := entryMod.Packages[mainPkgName]; !ok { return src.Build{}, &compiler.Error{ - Err: errors.New("main package not found"), + Message: "main package not found", Location: &location, } } @@ -89,17 +89,17 @@ func (a Analyzer) AnalyzeBuild(build src.Build) (src.Build, *compiler.Error) { func (a Analyzer) analyzeModule(modRef src.ModuleRef, build src.Build) (map[string]src.Package, *compiler.Error) { if modRef != build.EntryModRef && modRef.Version == "" { return nil, &compiler.Error{ - Err: errors.New("every dependency module must have version"), - Location: &src.Location{ModRef: modRef}, + Message: "every dependency module must have version", + Location: &src.Location{Module: modRef}, } } - location := src.Location{ModRef: modRef} + location := src.Location{Module: modRef} mod := build.Modules[modRef] if len(mod.Packages) == 0 { return nil, &compiler.Error{ - Err: errors.New("module must contain at least one package"), + Message: "module must contain at least one package", Location: &location, } } @@ -110,8 +110,8 @@ func (a Analyzer) analyzeModule(modRef src.ModuleRef, build src.Build) (map[stri for pkgName, pkg := range pkgsCopy { scope := src.Scope{ Location: src.Location{ - ModRef: modRef, - PkgName: pkgName, + Module: modRef, + Package: pkgName, }, Build: build, } @@ -120,7 +120,7 @@ func (a Analyzer) analyzeModule(modRef src.ModuleRef, build src.Build) (map[stri if err != nil { return nil, compiler.Error{ Location: &src.Location{ - PkgName: pkgName, + Package: pkgName, }, }.Wrap(err) } @@ -134,7 +134,7 @@ func (a Analyzer) analyzeModule(modRef src.ModuleRef, build src.Build) (map[stri func (a Analyzer) analyzePkg(pkg src.Package, scope src.Scope) (src.Package, *compiler.Error) { if len(pkg) == 0 { return nil, &compiler.Error{ - Err: errors.New("package must contain at least one file"), + Message: "package must contain at least one file", Location: &scope.Location, } } @@ -148,26 +148,22 @@ func (a Analyzer) analyzePkg(pkg src.Package, scope src.Scope) (src.Package, *co } } - if err := pkg.Entities(func(entity src.Entity, entityName, fileName string) error { - scopeWithFile := scope.WithLocation(src.Location{ - FileName: fileName, - ModRef: scope.Location.ModRef, - PkgName: scope.Location.PkgName, + for result := range pkg.Entities() { + relocatedScope := scope.Relocate(src.Location{ + Module: scope.Location.Module, + Package: scope.Location.Package, + Filename: result.FileName, }) - resolvedEntity, err := a.analyzeEntity(entity, scopeWithFile) + analyzedEntity, err := a.analyzeEntity(result.Entity, relocatedScope) if err != nil { - return compiler.Error{ - Location: &scopeWithFile.Location, - Meta: entity.Meta(), + return nil, compiler.Error{ + Location: &relocatedScope.Location, + Meta: result.Entity.Meta(), }.Wrap(err) } - analyzedFiles[fileName].Entities[entityName] = resolvedEntity - - return nil - }); err != nil { - return nil, err.(*compiler.Error) //nolint:forcetypeassert + analyzedFiles[result.FileName].Entities[result.EntityName] = analyzedEntity } return analyzedFiles, nil @@ -179,7 +175,7 @@ func (a Analyzer) analyzeEntity(entity src.Entity, scope src.Scope) (src.Entity, Kind: entity.Kind, } - isStd := scope.Location.ModRef.Path == "std" + isStd := scope.Location.Module.Path == "std" switch entity.Kind { case src.TypeEntity: @@ -226,7 +222,7 @@ func (a Analyzer) analyzeEntity(entity src.Entity, scope src.Scope) (src.Entity, resolvedEntity.Component = analyzedComponent default: return src.Entity{}, &compiler.Error{ - Err: fmt.Errorf("unknown entity kind: %v", entity.Kind), + Message: fmt.Sprintf("unknown entity kind: %v", entity.Kind), Location: &scope.Location, Meta: entity.Meta(), } diff --git a/internal/compiler/analyzer/component.go b/internal/compiler/analyzer/component.go index 27d8038d..f492dd56 100644 --- a/internal/compiler/analyzer/component.go +++ b/internal/compiler/analyzer/component.go @@ -1,7 +1,6 @@ package analyzer import ( - "errors" "strings" "github.com/nevalang/neva/internal/compiler" @@ -16,7 +15,7 @@ func (a Analyzer) analyzeComponent( if isRuntimeFunc && len(runtimeFuncArgs) == 0 { return src.Component{}, &compiler.Error{ - Err: errors.New("Flow that use #extern directive must provide at least one argument"), + Message: "Flow that use #extern directive must provide at least one argument", Location: &scope.Location, Meta: &component.Meta, } @@ -27,7 +26,7 @@ func (a Analyzer) analyzeComponent( parts := strings.Split(runtimeFuncArg, " ") if len(parts) != 2 { return src.Component{}, &compiler.Error{ - Err: errors.New("Component that use #extern with more than one argument must provide arguments in a form of pairs"), + Message: "Component that use #extern with more than one argument must provide arguments in a form of pairs", Location: &scope.Location, Meta: &component.Meta, } @@ -53,7 +52,7 @@ func (a Analyzer) analyzeComponent( if isRuntimeFunc { if len(component.Nodes) != 0 || len(component.Net) != 0 { return src.Component{}, &compiler.Error{ - Err: errors.New("Flow with nodes or network cannot use #extern directive"), + Message: "Component with nodes or network cannot use #extern directive", Location: &scope.Location, Meta: &component.Meta, } @@ -75,7 +74,7 @@ func (a Analyzer) analyzeComponent( if len(component.Net) == 0 { return src.Component{}, &compiler.Error{ - Err: errors.New("Flow must have network"), + Message: "Component must have network", Location: &scope.Location, Meta: &component.Meta, } diff --git a/internal/compiler/analyzer/const.go b/internal/compiler/analyzer/const.go index 9f099faa..d0b0e9a3 100644 --- a/internal/compiler/analyzer/const.go +++ b/internal/compiler/analyzer/const.go @@ -18,7 +18,7 @@ func (a Analyzer) analyzeConst( ) (src.Const, *compiler.Error) { if constant.Value.Message == nil && constant.Value.Ref == nil { return src.Const{}, &compiler.Error{ - Err: errors.New("Constant must either have value or reference to another constant"), + Message: "Constant must either have value or reference to another constant", Location: &scope.Location, Meta: &constant.Meta, } @@ -28,7 +28,7 @@ func (a Analyzer) analyzeConst( entity, location, err := scope.Entity(*constant.Value.Ref) if err != nil { return src.Const{}, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &location, Meta: entity.Meta(), } @@ -36,7 +36,7 @@ func (a Analyzer) analyzeConst( if entity.Kind != src.ConstEntity { return src.Const{}, &compiler.Error{ - Err: fmt.Errorf("Constant refers to an entity that is not constant: %v", entity.Kind), + Message: fmt.Sprintf("Constant refers to an entity that is not constant: %v", entity.Kind), Location: &location, Meta: entity.Meta(), } @@ -48,7 +48,7 @@ func (a Analyzer) analyzeConst( resolvedType, err := a.analyzeTypeExpr(constant.TypeExpr, scope) if err != nil { return src.Const{}, compiler.Error{ - Err: errors.New("Cannot resolve constant type"), + Message: "Cannot resolve constant type", Location: &scope.Location, Meta: &constant.Meta, }.Wrap(err) @@ -56,7 +56,7 @@ func (a Analyzer) analyzeConst( if resolvedType.Lit != nil && resolvedType.Lit.Union != nil { return src.Const{}, &compiler.Error{ - Err: errors.New("Constant cannot have type union"), + Message: "Constant cannot have type union", Location: &scope.Location, Meta: &constant.Meta, } @@ -77,7 +77,7 @@ func (a Analyzer) analyzeConst( case "bool": if constant.Value.Message.Bool == nil { return src.Const{}, &compiler.Error{ - Err: fmt.Errorf("Boolean value is missing in boolean contant: %v", constant), + Message: fmt.Sprintf("Boolean value is missing in boolean contant: %v", constant), Location: &scope.Location, Meta: &constant.Meta, } @@ -89,7 +89,7 @@ func (a Analyzer) analyzeConst( constant.Value.Message.DictOrStruct != nil || constant.Value.Message.Enum != nil { return src.Const{}, &compiler.Error{ - Err: fmt.Errorf("%w: %v", ErrConstSeveralValues, constant), + Message: fmt.Sprintf("%w: %v", ErrConstSeveralValues, constant), Location: &scope.Location, Meta: &constant.Meta, } @@ -97,7 +97,7 @@ func (a Analyzer) analyzeConst( case "int": if constant.Value.Message.Int == nil { return src.Const{}, &compiler.Error{ - Err: fmt.Errorf("Integer value is missing in integer contant: %v", constant), + Message: fmt.Sprintf("Integer value is missing in integer contant: %v", constant), Location: &scope.Location, Meta: &constant.Meta, } @@ -109,7 +109,7 @@ func (a Analyzer) analyzeConst( constant.Value.Message.DictOrStruct != nil || constant.Value.Message.Enum != nil { return src.Const{}, &compiler.Error{ - Err: fmt.Errorf("%w: %v", ErrConstSeveralValues, constant.Value.Message), + Message: fmt.Sprintf("%w: %v", ErrConstSeveralValues, constant.Value.Message), Location: &scope.Location, Meta: &constant.Meta, } @@ -119,14 +119,14 @@ func (a Analyzer) analyzeConst( // We must pass this case. Desugarer will turn integer literal into float. if constant.Value.Message.Float == nil && constant.Value.Message.Int == nil { return src.Const{}, &compiler.Error{ - Err: fmt.Errorf("Float or integer value is missing in float contant: %v", constant), + Message: fmt.Sprintf("Float or integer value is missing in float contant: %v", constant), Location: &scope.Location, Meta: &constant.Meta, } } if constant.Value.Message.Float != nil && constant.Value.Message.Int != nil { return src.Const{}, &compiler.Error{ - Err: fmt.Errorf("%w: %v", ErrConstSeveralValues, constant.Value.Message), + Message: fmt.Sprintf("%w: %v", ErrConstSeveralValues, constant.Value.Message), Location: &scope.Location, Meta: &constant.Meta, } @@ -137,7 +137,7 @@ func (a Analyzer) analyzeConst( constant.Value.Message.DictOrStruct != nil || constant.Value.Message.Enum != nil { return src.Const{}, &compiler.Error{ - Err: fmt.Errorf("%w: %v", ErrConstSeveralValues, constant.Value.Message), + Message: fmt.Sprintf("%w: %v", ErrConstSeveralValues, constant.Value.Message), Location: &scope.Location, Meta: &constant.Meta, } @@ -145,7 +145,7 @@ func (a Analyzer) analyzeConst( case "string": if constant.Value.Message.Str == nil { return src.Const{}, &compiler.Error{ - Err: fmt.Errorf("String value is missing in string contant: %v", constant), + Message: fmt.Sprintf("String value is missing in string contant: %v", constant), Location: &scope.Location, Meta: &constant.Meta, } @@ -157,7 +157,7 @@ func (a Analyzer) analyzeConst( constant.Value.Message.DictOrStruct != nil || constant.Value.Message.Enum != nil { return src.Const{}, &compiler.Error{ - Err: fmt.Errorf("%w: %v", ErrConstSeveralValues, constant.Value.Message), + Message: fmt.Sprintf("%w: %v", ErrConstSeveralValues, constant.Value.Message), Location: &scope.Location, Meta: &constant.Meta, } @@ -165,7 +165,7 @@ func (a Analyzer) analyzeConst( case "list": if constant.Value.Message.List == nil { return src.Const{}, &compiler.Error{ - Err: fmt.Errorf("List value is missing in list contant: %v", constant), + Message: fmt.Sprintf("List value is missing in list contant: %v", constant), Location: &scope.Location, Meta: &constant.Meta, } @@ -176,7 +176,7 @@ func (a Analyzer) analyzeConst( constant.Value.Message.DictOrStruct != nil || constant.Value.Message.Enum != nil { return src.Const{}, &compiler.Error{ - Err: fmt.Errorf("%w: %v", ErrConstSeveralValues, constant.Value.Message), + Message: fmt.Sprintf("%w: %v", ErrConstSeveralValues, constant.Value.Message), Location: &scope.Location, Meta: &constant.Meta, } @@ -184,7 +184,7 @@ func (a Analyzer) analyzeConst( case "map", "struct": if constant.Value.Message.DictOrStruct == nil { return src.Const{}, &compiler.Error{ - Err: fmt.Errorf("Map or struct value is missing in map or struct contant: %v", constant), + Message: fmt.Sprintf("Map or struct value is missing in map or struct contant: %v", constant), Location: &scope.Location, Meta: &constant.Meta, } @@ -195,7 +195,7 @@ func (a Analyzer) analyzeConst( constant.Value.Message.List != nil || constant.Value.Message.Enum != nil { return src.Const{}, &compiler.Error{ - Err: fmt.Errorf("%w: %v", ErrConstSeveralValues, constant.Value.Message), + Message: fmt.Sprintf("%w: %v", ErrConstSeveralValues, constant.Value.Message), Location: &scope.Location, Meta: &constant.Meta, } @@ -203,7 +203,7 @@ func (a Analyzer) analyzeConst( case "enum": if constant.Value.Message.Enum == nil { return src.Const{}, &compiler.Error{ - Err: fmt.Errorf("Enum value is missing in enum contant: %v", constant), + Message: fmt.Sprintf("Enum value is missing in enum contant: %v", constant), Location: &scope.Location, Meta: &constant.Meta, } @@ -214,7 +214,7 @@ func (a Analyzer) analyzeConst( constant.Value.Message.List != nil || constant.Value.Message.DictOrStruct != nil { return src.Const{}, &compiler.Error{ - Err: fmt.Errorf("%w: %v", ErrConstSeveralValues, constant.Value.Message), + Message: fmt.Sprintf("%w: %v", ErrConstSeveralValues, constant.Value.Message), Location: &scope.Location, Meta: &constant.Meta, } diff --git a/internal/compiler/analyzer/interface.go b/internal/compiler/analyzer/interface.go index af489bab..e536dc70 100644 --- a/internal/compiler/analyzer/interface.go +++ b/internal/compiler/analyzer/interface.go @@ -25,7 +25,7 @@ func (a Analyzer) analyzeInterface( resolvedParams, err := a.analyzeTypeParams(iface.TypeParams.Params, scope) if err != nil { return src.Interface{}, compiler.Error{ - Err: ErrInterfaceTypeParams, + Message: ErrInterfaceTypeParams.Error(), Location: &scope.Location, Meta: &iface.Meta, }.Wrap(err) @@ -34,7 +34,7 @@ func (a Analyzer) analyzeInterface( resolvedIO, err := a.analyzeIO(resolvedParams, iface.IO, scope, params) if err != nil { return src.Interface{}, compiler.Error{ - Err: ErrInterfaceTypeParams, + Message: ErrInterfaceTypeParams.Error(), Location: &scope.Location, Meta: &iface.Meta, }.Wrap(err) @@ -59,14 +59,14 @@ func (a Analyzer) analyzeIO( ) (src.IO, *compiler.Error) { if !params.allowEmptyInports && len(io.In) == 0 { return src.IO{}, &compiler.Error{ - Err: errors.New("Interface must have inports"), + Message: "Interface must have inports", Location: &scope.Location, } } if !params.allowEmptyOutports && len(io.Out) == 0 { return src.IO{}, &compiler.Error{ - Err: errors.New("Interface must have outports"), + Message: "Interface must have outports", Location: &scope.Location, } } @@ -74,7 +74,7 @@ func (a Analyzer) analyzeIO( resolvedIn, err := a.analyzePorts(typeParams, io.In, scope) if err != nil { return src.IO{}, compiler.Error{ - Err: errors.New("Invalid inports"), + Message: "Invalid inports", Location: &scope.Location, }.Wrap(err) } @@ -82,7 +82,7 @@ func (a Analyzer) analyzeIO( resolvedOut, err := a.analyzePorts(typeParams, io.Out, scope) if err != nil { return src.IO{}, compiler.Error{ - Err: errors.New("Invalid outports"), + Message: "Invalid outports", Location: &scope.Location, }.Wrap(err) } diff --git a/internal/compiler/analyzer/main_component.go b/internal/compiler/analyzer/main_component.go index 85334bc5..c3e07db3 100644 --- a/internal/compiler/analyzer/main_component.go +++ b/internal/compiler/analyzer/main_component.go @@ -1,7 +1,6 @@ package analyzer import ( - "errors" "fmt" "github.com/nevalang/neva/internal/compiler" @@ -11,8 +10,8 @@ import ( func (a Analyzer) analyzeMainComponent(cmp src.Component, scope src.Scope) *compiler.Error { if len(cmp.Interface.TypeParams.Params) != 0 { return &compiler.Error{ - Err: errors.New("Main flow cannot have type parameters"), - Meta: &cmp.Interface.Meta, + Message: "Main component cannot have type parameters", + Meta: &cmp.Interface.Meta, } } @@ -30,46 +29,48 @@ func (a Analyzer) analyzeMainComponent(cmp src.Component, scope src.Scope) *comp func (a Analyzer) analyzeMainFlowIO(io src.IO) *compiler.Error { if len(io.In) != 1 { return &compiler.Error{ - Err: fmt.Errorf("Main flow must have exactly 1 inport: got %v", len(io.In)), + Message: fmt.Sprintf("Main flow must have exactly 1 inport: got %v", len(io.In)), } } if len(io.Out) != 1 { return &compiler.Error{ - Err: fmt.Errorf("Main flow must have exactly 1 outport: got %v", len(io.Out)), + Message: fmt.Sprintf("Main flow must have exactly 1 outport: got %v", len(io.Out)), } } enterInport, ok := io.In["start"] if !ok { - return &compiler.Error{Err: errors.New("Main flow must have 'start' inport")} + return &compiler.Error{Message: "Main flow must have 'start' inport"} } - if err := a.analyzeMainFlowPort(enterInport); err != nil { - return &compiler.Error{ - Err: err, - Meta: &enterInport.Meta, - } + + if err := a.analyzeMainComponentPort(enterInport); err != nil { + return err } exitOutport, ok := io.Out["stop"] if !ok { - return &compiler.Error{Err: errors.New("Main flow must have 'stop' outport")} + return &compiler.Error{Message: "Main flow must have 'stop' outport"} } - if err := a.analyzeMainFlowPort(exitOutport); err != nil { - return &compiler.Error{ - Err: err, - Meta: &exitOutport.Meta, - } + + if err := a.analyzeMainComponentPort(exitOutport); err != nil { + return err } return nil } -func (a Analyzer) analyzeMainFlowPort(port src.Port) error { +func (a Analyzer) analyzeMainComponentPort(port src.Port) *compiler.Error { if port.IsArray { - return errors.New("Main flow's ports cannot be arrays") + return &compiler.Error{ + Message: "Main flow's ports cannot be arrays", + Meta: &port.Meta, + } } if !(src.Scope{}).IsTopType(port.TypeExpr) { - return errors.New("Main flow's ports must be of type any") + return &compiler.Error{ + Message: "Main flow's ports must be of type any", + Meta: &port.Meta, + } } return nil } @@ -82,7 +83,7 @@ func (Analyzer) analyzeMainFlowNodes( nodeEntity, loc, err := scope.Entity(node.EntityRef) if err != nil { return &compiler.Error{ - Err: fmt.Errorf( + Message: fmt.Sprintf( "Referenced entity not found: node '%v', ref '%v', details '%v'", nodeName, node.EntityRef, @@ -95,7 +96,7 @@ func (Analyzer) analyzeMainFlowNodes( if nodeEntity.Kind != src.ComponentEntity { return &compiler.Error{ - Err: fmt.Errorf("Main flow's nodes must only refer to flow entities: %v: %v", nodeName, node.EntityRef), + Message: fmt.Sprintf("Main flow's nodes must only refer to flow entities: %v: %v", nodeName, node.EntityRef), Location: &loc, Meta: nodeEntity.Meta(), } diff --git a/internal/compiler/analyzer/main_pkg.go b/internal/compiler/analyzer/main_pkg.go index f26a7fd0..e73d1158 100644 --- a/internal/compiler/analyzer/main_pkg.go +++ b/internal/compiler/analyzer/main_pkg.go @@ -1,7 +1,6 @@ package analyzer import ( - "errors" "fmt" "github.com/nevalang/neva/internal/compiler" @@ -11,56 +10,57 @@ import ( func (a Analyzer) mainSpecificPkgValidation(mainPkgName string, mod src.Module, scope src.Scope) *compiler.Error { mainPkg := mod.Packages[mainPkgName] - location := &src.Location{ - ModRef: scope.Location.ModRef, - PkgName: mainPkgName, + location := src.Location{ + Module: scope.Location.Module, + Package: mainPkgName, } entityMain, filename, ok := mainPkg.Entity("Main") if !ok { return &compiler.Error{ - Err: errors.New("Main entity is not found"), - Location: location, + Message: "Main entity is not found", + Location: &location, } } - location.FileName = filename + location.Filename = filename if entityMain.Kind != src.ComponentEntity { return &compiler.Error{ - Err: errors.New("Main entity must be a component"), - Location: location, + Message: "Main entity must be a component", + Location: &location, } } if entityMain.IsPublic { return &compiler.Error{ - Err: errors.New("Main entity cannot be exported"), - Location: location, + Message: "Main entity cannot be exported", + Location: &location, Meta: &entityMain.Component.Meta, } } - scope = scope.WithLocation(*location) + scope = scope.Relocate(location) if err := a.analyzeMainComponent(entityMain.Component, scope); err != nil { return compiler.Error{ - Location: location, + Location: &location, Meta: &entityMain.Component.Meta, }.Wrap(err) } - if err := mainPkg.Entities(func(entity src.Entity, entityName, _ string) error { - if entity.IsPublic { + for result := range mainPkg.Entities() { + if result.Entity.IsPublic { return &compiler.Error{ - Err: fmt.Errorf("Main package cannot have exported entities: %v", entityName), - Meta: entity.Meta(), - Location: location, + Message: fmt.Sprintf("Unexpected public entity in main package: %v", result.EntityName), + Meta: result.Entity.Meta(), + Location: &src.Location{ + Module: scope.Location.Module, + Package: mainPkgName, + Filename: result.FileName, + }, } } - return nil - }); err != nil { - return err.(*compiler.Error) //nolint:forcetypeassert } return nil diff --git a/internal/compiler/analyzer/network.go b/internal/compiler/analyzer/network.go index e7c06e71..2e4907d2 100644 --- a/internal/compiler/analyzer/network.go +++ b/internal/compiler/analyzer/network.go @@ -214,7 +214,7 @@ func (a Analyzer) analyzeReceiver( receiver.ChainedConnection == nil && receiver.DeferredConnection == nil { return nil, &compiler.Error{ - Err: errors.New("Connection must have receiver-side"), + Message: "Connection must have receiver-side", Location: &scope.Location, Meta: &receiver.Meta, } @@ -274,7 +274,7 @@ func (a Analyzer) analyzeReceiver( } return nil, &compiler.Error{ - Err: errors.New("Connection must have receiver-side"), + Message: "Connection must have receiver-side", Location: &scope.Location, Meta: &receiver.Meta, } @@ -306,7 +306,7 @@ func (a Analyzer) analyzePortAddrReceiver( if !isArrPort && portAddr.Idx != nil { return src.PortAddr{}, &compiler.Error{ - Err: errors.New("Index for non-array port"), + Message: "Index for non-array port", Meta: &portAddr.Meta, Location: &scope.Location, } @@ -314,7 +314,7 @@ func (a Analyzer) analyzePortAddrReceiver( if isArrPort && portAddr.Idx == nil { return src.PortAddr{}, &compiler.Error{ - Err: errors.New("Index needed for array inport"), + Message: "Index needed for array inport", Meta: &portAddr.Meta, Location: &scope.Location, } @@ -323,7 +323,7 @@ func (a Analyzer) analyzePortAddrReceiver( for i, resolvedSenderType := range resolvedSenderTypes { if err := a.resolver.IsSubtypeOf(*resolvedSenderType, typeExpr, scope); err != nil { return src.PortAddr{}, &compiler.Error{ - Err: fmt.Errorf( + Message: fmt.Sprintf( "Incompatible types: %v -> %v: %w", analyzedSenders[i], portAddr, err, ), @@ -335,7 +335,7 @@ func (a Analyzer) analyzePortAddrReceiver( if err := netNodesUsage(nodesUsage).trackInportUsage(portAddr); err != nil { return src.PortAddr{}, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &portAddr.Meta, } @@ -356,7 +356,7 @@ func (a Analyzer) analyzeChainedConnectionReceiver( ) (src.Connection, *compiler.Error) { if chainedConn.Normal == nil { return src.Connection{}, &compiler.Error{ - Err: errors.New("chained connection must be a normal connection"), + Message: "chained connection must be a normal connection", Location: &scope.Location, Meta: &chainedConn.Meta, } @@ -364,7 +364,7 @@ func (a Analyzer) analyzeChainedConnectionReceiver( if len(chainedConn.Normal.SenderSide) != 1 { return src.Connection{}, &compiler.Error{ - Err: errors.New("multiple senders are only allowed at the start of a connection"), + Message: "multiple senders are only allowed at the start of a connection", Location: &scope.Location, Meta: &chainedConn.Normal.Meta, } @@ -385,7 +385,7 @@ func (a Analyzer) analyzeChainedConnectionReceiver( for i, resolvedSenderType := range resolvedSenderTypes { if err := a.resolver.IsSubtypeOf(*resolvedSenderType, chainHeadType, scope); err != nil { return src.Connection{}, &compiler.Error{ - Err: fmt.Errorf( + Message: fmt.Sprintf( "Incompatible types: %v -> %v: %w", analyzedSenders[i], chainHead, err, ), @@ -411,7 +411,7 @@ func (a Analyzer) analyzeChainedConnectionReceiver( if chainHead.PortAddr != nil { if err := netNodesUsage(nodesUsage).trackInportUsage(*chainHead.PortAddr); err != nil { return src.Connection{}, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &chainedConn.Meta, } @@ -476,9 +476,7 @@ func (a Analyzer) analyzeSender( sender.TernaryExpr == nil && len(sender.StructSelector) == 0 { return nil, nil, &compiler.Error{ - Err: errors.New( - "Sender in network must contain port address, constant reference or message literal", - ), + Message: "Sender in network must contain port address, constant reference or message literal", Location: &scope.Location, Meta: &sender.Meta, } @@ -486,7 +484,7 @@ func (a Analyzer) analyzeSender( if sender.Range != nil && prevChainLink == nil { return nil, nil, &compiler.Error{ - Err: errors.New("range expression cannot be used in non-chained connection"), + Message: "range expression cannot be used in non-chained connection", Location: &scope.Location, Meta: &sender.Meta, } @@ -494,7 +492,7 @@ func (a Analyzer) analyzeSender( if sender.Const != nil && len(prevChainLink) != 0 { return nil, nil, &compiler.Error{ - Err: errors.New("constant cannot be used in chained connection"), + Message: "constant cannot be used in chained connection", Location: &scope.Location, Meta: &sender.Meta, } @@ -502,7 +500,7 @@ func (a Analyzer) analyzeSender( if len(sender.StructSelector) > 0 && prevChainLink == nil { return nil, nil, &compiler.Error{ - Err: errors.New("struct selectors cannot be used in non-chained connection"), + Message: "struct selectors cannot be used in non-chained connection", Location: &scope.Location, Meta: &sender.Meta, } @@ -520,11 +518,10 @@ func (a Analyzer) analyzeSender( prevChainLink, ) if err != nil { - return nil, nil, &compiler.Error{ - Err: err, + return nil, nil, compiler.Error{ Location: &scope.Location, Meta: &sender.TernaryExpr.Meta, - } + }.Wrap(err) } // ensure the condition is of boolean type @@ -533,7 +530,7 @@ func (a Analyzer) analyzeSender( } if err := a.resolver.IsSubtypeOf(*condType, boolType, scope); err != nil { return nil, nil, &compiler.Error{ - Err: errors.New("Condition of ternary expression must be of boolean type"), + Message: "Condition of ternary expression must be of boolean type", Location: &scope.Location, Meta: &sender.TernaryExpr.Meta, } @@ -550,11 +547,10 @@ func (a Analyzer) analyzeSender( prevChainLink, ) if err != nil { - return nil, nil, &compiler.Error{ - Err: err, + return nil, nil, compiler.Error{ Location: &scope.Location, Meta: &sender.TernaryExpr.Meta, - } + }.Wrap(err) } // analyze the falseVal part @@ -568,11 +564,10 @@ func (a Analyzer) analyzeSender( prevChainLink, ) if err != nil { - return nil, nil, &compiler.Error{ - Err: err, + return nil, nil, compiler.Error{ Location: &scope.Location, Meta: &sender.TernaryExpr.Meta, - } + }.Wrap(err) } // TODO support proper typing (see https://github.com/nevalang/neva/issues/737) @@ -598,7 +593,7 @@ func (a Analyzer) analyzeSender( if sender.PortAddr != nil { if !isSenderArr && sender.PortAddr.Idx != nil { return nil, nil, &compiler.Error{ - Err: errors.New("Index for non-array port"), + Message: "Index for non-array port", Meta: &sender.PortAddr.Meta, Location: &scope.Location, } @@ -606,7 +601,7 @@ func (a Analyzer) analyzeSender( if isSenderArr && sender.PortAddr.Idx == nil { return nil, nil, &compiler.Error{ - Err: errors.New("Index needed for array outport"), + Message: "Index needed for array outport", Meta: &sender.PortAddr.Meta, Location: &scope.Location, } @@ -614,7 +609,7 @@ func (a Analyzer) analyzeSender( if err := netNodesUsage(nodesUsage).trackOutportUsage(*sender.PortAddr); err != nil { return nil, nil, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &sender.PortAddr.Meta, } @@ -649,7 +644,7 @@ func (a Analyzer) analyzeArrayBypassConnection( } if !isArray { return &compiler.Error{ - Err: errors.New("Non-array outport in array-bypass connection"), + Message: "Non-array outport in array-bypass connection", Location: &scope.Location, Meta: &arrBypassConn.SenderOutport.Meta, } @@ -670,7 +665,7 @@ func (a Analyzer) analyzeArrayBypassConnection( } if !isArray { return &compiler.Error{ - Err: errors.New("Non-array outport in array-bypass connection"), + Message: "Non-array outport in array-bypass connection", Location: &scope.Location, Meta: &arrBypassConn.SenderOutport.Meta, } @@ -682,7 +677,7 @@ func (a Analyzer) analyzeArrayBypassConnection( scope, ); err != nil { return &compiler.Error{ - Err: fmt.Errorf( + Message: fmt.Sprintf( "Incompatible types: %v -> %v: %w", arrBypassConn.SenderOutport, arrBypassConn.ReceiverInport, err, ), @@ -693,7 +688,7 @@ func (a Analyzer) analyzeArrayBypassConnection( if err := netNodesUsage(nodesUsage).trackOutportUsage(arrBypassConn.SenderOutport); err != nil { return &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &conn.Meta, } @@ -701,7 +696,7 @@ func (a Analyzer) analyzeArrayBypassConnection( if err := netNodesUsage(nodesUsage).trackInportUsage(arrBypassConn.ReceiverInport); err != nil { return &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &conn.Meta, } @@ -722,7 +717,7 @@ func (a Analyzer) analyzeNetPortsUsage( inportsUsage, ok := nodesUsage["in"] if !ok { return &compiler.Error{ - Err: errors.New("Unused inports"), + Message: "Unused inports", Location: &scope.Location, Meta: &compInterface.Meta, } @@ -731,7 +726,7 @@ func (a Analyzer) analyzeNetPortsUsage( for inportName := range compInterface.IO.In { if _, ok := inportsUsage.Out[inportName]; !ok { // note that self inports are outports for the network return &compiler.Error{ - Err: fmt.Errorf("Unused inport: %v", inportName), + Message: fmt.Sprintf("Unused inport: %v", inportName), Location: &scope.Location, } } @@ -741,7 +736,7 @@ func (a Analyzer) analyzeNetPortsUsage( outportsUsage, ok := nodesUsage["out"] if !ok { return &compiler.Error{ - Err: errors.New("Unused outports"), + Message: "Unused outports", Location: &scope.Location, Meta: &compInterface.Meta, } @@ -758,7 +753,7 @@ func (a Analyzer) analyzeNetPortsUsage( } return &compiler.Error{ - Err: fmt.Errorf("Unused outport: %v", outportName), + Message: fmt.Sprintf("Unused outport: %v", outportName), Location: &scope.Location, } } @@ -769,7 +764,7 @@ func (a Analyzer) analyzeNetPortsUsage( nodeUsage, ok := nodesUsage[nodeName] if !ok { return &compiler.Error{ - Err: fmt.Errorf("Unused node found: %v", nodeName), + Message: fmt.Sprintf("Unused node found: %v", nodeName), Location: &scope.Location, } } @@ -786,7 +781,7 @@ func (a Analyzer) analyzeNetPortsUsage( } return &compiler.Error{ - Err: fmt.Errorf( + Message: fmt.Sprintf( "Unused node inport: %v:%v", nodeName, inportName, @@ -808,7 +803,7 @@ func (a Analyzer) analyzeNetPortsUsage( if portUsed { if outportName == "err" && nodes[nodeName].ErrGuard { return &compiler.Error{ - Err: fmt.Errorf( + Message: fmt.Sprintf( "if node has error guard '?' it's ':err' outport must not be explicitly used in the network: %v", nodeName, ), @@ -823,7 +818,7 @@ func (a Analyzer) analyzeNetPortsUsage( if outportName == "err" && !nodes[nodeName].ErrGuard { return &compiler.Error{ - Err: fmt.Errorf("unhandled error: %v:err", nodeName), + Message: fmt.Sprintf("unhandled error: %v:err", nodeName), Location: &scope.Location, Meta: &port.Meta, } @@ -836,7 +831,7 @@ func (a Analyzer) analyzeNetPortsUsage( } return &compiler.Error{ - Err: fmt.Errorf("All node's outports are unused: %v", nodeName), + Message: fmt.Sprintf("All node's outports are unused: %v", nodeName), Location: &scope.Location, Meta: &nodeIface.iface.Meta, } @@ -860,7 +855,7 @@ func (a Analyzer) analyzeNetPortsUsage( for i := uint8(0); i <= maxSlot; i++ { if _, ok := usedSlots[i]; !ok { return &compiler.Error{ - Err: fmt.Errorf( + Message: fmt.Sprintf( "array inport '%s:%s' is used incorrectly: slot %d is missing", nodeName, portName, @@ -887,7 +882,7 @@ func (a Analyzer) analyzeNetPortsUsage( for i := uint8(0); i <= maxSlot; i++ { if _, ok := usedSlots[i]; !ok { return &compiler.Error{ - Err: fmt.Errorf( + Message: fmt.Sprintf( "array outport '%s:%s' is used incorrectly: slot %d is missing", nodeName, portName, @@ -912,7 +907,7 @@ func (a Analyzer) getReceiverPortType( ) (ts.Expr, bool, *compiler.Error) { if receiverSide.Node == "in" { return ts.Expr{}, false, &compiler.Error{ - Err: errors.New("Flow cannot read from self inport"), + Message: "Flow cannot read from self inport", Location: &scope.Location, Meta: &receiverSide.Meta, } @@ -924,7 +919,7 @@ func (a Analyzer) getReceiverPortType( outport, ok := outports[receiverSide.Port] if !ok { return ts.Expr{}, false, &compiler.Error{ - Err: fmt.Errorf("Referenced inport not found in flow's interface: %v", receiverSide.Port), + Message: fmt.Sprintf("Referenced inport not found in flow's interface: %v", receiverSide.Port), Location: &scope.Location, Meta: &receiverSide.Meta, } @@ -937,7 +932,7 @@ func (a Analyzer) getReceiverPortType( ) if err != nil { return ts.Expr{}, false, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &receiverSide.Meta, } @@ -966,7 +961,7 @@ func (a Analyzer) getNodeInportType( node, ok := nodes[portAddr.Node] if !ok { return ts.Expr{}, false, &compiler.Error{ - Err: fmt.Errorf("Node not found '%v'", portAddr.Node), + Message: fmt.Sprintf("Node not found '%v'", portAddr.Node), Location: &scope.Location, Meta: &portAddr.Meta, } @@ -975,7 +970,7 @@ func (a Analyzer) getNodeInportType( nodeIface, ok := nodesIfaces[portAddr.Node] if !ok { return ts.Expr{}, false, &compiler.Error{ - Err: fmt.Errorf("Referenced node not found: %v", portAddr.Node), + Message: fmt.Sprintf("Referenced node not found: %v", portAddr.Node), Location: &scope.Location, Meta: &portAddr.Meta, } @@ -989,7 +984,7 @@ func (a Analyzer) getNodeInportType( nodeIface.iface.TypeParams.Params, portAddr, node, - scope.WithLocation(nodeIface.location), + scope.Relocate(nodeIface.location), ) if aerr != nil { return ts.Expr{}, false, compiler.Error{ @@ -1012,7 +1007,7 @@ func (a Analyzer) getResolvedPortType( if portAddr.Port == "" { if len(ports) > 1 { return ts.Expr{}, false, &compiler.Error{ - Err: fmt.Errorf("node '%v' has multiple ports but no port name", portAddr.Node), + Message: fmt.Sprintf("node '%v' has multiple ports but no port name", portAddr.Node), Location: &scope.Location, Meta: &portAddr.Meta, } @@ -1027,7 +1022,7 @@ func (a Analyzer) getResolvedPortType( port, ok := ports[portAddr.Port] if !ok { return ts.Expr{}, false, &compiler.Error{ - Err: fmt.Errorf( + Message: fmt.Sprintf( "Port not found `%v`", portAddr, ), @@ -1055,7 +1050,7 @@ func (a Analyzer) getResolvedPortType( ) if err != nil { return ts.Expr{}, false, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &port.Meta, } @@ -1100,7 +1095,7 @@ func (a Analyzer) getSenderSideType( if len(senderSide.StructSelector) > 0 { if len(prevChainLink) != 1 { return src.ConnectionSender{}, ts.Expr{}, false, &compiler.Error{ - Err: errors.New("fan-in with struct selectors is not supported"), + Message: "fan-in with struct selectors is not supported", Location: &scope.Location, Meta: &senderSide.Meta, } @@ -1124,11 +1119,10 @@ func (a Analyzer) getSenderSideType( scope, ) if err != nil { - return src.ConnectionSender{}, ts.Expr{}, false, &compiler.Error{ - Err: err, + return src.ConnectionSender{}, ts.Expr{}, false, compiler.Error{ Location: &scope.Location, Meta: &senderSide.Meta, - } + }.Wrap(err) } return senderSide, lastFieldType, false, nil @@ -1158,7 +1152,7 @@ func (a Analyzer) getPortSenderType( ) (ts.Expr, bool, *compiler.Error) { if senderSidePortAddr.Node == "out" { return ts.Expr{}, false, &compiler.Error{ - Err: errors.New("Flow cannot read from self outport"), + Message: "Flow cannot read from self outport", Location: &scope.Location, Meta: &senderSidePortAddr.Meta, } @@ -1170,7 +1164,7 @@ func (a Analyzer) getPortSenderType( inport, ok := inports[senderSidePortAddr.Port] if !ok { return ts.Expr{}, false, &compiler.Error{ - Err: fmt.Errorf("Referenced inport not found in flow's interface: %v", senderSidePortAddr.Port), + Message: fmt.Sprintf("Referenced inport not found in flow's interface: %v", senderSidePortAddr.Port), Location: &scope.Location, Meta: &senderSidePortAddr.Meta, } @@ -1183,7 +1177,7 @@ func (a Analyzer) getPortSenderType( ) if err != nil { return ts.Expr{}, false, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &senderSidePortAddr.Meta, } @@ -1214,7 +1208,7 @@ func (a Analyzer) getConstSenderType( if constSender.Value.Message == nil { return src.Const{}, ts.Expr{}, &compiler.Error{ - Err: errors.New("Literal sender type is empty"), + Message: "Literal sender type is empty", Location: &scope.Location, Meta: &constSender.Meta, } @@ -1226,7 +1220,7 @@ func (a Analyzer) getConstSenderType( ) if err != nil { return src.Const{}, ts.Expr{}, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &constSender.Value.Message.Meta, } @@ -1234,7 +1228,7 @@ func (a Analyzer) getConstSenderType( if err := a.validateLiteralSender(resolvedExpr); err != nil { return src.Const{}, ts.Expr{}, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &constSender.Value.Message.Meta, } @@ -1285,7 +1279,7 @@ func (a Analyzer) getNodeOutportType( node, ok := nodes[portAddr.Node] if !ok { return ts.Expr{}, false, &compiler.Error{ - Err: fmt.Errorf("Referenced node not found: %v", portAddr.Node), + Message: fmt.Sprintf("Referenced node not found: %v", portAddr.Node), Location: &scope.Location, Meta: &portAddr.Meta, } @@ -1294,7 +1288,7 @@ func (a Analyzer) getNodeOutportType( nodeIface, ok := nodesIfaces[portAddr.Node] if !ok { return ts.Expr{}, false, &compiler.Error{ - Err: fmt.Errorf("Referenced node not found: %v", portAddr.Node), + Message: fmt.Sprintf("Referenced node not found: %v", portAddr.Node), Location: &scope.Location, Meta: &portAddr.Meta, } @@ -1305,7 +1299,7 @@ func (a Analyzer) getNodeOutportType( nodeIface.iface.TypeParams.Params, portAddr, node, - scope.WithLocation(nodeIface.location), + scope.Relocate(nodeIface.location), ) } @@ -1313,7 +1307,7 @@ func (a Analyzer) getResolvedConstTypeByRef(ref core.EntityRef, scope src.Scope) entity, location, err := scope.Entity(ref) if err != nil { return ts.Expr{}, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &ref.Meta, } @@ -1321,7 +1315,7 @@ func (a Analyzer) getResolvedConstTypeByRef(ref core.EntityRef, scope src.Scope) if entity.Kind != src.ConstEntity { return ts.Expr{}, &compiler.Error{ - Err: fmt.Errorf("%w: %v", errors.New("Entity found but is not constant"), entity.Kind), + Message: fmt.Sprintf("%w: %v", errors.New("Entity found but is not constant"), entity.Kind), Location: &location, Meta: entity.Meta(), } @@ -1338,12 +1332,12 @@ func (a Analyzer) getResolvedConstTypeByRef(ref core.EntityRef, scope src.Scope) return expr, nil } - scope = scope.WithLocation(location) + scope = scope.Relocate(location) resolvedExpr, err := a.resolver.ResolveExpr(entity.Const.TypeExpr, scope) if err != nil { return ts.Expr{}, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &entity.Const.Value.Message.Meta, } @@ -1363,7 +1357,7 @@ func (a Analyzer) getSelectorsSenderType( if senderType.Lit == nil || senderType.Lit.Struct == nil { return ts.Expr{}, &compiler.Error{ - Err: fmt.Errorf("Type not struct: %v", senderType.String()), + Message: fmt.Sprintf("Type not struct: %v", senderType.String()), Location: &scope.Location, } } @@ -1372,7 +1366,7 @@ func (a Analyzer) getSelectorsSenderType( fieldType, ok := senderType.Lit.Struct[curField] if !ok { return ts.Expr{}, &compiler.Error{ - Err: fmt.Errorf("struct field '%v' not found", curField), + Message: fmt.Sprintf("struct field '%v' not found", curField), Location: &scope.Location, } } @@ -1407,7 +1401,7 @@ func (a Analyzer) getChainHeadType( } return ts.Expr{}, &compiler.Error{ - Err: errors.New("Chained connection must start with port address or range expression"), + Message: "Chained connection must start with port address or range expression", Location: &scope.Location, Meta: &chainHead.Meta, } diff --git a/internal/compiler/analyzer/nodes.go b/internal/compiler/analyzer/nodes.go index c1fc8631..e843c2ee 100644 --- a/internal/compiler/analyzer/nodes.go +++ b/internal/compiler/analyzer/nodes.go @@ -1,7 +1,6 @@ package analyzer import ( - "errors" "fmt" "github.com/nevalang/neva/internal/compiler" @@ -63,7 +62,7 @@ func (a Analyzer) analyzeNode( nodeEntity, location, err := scope.Entity(node.EntityRef) if err != nil { return src.Node{}, foundInterface{}, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &node.Meta, } @@ -72,7 +71,7 @@ func (a Analyzer) analyzeNode( if nodeEntity.Kind != src.ComponentEntity && nodeEntity.Kind != src.InterfaceEntity { return src.Node{}, foundInterface{}, &compiler.Error{ - Err: fmt.Errorf("Node can only refer to flows or interfaces: %v", nodeEntity.Kind), + Message: fmt.Sprintf("Node can only refer to flows or interfaces: %v", nodeEntity.Kind), Location: &location, Meta: nodeEntity.Meta(), } @@ -81,7 +80,7 @@ func (a Analyzer) analyzeNode( bindDirectiveArgs, usesBindDirective := node.Directives[compiler.BindDirective] if usesBindDirective && len(bindDirectiveArgs) != 1 { return src.Node{}, foundInterface{}, &compiler.Error{ - Err: errors.New("Node with #bind directive must provide exactly one argument"), + Message: "Node with #bind directive must provide exactly one argument", Location: &location, Meta: nodeEntity.Meta(), } @@ -96,7 +95,7 @@ func (a Analyzer) analyzeNode( ) if err != nil { return src.Node{}, foundInterface{}, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &location, Meta: &node.Meta, } @@ -112,7 +111,7 @@ func (a Analyzer) analyzeNode( ) if err != nil { return src.Node{}, foundInterface{}, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &location, Meta: &node.Meta, } @@ -133,14 +132,14 @@ func (a Analyzer) analyzeNode( if node.ErrGuard { if _, ok := flowIface.IO.Out["err"]; !ok { return src.Node{}, foundInterface{}, &compiler.Error{ - Err: errors.New("Guard is not allowed for nodes without 'err' output"), + Message: "Guard is not allowed for nodes without 'err' output", Location: &scope.Location, Meta: &node.Meta, } } if _, ok := nodeIface.IO.Out["err"]; !ok { return src.Node{}, foundInterface{}, &compiler.Error{ - Err: errors.New("Guard is not allowed for nodes without ':err' output"), + Message: "Guard is not allowed for nodes without ':err' output", Location: &scope.Location, Meta: &node.Meta, } @@ -166,7 +165,7 @@ func (a Analyzer) analyzeNode( scope, ); err != nil { return src.Node{}, foundInterface{}, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &node.Meta, } @@ -230,7 +229,7 @@ func (a Analyzer) getNodeInterface( if entity.Kind == src.InterfaceEntity { if usesBindDirective { return src.Interface{}, &compiler.Error{ - Err: errors.New("Interface node cannot use #bind directive"), + Message: "Interface node cannot use #bind directive", Location: &location, Meta: entity.Meta(), } @@ -238,7 +237,7 @@ func (a Analyzer) getNodeInterface( if node.Deps != nil { return src.Interface{}, &compiler.Error{ - Err: errors.New("Only flow node can have dependency injection"), + Message: "Only flow node can have dependency injection", Location: &location, Meta: entity.Meta(), } @@ -251,9 +250,7 @@ func (a Analyzer) getNodeInterface( if usesBindDirective && !hasExternDirective { return src.Interface{}, &compiler.Error{ - Err: errors.New( - "Node can't use #bind if it isn't instantiated with the flow that use #extern", - ), + Message: "Node can't use #bind if it isn't instantiated with the flow that use #extern", Location: &location, Meta: entity.Meta(), } @@ -261,9 +258,7 @@ func (a Analyzer) getNodeInterface( if len(externArgs) > 1 && len(resolvedNodeArgs) != 1 { return src.Interface{}, &compiler.Error{ - Err: errors.New( - "Flow that use #extern directive with > 1 argument, must have exactly one type-argument for overloading", - ), + Message: "Flow that use #extern directive with > 1 argument, must have exactly one type-argument for overloading", Location: &location, Meta: entity.Meta(), } @@ -280,7 +275,7 @@ func (a Analyzer) getNodeInterface( if len(iface.IO.In) != 0 { return src.Interface{}, &compiler.Error{ - Err: errors.New("Flow that uses struct inports directive must have no defined inports"), + Message: "Flow that uses struct inports directive must have no defined inports", Location: &location, Meta: entity.Meta(), } @@ -288,7 +283,7 @@ func (a Analyzer) getNodeInterface( if len(iface.TypeParams.Params) != 1 { return src.Interface{}, &compiler.Error{ - Err: errors.New("Exactly one type parameter expected"), + Message: "Exactly one type parameter expected", Location: &location, Meta: entity.Meta(), } @@ -297,7 +292,7 @@ func (a Analyzer) getNodeInterface( resolvedTypeParamConstr, err := a.resolver.ResolveExpr(iface.TypeParams.Params[0].Constr, scope) if err != nil { return src.Interface{}, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &location, Meta: entity.Meta(), } @@ -305,7 +300,7 @@ func (a Analyzer) getNodeInterface( if resolvedTypeParamConstr.Lit == nil || resolvedTypeParamConstr.Lit.Struct == nil { return src.Interface{}, &compiler.Error{ - Err: errors.New("Struct type expected"), + Message: "Struct type expected", Location: &location, Meta: entity.Meta(), } @@ -313,7 +308,7 @@ func (a Analyzer) getNodeInterface( if len(resolvedNodeArgs) != 1 { return src.Interface{}, &compiler.Error{ - Err: errors.New("Exactly one type argument expected"), + Message: "Exactly one type argument expected", Location: &location, Meta: entity.Meta(), } @@ -322,7 +317,7 @@ func (a Analyzer) getNodeInterface( resolvedNodeArg, err := a.resolver.ResolveExpr(resolvedNodeArgs[0], scope) if err != nil { return src.Interface{}, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &location, Meta: entity.Meta(), } @@ -330,7 +325,7 @@ func (a Analyzer) getNodeInterface( if resolvedNodeArg.Lit == nil || resolvedNodeArg.Lit.Struct == nil { return src.Interface{}, &compiler.Error{ - Err: errors.New("Struct argument expected"), + Message: "Struct argument expected", Location: &location, Meta: entity.Meta(), } diff --git a/internal/compiler/analyzer/semver.go b/internal/compiler/analyzer/semver.go index 07e3e2c8..acbad78f 100644 --- a/internal/compiler/analyzer/semver.go +++ b/internal/compiler/analyzer/semver.go @@ -16,14 +16,14 @@ func (a Analyzer) semverCheck(mod src.Module, modRef src.ModuleRef) *compiler.Er moduleVersion, semverErr := semver.NewVersion(mod.Manifest.LanguageVersion) if semverErr != nil { return &compiler.Error{ - Err: fmt.Errorf("%w: %v", ErrCompilerVersion, semverErr), + Message: fmt.Sprintf("%w: %v", ErrCompilerVersion, semverErr), } } compilerVersion, semverErr := semver.NewVersion(pkg.Version) if semverErr != nil { return &compiler.Error{ - Err: fmt.Errorf("%w: %v", ErrCompilerVersion, semverErr), + Message: fmt.Sprintf("%w: %v", ErrCompilerVersion, semverErr), } } @@ -32,7 +32,7 @@ func (a Analyzer) semverCheck(mod src.Module, modRef src.ModuleRef) *compiler.Er // and vice versa if got major less than ours if moduleVersion.Major() != compilerVersion.Major() { return &compiler.Error{ - Err: fmt.Errorf( + Message: fmt.Sprintf( "%w: different majors: module %v wants %v while current is %v", ErrCompilerVersion, modRef, mod.Manifest.LanguageVersion, pkg.Version, @@ -44,7 +44,7 @@ func (a Analyzer) semverCheck(mod src.Module, modRef src.ModuleRef) *compiler.Er // so we make sure module don't want any features we don't have if moduleVersion.Minor() > compilerVersion.Minor() { return &compiler.Error{ - Err: fmt.Errorf( + Message: fmt.Sprintf( "%w: incompatible minors: module %v wants %v while current is %v", ErrCompilerVersion, modRef, mod.Manifest.LanguageVersion, pkg.Version, @@ -68,7 +68,7 @@ func (a Analyzer) semverCheck(mod src.Module, modRef src.ModuleRef) *compiler.Er // but it's not ok if module a wants some patch we don't really have if moduleVersion.Patch() > compilerVersion.Patch() { return &compiler.Error{ - Err: fmt.Errorf( + Message: fmt.Sprintf( "%w: incompatible patch: module %v wants %v while current is %v", ErrCompilerVersion, modRef, mod.Manifest.LanguageVersion, pkg.Version, diff --git a/internal/compiler/analyzer/type.go b/internal/compiler/analyzer/type.go index 97e20aed..0ca8eea2 100644 --- a/internal/compiler/analyzer/type.go +++ b/internal/compiler/analyzer/type.go @@ -1,8 +1,6 @@ package analyzer import ( - "errors" - "github.com/nevalang/neva/internal/compiler" src "github.com/nevalang/neva/internal/compiler/sourcecode" "github.com/nevalang/neva/internal/compiler/sourcecode/core" @@ -17,7 +15,7 @@ func (a Analyzer) analyzeTypeDef(def ts.Def, scope src.Scope, params analyzeType if !params.allowEmptyBody && def.BodyExpr == nil { meta := def.Meta.(core.Meta) //nolint:forcetypeassert return ts.Def{}, &compiler.Error{ - Err: errors.New("Type definition must have non-empty body"), + Message: "Type definition must have non-empty body", Location: &scope.Location, Meta: &meta, } @@ -29,7 +27,7 @@ func (a Analyzer) analyzeTypeDef(def ts.Def, scope src.Scope, params analyzeType if err != nil { meta := def.Meta.(core.Meta) //nolint:forcetypeassert return ts.Def{}, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &meta, } @@ -46,7 +44,7 @@ func (a Analyzer) analyzeTypeExpr(expr ts.Expr, scope src.Scope) (ts.Expr, *comp if err != nil { meta := expr.Meta.(core.Meta) //nolint:forcetypeassert return ts.Expr{}, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &meta, } @@ -64,7 +62,7 @@ func (a Analyzer) analyzeTypeParams( resolvedParams, _, err := a.resolver.ResolveParams(params, scope) if err != nil { return nil, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, } } diff --git a/internal/compiler/compiler.go b/internal/compiler/compiler.go index 0753fcb5..d1670690 100644 --- a/internal/compiler/compiler.go +++ b/internal/compiler/compiler.go @@ -48,7 +48,7 @@ type FrontendResult struct { func (f Frontend) Process(ctx context.Context, main string) (FrontendResult, *Error) { raw, root, err := f.builder.Build(ctx, main) if err != nil { - return FrontendResult{}, Error{Location: &sourcecode.Location{PkgName: main}}.Wrap(err) + return FrontendResult{}, Error{Location: &sourcecode.Location{Package: main}}.Wrap(err) } parsedMods, err := f.parser.ParseModules(raw.Modules) diff --git a/internal/compiler/desugarer/component.go b/internal/compiler/desugarer/component.go index f2f6f7c1..e1b59c71 100644 --- a/internal/compiler/desugarer/component.go +++ b/internal/compiler/desugarer/component.go @@ -1,7 +1,6 @@ package desugarer import ( - "errors" "maps" "slices" @@ -9,10 +8,6 @@ import ( src "github.com/nevalang/neva/internal/compiler/sourcecode" ) -var ErrConstSenderEntityKind = errors.New( - "Entity that is used as a const reference in flow's network must be of kind constant", -) - type handleComponentResult struct { desugaredFlow src.Component virtualEntities map[string]src.Entity diff --git a/internal/compiler/desugarer/const_sender.go b/internal/compiler/desugarer/const_sender.go index c05f17c2..a5e587d9 100644 --- a/internal/compiler/desugarer/const_sender.go +++ b/internal/compiler/desugarer/const_sender.go @@ -61,10 +61,7 @@ func (d Desugarer) handleConstRefSender( constTypeExpr, err := d.getConstTypeByRef(ref, scope) if err != nil { return src.PortAddr{}, compiler.Error{ - Err: fmt.Errorf( - "Unable to get constant type by reference '%v'", - ref, - ), + Message: fmt.Sprintf("Unable to get constant type by reference '%v'", ref), Location: &scope.Location, Meta: &ref.Meta, }.Wrap(err) @@ -97,7 +94,7 @@ func (d Desugarer) getConstTypeByRef(ref core.EntityRef, scope src.Scope) (ts.Ex entity, _, err := scope.Entity(ref) if err != nil { return ts.Expr{}, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &ref.Meta, } @@ -105,7 +102,10 @@ func (d Desugarer) getConstTypeByRef(ref core.EntityRef, scope src.Scope) (ts.Ex if entity.Kind != src.ConstEntity { return ts.Expr{}, &compiler.Error{ - Err: fmt.Errorf("%w: %v", ErrConstSenderEntityKind, entity.Kind), + Message: fmt.Sprintf( + "Entity that is used as a const reference in flow's network must be of kind constant: %v", + entity.Kind, + ), Location: &scope.Location, Meta: entity.Meta(), } diff --git a/internal/compiler/desugarer/desugarer.go b/internal/compiler/desugarer/desugarer.go index 2673cb05..4fd5b6d8 100644 --- a/internal/compiler/desugarer/desugarer.go +++ b/internal/compiler/desugarer/desugarer.go @@ -18,7 +18,7 @@ func (d Desugarer) Desugar(build src.Build) (src.Build, *compiler.Error) { if err != nil { return src.Build{}, compiler.Error{ - Location: &src.Location{ModRef: modRef}, + Location: &src.Location{Module: modRef}, }.Wrap(err) } desugaredMods[modRef] = desugaredMod @@ -63,15 +63,15 @@ func (d Desugarer) desugarModule( scope := src.Scope{ Build: build, // it's important to patch build before desugar package so we can resolve references to std Location: src.Location{ - ModRef: modRef, - PkgName: pkgName, + Module: modRef, + Package: pkgName, }, } desugaredPkg, err := d.desugarPkg(pkg, scope) if err != nil { return src.Module{}, compiler.Error{ - Location: &src.Location{PkgName: pkgName}, + Location: &src.Location{Package: pkgName}, }.Wrap(err) } @@ -88,16 +88,16 @@ func (d Desugarer) desugarPkg(pkg src.Package, scope src.Scope) (src.Package, *c desugaredPkgs := make(src.Package, len(pkg)) for fileName, file := range pkg { - newScope := scope.WithLocation(src.Location{ - ModRef: scope.Location.ModRef, - PkgName: scope.Location.PkgName, - FileName: fileName, + newScope := scope.Relocate(src.Location{ + Module: scope.Location.Module, + Package: scope.Location.Package, + Filename: fileName, }) desugaredFile, err := d.desugarFile(file, newScope) if err != nil { return nil, compiler.Error{ - Location: &src.Location{FileName: fileName}, + Location: &src.Location{Filename: fileName}, }.Wrap(err) } diff --git a/internal/compiler/desugarer/network.go b/internal/compiler/desugarer/network.go index 17a085f9..218fc169 100644 --- a/internal/compiler/desugarer/network.go +++ b/internal/compiler/desugarer/network.go @@ -134,7 +134,7 @@ func (d Desugarer) desugarNormalConnection( ) if err != nil { return desugarConnectionResult{}, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &normConn.Meta, } @@ -231,7 +231,7 @@ func (d Desugarer) desugarSingleReceiver( firstInportName, err := getFirstInportName(scope, nodes, *receiver.PortAddr) if err != nil { return desugarReceiverResult{}, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &receiver.Meta, } @@ -316,7 +316,7 @@ func (d Desugarer) desugarChainedConnection( var err error chainHeadPort, err = getFirstInportName(scope, nodes, *chainHead.PortAddr) if err != nil { - return desugarConnectionResult{}, &compiler.Error{Err: err} + return desugarConnectionResult{}, &compiler.Error{Message: err.Error()} } } default: @@ -493,7 +493,7 @@ func (d Desugarer) desugarSingleSender( if sender.PortAddr.Port == "" { firstOutportName, err := getFirstOutportName(scope, nodes, *sender.PortAddr) if err != nil { - return desugarSenderResult{}, &compiler.Error{Err: err} + return desugarSenderResult{}, &compiler.Error{Message: err.Error()} } portName = firstOutportName normConn.SenderSide = []src.ConnectionSender{ @@ -642,7 +642,7 @@ func getNodeIOByPortAddr( node, ok := nodes[portAddr.Node] if !ok { return src.IO{}, &compiler.Error{ - Err: fmt.Errorf("node '%s' not found", portAddr.Node), + Message: fmt.Sprintf("node '%s' not found", portAddr.Node), Location: &scope.Location, Meta: &portAddr.Meta, } @@ -651,7 +651,7 @@ func getNodeIOByPortAddr( entity, _, err := scope.Entity(node.EntityRef) if err != nil { return src.IO{}, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &portAddr.Meta, } @@ -929,7 +929,7 @@ func (d Desugarer) desugarFanIn( ) if err != nil { return nil, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &normConn.Meta, } diff --git a/internal/compiler/desugarer/node.go b/internal/compiler/desugarer/node.go index 3fe99614..4390dc4e 100644 --- a/internal/compiler/desugarer/node.go +++ b/internal/compiler/desugarer/node.go @@ -44,7 +44,7 @@ func (Desugarer) handleNode( entity, _, err := scope.Entity(node.EntityRef) if err != nil { return nil, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, Meta: &node.Meta, } diff --git a/internal/compiler/error.go b/internal/compiler/error.go index aee6af58..1b2e06cc 100644 --- a/internal/compiler/error.go +++ b/internal/compiler/error.go @@ -2,24 +2,18 @@ package compiler import ( "fmt" + "strings" src "github.com/nevalang/neva/internal/compiler/sourcecode" "github.com/nevalang/neva/internal/compiler/sourcecode/core" ) type Error struct { - Err error + Message string Location *src.Location Meta *core.Meta - child *Error -} -func NewError(err error, meta *core.Meta, location *src.Location) *Error { - return &Error{ - Err: err, - Meta: meta, - Location: location, - } + child *Error } func (e Error) Wrap(child *Error) *Error { @@ -27,31 +21,26 @@ func (e Error) Wrap(child *Error) *Error { return &e } -// FIXME: it doesn't make sense to wrap if we don't use anything from parent -func (e Error) unwrap() Error { - for e.child != nil { - e = *e.child - } - return e -} - -func (e Error) Error() string { - e = e.unwrap() - - hasErr := e.Err != nil - hasMeta := e.Meta != nil - hasLocation := e.Location != nil - - switch { - case hasLocation && hasMeta: - return fmt.Sprintf("%v:%v: %v", *e.Location, e.Meta.Start, e.Err) - case hasLocation: - return fmt.Sprintf("%v: %v", *e.Location, e.Err) - case hasMeta: - return fmt.Sprintf("%v: %v", e.Meta.Start, e.Err) - case hasErr: - return e.Err.Error() +func (e *Error) Error() string { + var builder strings.Builder + + current := e + for current != nil { + hasLocation := current.Location != nil + hasMeta := current.Meta != nil + + if hasLocation && hasMeta { + fmt.Fprintf(&builder, "%v:%v: %v\n", *current.Location, current.Meta.Start, current.Message) + } else if hasLocation { + fmt.Fprintf(&builder, "%v: %v\n", *current.Location, current.Message) + } else if hasMeta { + fmt.Fprintf(&builder, "%v: %v\n", current.Meta.Start, current.Message) + } else { + builder.WriteString(current.Message + "\n") + } + + current = current.child } - panic(e) + return builder.String() } diff --git a/internal/compiler/irgen/func.go b/internal/compiler/irgen/func.go index 0964cb1b..f925a9c1 100644 --- a/internal/compiler/irgen/func.go +++ b/internal/compiler/irgen/func.go @@ -46,14 +46,14 @@ func getConfigMsg(node src.Node, scope src.Scope) (*ir.Message, *compiler.Error) entity, location, err := scope.Entity(compiler.ParseEntityRef(args[0])) if err != nil { return nil, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, } } return getIRMsgBySrcRef( entity.Const.Value, - scope.WithLocation(location), + scope.Relocate(location), entity.Const.TypeExpr, ) } diff --git a/internal/compiler/irgen/irgen.go b/internal/compiler/irgen/irgen.go index fb767b21..8004af3e 100644 --- a/internal/compiler/irgen/irgen.go +++ b/internal/compiler/irgen/irgen.go @@ -36,9 +36,9 @@ func (g Generator) Generate( scope := src.Scope{ Build: build, Location: src.Location{ - ModRef: build.EntryModRef, - PkgName: mainPkgName, - FileName: "", + Module: build.EntryModRef, + Package: mainPkgName, + Filename: "", }, } @@ -89,7 +89,7 @@ func (g Generator) processNode( entity, location, err := scope.Entity(nodeCtx.node.EntityRef) if err != nil { return &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, } } @@ -102,7 +102,7 @@ func (g Generator) processNode( runtimeFuncRef, err := g.getFuncRef(component, nodeCtx.node.TypeArgs) if err != nil { return &compiler.Error{ - Err: err, + Message: err.Error(), Location: &location, Meta: &component.Meta, } @@ -112,7 +112,7 @@ func (g Generator) processNode( cfgMsg, err := getConfigMsg(nodeCtx.node, scope) if err != nil { return &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, } } @@ -127,7 +127,7 @@ func (g Generator) processNode( return nil } - newScope := scope.WithLocation(location) // only use new location if that's not builtin + newScope := scope.Relocate(location) // only use new location if that's not builtin // We use network as a source of true about how subnodes ports instead subnodes interface definitions. // We cannot rely on them because there's no information about how many array slots are used (in case of array ports). @@ -139,7 +139,7 @@ func (g Generator) processNode( ) if err != nil { return &compiler.Error{ - Err: err, + Message: err.Error(), Location: &newScope.Location, } } @@ -148,7 +148,7 @@ func (g Generator) processNode( nodePortsUsage, ok := subnodesPortsUsage[nodeName] if !ok { return &compiler.Error{ - Err: fmt.Errorf("node usage not found: %v", nodeName), + Message: fmt.Sprintf("node usage not found: %v", nodeName), Location: &location, Meta: &node.Meta, } @@ -169,11 +169,7 @@ func (g Generator) processNode( } if err := g.processNode(subNodeCtx, scopeToUse, result); err != nil { - return &compiler.Error{ - Err: fmt.Errorf("%w: node '%v'", err, nodeName), - Location: &location, - Meta: &component.Meta, - } + return err } } diff --git a/internal/compiler/irgen/message.go b/internal/compiler/irgen/message.go index 3eef386d..8a98701a 100644 --- a/internal/compiler/irgen/message.go +++ b/internal/compiler/irgen/message.go @@ -1,8 +1,6 @@ package irgen import ( - "errors" - "github.com/nevalang/neva/internal/compiler" "github.com/nevalang/neva/internal/compiler/ir" src "github.com/nevalang/neva/internal/compiler/sourcecode" @@ -18,11 +16,11 @@ func getIRMsgBySrcRef( entity, location, err := scope.Entity(*constant.Ref) if err != nil { return nil, &compiler.Error{ - Err: err, + Message: err.Error(), Location: &scope.Location, } } - return getIRMsgBySrcRef(entity.Const.Value, scope.WithLocation(location), typeExpr) + return getIRMsgBySrcRef(entity.Const.Value, scope.Relocate(location), typeExpr) } switch { @@ -102,7 +100,7 @@ func getIRMsgBySrcRef( } return nil, &compiler.Error{ - Err: errors.New("unknown msg type"), + Message: "unknown msg type", Location: &scope.Location, } } diff --git a/internal/compiler/parser/err_handler.go b/internal/compiler/parser/err_handler.go index c7d15b2f..b1533821 100644 --- a/internal/compiler/parser/err_handler.go +++ b/internal/compiler/parser/err_handler.go @@ -1,8 +1,6 @@ package parser import ( - "errors" - "github.com/antlr4-go/antlr/v4" "github.com/nevalang/neva/internal/compiler" "github.com/nevalang/neva/internal/compiler/sourcecode/core" @@ -21,7 +19,7 @@ func (c *CustomErrorListener) SyntaxError( e antlr.RecognitionException, ) { c.Errors = append(c.Errors, &compiler.Error{ - Err: errors.New(msg), + Message: msg, Meta: &core.Meta{ Start: core.Position{ Line: line, diff --git a/internal/compiler/parser/listener_helpers.go b/internal/compiler/parser/listener_helpers.go index f2927892..2c24abe3 100644 --- a/internal/compiler/parser/listener_helpers.go +++ b/internal/compiler/parser/listener_helpers.go @@ -1,7 +1,6 @@ package parser import ( - "errors" "fmt" "strconv" "strings" @@ -65,7 +64,7 @@ func parseTypeExpr(expr generated.ITypeExprContext) (ts.Expr, *compiler.Error) { v, err := parseTypeInstExpr(instExpr) if err != nil { return ts.Expr{}, &compiler.Error{ - Err: err, + Message: err.Error(), Meta: &core.Meta{ Text: expr.GetText(), Start: core.Position{ @@ -94,7 +93,7 @@ func parseTypeExpr(expr generated.ITypeExprContext) (ts.Expr, *compiler.Error) { result = v } else { return ts.Expr{}, &compiler.Error{ - Err: errors.New("Missing type expression"), + Message: "Missing type expression", Meta: &core.Meta{ Text: expr.GetText(), Start: core.Position{ @@ -187,7 +186,7 @@ func parseLitExpr(litExpr generated.ITypeLitExprContext) (*ts.Expr, *compiler.Er } return nil, &compiler.Error{ - Err: errors.New("Unknown literal type"), + Message: "Unknown literal type", Meta: &core.Meta{ Text: litExpr.GetText(), Start: core.Position{ @@ -271,7 +270,7 @@ func parseTypeInstExpr(instExpr generated.ITypeInstExprContext) (*ts.Expr, *comp parsedRef, err := parseEntityRef(instExpr.EntityRef()) if err != nil { return nil, &compiler.Error{ - Err: err, + Message: err.Error(), Meta: &core.Meta{ Text: instExpr.GetText(), Start: core.Position{ @@ -339,8 +338,8 @@ func parseEntityRef(expr generated.IEntityRefContext) (core.EntityRef, *compiler parts := strings.Split(expr.GetText(), ".") if len(parts) > 2 { return core.EntityRef{}, &compiler.Error{ - Err: fmt.Errorf("Invalid entity reference %v", expr.GetText()), - Meta: &meta, + Message: fmt.Sprintf("Invalid entity reference %v", expr.GetText()), + Meta: &meta, } } @@ -453,7 +452,7 @@ func parseNodes( parsedRef, err := parseEntityRef(nodeInst.EntityRef()) if err != nil { return nil, &compiler.Error{ - Err: err, + Message: err.Error(), Meta: &core.Meta{ Text: node.GetText(), Start: core.Position{ @@ -557,11 +556,10 @@ func parsePortAddr( expr.SinglePortAddr() == nil && expr.LonelySinglePortAddr() == nil && expr.LonelyArrPortAddr() == nil { - return src.PortAddr{}, compiler.NewError( - fmt.Errorf("Invalid port address %v", expr.GetText()), - &meta, - nil, - ) + return src.PortAddr{}, &compiler.Error{ + Message: fmt.Sprintf("Invalid port address %v", expr.GetText()), + Meta: &meta, + } } if expr.LonelyArrPortAddr() != nil { @@ -574,11 +572,10 @@ func parsePortAddr( 8, ) if err != nil { - return src.PortAddr{}, compiler.NewError( - err, - &meta, - nil, - ) + return src.PortAddr{}, &compiler.Error{ + Message: err.Error(), + Meta: &meta, + } } idxUint8 := uint8(idxUint) @@ -613,11 +610,10 @@ func parsePortAddr( 8, ) if err != nil { - return src.PortAddr{}, compiler.NewError( - err, - &meta, - nil, - ) + return src.PortAddr{}, &compiler.Error{ + Message: err.Error(), + Meta: &meta, + } } nodeName := fallbackNode @@ -678,7 +674,7 @@ func parsePrimitiveConstLiteral( boolVal := lit.Bool_().GetText() if boolVal != "true" && boolVal != "false" { return src.Const{}, &compiler.Error{ - Err: fmt.Errorf("Invalid boolean value %v", boolVal), + Message: fmt.Sprintf("Invalid boolean value %v", boolVal), Meta: &core.Meta{ Text: lit.GetText(), Start: core.Position{ @@ -700,7 +696,7 @@ func parsePrimitiveConstLiteral( parsedInt, err := strconv.ParseInt(lit.INT().GetText(), 10, 64) if err != nil { return src.Const{}, &compiler.Error{ - Err: err, + Message: err.Error(), Meta: &core.Meta{ Text: lit.GetText(), Start: core.Position{ @@ -725,7 +721,7 @@ func parsePrimitiveConstLiteral( parsedFloat, err := strconv.ParseFloat(lit.FLOAT().GetText(), 64) if err != nil { return src.Const{}, &compiler.Error{ - Err: err, + Message: err.Error(), Meta: &core.Meta{ Text: lit.GetText(), Start: core.Position{ @@ -802,7 +798,7 @@ func parseMessage( boolVal := constVal.Bool_().GetText() if boolVal != "true" && boolVal != "false" { return src.MsgLiteral{}, &compiler.Error{ - Err: fmt.Errorf("Invalid boolean value %v", boolVal), + Message: fmt.Sprintf("Invalid boolean value %v", boolVal), Meta: &core.Meta{ Text: constVal.GetText(), Start: core.Position{ @@ -821,7 +817,7 @@ func parseMessage( parsedInt, err := strconv.ParseInt(constVal.INT().GetText(), 10, 64) if err != nil { return src.MsgLiteral{}, &compiler.Error{ - Err: err, + Message: err.Error(), Meta: &core.Meta{ Text: constVal.GetText(), Start: core.Position{ @@ -843,7 +839,7 @@ func parseMessage( parsedFloat, err := strconv.ParseFloat(constVal.FLOAT().GetText(), 64) if err != nil { return src.MsgLiteral{}, &compiler.Error{ - Err: err, + Message: err.Error(), Meta: &core.Meta{ Text: constVal.GetText(), Start: core.Position{ @@ -1052,8 +1048,8 @@ func parseConstDef( parsedTypeExpr, err := parseTypeExpr(actx.TypeExpr()) if err != nil { return src.Entity{}, &compiler.Error{ - Err: err, - Meta: &meta, + Message: err.Error(), + Meta: &meta, } } @@ -1066,8 +1062,8 @@ func parseConstDef( parsedRef, err := parseEntityRef(entityRef) if err != nil { return src.Entity{}, &compiler.Error{ - Err: err, - Meta: &meta, + Message: err.Error(), + Meta: &meta, } } parsedConst.Value.Ref = &parsedRef @@ -1080,8 +1076,8 @@ func parseConstDef( parsedMsgLit, err := parseMessage(constLit) if err != nil { return src.Entity{}, &compiler.Error{ - Err: err, - Meta: &meta, + Message: err.Error(), + Meta: &meta, } } diff --git a/internal/compiler/parser/net.go b/internal/compiler/parser/net.go index c7d94d28..74c7bd76 100644 --- a/internal/compiler/parser/net.go +++ b/internal/compiler/parser/net.go @@ -1,7 +1,6 @@ package parser import ( - "errors" "fmt" "strconv" @@ -11,8 +10,6 @@ import ( "github.com/nevalang/neva/internal/compiler/sourcecode/core" ) -var ErrEmptyConnDef error = errors.New("Connection must be either normal or array bypass") - func parseConnections(actx generated.IConnDefListContext) ([]src.Connection, *compiler.Error) { allConnDefs := actx.AllConnDef() parsedConns := make([]src.Connection, 0, len(allConnDefs)) @@ -45,11 +42,10 @@ func parseConnection(connDef generated.IConnDefContext) (src.Connection, *compil arrBypassConn := connDef.ArrBypassConnDef() if normConn == nil && arrBypassConn == nil { - return src.Connection{}, compiler.NewError( - ErrEmptyConnDef, - &meta, - nil, - ) + return src.Connection{}, &compiler.Error{ + Message: "Connection must be either normal or array bypass", + Meta: &meta, + } } if arrBypassConn != nil { @@ -158,10 +154,8 @@ func parseSenderSide( if singleSender == nil && mulSenders == nil { return nil, &compiler.Error{ - Err: errors.New( - "Connection must have at least one sender side", - ), - Meta: &meta, + Message: "Connection must have at least one sender side", + Meta: &meta, } } @@ -176,10 +170,7 @@ func parseSenderSide( for _, senderSide := range toParse { parsedSide, err := parseNormConnSenderSide(senderSide) if err != nil { - return nil, &compiler.Error{ - Err: err, - Meta: &meta, - } + return nil, err } parsedSenders = append(parsedSenders, parsedSide) } @@ -214,11 +205,10 @@ func parseSingleReceiverSide( case portAddr != nil: return parsePortAddrReceiver(portAddr) default: - return src.ConnectionReceiver{}, compiler.NewError( - errors.New("missing receiver side"), - &meta, - nil, - ) + return src.ConnectionReceiver{}, &compiler.Error{ + Message: "missing receiver side", + Meta: &meta, + } } } @@ -266,8 +256,8 @@ func parseReceiverSide( return parseMultipleReceiverSides(multipleReceiverSide) default: return nil, &compiler.Error{ - Err: errors.New("missing receiver side"), - Meta: &meta, + Message: "missing receiver side", + Meta: &meta, } } } @@ -309,10 +299,7 @@ func parseDeferredConn( parsedConns, err := parseConnection(deferredConns.ConnDef()) if err != nil { - return src.ConnectionReceiver{}, &compiler.Error{ - Err: err, - Meta: &meta, - } + return src.ConnectionReceiver{}, err } return src.ConnectionReceiver{ @@ -338,7 +325,7 @@ func parseNormConnSenderSide( structSelectors == nil && ternaryExprSender == nil { return src.ConnectionSender{}, &compiler.Error{ - Err: errors.New("Sender side is missing in connection"), + Message: "Sender side is missing in connection", Meta: &core.Meta{ Text: senderSide.GetText(), Start: core.Position{ @@ -392,7 +379,7 @@ func parseNormConnSenderSide( from, err := strconv.ParseInt(fromText, 10, 64) if err != nil { return src.ConnectionSender{}, &compiler.Error{ - Err: fmt.Errorf("Invalid range 'from' value: %v", err), + Message: fmt.Sprintf("Invalid range 'from' value: %v", err), Meta: &core.Meta{ Text: rangeExprSender.GetText(), Start: core.Position{ @@ -414,7 +401,7 @@ func parseNormConnSenderSide( to, err := strconv.ParseInt(toText, 10, 64) if err != nil { return src.ConnectionSender{}, &compiler.Error{ - Err: fmt.Errorf("Invalid range 'to' value: %v", err), + Message: fmt.Sprintf("Invalid range 'to' value: %v", err), Meta: &core.Meta{ Text: rangeExprSender.GetText(), Start: core.Position{ diff --git a/internal/compiler/parser/parser.go b/internal/compiler/parser/parser.go index 9a0dd479..51b6b7fa 100644 --- a/internal/compiler/parser/parser.go +++ b/internal/compiler/parser/parser.go @@ -71,9 +71,9 @@ func (p Parser) ParseFiles( parsedFile, err := p.parseFile(fileBytes) if err != nil { err.Location = compiler.Pointer(src.Location{ - ModRef: modRef, - PkgName: pkgName, - FileName: fileName, + Module: modRef, + Package: pkgName, + Filename: fileName, }) return nil, err } @@ -119,7 +119,7 @@ func walkTree(listener antlr.ParseTreeListener, tree antlr.ParseTree) (err *comp if e := recover(); e != nil { if _, ok := e.(*compiler.Error); !ok { err = &compiler.Error{ - Err: fmt.Errorf( + Message: fmt.Sprintf( "%v: %v", e, string(debug.Stack()), diff --git a/internal/compiler/sourcecode/scope.go b/internal/compiler/sourcecode/scope.go index 3be3de6f..99e40630 100644 --- a/internal/compiler/sourcecode/scope.go +++ b/internal/compiler/sourcecode/scope.go @@ -15,7 +15,7 @@ type Scope struct { Build Build } -func (s Scope) WithLocation(location Location) Scope { +func (s Scope) Relocate(location Location) Scope { return Scope{ Location: location, Build: s.Build, @@ -23,20 +23,20 @@ func (s Scope) WithLocation(location Location) Scope { } type Location struct { - ModRef ModuleRef - PkgName string - FileName string + Module ModuleRef + Package string + Filename string } func (l Location) String() string { var s string - if l.ModRef.Path == "@" { - s = l.PkgName + if l.Module.Path == "@" { + s = l.Package } else { - s = filepath.Join(l.ModRef.String(), l.PkgName) + s = filepath.Join(l.Module.String(), l.Package) } - if l.FileName != "" { - s = filepath.Join(s, l.FileName+".neva") + if l.Filename != "" { + s = filepath.Join(s, l.Filename+".neva") } return s } @@ -57,7 +57,7 @@ func (s Scope) GetType(ref core.EntityRef) (ts.Def, ts.Scope, error) { return ts.Def{}, nil, err } - return entity.Type, s.WithLocation(location), nil + return entity.Type, s.Relocate(location), nil } func (s Scope) Entity(entityRef core.EntityRef) (Entity, Location, error) { @@ -65,23 +65,23 @@ func (s Scope) Entity(entityRef core.EntityRef) (Entity, Location, error) { } func (s Scope) entity(entityRef core.EntityRef) (Entity, Location, error) { - curMod, ok := s.Build.Modules[s.Location.ModRef] + curMod, ok := s.Build.Modules[s.Location.Module] if !ok { - return Entity{}, Location{}, fmt.Errorf("module not found: %v", s.Location.ModRef) + return Entity{}, Location{}, fmt.Errorf("module not found: %v", s.Location.Module) } - curPkg := curMod.Packages[s.Location.PkgName] + curPkg := curMod.Packages[s.Location.Package] if !ok { - return Entity{}, Location{}, fmt.Errorf("package not found: %v", s.Location.PkgName) + return Entity{}, Location{}, fmt.Errorf("package not found: %v", s.Location.Package) } if entityRef.Pkg == "" { // local reference (current package or builtin) entity, fileName, ok := curPkg.Entity(entityRef.Name) if ok { return entity, Location{ - ModRef: s.Location.ModRef, - PkgName: s.Location.PkgName, - FileName: fileName, + Module: s.Location.Module, + Package: s.Location.Package, + Filename: fileName, }, nil } @@ -100,15 +100,15 @@ func (s Scope) entity(entityRef core.EntityRef) (Entity, Location, error) { } return entity, Location{ - ModRef: stdModRef, - PkgName: "builtin", - FileName: fileName, + Module: stdModRef, + Package: "builtin", + Filename: fileName, }, nil } - curFile, ok := curPkg[s.Location.FileName] + curFile, ok := curPkg[s.Location.Filename] if !ok { - return Entity{}, Location{}, fmt.Errorf("file not found: %v", s.Location.FileName) + return Entity{}, Location{}, fmt.Errorf("file not found: %v", s.Location.Filename) } pkgImport, ok := curFile.Imports[entityRef.Pkg] @@ -121,7 +121,7 @@ func (s Scope) entity(entityRef core.EntityRef) (Entity, Location, error) { modRef ModuleRef ) if pkgImport.Module == "@" { - modRef = s.Location.ModRef // FIXME s.Location.ModRef is where we are now (e.g. std) + modRef = s.Location.Module // FIXME s.Location.ModRef is where we are now (e.g. std) mod = curMod } else { modRef = curMod.Manifest.Deps[pkgImport.Module] @@ -147,8 +147,8 @@ func (s Scope) entity(entityRef core.EntityRef) (Entity, Location, error) { } return entity, Location{ - ModRef: modRef, - PkgName: pkgImport.Package, - FileName: fileName, + Module: modRef, + Package: pkgImport.Package, + Filename: fileName, }, nil } diff --git a/internal/compiler/sourcecode/sourcecode.go b/internal/compiler/sourcecode/sourcecode.go index a3553c7a..c7f39f6a 100644 --- a/internal/compiler/sourcecode/sourcecode.go +++ b/internal/compiler/sourcecode/sourcecode.go @@ -36,14 +36,6 @@ func (mod Module) Entity(entityRef core.EntityRef) (entity Entity, filename stri return entity, filename, nil } -func (mod Module) Files(f func(file File, pkgName, fileName string)) { - for pkgName, pkg := range mod.Packages { - for fileName, file := range pkg { - f(file, pkgName, fileName) - } - } -} - type ModuleManifest struct { LanguageVersion string `json:"neva,omitempty" yaml:"neva,omitempty"` Deps map[string]ModuleRef `json:"deps,omitempty" yaml:"deps,omitempty"` @@ -74,15 +66,27 @@ func (p Package) Entity(entityName string) (entity Entity, filename string, ok b return Entity{}, "", false } -func (p Package) Entities(f func(entity Entity, entityName string, fileName string) error) error { - for fileName, file := range p { - for entityName, entity := range file.Entities { - if err := f(entity, entityName, fileName); err != nil { - return err +type EntitiesResult struct { + EntityName string + FileName string + Entity Entity +} + +// Entities iterates over all entities in the package using the range-func protocol. +func (pkg Package) Entities() func(func(EntitiesResult) bool) { + return func(yield func(EntitiesResult) bool) { + for fileName, file := range pkg { + for entityName, entity := range file.Entities { + if !yield(EntitiesResult{ + EntityName: entityName, + FileName: fileName, + Entity: entity, + }) { + return + } } } } - return nil } type File struct { @@ -153,7 +157,6 @@ type TypeParams struct { Meta core.Meta `json:"meta,omitempty"` } -// ToFrame is a utility function. func (t TypeParams) ToFrame() map[string]ts.Def { frame := make(map[string]ts.Def, len(t.Params)) for _, param := range t.Params {