diff --git a/aqua-src/ret.aqua b/aqua-src/ret.aqua index 21ad8f88..13acd12d 100644 --- a/aqua-src/ret.aqua +++ b/aqua-src/ret.aqua @@ -1,3 +1,10 @@ +module Ret + +import Service from "service.aqua" + +use "error.aqua" +export GetStr, tupleFunc, Service as S + service GetStr("multiret-test"): retStr: string -> string @@ -10,6 +17,7 @@ const someStr = "some-str" func tupleFunc() -> string, u8: str <- GetStr.retStr(someStr) n <- GetNum.retNum() + Err.Peer.is_connected("Connected?") <- str, n func multiReturnFunc(somethingToReturn: []u8, smthOption: ?string) -> []string, u8, string, []u8, ?string, u8: diff --git a/aqua/error.aqua b/aqua/error.aqua index 80780d94..b71abb95 100644 --- a/aqua/error.aqua +++ b/aqua/error.aqua @@ -1,3 +1,5 @@ +module Err declares Peer, Op, include + service Peer("peer"): is_connected: string -> bool diff --git a/aqua/service.aqua b/aqua/service.aqua index 150ed423..62425f52 100644 --- a/aqua/service.aqua +++ b/aqua/service.aqua @@ -1,3 +1,5 @@ +module Srv declares * + import "chatApp.aqua" service Service("affinidi/chat"): diff --git a/cli/.jvm/src/main/scala/aqua/Test.scala b/cli/.jvm/src/main/scala/aqua/Test.scala index b999b59a..e6065358 100644 --- a/cli/.jvm/src/main/scala/aqua/Test.scala +++ b/cli/.jvm/src/main/scala/aqua/Test.scala @@ -12,19 +12,20 @@ object Test extends IOApp.Simple { implicit val aio: AquaIO[IO] = new AquaFilesIO[IO] override def run: IO[Unit] = - AquaPathCompiler - .compileFilesTo[IO]( - Path("./aqua-src"), - List(Path("./aqua")), - Path("./target"), - TypeScriptBackend, - TransformConfig() - ) - .map { - case Validated.Invalid(errs) => - errs.map(System.err.println): Unit - case Validated.Valid(res) => - res.map(println): Unit - } + IO.println("Start ms: " + System.currentTimeMillis()) *> + AquaPathCompiler + .compileFilesTo[IO]( + Path("./aqua-src"), + List(Path("./aqua")), + Path("./target"), + TypeScriptBackend, + TransformConfig() + ) + .map { + case Validated.Invalid(errs) => + errs.map(System.err.println): Unit + case Validated.Valid(res) => + res.map(println): Unit + } <* IO.println("End ms : " + System.currentTimeMillis()) } diff --git a/model/transform/src/main/scala/aqua/model/transform/res/AquaRes.scala b/model/transform/src/main/scala/aqua/model/transform/res/AquaRes.scala index 452fcef1..bb0cef02 100644 --- a/model/transform/src/main/scala/aqua/model/transform/res/AquaRes.scala +++ b/model/transform/src/main/scala/aqua/model/transform/res/AquaRes.scala @@ -16,8 +16,16 @@ object AquaRes { ctx.exports .map(ex => AquaRes( - funcs = Chain.fromSeq(ex.funcs.values.toSeq).map(Transform.fn(_, conf)), - services = Chain.fromSeq(ex.services.values.toSeq).map(ServiceRes.fromModel(_)) + funcs = Chain + .fromSeq(ex.funcs.map { case (fnName, fn) => + fn.copy(funcName = fnName) + }.toSeq) + .map(Transform.fn(_, conf)), + services = Chain + .fromSeq(ex.services.map { case (srvName, srv) => + srv.copy(name = srvName) + }.toSeq) + .map(ServiceRes.fromModel(_)) ) ) .getOrElse(blank) diff --git a/parser/src/main/scala/aqua/parser/head/UseExpr.scala b/parser/src/main/scala/aqua/parser/head/UseExpr.scala index 706f36d8..33b58c39 100644 --- a/parser/src/main/scala/aqua/parser/head/UseExpr.scala +++ b/parser/src/main/scala/aqua/parser/head/UseExpr.scala @@ -14,7 +14,7 @@ case class UseExpr[F[_]]( object UseExpr extends HeaderExpr.Leaf { override def p[F[_]: LiftParser: Comonad]: Parser[HeaderExpr[F]] = - (`use` *> Value + (`use` *> ` ` *> Value .string[F] ~ (` as ` *> Ability.ab[F]).?).map { case (filename, asModule) => UseExpr(filename, asModule) } diff --git a/parser/src/main/scala/aqua/parser/lexer/Token.scala b/parser/src/main/scala/aqua/parser/lexer/Token.scala index 91cf38e6..da94f69d 100644 --- a/parser/src/main/scala/aqua/parser/lexer/Token.scala +++ b/parser/src/main/scala/aqua/parser/lexer/Token.scala @@ -103,5 +103,5 @@ object Token { P.repSep0(p, `,` <* ` \n+`.rep0) def asOpt[T](p: P[T]): P[(T, Option[T])] = - p ~ (` as ` *> p).? + p ~ (` as `.backtrack *> p).? } diff --git a/parser/src/test/scala/aqua/parser/AbilitIdExprSpec.scala b/parser/src/test/scala/aqua/parser/AbilityIdExprSpec.scala similarity index 90% rename from parser/src/test/scala/aqua/parser/AbilitIdExprSpec.scala rename to parser/src/test/scala/aqua/parser/AbilityIdExprSpec.scala index d35c9593..bfdc291e 100644 --- a/parser/src/test/scala/aqua/parser/AbilitIdExprSpec.scala +++ b/parser/src/test/scala/aqua/parser/AbilityIdExprSpec.scala @@ -8,7 +8,7 @@ import cats.Id import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -class AbilitIdExprSpec extends AnyFlatSpec with Matchers with AquaSpec { +class AbilityIdExprSpec extends AnyFlatSpec with Matchers with AquaSpec { import AquaSpec._ "abilities" should "be parsed" in { diff --git a/parser/src/test/scala/aqua/parser/head/FromSpec.scala b/parser/src/test/scala/aqua/parser/head/FromSpec.scala new file mode 100644 index 00000000..d70db83a --- /dev/null +++ b/parser/src/test/scala/aqua/parser/head/FromSpec.scala @@ -0,0 +1,46 @@ +package aqua.parser.head + +import aqua.AquaSpec +import aqua.parser.expr.AbilityIdExpr +import aqua.parser.lexer.{Literal, Token} +import aqua.parser.lift.LiftParser.Implicits.* +import aqua.types.LiteralType +import cats.Id +import cats.data.NonEmptyList +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class FromSpec extends AnyFlatSpec with Matchers with AquaSpec { + + import AquaSpec.* + + "from expression" should "be parsed" in { + FromExpr.nameOrAbAs[Id].parseAll("Ability").value should be(Right(toAb("Ability") -> None)) + FromExpr.nameOrAbAs[Id].parseAll("Ability as Ab").value should be( + Right(toAb("Ability") -> Some(toAb("Ab"))) + ) + FromExpr.nameOrAbAs[Id].parseAll("function").value should be( + Left(toName("function") -> None) + ) + FromExpr.nameOrAbAs[Id].parseAll("function as fn").value should be( + Left(toName("function") -> Some(toName("fn"))) + ) + } + + "from list" should "be parsed" in { + Token.comma(FromExpr.nameOrAbAs[Id]).parseAll("Ability").value.head should be( + Right(toAb("Ability") -> None) + ) + Token.comma(FromExpr.nameOrAbAs[Id]).parseAll("Ability as Ab").value.head should be( + Right(toAb("Ability") -> Some(toAb("Ab"))) + ) + + FromExpr.importFrom[Id].parseAll("Ability as Ab from").value should be( + NonEmptyList.one(Right(toAb("Ability") -> Some(toAb("Ab")))) + ) + FromExpr.importFrom[Id].parseAll("Ability from").value should be( + NonEmptyList.one(Right(toAb("Ability") -> None)) + ) + } + +} diff --git a/parser/src/test/scala/aqua/parser/head/ImportFromSpec.scala b/parser/src/test/scala/aqua/parser/head/ImportFromSpec.scala new file mode 100644 index 00000000..48b8dd92 --- /dev/null +++ b/parser/src/test/scala/aqua/parser/head/ImportFromSpec.scala @@ -0,0 +1,45 @@ +package aqua.parser.head + +import aqua.AquaSpec +import aqua.parser.expr.AbilityIdExpr +import aqua.parser.lexer.{Literal, Token} +import aqua.parser.lift.LiftParser.Implicits.* +import aqua.types.LiteralType +import cats.Id +import cats.data.NonEmptyList +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class ImportFromSpec extends AnyFlatSpec with Matchers with AquaSpec { + import AquaSpec.* + + "import from" should "be parsed" in { + FromExpr.nameOrAbAs[Id].parseAll("") + + ImportFromExpr.p[Id].parseAll("import MyModule from \"file.aqua\"").value should be( + ImportFromExpr( + NonEmptyList.one(Right(toAb("MyModule") -> None)), + toStr("file.aqua") + ) + ) + + HeadExpr + .ast[Id] + .parseAll(s"""import MyModule, func as fn from "file.aqua" + |""".stripMargin) + .value + .tail + .value + .headOption + .get + .head should be( + ImportFromExpr( + NonEmptyList.fromListUnsafe( + Right(toAb("MyModule") -> None) :: Left(toName("func") -> Some(toName("fn"))) :: Nil + ), + toStr("file.aqua") + ) + ) + } + +} diff --git a/parser/src/test/scala/aqua/parser/head/ModuleSpec.scala b/parser/src/test/scala/aqua/parser/head/ModuleSpec.scala new file mode 100644 index 00000000..ed80871f --- /dev/null +++ b/parser/src/test/scala/aqua/parser/head/ModuleSpec.scala @@ -0,0 +1,40 @@ +package aqua.parser.head + +import aqua.AquaSpec +import aqua.parser.expr.AbilityIdExpr +import aqua.parser.lexer.{Literal, Token} +import aqua.types.LiteralType +import cats.Id +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import aqua.parser.lift.LiftParser.Implicits.* + +class ModuleSpec extends AnyFlatSpec with Matchers with AquaSpec { + import AquaSpec.* + + "module header" should "be parsed" in { + ModuleExpr.p[Id].parseAll("module MyModule").value should be( + ModuleExpr( + toAb("MyModule"), + None, + Nil, + Nil + ) + ) + + HeadExpr + .ast[Id] + .parseAll(s"""module MyModule declares * + |""".stripMargin) + .value + .head should be( + ModuleExpr( + toAb("MyModule"), + Some(Token.lift[Id, Unit](())), + 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 e2ce0edb..d7629659 100644 --- a/semantics/src/main/scala/aqua/semantics/header/HeaderSem.scala +++ b/semantics/src/main/scala/aqua/semantics/header/HeaderSem.scala @@ -66,13 +66,23 @@ object HeaderSem { ctx .pick(n.value, rn.map(_.value)) .map(validNec) - .getOrElse(error(n, s"Imported file has no ${n.value} declaration")) + .getOrElse( + error( + n, + s"Imported file declares [${ctx.declares.mkString(", ")}], no ${n.value} declared. Try adding `declares *` to that file." + ) + ) }, { case (n, rn) => ctx .pick(n.value, rn.map(_.value)) .map(validNec) - .getOrElse(error(n, s"Imported file has no ${n.value} declaration")) + .getOrElse( + error( + n, + s"Imported file declares [${ctx.declares.mkString(", ")}], no ${n.value} declared. Try adding `declares *` to that file." + ) + ) } ) ) @@ -86,7 +96,7 @@ object HeaderSem { .fold[ResAC[S]]( error( tkn, - "Used module has no `module` header. Please add `module` header or use ... as ModuleName, or switch to import" + s"Used module has no `module` header. Please add `module` header or use ... as ModuleName, or switch to import" ) )(modName => validNec(acm.empty.copy(abilities = Map(modName -> ctx)))) @@ -94,12 +104,13 @@ object HeaderSem { val onExpr: PartialFunction[HeaderExpr[S], Res[S]] = { // Module header, like `module A declares *` case ModuleExpr(name, declareAll, declareNames, declareCustom) => + val shouldDeclare = declareNames.map(_.value).toSet ++ declareCustom.map(_.value) validNec( HeaderSem[S]( // Save module header info acm.empty.copy( module = Some(name.value), - declares = declareNames.map(_.value).toSet ++ declareCustom.map(_.value) + declares = shouldDeclare ), ctx => // When file is handled, check that all the declarations exists @@ -124,7 +135,10 @@ object HeaderSem { ) ) }.combineAll - .map(_ => ctx) + .map(_ => + // TODO: why module name and declares is lost? where is it lost? + ctx.copy(module = Some(name.value), declares = shouldDeclare) + ) ) )