diff --git a/src/main/scala/io/septimalmind/baboon/BaboonModule.scala b/src/main/scala/io/septimalmind/baboon/BaboonModule.scala index 176647c..b943de5 100644 --- a/src/main/scala/io/septimalmind/baboon/BaboonModule.scala +++ b/src/main/scala/io/septimalmind/baboon/BaboonModule.scala @@ -4,14 +4,13 @@ import distage.{DIKey, ModuleDef} import io.septimalmind.baboon.BaboonCompiler.CompilerOptions import io.septimalmind.baboon.parser.BaboonParser import io.septimalmind.baboon.translator.BaboonAbstractTranslator -import io.septimalmind.baboon.translator.csharp.CSValue.CSPackageId import io.septimalmind.baboon.translator.csharp.* +import io.septimalmind.baboon.translator.csharp.CSValue.CSPackageId import io.septimalmind.baboon.typer.* -import io.septimalmind.baboon.typer.BaboonTyper.FullRawDefn import io.septimalmind.baboon.typer.model.* +import io.septimalmind.baboon.typer.model.Scope.NestedScope import io.septimalmind.baboon.util.BLogger import io.septimalmind.baboon.validator.BaboonValidator -import izumi.fundamentals.collections.nonempty.NEList import java.nio.file.Path @@ -47,7 +46,7 @@ class BaboonModule(options: CompilerOptions, .localDependencies( List( DIKey[Pkg], - DIKey[NEList[Scope[FullRawDefn]]], + DIKey[NestedScope[ExtendedRawDefn]], DIKey[Map[TypeId, DomainMember]] ) ) @@ -57,17 +56,6 @@ class BaboonModule(options: CompilerOptions, .extractWith { (translator: BaboonTranslator) => translator } -// make[LocalContext[Identity, BaboonTranslator]] -// .fromLocalContext(new ModuleDef { -// make[BaboonTranslator] -// }.running { (translator: BaboonTranslator) => -// translator -// }) -// .external( -// DIKey[Pkg], -// DIKey[NEList[Scope[FullRawDefn]]], -// DIKey[Map[TypeId, DomainMember]] -// ) makeSubcontext[IndividualConversionHandler] .localDependencies( @@ -87,20 +75,6 @@ class BaboonModule(options: CompilerOptions, handler } -// make[LocalContext[Identity, IndividualConversionHandler]] -// .fromLocalContext(new ModuleDef { -// make[IndividualConversionHandler] -// }.running { (handler: IndividualConversionHandler) => -// handler -// }) -// .external( -// DIKey[CSPackageId], -// DIKey[Version], -// DIKey.get[Domain].named("current"), -// DIKey.get[Domain].named("source"), -// DIKey.get[BaboonRuleset] -// ) - many[CSCodecTranslator] .add[CSNSJsonCodecGenerator] .add[CSUEBACodecGenerator] diff --git a/src/main/scala/io/septimalmind/baboon/parser/model/RawAdtMemberContract.scala b/src/main/scala/io/septimalmind/baboon/parser/model/RawAdtMemberContract.scala deleted file mode 100644 index 32cf37a..0000000 --- a/src/main/scala/io/septimalmind/baboon/parser/model/RawAdtMemberContract.scala +++ /dev/null @@ -1,2 +0,0 @@ -package io.septimalmind.baboon.parser.model - diff --git a/src/main/scala/io/septimalmind/baboon/parser/model/issues/BaboonIssue.scala b/src/main/scala/io/septimalmind/baboon/parser/model/issues/BaboonIssue.scala index bd5d3cd..56fc633 100644 --- a/src/main/scala/io/septimalmind/baboon/parser/model/issues/BaboonIssue.scala +++ b/src/main/scala/io/septimalmind/baboon/parser/model/issues/BaboonIssue.scala @@ -4,8 +4,6 @@ import fastparse.Parsed import io.septimalmind.baboon.parser.BaboonParser import io.septimalmind.baboon.parser.model.* import io.septimalmind.baboon.translator.OutputFile -import io.septimalmind.baboon.typer.BaboonTyper -import io.septimalmind.baboon.typer.BaboonTyper.FullRawDefn import io.septimalmind.baboon.typer.model.* import izumi.fundamentals.collections.nonempty.NEList import izumi.fundamentals.graphs.ToposortError @@ -64,7 +62,7 @@ object BaboonIssue { case class UnexpectedNonBuiltin(name: TypeName, pkg: Pkg, - path: NEList[Scope[FullRawDefn]], + path: Scope[ExtendedRawDefn], meta: RawNodeMeta) extends TyperIssue @@ -113,7 +111,8 @@ object BaboonIssue { meta: RawNodeMeta ) extends TyperIssue - case class UnexpectedScoping(e: List[Scope[FullRawDefn]], meta: RawNodeMeta) + case class UnexpectedScoping(e: List[Scope[ExtendedRawDefn]], + meta: RawNodeMeta) extends TyperIssue with BaboonBug @@ -128,7 +127,7 @@ object BaboonIssue { case class BadTypeName(name: String, meta: RawNodeMeta) extends TyperIssue case class BadInheritance( - bad: Map[TypeId.User, List[(Set[TypeId.User], BaboonTyper.ScopedDefn)]], + bad: Map[TypeId.User, List[(Set[TypeId.User], Scope[ExtendedRawDefn])]], meta: RawNodeMeta ) extends TyperIssue with BaboonBug @@ -140,11 +139,11 @@ object BaboonIssue { case class NameNotFound(pkg: Pkg, name: ScopedRef, meta: RawNodeMeta) extends TyperIssue - case class UnexpectedScopeLookup(b: Scope[FullRawDefn], meta: RawNodeMeta) + case class UnexpectedScopeLookup(b: Scope[ExtendedRawDefn], meta: RawNodeMeta) extends TyperIssue case class NamSeqeNotFound(names: Seq[RawTypeName], - scope: Scope.SubScope[FullRawDefn], + scope: Scope.SubScope[ExtendedRawDefn], meta: RawNodeMeta) extends TyperIssue diff --git a/src/main/scala/io/septimalmind/baboon/typer/BaboonTranslator.scala b/src/main/scala/io/septimalmind/baboon/typer/BaboonTranslator.scala index 1a7aaa4..d92f1d1 100644 --- a/src/main/scala/io/septimalmind/baboon/typer/BaboonTranslator.scala +++ b/src/main/scala/io/septimalmind/baboon/typer/BaboonTranslator.scala @@ -2,11 +2,7 @@ package io.septimalmind.baboon.typer import io.septimalmind.baboon.parser.model.* import io.septimalmind.baboon.parser.model.issues.BaboonIssue -import io.septimalmind.baboon.parser.model.issues.BaboonIssue.{ - MissingContractFields, - ScopedRefToNamespacedGeneric -} -import io.septimalmind.baboon.typer.BaboonTyper.{FullRawDefn, ScopedDefn} +import io.septimalmind.baboon.parser.model.issues.BaboonIssue.{MissingContractFields, ScopedRefToNamespacedGeneric} import io.septimalmind.baboon.typer.model.* import io.septimalmind.baboon.typer.model.Scope.NestedScope import izumi.functional.IzEither.* @@ -14,30 +10,29 @@ import izumi.fundamentals.collections.IzCollections.* import izumi.fundamentals.collections.nonempty.NEList class BaboonTranslator(pkg: Pkg, - path: NEList[Scope[FullRawDefn]], + defn: NestedScope[ExtendedRawDefn], defined: Map[TypeId, DomainMember], scopeSupport: ScopeSupport) { - def translate( - defn: ScopedDefn - ): Either[NEList[BaboonIssue.TyperIssue], List[DomainMember]] = { + def translate() + : Either[NEList[BaboonIssue.TyperIssue], List[DomainMember]] = { for { - rawDefn <- Right(defn.thisScope.defn) + rawDefn <- Right(defn.defn) id <- scopeSupport.resolveUserTypeId( rawDefn.defn.name, - path, + defn, pkg, rawDefn.defn.meta ) - members <- convertMember(id, rawDefn, defn.thisScope) + members <- convertMember(id, rawDefn, defn) } yield { - members.toList + members } } private def convertMember(id: TypeId.User, - defn: FullRawDefn, - thisScope: NestedScope[FullRawDefn], + defn: ExtendedRawDefn, + thisScope: NestedScope[ExtendedRawDefn], ): Either[NEList[BaboonIssue.TyperIssue], List[DomainMember.User]] = { val root = defn.gcRoot defn.defn match { @@ -117,7 +112,7 @@ class BaboonTranslator(pkg: Pkg, refMeta: RawNodeMeta ): Either[NEList[BaboonIssue.TyperIssue], Seq[Field]] = { for { - id <- scopeSupport.resolveScopedRef(parent, path, pkg, refMeta) + id <- scopeSupport.resolveScopedRef(parent, defn, pkg, refMeta) parentDef = defined(id) out <- parentDef match { case DomainMember.User(_, defn: Typedef.Dto, _) => @@ -158,7 +153,7 @@ class BaboonTranslator(pkg: Pkg, refMeta: RawNodeMeta, ): Either[NEList[BaboonIssue.TyperIssue], List[ContractContent]] = { for { - id <- scopeSupport.resolveScopedRef(c.contract.tpe, path, pkg, refMeta) + id <- scopeSupport.resolveScopedRef(c.contract.tpe, defn, pkg, refMeta) content <- readContractContent(id, meta) } yield { content @@ -256,11 +251,11 @@ class BaboonTranslator(pkg: Pkg, _ <- finalFields .map(m => (m.name.name.toLowerCase, m)) .toUniqueMap(e => NEList(BaboonIssue.NonUniqueFields(id, e, dto.meta))) - contractRefs = contracts.flatMap(_.refs).distinct.toList + contractRefs = contracts.flatMap(_.refs).distinct } yield { DomainMember.User( isRoot, - produce(id, finalFields.toList, contractRefs), + produce(id, finalFields, contractRefs), dto.meta ) } @@ -269,7 +264,7 @@ class BaboonTranslator(pkg: Pkg, private def convertAdt(id: TypeId.User, isRoot: Boolean, adt: RawAdt, - thisScope: NestedScope[FullRawDefn], + thisScope: NestedScope[ExtendedRawDefn], ): Either[NEList[BaboonIssue.TyperIssue], NEList[DomainMember.User]] = { for { converted <- adt.members @@ -277,12 +272,7 @@ class BaboonTranslator(pkg: Pkg, .map( member => scopeSupport - .resolveUserTypeId( - member.defn.name, - path :+ thisScope, - pkg, - member.meta - ) + .resolveUserTypeId(member.defn.name, thisScope, pkg, member.meta) ) .biSequence nel <- NEList @@ -291,7 +281,8 @@ class BaboonTranslator(pkg: Pkg, contracts <- adt.contracts .map( ref => - scopeSupport.resolveScopedRef(ref.contract.tpe, path, pkg, ref.meta) + scopeSupport + .resolveScopedRef(ref.contract.tpe, defn, pkg, ref.meta) ) .biSequence .map(_.toList) @@ -316,7 +307,7 @@ class BaboonTranslator(pkg: Pkg, tpe match { case RawTypeRef.Simple(name, prefix) => for { - id <- scopeSupport.resolveTypeId(prefix, name, path, pkg, meta) + id <- scopeSupport.resolveTypeId(prefix, name, defn, pkg, meta) asScalar <- id match { case scalar: TypeId.Scalar => Right(scalar) @@ -331,7 +322,7 @@ class BaboonTranslator(pkg: Pkg, _ <- Either.ifThenFail(prefix.nonEmpty)( NEList(ScopedRefToNamespacedGeneric(prefix, meta)) ) - id <- scopeSupport.resolveTypeId(prefix, name, path, pkg, meta) + id <- scopeSupport.resolveTypeId(prefix, name, defn, pkg, meta) asCollection <- id match { case coll: TypeId.BuiltinCollection => Right(coll) diff --git a/src/main/scala/io/septimalmind/baboon/typer/BaboonTyper.scala b/src/main/scala/io/septimalmind/baboon/typer/BaboonTyper.scala index 5c61f86..1c61aa9 100644 --- a/src/main/scala/io/septimalmind/baboon/typer/BaboonTyper.scala +++ b/src/main/scala/io/septimalmind/baboon/typer/BaboonTyper.scala @@ -7,7 +7,7 @@ import io.septimalmind.baboon.typer.model.* import io.septimalmind.baboon.typer.model.Scope.* import izumi.functional.IzEither.* import izumi.fundamentals.collections.IzCollections.* -import izumi.fundamentals.collections.nonempty.{NEList, NEMap} +import izumi.fundamentals.collections.nonempty.NEList import izumi.fundamentals.graphs.struct.IncidenceMatrix import izumi.fundamentals.graphs.tools.{Toposort, ToposortLoopBreaker} import izumi.fundamentals.graphs.{DG, GraphMeta} @@ -19,10 +19,6 @@ trait BaboonTyper { } object BaboonTyper { - case class FullRawDefn(defn: RawDefn, gcRoot: Boolean) - - case class ScopedDefn(thisScope: NestedScope[FullRawDefn], - path: NEList[Scope[FullRawDefn]]) class BaboonTyperImpl(enquiries: BaboonEnquiries, translator: Subcontext[BaboonTranslator], @@ -233,8 +229,8 @@ object BaboonTyper { initial <- Right( TypeId.Builtins.all.map(id => DomainMember.Builtin(id)) ) - - scopes <- buildScopes(pkg, members, meta) + builder = new ScopeBuilder() + scopes <- builder.buildScopes(pkg, members, meta) flattened = flattenScopes(scopes) ordered <- order(pkg, flattened, meta) @@ -243,10 +239,10 @@ object BaboonTyper { for { next <- translator .provide(pkg) - .provide(defn.path) + .provide(defn) .provide(acc) .produce() - .use(_.translate(defn)) + .use(_.translate()) mapped = next.map(m => (m.id, m)) dupes = acc.keySet.intersect(mapped.map(_._1).toSet) _ <- Either.ifThenFail(dupes.nonEmpty)( @@ -266,9 +262,11 @@ object BaboonTyper { private def order( pkg: Pkg, - flattened: List[ScopedDefn], + flattened: List[NestedScope[ExtendedRawDefn]], meta: RawNodeMeta - ): Either[NEList[BaboonIssue.TyperIssue], List[ScopedDefn]] = { + ): Either[NEList[BaboonIssue.TyperIssue], List[ + NestedScope[ExtendedRawDefn] + ]] = { for { depmap <- flattened.map(d => deps(pkg, d)).biSequence asMap <- depmap.toUniqueMap( @@ -286,25 +284,24 @@ object BaboonTyper { } } - private def deps( - pkg: Pkg, - defn: ScopedDefn - ): Either[NEList[BaboonIssue.TyperIssue], - (TypeId.User, (Set[TypeId.User], ScopedDefn))] = { + private def deps(pkg: Pkg, + defn: NestedScope[ExtendedRawDefn]): Either[NEList[ + BaboonIssue.TyperIssue + ], (TypeId.User, (Set[TypeId.User], NestedScope[ExtendedRawDefn]))] = { for { - rawDefn <- Right(defn.thisScope.defn) + rawDefn <- Right(defn.defn) id <- scopeSupport.resolveUserTypeId( rawDefn.defn.name, - defn.path, + defn, pkg, rawDefn.defn.meta ) mappedDeps <- enquiries - .hardDepsOfRawDefn(defn.thisScope.defn.defn) + .hardDepsOfRawDefn(defn.defn.defn) .map( v => scopeSupport - .resolveScopedRef(v, defn.path, pkg, rawDefn.defn.meta) + .resolveScopedRef(v, defn, pkg, rawDefn.defn.meta) ) .biSequence } yield { @@ -318,105 +315,24 @@ object BaboonTyper { } private def flattenScopes( - root: RootScope[FullRawDefn] - ): List[ScopedDefn] = { - root.nested.values - .flatMap(defn => flattenScopes(NEList(root), defn)) - .toList - } - - private def flattenScopes( - path: NEList[Scope[FullRawDefn]], - current: NestedScope[FullRawDefn] - ): List[ScopedDefn] = { - - current match { - case s: SubScope[FullRawDefn] => - List(ScopedDefn(s, path)) ++ s.nested.toMap.values - .flatMap(n => flattenScopes(path :+ current, n)) - .toList - case l: LeafScope[FullRawDefn] => - List(ScopedDefn(l, path)) - } - } - - private def buildScopes( - pkg: Pkg, - members: Seq[RawTLDef], - meta: RawNodeMeta - ): Either[NEList[BaboonIssue.TyperIssue], RootScope[FullRawDefn]] = { - for { - sub <- members.map(m => buildScope(m.value, m.root)).biSequence - asMap <- sub - .map(s => (s.name, s)) - .toUniqueMap(nus => NEList(BaboonIssue.NonUniqueScope(nus, meta))) - } yield { - RootScope(pkg, asMap) + root: RootScope[ExtendedRawDefn] + ): List[NestedScope[ExtendedRawDefn]] = { + def flattenScopes( + current: NestedScope[ExtendedRawDefn] + ): List[NestedScope[ExtendedRawDefn]] = { + current match { + case s: SubScope[ExtendedRawDefn] => + List(s) ++ s.nested.toMap.values + .flatMap(n => flattenScopes(n)) + .toList + case l: LeafScope[ExtendedRawDefn] => + List(l) + } } - } - - private def buildScope( - member: RawDefn, - isRoot: Boolean - ): Either[NEList[BaboonIssue.TyperIssue], NestedScope[FullRawDefn]] = { - member match { - case namespace: RawNamespace => - for { - sub <- namespace.defns - .map(m => buildScope(m.value, isRoot = m.root)) - .biSequence - asMap <- sub - .map(s => (s.name, s)) - .toUniqueMap( - nus => NEList(BaboonIssue.NonUniqueScope(nus, member.meta)) - ) - asNEMap <- NEMap - .from(asMap) - .toRight(NEList(BaboonIssue.ScopeCannotBeEmpty(member))) - } yield { - SubScope( - ScopeName(namespace.name.name), - FullRawDefn(namespace, isRoot), - asNEMap - ) - } - - case dto: RawDto => - Right(LeafScope(ScopeName(dto.name.name), FullRawDefn(dto, isRoot))) - case contract: RawContract => - Right( - LeafScope( - ScopeName(contract.name.name), - FullRawDefn(contract, isRoot) - ) - ) - case e: RawEnum => - Right(LeafScope(ScopeName(e.name.name), FullRawDefn(e, isRoot))) - case f: RawForeign => - Right(LeafScope(ScopeName(f.name.name), FullRawDefn(f, isRoot))) - case adt: RawAdt => - for { - sub <- adt.members - .collect { case d: RawAdtMember => d } - .map(m => buildScope(m.defn, isRoot = false)) - .biSequence - asMap <- sub - .map(s => (s.name, s)) - .toUniqueMap( - nus => NEList(BaboonIssue.NonUniqueScope(nus, member.meta)) - ) - asNEMap <- NEMap - .from(asMap) - .toRight(NEList(BaboonIssue.ScopeCannotBeEmpty(member))) - } yield { - SubScope( - ScopeName(adt.name.name), - FullRawDefn(adt, isRoot), - asNEMap - ) - } - } + root.nested.values + .flatMap(defn => flattenScopes(defn)) + .toList } } diff --git a/src/main/scala/io/septimalmind/baboon/typer/ScopeBuilder.scala b/src/main/scala/io/septimalmind/baboon/typer/ScopeBuilder.scala new file mode 100644 index 0000000..aec58d9 --- /dev/null +++ b/src/main/scala/io/septimalmind/baboon/typer/ScopeBuilder.scala @@ -0,0 +1,127 @@ +package io.septimalmind.baboon.typer + +import io.septimalmind.baboon.parser.model.* +import io.septimalmind.baboon.parser.model.issues.BaboonIssue +import io.septimalmind.baboon.typer.model.* +import io.septimalmind.baboon.typer.model.Scope.{NestedScope, *} +import izumi.functional.IzEither.* +import izumi.fundamentals.collections.IzCollections.* +import izumi.fundamentals.collections.nonempty.{NEList, NEMap} + +import java.util.concurrent.atomic.AtomicInteger + +class ScopeBuilder() { + trait UIDGen { + val uidGen = new AtomicInteger(0) + + def next(): ScopeUID = { + ScopeUID(uidGen.getAndIncrement()) + } + } + + def buildScopes( + pkg: Pkg, + members: Seq[RawTLDef], + meta: RawNodeMeta + ): Either[NEList[BaboonIssue.TyperIssue], RootScope[ExtendedRawDefn]] = { + + val gen = new UIDGen {} + + for { + sub <- members.map(m => buildScope(m.value, m.root, gen)).biSequence + asMap <- sub + .map(s => (s.name, s)) + .toUniqueMap(nus => NEList(BaboonIssue.NonUniqueScope(nus, meta))) + } yield { + val out = RootScope(gen.next(), pkg, asMap) + val parents = out.identifyParents + val index = out.index + + out.map(defn => ExtendedRawDefn(defn, ScopeContext(parents, index))) + } + + } + + private def buildScope(member: RawDefn, + isRoot: Boolean, + //parents: util.IdentityHashMap[NestedScope[FullRawDefn], Scope[FullRawDefn]], + gen: UIDGen, + ): Either[NEList[BaboonIssue.TyperIssue], NestedScope[FullRawDefn]] = { + def finish(defn: RawDefn, + asNEMap: NEMap[ScopeName, NestedScope[FullRawDefn]]) = { + SubScope( + gen.next(), + ScopeName(defn.name.name), + FullRawDefn(defn, isRoot), + asNEMap, + ) + } + + member match { + case namespace: RawNamespace => + for { + sub <- namespace.defns + .map(m => buildScope(m.value, isRoot = m.root, gen)) + .biSequence + asMap <- sub + .map(s => (s.name, s)) + .toUniqueMap( + nus => NEList(BaboonIssue.NonUniqueScope(nus, member.meta)) + ) + asNEMap <- NEMap + .from(asMap) + .toRight(NEList(BaboonIssue.ScopeCannotBeEmpty(member))) + } yield { + finish(namespace, asNEMap) + } + + case adt: RawAdt => + for { + sub <- adt.members + .collect { case d: RawAdtMember => d } + .map(m => buildScope(m.defn, isRoot = false, gen)) + .biSequence + asMap <- sub + .map(s => (s.name, s)) + .toUniqueMap( + nus => NEList(BaboonIssue.NonUniqueScope(nus, member.meta)) + ) + asNEMap <- NEMap + .from(asMap) + .toRight(NEList(BaboonIssue.ScopeCannotBeEmpty(member))) + } yield { + finish(adt, asNEMap) + } + + case dto: RawDto => + Right( + LeafScope( + gen.next(), + ScopeName(dto.name.name), + FullRawDefn(dto, isRoot) + ) + ) + + case contract: RawContract => + Right( + LeafScope( + gen.next(), + ScopeName(contract.name.name), + FullRawDefn(contract, isRoot) + ) + ) + + case e: RawEnum => + Right( + LeafScope(gen.next(), ScopeName(e.name.name), FullRawDefn(e, isRoot)) + ) + + case f: RawForeign => + Right( + LeafScope(gen.next(), ScopeName(f.name.name), FullRawDefn(f, isRoot)) + ) + + } + } + +} diff --git a/src/main/scala/io/septimalmind/baboon/typer/ScopeSupport.scala b/src/main/scala/io/septimalmind/baboon/typer/ScopeSupport.scala index 0bbf485..784bacc 100644 --- a/src/main/scala/io/septimalmind/baboon/typer/ScopeSupport.scala +++ b/src/main/scala/io/septimalmind/baboon/typer/ScopeSupport.scala @@ -1,8 +1,7 @@ package io.septimalmind.baboon.typer -import io.septimalmind.baboon.parser.model.issues.BaboonIssue import io.septimalmind.baboon.parser.model.* -import io.septimalmind.baboon.typer.BaboonTyper.FullRawDefn +import io.septimalmind.baboon.parser.model.issues.BaboonIssue import io.septimalmind.baboon.typer.model.* import io.septimalmind.baboon.typer.model.Scope.{ LeafScope, @@ -18,7 +17,7 @@ import scala.annotation.tailrec trait ScopeSupport { def resolveScopedRef( name: ScopedRef, - path: NEList[Scope[FullRawDefn]], + scope: Scope[ExtendedRawDefn], pkg: Pkg, meta: RawNodeMeta ): Either[NEList[BaboonIssue.TyperIssue], TypeId.User] @@ -26,45 +25,39 @@ trait ScopeSupport { def resolveTypeId( prefix: List[RawTypeName], name: RawTypeName, - path: NEList[Scope[FullRawDefn]], + scope: Scope[ExtendedRawDefn], pkg: Pkg, meta: RawNodeMeta ): Either[NEList[BaboonIssue.TyperIssue], TypeId] def resolveUserTypeId( name: RawTypeName, - path: NEList[Scope[FullRawDefn]], + scope: Scope[ExtendedRawDefn], pkg: Pkg, meta: RawNodeMeta ): Either[NEList[BaboonIssue.TyperIssue], TypeId.User] } object ScopeSupport { - case class FoundDefn(path: List[Scope[FullRawDefn]], - scope: NestedScope[FullRawDefn]) { - def defn: FullRawDefn = scope.defn - } - - case class LookupResult(suffix: List[Scope[FullRawDefn]], - scope: LeafScope[FullRawDefn]) + case class LookupResult(suffix: List[Scope[ExtendedRawDefn]], + scope: LeafScope[ExtendedRawDefn]) class ScopeSupportImpl extends ScopeSupport { def resolveScopedRef( name: ScopedRef, - path: NEList[Scope[FullRawDefn]], + scope: Scope[ExtendedRawDefn], pkg: Pkg, meta: RawNodeMeta ): Either[NEList[BaboonIssue.TyperIssue], TypeId.User] = { - val found = findDefn(ScopeName(name.path.head.name), path.reverse.toList) - + val found = findScope(NEList(ScopeName(name.path.head.name)), scope) found match { case Some(found) => for { - scope <- lookupName(name.path.tail, found.scope, List.empty, meta) - fullPath = (found.path :+ found.scope) ++ scope.suffix + scopeOfFound <- lookupName(name.path.tail, found, List.empty, meta) + fullPath = List(found) ++ scopeOfFound.suffix resolved <- resolveUserTypeId( name.path.last, - NEList.unsafeFrom(fullPath.init), + fullPath.last, pkg, meta ) @@ -73,7 +66,6 @@ object ScopeSupport { } case None => - System.exit(1) Left(NEList(BaboonIssue.NameNotFound(pkg, name, meta))) } } @@ -81,14 +73,14 @@ object ScopeSupport { @tailrec private def lookupName( names: Seq[RawTypeName], - in: Scope[FullRawDefn], - suffix: List[Scope[FullRawDefn]], + in: Scope[ExtendedRawDefn], + suffix: List[Scope[ExtendedRawDefn]], meta: RawNodeMeta ): Either[NEList[BaboonIssue.TyperIssue], LookupResult] = { names.headOption match { case Some(value) => in match { - case s: SubScope[FullRawDefn] => + case s: SubScope[ExtendedRawDefn] => s.nested.toMap.get(ScopeName(value.name)) match { case Some(value) => lookupName(names.tail, value, suffix :+ value, meta) @@ -100,7 +92,7 @@ object ScopeSupport { } case None => in match { - case s: LeafScope[FullRawDefn] => + case s: LeafScope[ExtendedRawDefn] => Right(LookupResult(suffix, s)) case b => Left(NEList(BaboonIssue.UnexpectedScopeLookup(b, meta))) @@ -111,12 +103,12 @@ object ScopeSupport { def resolveUserTypeId( name: RawTypeName, - path: NEList[Scope[FullRawDefn]], + scope: Scope[ExtendedRawDefn], pkg: Pkg, meta: RawNodeMeta ): Either[NEList[BaboonIssue.TyperIssue], TypeId.User] = { for { - id <- resolveTypeId(List.empty, name, path, pkg, meta) + id <- resolveTypeId(List.empty, name, scope, pkg, meta) userId <- id match { case id: TypeId.Builtin => Left(NEList(BaboonIssue.UnexpectedBuiltin(id, pkg, meta))) @@ -143,18 +135,20 @@ object ScopeSupport { def resolveTypeId( prefix: List[RawTypeName], name: RawTypeName, - path: NEList[Scope[FullRawDefn]], + scope: Scope[ExtendedRawDefn], pkg: Pkg, meta: RawNodeMeta ): Either[NEList[BaboonIssue.TyperIssue], TypeId] = { for { typename <- convertTypename(name, meta) - needle = prefix.map(_.name).map(ScopeName) ++ List(ScopeName(name.name)) - found = findPrefixedDefn(needle, path.reverse.toList) + needle = prefix.map(_.name).map(ScopeName.apply) ++: NEList( + ScopeName(name.name) + ) + found = findScope(needle, scope) result <- found match { case Some(value) => for { - owner <- pathToOwner(value.path, pkg) + owner <- pathToOwner(asPath(value), pkg) } yield { val out = TypeId.User(pkg, owner, typename) out @@ -162,7 +156,8 @@ object ScopeSupport { case None => asBuiltin(typename).toRight( NEList( - BaboonIssue.UnexpectedNonBuiltin(typename, pkg, path, meta) + BaboonIssue + .UnexpectedNonBuiltin(typename, pkg, scope, meta) ) ) } @@ -171,39 +166,7 @@ object ScopeSupport { } } - private def findPrefixedDefn( - needles: List[ScopeName], - reversePath: List[Scope[FullRawDefn]] - ): Option[FoundDefn] = { - - def subfind(needle: ScopeName, - reversePath: List[Scope[FullRawDefn]]): Option[FoundDefn] = { - findDefn(needle, reversePath).orElse { - reversePath match { - case _ :: tail => - subfind(needle, tail) - case Nil => - None - } - - } - } - - val out = needles match { - case Nil => - None - case head :: Nil => - subfind(head, reversePath) - case head :: tail => - subfind(head, reversePath).flatMap { found => - val newpath = (found.path ++ List(found.scope)).reverse - findPrefixedDefn(tail, newpath) - } - } - out - } - - private def pathToOwner(defnPath: List[Scope[FullRawDefn]], + private def pathToOwner(defnPath: List[Scope[ExtendedRawDefn]], pkg: Pkg, ): Either[NEList[BaboonIssue.TyperIssue], Owner] = { @@ -215,7 +178,7 @@ object ScopeSupport { Right(Owner.Ns(ns)) } out <- defnPath.lastOption match { - case Some(value: SubScope[FullRawDefn]) => + case Some(value: SubScope[ExtendedRawDefn]) => value.defn.defn match { case a: RawAdt => for { @@ -241,7 +204,7 @@ object ScopeSupport { } private def namespaceOf( - path: List[Scope[FullRawDefn]], + path: List[Scope[ExtendedRawDefn]], ): Either[NEList[BaboonIssue.TyperIssue], List[TypeName]] = { path match { case head :: tail => @@ -274,40 +237,49 @@ object ScopeSupport { } } - private def findDefn( - needle: ScopeName, - reversePath: List[Scope[FullRawDefn]] - ): Option[FoundDefn] = { - def found(p: List[Scope[FullRawDefn]], n: NestedScope[FullRawDefn]) = { - if (p.last == n) { - FoundDefn(p.init, n) - } else { - FoundDefn(p, n) + private def asPath( + scope: Scope[ExtendedRawDefn] + ): List[Scope[ExtendedRawDefn]] = { + + def go(s: Scope[ExtendedRawDefn]): NEList[Scope[ExtendedRawDefn]] = { + s match { + case r: RootScope[ExtendedRawDefn] => NEList(r) + case n: NestedScope[ExtendedRawDefn] => + go(n.defn.parentOf(n)) ++ NEList(n) } } + go(scope).toList.init - reversePath.headOption match { - case Some(s: RootScope[FullRawDefn]) => - s.nested - .get(needle) - .map(n => found(reversePath, n)) + } + + private def findScope(needles: NEList[ScopeName], + scope: Scope[ExtendedRawDefn], + ): Option[NestedScope[ExtendedRawDefn]] = { + + val head = needles.head - case Some(s: LeafScope[FullRawDefn]) => + val headScope = scope match { + case s: RootScope[ExtendedRawDefn] => + s.nested.get(head) + + case s: LeafScope[ExtendedRawDefn] => Some(s) - .filter(_.name == needle) - .map(n => found(reversePath.reverse, n)) - .orElse(findDefn(needle, reversePath.tail)) + .filter(_.name == head) + .orElse(findScope(needles, s.defn.parentOf(s))) - case Some(s: SubScope[FullRawDefn]) => + case s: SubScope[ExtendedRawDefn] => s.nested.toMap - .get(needle) - .orElse(Some(s).filter(_.name == needle)) - .map(n => found(reversePath.reverse, n)) - .orElse(findDefn(needle, reversePath.tail)) + .get(head) + .orElse(Some(s).filter(_.name == head)) + .orElse(findScope(needles, s.defn.parentOf(s))) + } + NEList.from(needles.tail) match { + case Some(value) => + headScope.flatMap(nested => findScope(value, nested)) case None => - None + headScope } } diff --git a/src/main/scala/io/septimalmind/baboon/typer/model/FullRawDefn.scala b/src/main/scala/io/septimalmind/baboon/typer/model/FullRawDefn.scala new file mode 100644 index 0000000..9860b65 --- /dev/null +++ b/src/main/scala/io/septimalmind/baboon/typer/model/FullRawDefn.scala @@ -0,0 +1,38 @@ +package io.septimalmind.baboon.typer.model + +import io.septimalmind.baboon.parser.model.* +import io.septimalmind.baboon.typer.model.Scope.{NestedScope, ScopeUID} + +case class FullRawDefn(defn: RawDefn, gcRoot: Boolean) + +object FullRawDefn { + implicit class DebugExt(defn: FullRawDefn) { + def debugRepr: String = { + val n = defn.defn.name.name + val name = defn.defn match { + case _: RawDto => s"dto" + case _: RawContract => s"contract" + case _: RawEnum => s"contract" + case _: RawAdt => s"adt" + case _: RawForeign => s"foreign" + case _: RawNamespace => s"namespace" + } + + val root = if (defn.gcRoot) { "!" } else { "" } + s"$root$name $n" + } + } +} + +case class ScopeContext(parents: Map[ScopeUID, ScopeUID], + index: Map[ScopeUID, Scope[FullRawDefn]]) + +case class ExtendedRawDefn(origin: FullRawDefn, context: ScopeContext) { + def defn: RawDefn = origin.defn + def gcRoot: Boolean = origin.gcRoot + + def parentOf(s: NestedScope[ExtendedRawDefn]): Scope[ExtendedRawDefn] = + s.defn.context + .index(s.defn.context.parents(s.id)) + .map(d => ExtendedRawDefn(d, s.defn.context)) +} diff --git a/src/main/scala/io/septimalmind/baboon/typer/model/Scope.scala b/src/main/scala/io/septimalmind/baboon/typer/model/Scope.scala index 0809f7f..ae809dd 100644 --- a/src/main/scala/io/septimalmind/baboon/typer/model/Scope.scala +++ b/src/main/scala/io/septimalmind/baboon/typer/model/Scope.scala @@ -1,37 +1,122 @@ package io.septimalmind.baboon.typer.model +import io.septimalmind.baboon.typer.model.Scope.ScopeUID import izumi.fundamentals.collections.nonempty.NEMap -sealed trait Scope[Def] +sealed trait Scope[Def] { + def id: ScopeUID + + def map[Def2](defnMap: Def => Def2): Scope[Def2] +} object Scope { + case class ScopeUID(id: Int) extends AnyVal { + override def toString: String = s"[$id]" + } + case class ScopeName(name: String) extends AnyVal - case class RootScope[Def](pkg: Pkg, nested: Map[ScopeName, NestedScope[Def]]) - extends Scope[Def] + case class RootScope[Def](id: ScopeUID, + pkg: Pkg, + nested: Map[ScopeName, NestedScope[Def]]) + extends Scope[Def] { + + def map[Def2](defnMap: Def => Def2): RootScope[Def2] = { + copy( + nested = nested.view + .mapValues(_.map(defnMap).asInstanceOf[NestedScope[Def2]]) + .toMap + ) + } + + } sealed trait NestedScope[Def] extends Scope[Def] { def name: ScopeName def defn: Def } - case class SubScope[Def](name: ScopeName, + case class SubScope[Def](id: ScopeUID, + name: ScopeName, defn: Def, nested: NEMap[ScopeName, NestedScope[Def]]) - extends NestedScope[Def] + extends NestedScope[Def] { + def map[Def2](defnMap: Def => Def2): SubScope[Def2] = { + copy(defn = defnMap(defn), nested = nested.map { + case (n, v) => (n, v.map(defnMap).asInstanceOf[NestedScope[Def2]]) + }) + } + + } - case class LeafScope[Def](name: ScopeName, defn: Def) extends NestedScope[Def] + case class LeafScope[Def](id: ScopeUID, name: ScopeName, defn: Def) + extends NestedScope[Def] { + def map[Def2](defnMap: Def => Def2): LeafScope[Def2] = { + copy(defn = defnMap(defn)) + } + } - implicit class DebugExt[Def](path: List[Scope[Def]]) { - private def asPathElement(s: Scope[?]): String = { - s match { - case RootScope(pkg, _) => pkg.toString - case scope: NestedScope[_] => scope.name.name + implicit class RootScopeExt[Def](scope: RootScope[Def]) { + def identifyParents: Map[ScopeUID, ScopeUID] = { + identifyParents(scope) + } + + def index: Map[ScopeUID, Scope[Def]] = { + index(scope) + } + + private def identifyParents(root: RootScope[?]): Map[ScopeUID, ScopeUID] = { + def identifySubParents( + scope: NestedScope[?], + currentParent: ScopeUID + ): List[(ScopeUID, ScopeUID)] = { + scope match { + case s: SubScope[_] => + List((s.id, currentParent)) ++ s.nested.toIterable + .flatMap(n => identifySubParents(n._2, s.id)) + case s: LeafScope[_] => + List((s.id, currentParent)) + } + } + val list = root.nested.toList + .flatMap(n => identifySubParents(n._2, root.id)) + + assert(list.map(_._1).toSet.size == list.size) + list.toMap + } + + private def index[Def](root: RootScope[Def]): Map[ScopeUID, Scope[Def]] = { + def identifySubParents( + scope: NestedScope[Def] + ): List[(ScopeUID, Scope[Def])] = { + scope match { + case s: SubScope[_] => + List((s.id, s: Scope[Def])) ++ s.nested.toIterable + .flatMap(n => identifySubParents(n._2)) + case s: LeafScope[_] => + List((s.id, s)) + } } + val list = List((root.id, root)) ++ root.nested.toList + .flatMap(n => identifySubParents(n._2)) + + assert(list.map(_._1).toSet.size == list.size) + list.toMap } + } - def asStrDebug: String = { - path.map(v => asPathElement(v)).mkString("/") + implicit class ScopeExt[Def](scope: Scope[Def]) { + def debugRepr(defnRepr: Def => String): String = { + import izumi.fundamentals.platform.strings.IzString.* + scope match { + case s: RootScope[Def] => + s"${s.id}: ${s.pkg.toString}${s.nested.view.values.map(_.debugRepr(defnRepr)).niceList().shift(2)}" + case s: SubScope[Def] => + s"${s.id}: ${s.name.name} <- ${defnRepr(s.defn)} ${s.nested.toMap.view.values.map(_.debugRepr(defnRepr)).niceList().shift(2)}" + case s: LeafScope[Def] => + s"${s.id}: ${s.name.name} := ${defnRepr(s.defn)}" + } } } + }