From 3e7b11db10d273a9a9a1541a5c6b6a8d9ec84e5c Mon Sep 17 00:00:00 2001 From: Dmitry Kurinskiy Date: Tue, 31 Aug 2021 13:05:26 +0300 Subject: [PATCH] Imports/exports fixes (#258) * Fix for export in headerless file * Ability arrow resolution bugfix * Trying to reproduce a bug * Allow dots in module declaration --- aqua-src/export.aqua | 16 ++++++++ aqua-src/gen/OneMore.aqua | 3 ++ aqua-src/import.aqua | 23 +++++++++++ build.sbt | 6 +-- .../main/scala/aqua/model/AquaContext.scala | 26 ++++++------- .../scala/aqua/parser/head/ModuleExpr.scala | 2 +- .../aqua/semantics/header/HeaderSem.scala | 39 +++++++++++-------- .../abilities/AbilitiesInterpreter.scala | 2 +- types/src/main/scala/aqua/types/Type.scala | 4 +- 9 files changed, 84 insertions(+), 37 deletions(-) create mode 100644 aqua-src/export.aqua create mode 100644 aqua-src/gen/OneMore.aqua create mode 100644 aqua-src/import.aqua diff --git a/aqua-src/export.aqua b/aqua-src/export.aqua new file mode 100644 index 00000000..24ef4491 --- /dev/null +++ b/aqua-src/export.aqua @@ -0,0 +1,16 @@ +module Export.Test declares foobar, foo, bar + +func bar() -> string: + <- " I am MyFooBar bar" + +func foo() -> string: + <- "I am MyFooBar foo" + +func foobar() -> []string: + res: *string + res <- foo() + res <- bar() + <- res + +service ExpSrv: + baz() \ No newline at end of file diff --git a/aqua-src/gen/OneMore.aqua b/aqua-src/gen/OneMore.aqua new file mode 100644 index 00000000..52365d83 --- /dev/null +++ b/aqua-src/gen/OneMore.aqua @@ -0,0 +1,3 @@ +service OneMore: + more_call() + consume(s: string) \ No newline at end of file diff --git a/aqua-src/import.aqua b/aqua-src/import.aqua new file mode 100644 index 00000000..7a2219f2 --- /dev/null +++ b/aqua-src/import.aqua @@ -0,0 +1,23 @@ +-- import.aqua +module Import.Test +import foobar from "export.aqua" + +use foo as f from "export.aqua" as Exp + +use "export.aqua" + +import "gen/OneMore.aqua" + +import OneMore as OM from "gen/OneMore.aqua" + +export foo_wrapper as wrap, foobar as barfoo + +func foo_wrapper() -> string: + z <- Exp.f() + q <- Export.Test.bar() + OneMore "hello" + OneMore.more_call() + OM "ohmygod" + OM.more_call() + OM.consume(q) + <- z \ No newline at end of file diff --git a/build.sbt b/build.sbt index b6e25513..8a217fa5 100644 --- a/build.sbt +++ b/build.sbt @@ -17,7 +17,7 @@ val declineV = "2.1.0" name := "aqua-hll" val commons = Seq( - baseAquaVersion := "0.2.0", + baseAquaVersion := "0.2.1", version := baseAquaVersion.value + "-" + sys.env.getOrElse("BUILD_NUMBER", "SNAPSHOT"), scalaVersion := dottyVersion, libraryDependencies ++= Seq( @@ -85,8 +85,8 @@ lazy val parser = crossProject(JVMPlatform, JSPlatform) .settings(commons: _*) .settings( libraryDependencies ++= Seq( - "org.typelevel" %%% "cats-parse" % catsParseV, - "org.typelevel" %%% "cats-free" % catsV + "org.typelevel" %%% "cats-parse" % catsParseV, + "org.typelevel" %%% "cats-free" % catsV ) ) .dependsOn(types) diff --git a/model/src/main/scala/aqua/model/AquaContext.scala b/model/src/main/scala/aqua/model/AquaContext.scala index fcb4c916..845fa017 100644 --- a/model/src/main/scala/aqua/model/AquaContext.scala +++ b/model/src/main/scala/aqua/model/AquaContext.scala @@ -5,6 +5,7 @@ import aqua.model.func.{ArgsCall, FuncCallable, FuncModel} import aqua.types.{StructType, Type} import cats.Monoid import cats.data.NonEmptyMap +import cats.kernel.Semigroup import cats.syntax.functor.* import cats.syntax.monoid.* import scribe.Logging @@ -150,37 +151,34 @@ object AquaContext extends Logging { ) def fromScriptModel(sm: ScriptModel, init: AquaContext)(implicit - aqum: Monoid[AquaContext] + aqum: Semigroup[AquaContext] ): AquaContext = sm.models - .foldLeft((init, Monoid.empty[AquaContext])) { + .foldLeft((init, blank)) { case ((ctx, exportContext), c: ConstantModel) => val add = - Monoid - .empty[AquaContext] + blank .copy(values = - if (c.allowOverrides && ctx.values.contains(c.name)) ctx.values - else ctx.values.updated(c.name, c.value.resolveWith(ctx.values)) + if (c.allowOverrides && ctx.values.contains(c.name)) Map.empty + else Map(c.name -> c.value.resolveWith(ctx.values)) ) (ctx |+| add, exportContext |+| add) case ((ctx, exportContext), func: FuncModel) => val fr = func.capture(ctx.allFuncs(), ctx.allValues()) val add = - Monoid.empty[AquaContext].copy(funcs = ctx.funcs.updated(func.name, fr)) + blank.copy(funcs = Map(func.name -> fr)) (ctx |+| add, exportContext |+| add) case ((ctx, exportContext), t: TypeModel) => val add = - Monoid.empty[AquaContext].copy(types = ctx.types.updated(t.name, t.`type`)) + blank.copy(types = Map(t.name -> t.`type`)) (ctx |+| add, exportContext |+| add) case ((ctx, exportContext), m: ServiceModel) => val add = - Monoid - .empty[AquaContext] + blank .copy( - abilities = m.defaultId.fold(ctx.abilities)(id => - ctx.abilities.updated(m.name, fromServiceModel(m, id)) - ), - services = ctx.services.updated(m.name, m) + abilities = + m.defaultId.fold(Map.empty)(id => Map(m.name -> fromServiceModel(m, id))), + services = Map(m.name -> m) ) (ctx |+| add, exportContext |+| add) case (ce, _) => ce diff --git a/parser/src/main/scala/aqua/parser/head/ModuleExpr.scala b/parser/src/main/scala/aqua/parser/head/ModuleExpr.scala index 554644a8..923ef522 100644 --- a/parser/src/main/scala/aqua/parser/head/ModuleExpr.scala +++ b/parser/src/main/scala/aqua/parser/head/ModuleExpr.scala @@ -40,7 +40,7 @@ object ModuleExpr extends HeaderExpr.Leaf { nameOrAbList[F].map(Left(_)) | `star`.lift.map(Token.lift(_)).map(Right(_)) override def p[F[_]: LiftParser: Comonad]: Parser[ModuleExpr[F]] = - (`module` *> ` ` *> Ability.ab[F] ~ + (`module` *> ` ` *> Ability.dotted[F] ~ (` declares ` *> nameOrAbListOrAll[F]).?).map { case (name, None) => ModuleExpr(name, None, Nil, Nil) diff --git a/semantics/src/main/scala/aqua/semantics/header/HeaderSem.scala b/semantics/src/main/scala/aqua/semantics/header/HeaderSem.scala index d7629659..20420bdf 100644 --- a/semantics/src/main/scala/aqua/semantics/header/HeaderSem.scala +++ b/semantics/src/main/scala/aqua/semantics/header/HeaderSem.scala @@ -13,11 +13,16 @@ import cats.syntax.semigroup.* import cats.instances.list.* import cats.instances.option.* import cats.free.Cofree +import cats.kernel.Semigroup case class HeaderSem[S[_]]( initCtx: AquaContext, - finCtx: AquaContext => ValidatedNec[SemanticError[S], AquaContext] -) + finInitCtx: (AquaContext, AquaContext) => ValidatedNec[SemanticError[S], AquaContext] +) { + + def finCtx: AquaContext => ValidatedNec[SemanticError[S], AquaContext] = + finInitCtx(_, initCtx) +} object HeaderSem { type Res[S[_]] = ValidatedNec[SemanticError[S], HeaderSem[S]] @@ -27,12 +32,12 @@ object HeaderSem { acm: Monoid[AquaContext] ): Monoid[HeaderSem[S]] = new Monoid[HeaderSem[S]] { - override def empty: HeaderSem[S] = HeaderSem(acm.empty, validNec(_)) + override def empty: HeaderSem[S] = HeaderSem(acm.empty, (c, _) => validNec(c)) override def combine(a: HeaderSem[S], b: HeaderSem[S]): HeaderSem[S] = HeaderSem( a.initCtx |+| b.initCtx, - a.finCtx.andThen(_.andThen(b.finCtx)) + (c, i) => a.finInitCtx(c, i).andThen(b.finInitCtx(_, i)) ) } @@ -69,7 +74,7 @@ object HeaderSem { .getOrElse( error( n, - s"Imported file declares [${ctx.declares.mkString(", ")}], no ${n.value} declared. Try adding `declares *` to that file." + s"Imported file `declares ${ctx.declares.mkString(", ")}`, no ${n.value} declared. Try adding `declares ${n.value}` to that file." ) ) }, @@ -80,7 +85,7 @@ object HeaderSem { .getOrElse( error( n, - s"Imported file declares [${ctx.declares.mkString(", ")}], no ${n.value} declared. Try adding `declares *` to that file." + s"Imported file `declares ${ctx.declares.mkString(", ")}`, no ${n.value} declared. Try adding `declares ${n.value}` to that file." ) ) } @@ -112,7 +117,7 @@ object HeaderSem { module = Some(name.value), declares = shouldDeclare ), - ctx => + (ctx, _) => // When file is handled, check that all the declarations exists if (declareAll.nonEmpty) validNec( @@ -144,25 +149,25 @@ object HeaderSem { case f @ ImportExpr(_) => // Import everything from a file - resolve(f).map(fc => HeaderSem[S](fc, validNec(_))) + resolve(f).map(fc => HeaderSem[S](fc, (c, _) => validNec(c))) case f @ ImportFromExpr(_, _) => // Import, map declarations resolve(f) .andThen(getFrom(f, _)) - .map(ctx => HeaderSem[S](ctx, validNec(_))) + .map(ctx => HeaderSem[S](ctx, (c, _) => validNec(c))) case f @ UseExpr(_, asModule) => // Import, move into a module scope resolve(f) .andThen(toModule(_, f.token, asModule)) - .map(fc => HeaderSem[S](fc, validNec(_))) + .map(fc => HeaderSem[S](fc, (c, _) => validNec(c))) case f @ UseFromExpr(_, _, asModule) => // Import, cherry-pick declarations, move to a module scope resolve(f) .andThen(getFrom(f, _)) .andThen(toModule(_, f.token, Some(asModule))) - .map(fc => HeaderSem[S](fc, validNec(_))) + .map(fc => HeaderSem[S](fc, (c, _) => validNec(c))) case ExportExpr(pubs) => // Save exports, finally handle them @@ -170,12 +175,12 @@ object HeaderSem { HeaderSem[S]( // Nothing there acm.empty, - ctx => + (ctx, initCtx) => pubs .map( _.fold( { case (n, rn) => - ctx + (initCtx |+| ctx) .pick(n.value, rn.map(_.value), declared = false) .map(validNec) .getOrElse( @@ -183,7 +188,7 @@ object HeaderSem { ) }, { case (n, rn) => - ctx + (initCtx |+| ctx) .pick(n.value, rn.map(_.value), declared = false) .map(validNec) .getOrElse( @@ -198,11 +203,11 @@ object HeaderSem { ) case HeadExpr(token) => - // Old file exports everything - validNec(HeaderSem[S](acm.empty, ctx => validNec(ctx.copy(exports = Some(ctx))))) + // Old file exports everything that it declares + validNec(HeaderSem[S](acm.empty, (ctx, _) => validNec(ctx.copy(exports = Some(ctx))))) case f: FilenameExpr[S] => - resolve(f).map(fc => HeaderSem[S](fc, validNec(_))) + resolve(f).map(fc => HeaderSem[S](fc, (c, _) => validNec(c))) } Cofree diff --git a/semantics/src/main/scala/aqua/semantics/rules/abilities/AbilitiesInterpreter.scala b/semantics/src/main/scala/aqua/semantics/rules/abilities/AbilitiesInterpreter.scala index 65fd1266..e083dd55 100644 --- a/semantics/src/main/scala/aqua/semantics/rules/abilities/AbilitiesInterpreter.scala +++ b/semantics/src/main/scala/aqua/semantics/rules/abilities/AbilitiesInterpreter.scala @@ -62,7 +62,7 @@ class AbilitiesInterpreter[F[_], X](implicit s"Ability is found, but arrow is undefined, available: ${abCtx.funcs.keys.toList .mkString(", ")}" ).as(Option.empty[ArrowType]) - )(a => State.pure(Some(a))) + )(fn => State.pure(Some(fn.arrowType))) case None => report(ga.name, "Ability with this name is undefined").as(Option.empty[ArrowType]) } diff --git a/types/src/main/scala/aqua/types/Type.scala b/types/src/main/scala/aqua/types/Type.scala index 13e02c96..f810147f 100644 --- a/types/src/main/scala/aqua/types/Type.scala +++ b/types/src/main/scala/aqua/types/Type.scala @@ -199,7 +199,9 @@ case class ArrowType(domain: ProductType, codomain: ProductType) extends Type { s"$domain -> $codomain" } -case class StreamType(element: Type) extends BoxType +case class StreamType(element: Type) extends BoxType { + override def toString: String = s"*$element" +} object Type {