Skip to content

Commit

Permalink
wip: analyzer
Browse files Browse the repository at this point in the history
  • Loading branch information
emil14 committed Sep 27, 2023
1 parent fd683e0 commit da13e97
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 50 deletions.
17 changes: 15 additions & 2 deletions internal/compiler/analyzer/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ import "github.com/nevalang/neva/internal/compiler/src"

type Analyzer struct{}

func (a Analyzer) Analyze(prog src.Program) (src.File, error) {
return src.File{}, nil
func (a Analyzer) Analyze(prog src.Program) (src.Program, error) {
if prog == nil {
panic("analyzer: nil program")
}

mainPkg, ok := prog["main"]
if !ok {
panic("analyzer: no main package")
}

if err := a.mainSpecificPkgValidation(mainPkg, prog); err != nil {
panic(err)
}

return nil, nil
}
88 changes: 88 additions & 0 deletions internal/compiler/analyzer/main_component.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package analyzer

import (
"errors"
"fmt"

"github.com/nevalang/neva/internal/compiler/src"
)

var (
ErrMainComponentWithTypeParams = errors.New("main component can't have type parameters")
ErrMainComponentNodes = errors.New("something wrong with main component's nodes")
ErrEntityNotFoundByNodeRef = errors.New("entity not found by node ref")
ErrMainComponentInportsCount = errors.New("main component must have one inport")
ErrMainComponentOutportsCount = errors.New("main component must have exactly one outport")
ErrMainComponentWithoutEnterInport = errors.New("main component must have 'enter' inport")
ErrMainComponentWithoutExitOutport = errors.New("main component must have 'exit' outport")
ErrMainPortIsArray = errors.New("main component's ports cannot not be arrays")
ErrMainComponentPortTypeNotAny = errors.New("main component's ports must be of type any")
ErrMainComponentNodeNotComponent = errors.New("main component's nodes must be components only")
)

func (a Analyzer) analyzeMainComponent(cmp src.Component, pkg src.Package, pkgs map[string]src.Package) error { //nolint:funlen,unparam,lll
if len(cmp.Interface.Params) != 0 {
return fmt.Errorf("%w: %v", ErrMainComponentWithTypeParams, cmp.Interface.Params)
}

if err := a.analyzeMainComponentIO(cmp.Interface.IO); err != nil {
return fmt.Errorf("main component io: %w", err)
}

if err := a.analyzeMainComponentNodes(cmp.Nodes, pkg, pkgs); err != nil {
return fmt.Errorf("%w: %v", ErrMainComponentNodes, err)
}

return nil
}

func (a Analyzer) analyzeMainComponentIO(io src.IO) error {
if len(io.Out) != 1 {
return fmt.Errorf("%w: %v", ErrMainComponentOutportsCount, io.Out)
}

if len(io.In) != 1 {
return fmt.Errorf("%w: %v", ErrMainComponentInportsCount, io.In)
}

enterInport, ok := io.In["enter"]
if !ok {
return ErrMainComponentWithoutEnterInport
}

if enterInport.IsArr {
return ErrMainPortIsArray
}

if enterInport.Type != nil {
return ErrMainComponentPortTypeNotAny
}

exitInport, ok := io.In["exit"]
if !ok {
return ErrMainComponentWithoutExitOutport
}

if exitInport.IsArr {
return ErrMainPortIsArray
}

if exitInport.Type != nil {
return ErrMainComponentPortTypeNotAny
}

return nil
}

func (Analyzer) analyzeMainComponentNodes(nodes map[string]src.Node, pkg src.Package, prog src.Program) error {
for _, node := range nodes {
nodeEntity, err := prog.Entity(node.EntityRef)
if err != nil {
return fmt.Errorf("%w: %v", ErrEntityNotFoundByNodeRef, node.EntityRef)
}
if nodeEntity.Kind != src.ComponentEntity {
return fmt.Errorf("%w: %v", ErrMainComponentNodeNotComponent, node.EntityRef)
}
}
return nil
}
28 changes: 28 additions & 0 deletions internal/compiler/analyzer/main_pkg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package analyzer

import (
"fmt"

"github.com/nevalang/neva/internal/compiler/src"
)

func (a Analyzer) mainSpecificPkgValidation(pkg src.Package, pkgs map[string]src.Package) error {
entityMain, ok := pkg.Entity("main")
if !ok {
panic("analyzer: no main entity")
}

if entityMain.Kind != src.ComponentEntity {
panic("analyzer: main entity is not a component")
}

if entityMain.Exported {
panic("analyzer: main entity is exported")
}

if err := a.analyzeMainComponent(entityMain.Component, pkg, pkgs); err != nil {
panic(fmt.Errorf("analyzer: %w", err))
}

return nil
}
4 changes: 2 additions & 2 deletions internal/compiler/parser/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (s *treeShapeListener) EnterTypeDef(actx *generated.TypeDefContext) {
Kind: src.TypeEntity,
Type: ts.Def{
Params: parseTypeParams(actx.TypeParams()),
BodyExpr: parseTypeExpr(actx.TypeExpr()),
BodyExpr: *parseTypeExpr(actx.TypeExpr()),
},
}
s.file.Entities[name] = result
Expand All @@ -51,7 +51,7 @@ func (s *treeShapeListener) EnterConstDef(actx *generated.ConstDefContext) {
name := actx.IDENTIFIER().GetText()
typeExpr := parseTypeExpr(actx.TypeExpr())
constVal := actx.ConstVal()
val := src.ConstValue{TypeExpr: typeExpr}
val := src.ConstValue{TypeExpr: *typeExpr}

//nolint:nosnakecase
switch {
Expand Down
17 changes: 7 additions & 10 deletions internal/compiler/parser/listener_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,20 @@ func parseTypeParams(params generated.ITypeParamsContext) []ts.Param {
parsedParamExpr := parseTypeExpr(typeParam.TypeExpr())
result = append(result, ts.Param{
Name: paramName,
Constr: parsedParamExpr,
Constr: *parsedParamExpr,
})
}

return result
}

func parseTypeExpr(expr generated.ITypeExprContext) ts.Expr {
func parseTypeExpr(expr generated.ITypeExprContext) *ts.Expr {
if expr == nil {
return ts.Expr{
Inst: ts.InstExpr{
Ref: "any",
},
}
return nil
}

return parseTypeInstExpr(expr.TypeInstExpr())
tmp := parseTypeInstExpr(expr.TypeInstExpr())
return &tmp
}

func parseTypeInstExpr(instExpr generated.ITypeInstExprContext) ts.Expr {
Expand All @@ -57,7 +54,7 @@ func parseTypeInstExpr(instExpr generated.ITypeInstExprContext) ts.Expr {
argExprs := args.AllTypeExpr()
parsedArgs := make([]ts.Expr, 0, len(argExprs))
for _, arg := range argExprs {
parsedArgs = append(parsedArgs, parseTypeExpr(arg))
parsedArgs = append(parsedArgs, *parseTypeExpr(arg))
}
result.Inst.Args = parsedArgs

Expand Down Expand Up @@ -186,7 +183,7 @@ func parseConcreteNode(nodeInst generated.IConcreteNodeInstContext) src.Node {
func parseTypeExprs(in []generated.ITypeExprContext) []ts.Expr {
result := make([]ts.Expr, 0, len(in))
for _, expr := range in {
result = append(result, parseTypeExpr(expr))
result = append(result, *parseTypeExpr(expr))
}
return result
}
Expand Down
43 changes: 31 additions & 12 deletions internal/compiler/src/src.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,44 @@ import (
ts "github.com/nevalang/neva/pkg/ts"
)

// Program represents executable set of source code packages. This abstraction does not exists after optimization.
type Program map[string]Package

// Package represents both source code package and program after optimization
var (
ErrPkgNotFound = fmt.Errorf("package not found")
ErrEntityNotFound = fmt.Errorf("entity not found")
)

func (p Program) Entity(entityRef EntityRef) (Entity, error) {
pkg, ok := p[entityRef.Pkg]
if !ok {
return Entity{}, ErrPkgNotFound
}
for _, file := range pkg {
entity, ok := file.Entities[entityRef.Name]
if ok {
return entity, nil
}
}
return Entity{}, ErrEntityNotFound
}

type Package map[string]File

// File represents source code file. Also can represent single-file package.
func (p Package) Entity(name string) (Entity, bool) {
for _, file := range p {
entity, ok := file.Entities[name]
if ok {
return entity, true
}
}
return Entity{}, false
}

type File struct {
Imports map[string]string
Entities map[string]Entity
}

// Entity is optionally exportable declaration of constant, type, interface or component.
type Entity struct {
Exported bool
Kind EntityKind
Expand Down Expand Up @@ -53,27 +78,23 @@ func (e EntityKind) String() string {
}
}

// Component represents unit of computation.
type Component struct {
Interface Interface
Nodes map[string]Node
Net []Connection // can't be map due to slice in key
}

// Interface is basically component's signature. It is used for dependency injection.
type Interface struct {
Params []ts.Param
IO IO
}

// Node is a component or interface instance.
type Node struct {
EntityRef EntityRef
TypeArgs []ts.Expr
ComponentDI map[string]Node
}

// EntityRef is a pointer to entity. Empty Pkg means local reference.
type EntityRef struct {
Pkg string
Name string
Expand All @@ -86,14 +107,13 @@ func (e EntityRef) String() string {
return fmt.Sprintf("%s.%s", e.Pkg, e.Name)
}

// Const is immutable value that is known at compile-time or reference to another constant.
type Const struct {
Ref *EntityRef
Value ConstValue
}

type ConstValue struct {
TypeExpr ts.Expr
TypeExpr ts.Expr // Cannot be any
Bool bool
Int int
Float float64
Expand All @@ -102,13 +122,12 @@ type ConstValue struct {
Map map[string]Const
}

// IO represents input and output ports of a component' interface.
type IO struct {
In, Out map[string]Port
}

type Port struct {
Type ts.Expr
Type *ts.Expr // empty means any
IsArr bool
}

Expand Down
Loading

0 comments on commit da13e97

Please sign in to comment.