github.com/nevalang/neva@v0.23.1-0.20240507185603-7696a9bb8dda/internal/compiler/desugarer/desugarer.go (about)

     1  package desugarer
     2  
     3  import (
     4  	"maps"
     5  
     6  	"github.com/nevalang/neva/internal/compiler"
     7  	src "github.com/nevalang/neva/internal/compiler/sourcecode"
     8  	"github.com/nevalang/neva/pkg"
     9  )
    10  
    11  type Desugarer struct{}
    12  
    13  func (d Desugarer) Desugar(build src.Build) (src.Build, *compiler.Error) {
    14  	desugaredMods := make(map[src.ModuleRef]src.Module, len(build.Modules))
    15  
    16  	for modRef := range build.Modules {
    17  		desugaredMod, err := d.desugarModule(build, modRef)
    18  		if err != nil {
    19  			return src.Build{},
    20  				compiler.Error{
    21  					Location: &src.Location{
    22  						ModRef: modRef,
    23  					},
    24  				}.Wrap(err)
    25  		}
    26  		desugaredMods[modRef] = desugaredMod
    27  	}
    28  
    29  	return src.Build{
    30  		EntryModRef: build.EntryModRef,
    31  		Modules:     desugaredMods,
    32  	}, nil
    33  }
    34  
    35  func (d Desugarer) desugarModule(build src.Build, modRef src.ModuleRef) (src.Module, *compiler.Error) {
    36  	mod := build.Modules[modRef]
    37  
    38  	// create manifest copy with std module dependency
    39  	desugaredManifest := src.ModuleManifest{
    40  		LanguageVersion: mod.Manifest.LanguageVersion,
    41  		Deps:            make(map[string]src.ModuleRef, len(mod.Manifest.Deps)+1),
    42  	}
    43  	maps.Copy(desugaredManifest.Deps, mod.Manifest.Deps)
    44  	desugaredManifest.Deps["std"] = src.ModuleRef{Path: "std", Version: pkg.Version}
    45  
    46  	// copy all modules but replace manifest in current one
    47  	modsCopy := maps.Clone(build.Modules)
    48  	modsCopy[modRef] = src.Module{
    49  		Manifest: desugaredManifest,
    50  		Packages: mod.Packages,
    51  	}
    52  
    53  	// create new build with patched modeles (current module have patched manifest with std dependency)
    54  	build = src.Build{
    55  		EntryModRef: modRef,
    56  		Modules:     modsCopy,
    57  	}
    58  
    59  	desugaredPkgs := make(map[string]src.Package, len(mod.Packages))
    60  
    61  	for pkgName, pkg := range mod.Packages {
    62  		scope := src.Scope{
    63  			Build: build, // it's important to patch build before desugar package so we can resolve references to std
    64  			Location: src.Location{
    65  				ModRef:  modRef,
    66  				PkgName: pkgName,
    67  			},
    68  		}
    69  
    70  		desugaredPkg, err := d.desugarPkg(pkg, scope)
    71  		if err != nil {
    72  			return src.Module{}, compiler.Error{
    73  				Location: &src.Location{PkgName: pkgName},
    74  			}.Wrap(err)
    75  		}
    76  
    77  		desugaredPkgs[pkgName] = desugaredPkg
    78  	}
    79  
    80  	return src.Module{
    81  		Manifest: desugaredManifest,
    82  		Packages: desugaredPkgs,
    83  	}, nil
    84  }
    85  
    86  func (d Desugarer) desugarPkg(pkg src.Package, scope src.Scope) (src.Package, *compiler.Error) {
    87  	desugaredPkgs := make(src.Package, len(pkg))
    88  
    89  	for fileName, file := range pkg {
    90  		newScope := scope.WithLocation(src.Location{
    91  			ModRef:   scope.Location.ModRef,
    92  			PkgName:  scope.Location.PkgName,
    93  			FileName: fileName,
    94  		})
    95  
    96  		desugaredFile, err := d.desugarFile(file, newScope)
    97  		if err != nil {
    98  			return nil, compiler.Error{
    99  				Location: &src.Location{FileName: fileName},
   100  			}.Wrap(err)
   101  		}
   102  
   103  		desugaredPkgs[fileName] = desugaredFile
   104  	}
   105  
   106  	return desugaredPkgs, nil
   107  }
   108  
   109  // desugarFile injects import of std/builtin into every pkg's file and desugares it's every entity
   110  func (d Desugarer) desugarFile(file src.File, scope src.Scope) (src.File, *compiler.Error) {
   111  	desugaredEntities := make(map[string]src.Entity, len(file.Entities))
   112  
   113  	for entityName, entity := range file.Entities {
   114  		entityResult, err := d.desugarEntity(entity, scope)
   115  		if err != nil {
   116  			return src.File{}, compiler.Error{
   117  				Meta: entity.Meta(),
   118  			}.Wrap(err)
   119  		}
   120  
   121  		desugaredEntities[entityName] = entityResult.entity
   122  
   123  		for name, entityToInsert := range entityResult.entitiesToInsert {
   124  			desugaredEntities[name] = entityToInsert
   125  		}
   126  	}
   127  
   128  	desugaredImports := maps.Clone(file.Imports)
   129  	if desugaredImports == nil {
   130  		desugaredImports = map[string]src.Import{}
   131  	}
   132  
   133  	desugaredImports["builtin"] = src.Import{ // inject std/builtin import
   134  		Module:  "std",
   135  		Package: "builtin",
   136  	}
   137  
   138  	return src.File{
   139  		Imports:  desugaredImports,
   140  		Entities: desugaredEntities,
   141  	}, nil
   142  }
   143  
   144  type desugarEntityResult struct {
   145  	entity           src.Entity
   146  	entitiesToInsert map[string]src.Entity
   147  }
   148  
   149  func (d Desugarer) desugarEntity(entity src.Entity, scope src.Scope) (desugarEntityResult, *compiler.Error) {
   150  	if entity.Kind != src.ComponentEntity && entity.Kind != src.ConstEntity {
   151  		return desugarEntityResult{entity: entity}, nil
   152  	}
   153  
   154  	if entity.Kind == src.ConstEntity {
   155  		desugaredConst, err := d.handleConst(entity.Const)
   156  		if err != nil {
   157  			return desugarEntityResult{}, compiler.Error{Meta: &entity.Component.Meta}.Wrap(err)
   158  		}
   159  
   160  		return desugarEntityResult{
   161  			entity: src.Entity{
   162  				IsPublic: entity.IsPublic,
   163  				Kind:     entity.Kind,
   164  				Const:    desugaredConst,
   165  			},
   166  		}, nil
   167  	}
   168  
   169  	componentResult, err := d.handleComponent(entity.Component, scope)
   170  	if err != nil {
   171  		return desugarEntityResult{}, compiler.Error{Meta: &entity.Component.Meta}.Wrap(err)
   172  	}
   173  
   174  	return desugarEntityResult{
   175  		entitiesToInsert: componentResult.virtualEntities,
   176  		entity: src.Entity{
   177  			IsPublic:  entity.IsPublic,
   178  			Kind:      entity.Kind,
   179  			Component: componentResult.desugaredComponent,
   180  		},
   181  	}, nil
   182  }
   183  
   184  func New() Desugarer {
   185  	return Desugarer{}
   186  }