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 }