Skip to content

Commit

Permalink
feat(desugarer): implement struct builder (untested)
Browse files Browse the repository at this point in the history
  • Loading branch information
emil14 committed Jan 10, 2024
1 parent 47a4db2 commit 0b68145
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 32 deletions.
2 changes: 1 addition & 1 deletion docs/design_principles.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Runtime won't do any checks after startup. The program that runtime consumes mus

Language must allow to implement everything without using of compiler directives.

**Compiler directives are unsafe** (analyzer won't validate their usage - that will make analyzer complicated) and thus must be used by language/stdlib developers or at _for users that know what they are doing_.
**Compiler directives are not always unsafe** (analyzer won't always validate their usage - that will make implementation more complicated) and thus must be used by language/stdlib developers or at _for users that know what they are doing_.

It's good for user to understand what compiler directives are and how syntax sugar use them under the hood though.

Expand Down
1 change: 1 addition & 0 deletions internal/compiler/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
const (
RuntimeFuncDirective src.Directive = "runtime_func"
RuntimeFuncMsgDirective src.Directive = "runtime_func_msg"
StructInports src.Directive = "struct_inports"
)

type (
Expand Down
85 changes: 77 additions & 8 deletions internal/compiler/desugarer/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@ package desugarer

import (
"errors"
"fmt"
"maps"

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

var ErrConstSenderEntityKind = errors.New("Entity that is used as a const reference in component's network must be of kind constant") //nolint:lll
var ErrConstSenderEntityKind = errors.New(
"Entity that is used as a const reference in component's network must be of kind constant",
)

type desugarComponentResult struct {
component src.Component // desugared component to replace
constsToInsert map[string]src.Const //nolint:lll // sometimes after desugaring component we need to insert some constants to the package
component src.Component // desugared component to replace
entitiesToInsert map[string]src.Entity //nolint:lll // sometimes after desugaring component we need to insert some entities to the file
}

func (d Desugarer) desugarComponent(
func (d Desugarer) desugarComponent( //nolint:funlen
component src.Component,
scope src.Scope,
) (desugarComponentResult, *compiler.Error) {
Expand All @@ -25,9 +28,68 @@ func (d Desugarer) desugarComponent(
}, nil
}

desugaredNodes := maps.Clone(component.Nodes)
if desugaredNodes == nil {
desugaredNodes = map[string]src.Node{}
entitiesToInsert := map[string]src.Entity{}

desugaredNodes := make(map[string]src.Node, len(component.Nodes))
for nodeName, node := range component.Nodes {
entity, _, err := scope.Entity(node.EntityRef)
if err != nil {
return desugarComponentResult{}, &compiler.Error{
Err: err,
Location: &scope.Location,
}
}

if entity.Kind != src.ComponentEntity {
desugaredNodes[nodeName] = node
continue
}

_, ok := entity.Component.Directives[compiler.StructInports]
if !ok {
desugaredNodes[nodeName] = node
continue
}

structFields := node.TypeArgs[0].Lit.Struct // must be resolved struct after analysis stage

inports := make(map[string]src.Port, len(structFields))
for fieldName, fieldTypeExpr := range structFields {
inports[fieldName] = src.Port{
TypeExpr: fieldTypeExpr,
}
}

outports := map[string]src.Port{
"v": {
TypeExpr: node.TypeArgs[0],
},
}

// create local variation of the struct builder component with inports corresponding to struct fields
localBuilderComponent := src.Component{
Interface: src.Interface{
IO: src.IO{In: inports, Out: outports}, // these ports gonna be used by irgen and then by runtime func
},
}

localBuilderName := fmt.Sprintf("__struct_builder_%v__", nodeName)

entitiesToInsert[localBuilderName] = src.Entity{
Kind: src.ComponentEntity,
Component: localBuilderComponent,
}

// finally replace component ref for this current node with the ref to newly created local builder variation
desugaredNodes[nodeName] = src.Node{
EntityRef: src.EntityRef{
Name: localBuilderName,
},
Directives: node.Directives,
TypeArgs: node.TypeArgs,
Deps: node.Deps,
Meta: node.Meta,
}
}

handleConnsResult, err := d.handleConns(component.Net, desugaredNodes, scope)
Expand All @@ -45,6 +107,13 @@ func (d Desugarer) desugarComponent(
desugaredNodes[voidResult.voidNodeName] = voidResult.voidNode
}

for name, constant := range handleConnsResult.extraConsts {
entitiesToInsert[name] = src.Entity{
Kind: src.ConstEntity,
Const: constant,
}
}

return desugarComponentResult{
component: src.Component{
Directives: component.Directives,
Expand All @@ -53,7 +122,7 @@ func (d Desugarer) desugarComponent(
Net: desugaredNet,
Meta: component.Meta,
},
constsToInsert: handleConnsResult.extraConsts,
entitiesToInsert: entitiesToInsert,
}, nil
}

Expand Down
19 changes: 1 addition & 18 deletions internal/compiler/desugarer/desugarer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,6 @@ import (
src "github.com/nevalang/neva/pkg/sourcecode"
)

// FIXME since desugarer operates before analyzer
// we must inject std mod dep and builtin import before we analyze
// i.e not in desugarer
// TODO consider changing the strategy to previous one where
// scope.Entity() know about stdlib and stuff
// it should be enough to use "builtin" in resolution
// but this could broke other virtual entities we create

// Desugarer does the following:
// 1. Replaces const ref senders with normal nodes that uses Const component with compiler directive;
// 2. Inserts void nodes and connections for every unused outport in the program;
Expand Down Expand Up @@ -166,17 +158,8 @@ func (d Desugarer) desugarEntity(entity src.Entity, scope src.Scope) (desugarEnt
return desugarEntityResult{}, compiler.Error{Meta: &entity.Component.Meta}.Merge(err)
}

entitiesToInsert := make(map[string]src.Entity, len(componentResult.constsToInsert))
for constName, constant := range componentResult.constsToInsert {
entitiesToInsert[constName] = src.Entity{
IsPublic: false,
Kind: src.ConstEntity,
Const: constant,
}
}

return desugarEntityResult{
entitiesToInsert: entitiesToInsert,
entitiesToInsert: componentResult.entitiesToInsert,
entity: src.Entity{
IsPublic: entity.IsPublic,
Kind: entity.Kind,
Expand Down
7 changes: 7 additions & 0 deletions internal/compiler/desugarer/struct_builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package desugarer

import "github.com/nevalang/neva/internal/compiler"

func (d Desugarer) handleStructBuilder() *compiler.Error {
return nil
}
6 changes: 3 additions & 3 deletions internal/pkgmanager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ type Parser interface {
}

func (p Manager) Build(ctx context.Context, workdir string) (compiler.RawBuild, error) {
entryMod, err := p.buildModule(ctx, workdir)
entryMod, err := p.BuildModule(ctx, workdir)
if err != nil {
return compiler.RawBuild{}, fmt.Errorf("build entry mod: %w", err)
}
entryMod.Manifest.Deps["std"] = src.ModuleRef{Path: "std", Version: "0.0.1"} // inject stdlib mod dep

stdMod, err := p.buildModule(ctx, p.stdLibLocation)
stdMod, err := p.BuildModule(ctx, p.stdLibLocation)
if err != nil {
return compiler.RawBuild{}, fmt.Errorf("build stdlib mod: %w", err)
}
Expand All @@ -49,7 +49,7 @@ func (p Manager) Build(ctx context.Context, workdir string) (compiler.RawBuild,
return compiler.RawBuild{}, fmt.Errorf("download dep: %w", err)
}

depMod, err := p.buildModule(ctx, depPath)
depMod, err := p.BuildModule(ctx, depPath)
if err != nil {
return compiler.RawBuild{}, fmt.Errorf("build entry mod: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/pkgmanager/mod.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/nevalang/neva/internal/compiler"
)

func (p Manager) buildModule(ctx context.Context, workdir string) (compiler.RawModule, error) {
func (p Manager) BuildModule(ctx context.Context, workdir string) (compiler.RawModule, error) {
manifest, err := p.retrieveManifest(workdir)
if err != nil {
return compiler.RawModule{}, fmt.Errorf("retrieve manifest: %w", err)
Expand Down
2 changes: 1 addition & 1 deletion std/builtin/builtin.neva
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ components {

#runtime_func(StructBuilder)
#struct_inports
StructBuilder<T struct {}> () (T)
pub StructBuilder<T struct {}> () (T)
}

0 comments on commit 0b68145

Please sign in to comment.