feat(compiler)!: Force aqua header [LNG-308] (#1028)

* Refactor

* deprecate `module`, force `aqua`, fix integration and unit tests

* fix ImportFromSpec

* Savepoint

* Fix

* Semantic error on module header

* Refactor

* Add begin token

* Fix tests

* Remove DHT examples

* Use git aqua-lib

* Fix headers

* Fix headers

* Fix headers

* Fix headers

* Fix headers

* Fix headers

* Fix headers

* Fix test

* Unignore tests

* Update aqua-lib

---------

Co-authored-by: DieMyst <dmitry.shakhtarin@fluence.ai>
This commit is contained in:
InversionSpaces 2024-01-18 10:43:23 +01:00 committed by GitHub
parent 34e274d45e
commit d057a5e695
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
89 changed files with 7342 additions and 434 deletions

View File

@ -19,8 +19,6 @@ import aqua.logging.{LogFormatter, LogLevels}
import aqua.model.AquaContext import aqua.model.AquaContext
import aqua.model.transform.{Transform, TransformConfig} import aqua.model.transform.{Transform, TransformConfig}
import aqua.parser.lexer.{LiteralToken, Token} import aqua.parser.lexer.{LiteralToken, Token}
import aqua.parser.lift.FileSpan.F
import aqua.parser.lift.{FileSpan, Span}
import aqua.parser.{ArrowReturnError, BlockIndentError, LexerError, ParserError} import aqua.parser.{ArrowReturnError, BlockIndentError, LexerError, ParserError}
import aqua.raw.ops.Call import aqua.raw.ops.Call
import aqua.raw.ops.CallArrowRawTag import aqua.raw.ops.CallArrowRawTag

View File

@ -16,7 +16,7 @@ import aqua.parser.expr.AbilityExpr.p
import aqua.parser.lexer.{LiteralToken, Token} import aqua.parser.lexer.{LiteralToken, Token}
import aqua.parser.lift.FileSpan.F import aqua.parser.lift.FileSpan.F
import aqua.parser.lift.{FileSpan, Span} import aqua.parser.lift.{FileSpan, Span}
import aqua.parser.{ArrowReturnError, BlockIndentError, LexerError, ParserError} import aqua.parser.{ArrowReturnError, LexerError, ParserError}
import aqua.raw.ConstantRaw import aqua.raw.ConstantRaw
import aqua.raw.ops.Call import aqua.raw.ops.Call
import aqua.raw.value.ValueRaw import aqua.raw.value.ValueRaw

View File

@ -2,7 +2,6 @@ package aqua.run
import aqua.backend.air.FuncAirGen import aqua.backend.air.FuncAirGen
import aqua.definitions.{FunctionDef, TypeDefinition} import aqua.definitions.{FunctionDef, TypeDefinition}
import aqua.io.OutputPrinter
import aqua.model.transform.{Transform, TransformConfig} import aqua.model.transform.{Transform, TransformConfig}
import aqua.model.{FuncArrow, ValueModel, VarModel} import aqua.model.{FuncArrow, ValueModel, VarModel}
import aqua.parser.lexer.CallArrowToken import aqua.parser.lexer.CallArrowToken
@ -10,6 +9,7 @@ import aqua.parser.lift.Span
import aqua.raw.ops.{Call, CallArrowRawTag, SeqTag} import aqua.raw.ops.{Call, CallArrowRawTag, SeqTag}
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw} import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
import aqua.types.* import aqua.types.*
import cats.data.Validated.{invalid, invalidNec, invalidNel, validNec, validNel} import cats.data.Validated.{invalid, invalidNec, invalidNel, validNec, validNel}
import cats.data.{NonEmptyList, Validated, ValidatedNec} import cats.data.{NonEmptyList, Validated, ValidatedNec}
import cats.effect.kernel.Async import cats.effect.kernel.Async
@ -18,8 +18,7 @@ import cats.syntax.flatMap.*
import cats.syntax.partialOrder.* import cats.syntax.partialOrder.*
import cats.syntax.show.* import cats.syntax.show.*
import cats.syntax.traverse.* import cats.syntax.traverse.*
import cats.{~>, Id} import cats.{Id, ~>}
import scala.collection.immutable.SortedMap import scala.collection.immutable.SortedMap
import scala.concurrent.ExecutionContext import scala.concurrent.ExecutionContext

View File

@ -37,4 +37,4 @@ func bugLng317() -> []string:
worker_job = WorkerJob(runOnSingleWorker = clos()) worker_job = WorkerJob(runOnSingleWorker = clos())
subnet_job <- disjoint_run{worker_job}() subnet_job <- disjoint_run{worker_job}()
finalRes <- runJob(subnet_job) finalRes <- runJob(subnet_job)
<- finalRes <- finalRes

View File

@ -6,20 +6,19 @@ import aqua.parser.head.{FilenameExpr, ImportExpr}
import aqua.parser.lift.{LiftParser, Span} import aqua.parser.lift.{LiftParser, Span}
import aqua.parser.{Ast, ParserError} import aqua.parser.{Ast, ParserError}
import cats.data.{Chain, EitherNec, EitherT, NonEmptyChain, Validated, ValidatedNec}
import cats.parse.Parser0
import cats.syntax.either.*
import cats.syntax.applicative.*
import cats.syntax.flatMap.*
import cats.syntax.functor.*
import cats.syntax.monad.*
import cats.syntax.foldable.*
import cats.syntax.traverse.*
import cats.syntax.validated.*
import cats.data.Chain.* import cats.data.Chain.*
import cats.data.Validated.* import cats.data.Validated.*
import cats.data.{Chain, EitherNec, EitherT, NonEmptyChain, Validated, ValidatedNec}
import cats.parse.Parser0
import cats.syntax.applicative.*
import cats.syntax.either.*
import cats.syntax.flatMap.*
import cats.syntax.foldable.*
import cats.syntax.functor.*
import cats.syntax.monad.*
import cats.syntax.traverse.* import cats.syntax.traverse.*
import cats.{~>, Comonad, Monad} import cats.syntax.validated.*
import cats.{Comonad, Monad, ~>}
import scribe.Logging import scribe.Logging
// TODO: add tests // TODO: add tests
@ -48,9 +47,9 @@ class AquaParser[F[_]: Monad, E, I, S[_]: Comonad](
// Resolve imports (not parse, just resolve) of the given file // Resolve imports (not parse, just resolve) of the given file
private def resolveImports(id: I, ast: Body): F[ValidatedNec[Err, AquaModule[I, Err, Body]]] = private def resolveImports(id: I, ast: Body): F[ValidatedNec[Err, AquaModule[I, Err, Body]]] =
ast.collectHead { case fe: FilenameExpr[S] => ast.head.collect { case fe: FilenameExpr[S] =>
fe.fileValue -> fe.token fe.fileValue -> fe.token
}.value.traverse { case (filename, token) => }.traverse { case (filename, token) =>
sources sources
.resolveImport(id, filename) .resolveImport(id, filename)
.map( .map(

View File

@ -89,7 +89,7 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
val src = Map( val src = Map(
"index.aqua" -> "index.aqua" ->
"""module Foo declares X """aqua Foo declares X
| |
|export foo, foo2 as foo_two, X |export foo, foo2 as foo_two, X
| |
@ -135,7 +135,11 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
it should "create right topology" in { it should "create right topology" in {
val src = Map( val src = Map(
"index.aqua" -> "index.aqua" ->
"""service Op("op"): """aqua Test
|
|export exec
|
|service Op("op"):
| identity(s: string) -> string | identity(s: string) -> string
| |
|func exec(peers: []string) -> []string: |func exec(peers: []string) -> []string:
@ -224,7 +228,11 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
it should "not generate hop back with empty response" in { it should "not generate hop back with empty response" in {
val src = Map( val src = Map(
"index.aqua" -> "index.aqua" ->
"""service Op("op"): """aqua HopBackTest
|
|export exec
|
|service Op("op"):
| call(s: string) | call(s: string)
| |
|func exec(peers: []string): |func exec(peers: []string):
@ -288,7 +296,7 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
val src = Map( val src = Map(
"index.aqua" -> "index.aqua" ->
"""module Import """aqua Import
|import foobar from "export2.aqua" |import foobar from "export2.aqua"
| |
|use foo as f from "export2.aqua" as Exp |use foo as f from "export2.aqua" as Exp
@ -307,7 +315,7 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
) )
val imports = Map( val imports = Map(
"export2.aqua" -> "export2.aqua" ->
"""module Export declares foobar, foo """aqua Export declares foobar, foo
| |
|func bar() -> string: |func bar() -> string:
| <- " I am MyFooBar bar" | <- " I am MyFooBar bar"
@ -323,7 +331,7 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
| |
|""".stripMargin, |""".stripMargin,
"../gen/OneMore.aqua" -> "../gen/OneMore.aqua" ->
""" """aqua Test declares OneMore
|service OneMore: |service OneMore:
| more_call() | more_call()
| consume(s: string) | consume(s: string)
@ -379,7 +387,10 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
it should "optimize math inside stream join" in { it should "optimize math inside stream join" in {
val src = Map( val src = Map(
"main.aqua" -> """ "main.aqua" -> """aqua Test
|
|export main
|
|func main(i: i32): |func main(i: i32):
| stream: *string | stream: *string
| stream <<- "a" | stream <<- "a"
@ -434,8 +445,7 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
it should "allow returning and passing services as abilities" in { it should "allow returning and passing services as abilities" in {
val src = Map( val src = Map(
"main.aqua" -> """ "main.aqua" -> """aqua Test
|aqua Test
| |
|export test |export test
| |

View File

@ -1,16 +0,0 @@
import "@fluencelabs/aqua-dht/pubsub.aqua"
import "@fluencelabs/aqua-dht/dht.aqua"
import "@fluencelabs/aqua-lib/builtin.aqua"
export getNeighbours, initTopicAndSubscribe, findSubscribers
func put_value(initial_peer: string, value: string) -> string:
initTopicAndSubscribe(initial_peer, value, nil, nil)
<- "OK"
func registerKeyPutValue(node_id: string, key: string, value: string, relay_id: ?string, service_id: ?string) -> []string:
nodes <- getNeighbours(key)
for n <- nodes par:
on n:
t <- Peer.timestamp_sec()
<- nodes

View File

@ -1,9 +1,9 @@
aqua Main aqua Main
use DECLARE_CONST, decl_bar from "imports_exports/declare.aqua" as Declare
export handleAb, SomeService, bug214, checkAbCalls, bugLNG258_1, bugLNG258_2, bugLNG258_3, multipleAbilityWithClosure, MySrv, returnSrvAsAbility export handleAb, SomeService, bug214, checkAbCalls, bugLNG258_1, bugLNG258_2, bugLNG258_3, multipleAbilityWithClosure, MySrv, returnSrvAsAbility
use DECLARE_CONST, decl_bar from "imports_exports/declare.aqua" as Declare
service SomeService("wed"): service SomeService("wed"):
getStr(s: string) -> string getStr(s: string) -> string

View File

@ -1,24 +0,0 @@
data SomeData:
value: string
otherValue: u64
data SubData:
someStr: string
someNum: i32
data SecondData:
value: string
complex: SubData
data ThirdData:
value: string
complex: SomeData
service ComplexService("op-ha"):
call(d: SomeData, sd: SecondData) -> SubData
identity() -> SecondData
func doSmth(d: SomeData, d2: SomeData, sd: SecondData, c: SubData, SecondData -> ThirdData) -> ThirdData:
res <- ComplexService.call(d, sd)
res2 <- c(res, sd)
<- res2

View File

@ -1,3 +1,7 @@
aqua Assignment
export doSmth
data Prod: data Prod:
value: string value: string

View File

@ -1,6 +1,11 @@
aqua CallArrow
export passFunctionAsArg, reproArgsBug426
import "println.aqua" import "println.aqua"
import "@fluencelabs/aqua-lib/builtin.aqua" import "@fluencelabs/aqua-lib/builtin.aqua"
-- functions like `c` are called an 'arrow function' in Aqua -- functions like `c` are called an 'arrow function' in Aqua
-- `c` passed to a function from a client, so, it could be called only on a client -- `c` passed to a function from a client, so, it could be called only on a client
func passFunctionAsArg(node: string, str: string, c: string -> string): func passFunctionAsArg(node: string, str: string, c: string -> string):

View File

@ -1,3 +1,7 @@
aqua Canon
export Ser, bugLng79
data Record: data Record:
relay_id: []string relay_id: []string
peer_id: string peer_id: string

View File

@ -1,9 +1,9 @@
module Closure declares * aqua Closure declares *
import "@fluencelabs/aqua-lib/builtin.aqua"
export LocalSrv, closureIn, closureOut, closureBig, closureOut2, lng58Bug, multipleClosuresBugLNG262, lng317Bug export LocalSrv, closureIn, closureOut, closureBig, closureOut2, lng58Bug, multipleClosuresBugLNG262, lng317Bug
import "@fluencelabs/aqua-lib/builtin.aqua"
service MyOp("op"): service MyOp("op"):
identity(s: string) -> string identity(s: string) -> string

View File

@ -1,3 +1,7 @@
aqua Co
export CoService, coFunc
import "@fluencelabs/aqua-lib/builtin.aqua" import "@fluencelabs/aqua-lib/builtin.aqua"
service CoService("coservice-id"): service CoService("coservice-id"):

View File

@ -1,3 +1,7 @@
aqua CollectionSugar
export arraySugar, streamSugar, optionSugar, GetArr, bugLNG59
import "@fluencelabs/aqua-lib/builtin.aqua" import "@fluencelabs/aqua-lib/builtin.aqua"
func arraySugar(n: u32, m: u32) -> []u32, []u32: func arraySugar(n: u32, m: u32) -> []u32, []u32:

View File

@ -1,3 +1,7 @@
aqua Complex
export TestS, doStuff
import "helloWorld.aqua" import "helloWorld.aqua"
import "println.aqua" import "println.aqua"
import "@fluencelabs/aqua-lib/builtin.aqua" import "@fluencelabs/aqua-lib/builtin.aqua"

View File

@ -1,3 +1,7 @@
aqua Constants
export Getter, callConstant, timestampAndTtl
import "@fluencelabs/aqua-lib/builtin.aqua" import "@fluencelabs/aqua-lib/builtin.aqua"
service Getter("test"): service Getter("test"):

View File

@ -1,3 +1,7 @@
aqua DataAlias
export NodeIdGetter, getAliasedData
-- set `PeerId` name to be a type alias for `string` type -- set `PeerId` name to be a type alias for `string` type
alias PeerId : string alias PeerId : string

View File

@ -1,3 +1,5 @@
aqua Example
service Peer("peer"): service Peer("peer"):
is_connected: string -> bool is_connected: string -> bool

View File

@ -1,3 +1,7 @@
aqua Fold
export iterateAndPrint, iterateAndPrintParallel, forBug499
import "println.aqua" import "println.aqua"
import "@fluencelabs/aqua-lib/builtin.aqua" import "@fluencelabs/aqua-lib/builtin.aqua"

View File

@ -1,9 +1,9 @@
module FoldJoin aqua FoldJoin
import "@fluencelabs/aqua-lib/builtin.aqua"
export getTwoResults export getTwoResults
import "@fluencelabs/aqua-lib/builtin.aqua"
service Op2("op"): service Op2("op"):
identity(s: u64) identity(s: u64)

View File

@ -1,3 +1,7 @@
aqua Func
export TestSrv, testFunc
service TestSrv("test-service-id"): service TestSrv("test-service-id"):
str: -> string str: -> string

View File

@ -1,4 +1,4 @@
module Funcs declares main, A, calc aqua Funcs declares main, A, calc
export main, A, calc, calc2, ifCalc, bugLNG260 export main, A, calc, calc2, ifCalc, bugLNG260

View File

@ -1,3 +1,7 @@
aqua Functors
export lng119Bug
func lng119Bug() -> []u32: func lng119Bug() -> []u32:
nums = [1,2,3,4,5] nums = [1,2,3,4,5]
results: *u32 results: *u32

View File

@ -1,3 +1,7 @@
aqua HelloWorld
export StringExtra, helloWorld
service StringExtra("service-id"): service StringExtra("service-id"):
addNameToHello: string -> string addNameToHello: string -> string

View File

@ -1,3 +1,7 @@
aqua If
export ifElseCall, ifElseNumCall, ifCorrectXorWrap, bugLNG69
import "println.aqua" import "println.aqua"
import "@fluencelabs/aqua-lib/builtin.aqua" import "@fluencelabs/aqua-lib/builtin.aqua"

View File

@ -1,4 +1,4 @@
module FooBars declares decl_foo, decl_bar, SuperFoo, DECLARE_CONST, DECLARE_CONST2 aqua FooBars declares decl_foo, decl_bar, SuperFoo, DECLARE_CONST, DECLARE_CONST2
export SuperFoo export SuperFoo
const DECLARE_CONST = "declare_const" const DECLARE_CONST = "declare_const"

View File

@ -1,4 +1,4 @@
module Export declares foobar, foo aqua Export declares foobar, foo
import Op as Noop from "@fluencelabs/aqua-lib/builtin.aqua" import Op as Noop from "@fluencelabs/aqua-lib/builtin.aqua"

View File

@ -1,5 +1,5 @@
-- exports3.aqua -- exports3.aqua
module Export3 declares * aqua Export3 declares *
import Op as Noop from "@fluencelabs/aqua-lib/builtin.aqua" import Op as Noop from "@fluencelabs/aqua-lib/builtin.aqua"

View File

@ -1,4 +1,4 @@
module Exports declares some_string, MyExportSrv, EXPORT_CONST, some_random_func aqua Exports declares some_string, MyExportSrv, EXPORT_CONST, some_random_func
import Op as Noop from "@fluencelabs/aqua-lib/builtin.aqua" import Op as Noop from "@fluencelabs/aqua-lib/builtin.aqua"

View File

@ -1,2 +1,4 @@
aqua OneMore declares OneMore
service OneMore: service OneMore:
more_call() more_call()

View File

@ -1,4 +1,4 @@
module Import aqua Import
import foobar from "export2.aqua" import foobar from "export2.aqua"
use foo as f from "export2.aqua" as Exp use foo as f from "export2.aqua" as Exp

View File

@ -1,5 +1,5 @@
-- imports3.aqua -- imports3.aqua
module Import3 declares * aqua Import3 declares *
import Op as Noop from "@fluencelabs/aqua-lib/builtin.aqua" import Op as Noop from "@fluencelabs/aqua-lib/builtin.aqua"
export foo_wrapper export foo_wrapper

View File

@ -1,3 +1,5 @@
aqua ImportsEmpty
import decl_foo, decl_bar from "declare.aqua" import decl_foo, decl_bar from "declare.aqua"
use DECLARE_CONST, SuperFoo, DECLARE_CONST2 as DC2 from "declare.aqua" as Declare use DECLARE_CONST, SuperFoo, DECLARE_CONST2 as DC2 from "declare.aqua" as Declare
import Op as Noop from "@fluencelabs/aqua-lib/builtin.aqua" import Op as Noop from "@fluencelabs/aqua-lib/builtin.aqua"

View File

@ -1,3 +1,7 @@
aqua Imports
export StringService, concat_foobars
import decl_foo, decl_bar from "declare.aqua" import decl_foo, decl_bar from "declare.aqua"
use DECLARE_CONST, SuperFoo, DECLARE_CONST2 as DC2 from "declare.aqua" as Declare use DECLARE_CONST, SuperFoo, DECLARE_CONST2 as DC2 from "declare.aqua" as Declare
import Op as Noop from "@fluencelabs/aqua-lib/builtin.aqua" import Op as Noop from "@fluencelabs/aqua-lib/builtin.aqua"

View File

@ -1,3 +1,4 @@
aqua SubImport declares *
alias SomeString : string alias SomeString : string

View File

@ -1,3 +1,7 @@
aqua Join
export joinIdxLocal, joinIdxRelay, joinIdx
import "@fluencelabs/aqua-lib/builtin.aqua" import "@fluencelabs/aqua-lib/builtin.aqua"
func joinIdxLocal(idx: i16, nodes: []string) -> []string: func joinIdxLocal(idx: i16, nodes: []string) -> []string:

View File

@ -1,3 +1,7 @@
aqua MultiReturn
export GetStr, GetNum, multiReturnFunc
import "@fluencelabs/aqua-lib/builtin.aqua" import "@fluencelabs/aqua-lib/builtin.aqua"
service GetStr("multiret-test"): service GetStr("multiret-test"):

View File

@ -1,3 +1,7 @@
aqua NestedData
export Test, test
data NestedType: data NestedType:
val: string val: string

View File

@ -1,3 +1,7 @@
aqua NestedFuncs
export OpH, d
import "@fluencelabs/aqua-lib/builtin.aqua" import "@fluencelabs/aqua-lib/builtin.aqua"
service OpH("opa"): service OpH("opa"):

View File

@ -1,3 +1,7 @@
aqua On
export getPeerExternalAddresses
import "@fluencelabs/aqua-lib/builtin.aqua" import "@fluencelabs/aqua-lib/builtin.aqua"
func getPeerExternalAddresses(otherNodePeerId: string) -> []string: func getPeerExternalAddresses(otherNodePeerId: string) -> []string:

View File

@ -1,3 +1,7 @@
aqua OnErrorPropagation
export Test, onPropagate, nestedOnPropagate, seqOnPropagate
service Test("test-service"): service Test("test-service"):
fail(err: string) fail(err: string)

View File

@ -1,3 +1,7 @@
aqua Option
export SomeS, useOptional, returnOptional, returnNone
import "@fluencelabs/aqua-lib/builtin.aqua" import "@fluencelabs/aqua-lib/builtin.aqua"
service SomeS("test2"): service SomeS("test2"):

View File

@ -1,3 +1,7 @@
aqua OptionGen
export OptionString, emptyString, checkEmpty, checkNoneEmpty
service OptionString("opt_str"): service OptionString("opt_str"):
checkOption(str: ?string) -> string checkOption(str: ?string) -> string

View File

@ -1,3 +1,7 @@
aqua Par
export ParService, parFunc, testTimeout
import "@fluencelabs/aqua-lib/builtin.aqua" import "@fluencelabs/aqua-lib/builtin.aqua"
service ParService("parservice-id"): service ParService("parservice-id"):

View File

@ -1,3 +1,7 @@
aqua ParSeq
export testParSeq
import "@fluencelabs/aqua-lib/builtin.aqua" import "@fluencelabs/aqua-lib/builtin.aqua"
service NumOp("op"): service NumOp("op"):

View File

@ -1,3 +1,7 @@
aqua PassArgs
export AquaDHT, create_client_util, bugLNG60
import Op from "@fluencelabs/aqua-lib/builtin.aqua" import Op from "@fluencelabs/aqua-lib/builtin.aqua"
service AquaDHT("test-dht"): service AquaDHT("test-dht"):

View File

@ -1,3 +1,7 @@
aqua Println declares *
export Println, print
service Println("println-service-id"): service Println("println-service-id"):
print: string -> () print: string -> ()

View File

@ -1,3 +1,7 @@
aqua PushToStream
export OpA, get_results
service OpA("pop"): service OpA("pop"):
get_str() -> string get_str() -> string

View File

@ -1,9 +1,9 @@
aqua ReturnArrow aqua ReturnArrow
import "@fluencelabs/aqua-lib/builtin.aqua"
export callReturnedArrow, callReturnedChainArrow export callReturnedArrow, callReturnedChainArrow
import "@fluencelabs/aqua-lib/builtin.aqua"
func returnCall(arg: string) -> string -> string, string: func returnCall(arg: string) -> string -> string, string:
str <- Op.concat_strings(arg, " literal") str <- Op.concat_strings(arg, " literal")
closure = (s: string) -> string, string: closure = (s: string) -> string, string:

View File

@ -1,2 +1,6 @@
aqua ReturnLiteral
export returnLiteral
func returnLiteral() -> string: func returnLiteral() -> string:
<- "some literal" <- "some literal"

View File

@ -1,8 +1,5 @@
aqua Stream aqua Stream
import "@fluencelabs/aqua-lib/builtin.aqua"
import "println.aqua"
export Stringer export Stringer
export checkStreams, returnStreamFromFunc export checkStreams, returnStreamFromFunc
export stringEmpty, returnEmptyLiteral export stringEmpty, returnEmptyLiteral
@ -10,6 +7,9 @@ export returnNilLength, stringNone
export streamFunctor, streamAssignment export streamFunctor, streamAssignment
export streamIntFunctor, streamJoin export streamIntFunctor, streamJoin
import "@fluencelabs/aqua-lib/builtin.aqua"
import "println.aqua"
service Stringer("stringer-id"): service Stringer("stringer-id"):
returnString: string -> string returnString: string -> string

View File

@ -1,4 +1,4 @@
module Ret declares * aqua Ret declares *
export someFunc export someFunc

View File

@ -1,4 +1,6 @@
export accumRes, bugLNG63, bugLNG63_2 aqua StreamCan
export accumRes, bugLNG63, bugLNG63_2, bugLNG63_3
func toOpt(s: string) -> ?string: func toOpt(s: string) -> ?string:
str: *string str: *string

View File

@ -1,3 +1,7 @@
aqua StreamRestriction
export streamFold, streamRes
func streamFold(arr: []string) -> []string: func streamFold(arr: []string) -> []string:
res: *string res: *string
for n <- arr: for n <- arr:

View File

@ -1,3 +1,7 @@
aqua StreamResults
export DTGetter, use_name1, use_name2
data DT: data DT:
field: string field: string

View File

@ -1,9 +1,9 @@
aqua Aaa aqua Aaa
import "@fluencelabs/aqua-lib/builtin.aqua"
export structuralTypingTest export structuralTypingTest
import "@fluencelabs/aqua-lib/builtin.aqua"
data WideData: data WideData:
s: string s: string
n: u32 n: u32

View File

@ -1,3 +1,7 @@
aqua SubImportUsage
export subImportUsage, ConcatSubs
import "imports_exports/subImport.aqua" import "imports_exports/subImport.aqua"
service ConcatSubs("concat_subs"): service ConcatSubs("concat_subs"):

View File

@ -1,3 +1,7 @@
aqua TryCatch
export tryCatchTest
import "@fluencelabs/aqua-lib/builtin.aqua" import "@fluencelabs/aqua-lib/builtin.aqua"
service Unexisted("unex"): service Unexisted("unex"):

View File

@ -1,3 +1,7 @@
aqua TryOtherwise
export tryOtherwiseTest
service Unexisted("unex"): service Unexisted("unex"):
getStr() -> string getStr() -> string

6818
integration-tests/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -32,7 +32,6 @@
"prettier": {}, "prettier": {},
"devDependencies": { "devDependencies": {
"@fluencelabs/aqua-api": "0.13.4", "@fluencelabs/aqua-api": "0.13.4",
"@fluencelabs/aqua-dht": "0.2.5",
"@fluencelabs/aqua-lib": "0.9.0", "@fluencelabs/aqua-lib": "0.9.0",
"@types/jest": "29.5.2", "@types/jest": "29.5.2",
"@types/node": "18.19.3", "@types/node": "18.19.3",

View File

@ -3,29 +3,27 @@ package aqua
import aqua.files.FileModuleId import aqua.files.FileModuleId
import aqua.parser.lift.{FileSpan, Span} import aqua.parser.lift.{FileSpan, Span}
import aqua.parser.{Ast, Parser, ParserError} import aqua.parser.{Ast, Parser, ParserError}
import cats.data.* import cats.data.*
import cats.parse.LocationMap import cats.parse.LocationMap
import cats.{~>, Comonad, Eval, Monad, Monoid, Order} import cats.{Comonad, Eval, Monad, Monoid, Order, ~>}
object SpanParser extends scribe.Logging { object SpanParser extends scribe.Logging {
def parser: FileModuleId => String => ValidatedNec[ParserError[FileSpan.F], Ast[FileSpan.F]] = { def parser: FileModuleId => String => ValidatedNec[ParserError[FileSpan.F], Ast[FileSpan.F]] =
id => id =>
{ source => source => {
{ logger.trace(s"creating parser for $id...")
logger.trace(s"creating parser for $id...") val nat = new (Span.S ~> FileSpan.F) {
val nat = new (Span.S ~> FileSpan.F) { override def apply[A](span: Span.S[A]): FileSpan.F[A] = {
override def apply[A](span: Span.S[A]): FileSpan.F[A] = { (
( FileSpan(id.file.absolute.toString, Eval.later(LocationMap(source)), span._1),
FileSpan(id.file.absolute.toString, Eval.later(LocationMap(source)), span._1), span._2
span._2 )
)
}
} }
val parser = Parser.natParser(Parser.spanParser, nat)(source)
logger.trace("parser created")
parser
} }
val parser = Parser.natParser(Parser.spanParser, nat)(source)
logger.trace("parser created")
parser
} }
}
} }

View File

@ -7,26 +7,26 @@ import aqua.semantics.rules.locations.LocationsState
import aqua.semantics.{CompilerState, RawSemantics, SemanticError, SemanticWarning, Semantics} import aqua.semantics.{CompilerState, RawSemantics, SemanticError, SemanticWarning, Semantics}
import cats.data.Validated.{Invalid, Valid} import cats.data.Validated.{Invalid, Valid}
import cats.data.{NonEmptyChain, ValidatedNec}
import cats.syntax.applicative.* import cats.syntax.applicative.*
import cats.syntax.apply.* import cats.syntax.apply.*
import cats.syntax.flatMap.*
import cats.syntax.functor.*
import cats.syntax.foldable.*
import cats.syntax.either.* import cats.syntax.either.*
import cats.syntax.flatMap.*
import cats.syntax.foldable.*
import cats.syntax.functor.*
import cats.syntax.reducible.* import cats.syntax.reducible.*
import cats.data.{NonEmptyChain, ValidatedNec}
import monocle.Lens import monocle.Lens
import monocle.macros.GenLens import monocle.macros.GenLens
class LspSemantics[S[_]] extends Semantics[S, LspContext[S]] { class LspSemantics[S[_]] extends Semantics[S, LspContext[S]] {
private def getImportTokens(ast: Ast[S]): List[LiteralToken[S]] = private def getImportTokens(ast: Ast[S]): List[LiteralToken[S]] =
ast.collectHead { ast.head.collect {
case ImportExpr(fn) => fn case ImportExpr(fn) => fn
case ImportFromExpr(_, fn) => fn case ImportFromExpr(_, fn) => fn
case UseExpr(fn, _) => fn case UseExpr(fn, _) => fn
case UseFromExpr(_, fn, _) => fn case UseFromExpr(_, fn, _) => fn
}.value.toList }.toList
/** /**
* Process the AST and return the semantics result. * Process the AST and return the semantics result.

View File

@ -108,22 +108,18 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
def compile( def compile(
src: Map[String, String], src: Map[String, String],
imports: Map[String, String] = Map.empty imports: Map[String, String] = Map.empty
): ValidatedNec[AquaError[String, String, S], Map[String, LspContext[S]]] = { ): ValidatedNec[AquaError[String, String, S], Map[String, LspContext[S]]] =
LSPCompiler LSPCompiler
.compileToLsp[Id, String, String, Span.S]( .compileToLsp[Id, String, String, Span.S](
aquaSource(src, imports), aquaSource(src, imports),
id => txt => Parser.parse(Parser.parserSchema)(txt), id => txt => Parser.parse(Parser.parserSchema)(txt),
AquaCompilerConf(ConstantRaw.defaultConstants(None)) AquaCompilerConf(ConstantRaw.defaultConstants(None))
) )
.leftMap { errors =>
println(errors)
errors
}
}
it should "return right tokens" in { it should "return right tokens" in {
val main = val main =
"""module Import """aqua Import
|
|import foo, strFunc, num from "export2.aqua" |import foo, strFunc, num from "export2.aqua"
| |
|import "../gen/OneMore.aqua" |import "../gen/OneMore.aqua"
@ -156,7 +152,7 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
) )
val firstImport = val firstImport =
"""module Export declares strFunc, num, foo """aqua Export declares strFunc, num, foo
| |
|func absb() -> string: |func absb() -> string:
| <- "ff" | <- "ff"
@ -173,7 +169,8 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
|""".stripMargin |""".stripMargin
val secondImport = val secondImport =
""" """aqua Export declares OneMore
|
|service OneMore: |service OneMore:
| more_call() | more_call()
| consume(s: string) | consume(s: string)
@ -226,7 +223,7 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
// this is tokens from imports, if we will use `FileSpan.F` file names will be different // this is tokens from imports, if we will use `FileSpan.F` file names will be different
// OneMore service // OneMore service
res.checkTokenLoc(secondImport, "OneMore", 0, serviceType) shouldBe true res.checkTokenLoc(secondImport, "OneMore", 1, serviceType) shouldBe true
res.checkTokenLoc( res.checkTokenLoc(
secondImport, secondImport,
"more_call", "more_call",
@ -265,7 +262,7 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
ProductType(ScalarType.u32 :: Nil) ProductType(ScalarType.u32 :: Nil)
) )
) shouldBe true ) shouldBe true
res.checkTokenLoc(firstImport, "someVar", 2, ScalarType.u32, None, true) shouldBe true res.checkTokenLoc(firstImport, "someVar", 2, ScalarType.u32, None) shouldBe true
// foo function // foo function
res.checkTokenLoc( res.checkTokenLoc(

View File

@ -1,43 +1,48 @@
package aqua.parser package aqua.parser
import aqua.helpers.tree.Tree import aqua.helpers.tree.Tree
import aqua.parser.expr.* import aqua.parser.head.HeaderExpr
import aqua.parser.head.{HeadExpr, HeaderExpr} import aqua.parser.lexer.Token
import aqua.parser.lift.{LiftParser, Span}
import aqua.parser.lift.LiftParser.*
import cats.data.{Chain, Validated, ValidatedNec} import cats.data.Chain
import cats.syntax.flatMap.*
import cats.free.Cofree import cats.free.Cofree
import cats.{Comonad, Eval} import cats.syntax.flatMap.*
import cats.~> import cats.syntax.show.*
import cats.Show import cats.{Comonad, ~>}
import cats.{Eval, Show}
case class Ast[S[_]](head: Ast.Head[S], tree: Ast.Tree[S]) { case class Ast[S[_]](head: Ast.Head[S], tree: Ast.Tree[S]) {
def mapK[K[_]: Comonad](nt: S ~> K): Ast[K] =
Ast(head.mapK(nt), tree.map(_.mapK(nt)))
def cata[T](folder: (Expr[S], Chain[T]) => Eval[T]): Eval[T] = def cata[T](folder: (Expr[S], Chain[T]) => Eval[T]): Eval[T] =
Cofree.cata[Chain, Expr[S], T](tree)(folder) Cofree.cata[Chain, Expr[S], T](tree)(folder)
def cataHead[T](folder: (HeaderExpr[S], Chain[T]) => Eval[T]): Eval[T] =
Cofree.cata[Chain, HeaderExpr[S], T](head)(folder)
def collectHead[T](pf: PartialFunction[HeaderExpr[S], T]): Eval[Chain[T]] =
cataHead((e, acc: Chain[Chain[T]]) =>
Eval.later {
val flatAcc = acc.flatten
if (pf.isDefinedAt(e)) flatAcc :+ pf(e) else flatAcc
}
)
} }
object Ast { object Ast {
type Head[S[_]] = Cofree[Chain, HeaderExpr[S]]
final case class Head[S[_]](
begin: Token[S],
headers: Chain[HeaderExpr[S]]
) {
def mapK[K[_]: Comonad](nt: S ~> K): Head[K] =
copy(
begin = begin.mapK(nt),
headers = headers.map(_.mapK(nt))
)
def collect[T](pf: PartialFunction[HeaderExpr[S], T]): Chain[T] =
headers.collect(pf)
}
type Tree[S[_]] = Cofree[Chain, Expr[S]] type Tree[S[_]] = Cofree[Chain, Expr[S]]
given [S[_]]: Show[Ast[S]] with { given [S[_]]: Show[Ast[S]] with {
def show(ast: Ast[S]): String = { def show(ast: Ast[S]): String = {
val head = Tree.show(ast.head) val head = ast.head.headers.map(_.show).toList.mkString("\n")
val body = Tree.show(ast.tree) val body = Tree.show(ast.tree)
s"$head\n$body" s"$head\n$body"

View File

@ -1,13 +1,15 @@
package aqua.parser package aqua.parser
import aqua.parser.expr.RootExpr import aqua.parser.expr.RootExpr
import aqua.parser.head.HeadExpr import aqua.parser.head.Header
import aqua.parser.lift.LiftParser.LiftErrorOps import aqua.parser.lift.LiftParser.LiftErrorOps
import aqua.parser.lift.Span.S import aqua.parser.lift.Span.S
import aqua.parser.lift.{LiftParser, Span} import aqua.parser.lift.{LiftParser, Span}
import cats.data.{Validated, ValidatedNec} import cats.data.{Validated, ValidatedNec}
import cats.free.Cofree
import cats.parse.{Parser as P, Parser0 as P0} import cats.parse.{Parser as P, Parser0 as P0}
import cats.syntax.validated.*
import cats.{Comonad, ~>} import cats.{Comonad, ~>}
object Parser extends scribe.Logging { object Parser extends scribe.Logging {
@ -15,7 +17,7 @@ object Parser extends scribe.Logging {
def parserSchema: P0[ValidatedNec[ParserError[Span.S], Ast[Span.S]]] = { def parserSchema: P0[ValidatedNec[ParserError[Span.S], Ast[Span.S]]] = {
logger.trace("creating schema...") logger.trace("creating schema...")
val parser = (HeadExpr.ast ~ RootExpr.ast0).map { case (head, bodyMaybe) => val parser = (Header.p ~ RootExpr.ast0).map { case (head, bodyMaybe) =>
bodyMaybe.map(Ast(head, _)) bodyMaybe.map(Ast(head, _))
} }
logger.trace("schema created") logger.trace("schema created")
@ -24,19 +26,15 @@ object Parser extends scribe.Logging {
def parse[S[_]: LiftParser: Comonad]( def parse[S[_]: LiftParser: Comonad](
p: P0[ValidatedNec[ParserError[S], Ast[S]]] p: P0[ValidatedNec[ParserError[S], Ast[S]]]
)(source: String): ValidatedNec[ParserError[S], Ast[S]] = { )(source: String): ValidatedNec[ParserError[S], Ast[S]] =
p.parseAll(source) match { p.parseAll(source).left.map(e => LexerError(e.wrapErr).invalidNec).merge
case Right(value) => value
case Left(e) => Validated.invalidNec(LexerError(e.wrapErr))
}
}
def natParser[S[_]: LiftParser: Comonad, K[_]: Comonad]( def natParser[S[_]: LiftParser: Comonad, K[_]: Comonad](
p: P0[ValidatedNec[ParserError[S], Ast[S]]], p: P0[ValidatedNec[ParserError[S], Ast[S]]],
nat: S ~> K nat: S ~> K
)(source: String): ValidatedNec[ParserError[K], Ast[K]] = )(source: String): ValidatedNec[ParserError[K], Ast[K]] =
parse[S](p)(source).bimap( parse(p)(source).bimap(
e => e.map(_.mapK(nat)), e => e.map(_.mapK(nat)),
ast => Ast[K](ast.head.map(_.mapK(nat)), ast.tree.map(_.mapK(nat))) ast => ast.mapK(nat)
) )
} }

View File

@ -3,12 +3,14 @@ package aqua.parser.expr
import aqua.parser.Ast.Tree import aqua.parser.Ast.Tree
import aqua.parser.lexer.Token import aqua.parser.lexer.Token
import aqua.parser.lexer.Token.* import aqua.parser.lexer.Token.*
import aqua.parser.lift.{LiftParser, Span}
import aqua.parser.lift.LiftParser.* import aqua.parser.lift.LiftParser.*
import aqua.parser.lift.{LiftParser, Span}
import aqua.parser.{Expr, ParserError} import aqua.parser.{Expr, ParserError}
import cats.data.{Chain, NonEmptyChain, NonEmptyList, Validated, ValidatedNec} import cats.data.{Chain, NonEmptyChain, NonEmptyList, Validated, ValidatedNec}
import cats.free.Cofree import cats.free.Cofree
import cats.parse.{Parser0 as P0, Parser as P} import cats.parse.{Parser as P, Parser0 as P0}
import cats.syntax.option.*
import cats.{Comonad, Eval} import cats.{Comonad, Eval}
import cats.~> import cats.~>
@ -24,7 +26,9 @@ object RootExpr extends Expr.Companion {
def validChildren: List[Expr.Lexem] = def validChildren: List[Expr.Lexem] =
ServiceExpr :: AliasExpr :: DataStructExpr :: AbilityExpr :: ConstantExpr :: FuncExpr :: Nil ServiceExpr :: AliasExpr :: DataStructExpr :: AbilityExpr :: ConstantExpr :: FuncExpr :: Nil
private def gatherResults[F[_]: LiftParser: Comonad](results: NonEmptyList[ValidatedNec[ParserError[F], Tree[F]]]): (Chain[ParserError[F]], Chain[Tree[F]]) = { private def gatherResults[F[_]: LiftParser: Comonad](
results: NonEmptyList[ValidatedNec[ParserError[F], Tree[F]]]
): (Chain[ParserError[F]], Chain[Tree[F]]) = {
results.foldLeft[(Chain[ParserError[F]], Chain[Tree[F]])](Chain.empty -> Chain.empty) { results.foldLeft[(Chain[ParserError[F]], Chain[Tree[F]])](Chain.empty -> Chain.empty) {
case ((errs, trees), Validated.Valid(tree)) => (errs, trees :+ tree) case ((errs, trees), Validated.Valid(tree)) => (errs, trees :+ tree)
case ((errs, trees), Validated.Invalid(err)) => (errs ++ err.toChain, trees) case ((errs, trees), Validated.Invalid(err)) => (errs ++ err.toChain, trees)
@ -54,12 +58,11 @@ object RootExpr extends Expr.Companion {
empty.backtrack | ast empty.backtrack | ast
override val ast: P[ValidatedNec[ParserError[Span.S], Tree[Span.S]]] = override val ast: P[ValidatedNec[ParserError[Span.S], Tree[Span.S]]] =
parserSchema parserSchema.map { case (point, (errs, trees)) =>
.map { case (point, (errs, trees)) =>
NonEmptyChain NonEmptyChain
.fromChain(errs) .fromChain(errs)
.fold[ValidatedNec[ParserError[Span.S], Tree[Span.S]]]( .toInvalid(
Validated.validNec(Cofree(RootExpr[Span.S](point), Eval.now(trees))) Cofree(RootExpr(point), Eval.now(trees))
)(Validated.invalid) )
} }
} }

View File

@ -3,13 +3,14 @@ package aqua.parser.head
import aqua.parser.lexer.Token.* import aqua.parser.lexer.Token.*
import aqua.parser.lexer.{LiteralToken, Token, ValueToken} import aqua.parser.lexer.{LiteralToken, Token, ValueToken}
import aqua.parser.lift.LiftParser import aqua.parser.lift.LiftParser
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import cats.Comonad import cats.Comonad
import cats.data.NonEmptyList import cats.data.NonEmptyList
import cats.parse.Parser import cats.parse.Parser
import cats.syntax.either.* import cats.syntax.either.*
import cats.~> import cats.~>
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
case class ExportExpr[F[_]](pubs: NonEmptyList[FromExpr.NameOrAbAs[F]]) extends HeaderExpr[F] { case class ExportExpr[F[_]](pubs: NonEmptyList[FromExpr.NameOrAbAs[F]]) extends HeaderExpr[F] {
@ -20,7 +21,7 @@ case class ExportExpr[F[_]](pubs: NonEmptyList[FromExpr.NameOrAbAs[F]]) extends
copy(FromExpr.mapK(pubs)(fk)) copy(FromExpr.mapK(pubs)(fk))
} }
object ExportExpr extends HeaderExpr.Leaf { object ExportExpr extends HeaderExpr.Companion {
override val p: Parser[ExportExpr[Span.S]] = override val p: Parser[ExportExpr[Span.S]] =
(`_export` *> ` `) *> comma(FromExpr.nameOrAbAs).map(ExportExpr(_)) (`_export` *> ` `) *> comma(FromExpr.nameOrAbAs).map(ExportExpr(_))

View File

@ -1,39 +0,0 @@
package aqua.parser.head
import aqua.parser.Ast
import aqua.parser.lexer.Token.` \n+`
import aqua.parser.lift.LiftParser
import aqua.parser.lift.LiftParser.*
import cats.{Comonad, Eval}
import cats.data.Chain
import cats.free.Cofree
import cats.parse.{Parser => P, Parser0 => P0}
import aqua.parser.lexer.Token
import cats.~>
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
case class HeadExpr[S[_]](token: Token[S]) extends HeaderExpr[S] {
def mapK[K[_]: Comonad](fk: S ~> K): HeadExpr[K] =
copy(token.mapK(fk))
}
object HeadExpr {
def headExprs: List[HeaderExpr.Companion] =
UseFromExpr :: UseExpr :: ImportFromExpr :: ImportExpr :: ExportExpr :: Nil
val ast: P0[Ast.Head[Span.S]] =
(P.unit.lift0.map(Token.lift) ~ ((ModuleExpr.p <* ` \n+`).? ~
P.repSep0(P.oneOf(headExprs.map(_.ast.backtrack)), ` \n+`).map(Chain.fromSeq))
.surroundedBy(` \n+`.?)
.?).map {
case (p, Some((maybeMod, exprs))) =>
Cofree(
maybeMod.getOrElse(HeadExpr[Span.S](p)),
Eval.now(exprs)
)
case (p, None) => Cofree(HeadExpr[Span.S](p), Eval.now(Chain.nil))
}
}

View File

@ -0,0 +1,31 @@
package aqua.parser.head
import aqua.parser.Ast
import aqua.parser.lexer.Token
import aqua.parser.lexer.Token.` \n+`
import aqua.parser.lift.LiftParser
import aqua.parser.lift.LiftParser.*
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import cats.data.Chain
import cats.free.Cofree
import cats.parse.{Parser => P, Parser0 => P0}
import cats.{Comonad, Eval}
import cats.~>
object Header {
def headExprs: List[HeaderExpr.Companion] =
ModuleExpr :: UseFromExpr :: UseExpr :: ImportFromExpr :: ImportExpr :: ExportExpr :: Nil
val p: P0[Ast.Head[Span.S]] = (
P.unit.lift0 ~
P.repSep0(
P.oneOf(headExprs.map(_.p.backtrack)),
` \n+`
).surroundedBy(` \n+`.?)
).map { case (point, headers) =>
Ast.Head(Token.lift(point), Chain.fromSeq(headers))
}
}

View File

@ -6,11 +6,11 @@ import aqua.parser.lift.LiftParser
import aqua.parser.lift.Span import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan} import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import cats.{Comonad, Eval} import cats.Show
import cats.data.Chain import cats.data.Chain
import cats.free.Cofree import cats.free.Cofree
import cats.Show
import cats.parse.Parser as P import cats.parse.Parser as P
import cats.{Comonad, Eval}
import cats.~> import cats.~>
trait HeaderExpr[S[_]] { trait HeaderExpr[S[_]] {
@ -23,14 +23,6 @@ object HeaderExpr {
trait Companion { trait Companion {
def p: P[HeaderExpr[Span.S]] def p: P[HeaderExpr[Span.S]]
def ast: P[Ast.Head[Span.S]]
}
abstract class Leaf extends Companion {
override def ast: P[Ast.Head[Span.S]] =
p.map(Cofree[Chain, HeaderExpr[Span.S]](_, Eval.now(Chain.empty)))
} }
given [S[_]]: Show[HeaderExpr[S]] with { given [S[_]]: Show[HeaderExpr[S]] with {

View File

@ -3,11 +3,12 @@ package aqua.parser.head
import aqua.parser.lexer.Token._ import aqua.parser.lexer.Token._
import aqua.parser.lexer.{LiteralToken, ValueToken} import aqua.parser.lexer.{LiteralToken, ValueToken}
import aqua.parser.lift.LiftParser import aqua.parser.lift.LiftParser
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import cats.Comonad import cats.Comonad
import cats.parse.Parser import cats.parse.Parser
import cats.~> import cats.~>
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
case class ImportExpr[F[_]](filename: LiteralToken[F]) extends FilenameExpr[F] { case class ImportExpr[F[_]](filename: LiteralToken[F]) extends FilenameExpr[F] {
@ -17,7 +18,7 @@ case class ImportExpr[F[_]](filename: LiteralToken[F]) extends FilenameExpr[F] {
override def toString: String = s"import ${filename.value}" override def toString: String = s"import ${filename.value}"
} }
object ImportExpr extends HeaderExpr.Leaf { object ImportExpr extends HeaderExpr.Companion {
override val p: Parser[HeaderExpr[Span.S]] = override val p: Parser[HeaderExpr[Span.S]] =
`import` *> ` ` *> ValueToken.string.map(ImportExpr(_)) `import` *> ` ` *> ValueToken.string.map(ImportExpr(_))

View File

@ -3,12 +3,13 @@ package aqua.parser.head
import aqua.parser.lexer.Token.* import aqua.parser.lexer.Token.*
import aqua.parser.lexer.{LiteralToken, ValueToken} import aqua.parser.lexer.{LiteralToken, ValueToken}
import aqua.parser.lift.LiftParser import aqua.parser.lift.LiftParser
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import cats.Comonad import cats.Comonad
import cats.data.NonEmptyList import cats.data.NonEmptyList
import cats.parse.Parser import cats.parse.Parser
import cats.~> import cats.~>
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
case class ImportFromExpr[F[_]]( case class ImportFromExpr[F[_]](
imports: NonEmptyList[FromExpr.NameOrAbAs[F]], imports: NonEmptyList[FromExpr.NameOrAbAs[F]],
@ -21,7 +22,7 @@ case class ImportFromExpr[F[_]](
override def toString: String = s"import ${FromExpr.show(imports)} from ${filename.value}" override def toString: String = s"import ${FromExpr.show(imports)} from ${filename.value}"
} }
object ImportFromExpr extends HeaderExpr.Leaf { object ImportFromExpr extends HeaderExpr.Companion {
override val p: Parser[HeaderExpr[Span.S]] = override val p: Parser[HeaderExpr[Span.S]] =
(`import` *> FromExpr.importFrom.surroundedBy(` `) ~ ValueToken.string).map { (`import` *> FromExpr.importFrom.surroundedBy(` `) ~ ValueToken.string).map {

View File

@ -1,17 +1,21 @@
package aqua.parser.head package aqua.parser.head
import aqua.parser.lexer.Token.*
import aqua.parser.lexer.Token import aqua.parser.lexer.Token
import aqua.parser.lexer.Token.*
import aqua.parser.lexer.{Ability, LiteralToken, Name, ValueToken} import aqua.parser.lexer.{Ability, LiteralToken, Name, ValueToken}
import aqua.parser.lift.LiftParser import aqua.parser.lift.LiftParser
import aqua.parser.lift.LiftParser.* import aqua.parser.lift.LiftParser.*
import cats.Comonad
import cats.parse.Parser
import cats.~>
import aqua.parser.lift.Span import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan} import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import cats.Comonad
import cats.parse.Parser
import cats.syntax.comonad.*
import cats.syntax.functor.*
import cats.~>
case class ModuleExpr[F[_]]( case class ModuleExpr[F[_]](
word: ModuleExpr.Word[F],
name: Ability[F], name: Ability[F],
declareAll: Option[Token[F]], declareAll: Option[Token[F]],
declareNames: List[Name[F]], declareNames: List[Name[F]],
@ -21,40 +25,75 @@ case class ModuleExpr[F[_]](
override def mapK[K[_]: Comonad](fk: F ~> K): ModuleExpr[K] = override def mapK[K[_]: Comonad](fk: F ~> K): ModuleExpr[K] =
copy( copy(
name.mapK(fk), word = word.mapK(fk),
declareAll.map(_.mapK(fk)), name = name.mapK(fk),
declareNames.map(_.mapK(fk)), declareAll = declareAll.map(_.mapK(fk)),
declareCustom.map(_.mapK(fk)) declareNames = declareNames.map(_.mapK(fk)),
declareCustom = declareCustom.map(_.mapK(fk))
) )
} }
object ModuleExpr extends HeaderExpr.Leaf { object ModuleExpr extends HeaderExpr.Companion {
final case class Word[F[_]: Comonad](
token: F[Word.Kind]
) extends Token[F] {
override def mapK[K[_]: Comonad](fk: F ~> K): Word[K] = copy(fk(token))
override def as[T](v: T): F[T] = token.as(v)
def value: Word.Kind = token.extract
}
object Word {
enum Kind {
case Module, Aqua
def fold[A](
module: => A,
aqua: => A
): A = this match {
case Kind.Module => module
case Kind.Aqua => aqua
}
}
}
type NameOrAb[F[_]] = Either[Name[F], Ability[F]] type NameOrAb[F[_]] = Either[Name[F], Ability[F]]
val nameOrAb: Parser[NameOrAb[Span.S]] = private val nameOrAb: Parser[NameOrAb[Span.S]] =
Name.p.map(Left(_)) | Ability.ab.map(Right(_)) Name.p.map(Left(_)) | Ability.ab.map(Right(_))
val nameOrAbList: Parser[List[NameOrAb[Span.S]]] = private val nameOrAbList: Parser[List[NameOrAb[Span.S]]] =
comma[NameOrAb[Span.S]](nameOrAb).map(_.toList) comma[NameOrAb[Span.S]](nameOrAb).map(_.toList)
val nameOrAbListOrAll: Parser[Either[List[NameOrAb[Span.S]], Token[Span.S]]] = private val nameOrAbListOrAll: Parser[Either[List[NameOrAb[Span.S]], Token[Span.S]]] =
nameOrAbList.map(Left(_)) | `star`.lift.map(Token.lift(_)).map(Right(_)) nameOrAbList.map(Left(_)) | `star`.lift.map(Token.lift(_)).map(Right(_))
private val moduleWord: Parser[Word[Span.S]] =
(`module`.as(Word.Kind.Module).lift.backtrack |
`aqua-word`.as(Word.Kind.Aqua).lift).map(Word(_))
override val p: Parser[ModuleExpr[Span.S]] = override val p: Parser[ModuleExpr[Span.S]] =
((`module` | `aqua-word`) *> ` ` *> Ability.dotted ~ (
(` declares ` *> nameOrAbListOrAll).?).map { (` *`.with1 *> moduleWord) ~
case (name, None) => (` ` *> Ability.dotted) ~
ModuleExpr(name, None, Nil, Nil) (` declares ` *> nameOrAbListOrAll).?
case (name, Some(Left(exportMembers))) => ).map {
case ((word, name), None) =>
ModuleExpr(word, name, None, Nil, Nil)
case ((word, name), Some(Left(exportMembers))) =>
ModuleExpr( ModuleExpr(
word,
name, name,
None, None,
exportMembers.collect { case Left(x) => x }, exportMembers.collect { case Left(x) => x },
exportMembers.collect { case Right(x) => x } exportMembers.collect { case Right(x) => x }
) )
case (name, Some(Right(point))) => case ((word, name), Some(Right(point))) =>
ModuleExpr( ModuleExpr(
word,
name, name,
Some(point), Some(point),
Nil, Nil,

View File

@ -3,11 +3,12 @@ package aqua.parser.head
import aqua.parser.lexer.Token.* import aqua.parser.lexer.Token.*
import aqua.parser.lexer.{Ability, LiteralToken, ValueToken} import aqua.parser.lexer.{Ability, LiteralToken, ValueToken}
import aqua.parser.lift.LiftParser import aqua.parser.lift.LiftParser
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import cats.Comonad import cats.Comonad
import cats.parse.Parser import cats.parse.Parser
import cats.~> import cats.~>
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
case class UseExpr[F[_]]( case class UseExpr[F[_]](
filename: LiteralToken[F], filename: LiteralToken[F],
@ -21,7 +22,7 @@ case class UseExpr[F[_]](
s"use ${filename.value}${asModule.map(_.value).fold("")(" as " + _)}" s"use ${filename.value}${asModule.map(_.value).fold("")(" as " + _)}"
} }
object UseExpr extends HeaderExpr.Leaf { object UseExpr extends HeaderExpr.Companion {
override val p: Parser[HeaderExpr[Span.S]] = override val p: Parser[HeaderExpr[Span.S]] =
(`use` *> ` ` *> ValueToken.string ~ (` as ` *> Ability.ab).?).map { (`use` *> ` ` *> ValueToken.string ~ (` as ` *> Ability.ab).?).map {

View File

@ -3,12 +3,13 @@ package aqua.parser.head
import aqua.parser.lexer.Token.* import aqua.parser.lexer.Token.*
import aqua.parser.lexer.{Ability, LiteralToken, Name, ValueToken} import aqua.parser.lexer.{Ability, LiteralToken, Name, ValueToken}
import aqua.parser.lift.LiftParser import aqua.parser.lift.LiftParser
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import cats.Comonad import cats.Comonad
import cats.data.NonEmptyList import cats.data.NonEmptyList
import cats.parse.Parser import cats.parse.Parser
import cats.~> import cats.~>
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
case class UseFromExpr[F[_]]( case class UseFromExpr[F[_]](
imports: NonEmptyList[FromExpr.NameOrAbAs[F]], imports: NonEmptyList[FromExpr.NameOrAbAs[F]],
@ -23,7 +24,7 @@ case class UseFromExpr[F[_]](
s"use ${FromExpr.show(imports)} from ${filename.value} as ${asModule.value}" s"use ${FromExpr.show(imports)} from ${filename.value} as ${asModule.value}"
} }
object UseFromExpr extends HeaderExpr.Leaf { object UseFromExpr extends HeaderExpr.Companion {
override val p: Parser[UseFromExpr[Span.S]] = override val p: Parser[UseFromExpr[Span.S]] =
(`use` *> FromExpr.importFrom.surroundedBy( (`use` *> FromExpr.importFrom.surroundedBy(

View File

@ -1,7 +1,6 @@
package aqua.parser.lexer package aqua.parser.lexer
import aqua.parser.Expr import aqua.parser.Expr
import aqua.parser.head.FilenameExpr
import aqua.parser.lexer.NamedArg.namedArgs import aqua.parser.lexer.NamedArg.namedArgs
import aqua.parser.lexer.Token.* import aqua.parser.lexer.Token.*
import aqua.parser.lift.LiftParser import aqua.parser.lift.LiftParser

View File

@ -2,7 +2,6 @@ package aqua.parser.lift
import cats.parse.{LocationMap, Parser => P, Parser0} import cats.parse.{LocationMap, Parser => P, Parser0}
import cats.{Comonad, Eval} import cats.{Comonad, Eval}
import scala.language.implicitConversions import scala.language.implicitConversions
// TODO: rewrite FileSpan and Span under one trait // TODO: rewrite FileSpan and Span under one trait
@ -19,7 +18,7 @@ case class FileSpan(name: String, locationMap: Eval[LocationMap], span: Span) {
span.focus(locationMap.value, ctx).map(FileSpan.Focus(name, locationMap, ctx, _)) span.focus(locationMap.value, ctx).map(FileSpan.Focus(name, locationMap, ctx, _))
override def hashCode(): Int = (name, span).hashCode() override def hashCode(): Int = (name, span).hashCode()
override def equals(obj: Any): Boolean = { override def equals(obj: Any): Boolean = {
obj match { obj match {
case FileSpan(n, _, s) => n == name && s == span case FileSpan(n, _, s) => n == name && s == span
@ -56,29 +55,4 @@ object FileSpan {
override def map[A, B](fa: F[A])(f: A B): F[B] = fa.copy(_2 = f(fa._2)) override def map[A, B](fa: F[A])(f: A B): F[B] = fa.copy(_2 = f(fa._2))
} }
def fileSpanLiftParser(name: String, source: String): LiftParser[F] = new LiftParser[F] {
private val memoizedLocationMap = Eval.later(LocationMap(source)).memoize
override def lift[T](p: P[T]): P[F[T]] = {
implicitly[LiftParser[Span.S]].lift(p).map { case (span, value) =>
(FileSpan(name, memoizedLocationMap, span), value)
}
}
override def lift0[T](p0: Parser0[T]): Parser0[(FileSpan, T)] = {
implicitly[LiftParser[Span.S]].lift0(p0).map { case (span, value) =>
(FileSpan(name, memoizedLocationMap, span), value)
}
}
override def wrapErr(e: P.Error): (FileSpan, P.Error) = (
FileSpan(
name,
memoizedLocationMap,
Span(e.failedAtOffset, e.failedAtOffset + 1)
),
e
)
}
} }

View File

@ -31,7 +31,9 @@ class ClosureExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
"closure" should "parse" in { "closure" should "parse" in {
val script = val script =
"""func f() -> string: """aqua Test
|
|func f() -> string:
| closure = (s: string) -> string: | closure = (s: string) -> string:
| LocalSrv.inside() | LocalSrv.inside()
| p2Id <- Peer.identify() | p2Id <- Peer.identify()

View File

@ -22,7 +22,7 @@ import scala.language.implicitConversions
class FuncExprSpec extends AnyFlatSpec with Matchers with Inside with Inspectors with AquaSpec { class FuncExprSpec extends AnyFlatSpec with Matchers with Inside with Inspectors with AquaSpec {
import AquaSpec.{given, *} import AquaSpec.{given, *}
private val parser = Parser.spanParser private val parser = RootExpr.ast0
"func header" should "parse" in { "func header" should "parse" in {
funcExpr("func some") should be( funcExpr("func some") should be(
@ -237,7 +237,7 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with Inside with Inspectors
val tree = parser.parseAll(script).value.toEither.value val tree = parser.parseAll(script).value.toEither.value
val qTree = tree.tree.foldLeft(mutable.Queue.empty[Expr[Id]]) { case (acc, tag) => val qTree = tree.foldLeft(mutable.Queue.empty[Expr[Id]]) { case (acc, tag) =>
acc.enqueue(tag.mapK(nat)) acc.enqueue(tag.mapK(nat))
} }
@ -311,8 +311,7 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with Inside with Inspectors
|""".stripMargin |""".stripMargin
inside(parser.parseAll(script).value) { case Valid(ast) => inside(parser.parseAll(script).value) { case Valid(ast) =>
ast Cofree.cata[Chain, Expr[Span.S], Int](ast)((expr, results) =>
.cata[Int]((expr, results) =>
// Count `if`s inside the tree // Count `if`s inside the tree
Eval.later(results.sumAll + (expr match { Eval.later(results.sumAll + (expr match {
case IfExpr(_) => 1 case IfExpr(_) => 1

View File

@ -5,6 +5,7 @@ import aqua.parser.expr.func.ServiceIdExpr
import aqua.parser.lexer.{LiteralToken, Token} import aqua.parser.lexer.{LiteralToken, Token}
import aqua.parser.lift.LiftParser.Implicits.* import aqua.parser.lift.LiftParser.Implicits.*
import aqua.types.LiteralType import aqua.types.LiteralType
import cats.Id import cats.Id
import cats.data.NonEmptyList import cats.data.NonEmptyList
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.flatspec.AnyFlatSpec
@ -23,15 +24,9 @@ class ImportFromSpec extends AnyFlatSpec with Matchers with AquaSpec {
) )
) )
HeadExpr.ast ImportFromExpr.p
.parseAll(s"""import MyModule, func as fn from "file.aqua" .parseAll("""import MyModule, func as fn from "file.aqua"""")
|""".stripMargin)
.value .value
.tail
.value
.headOption
.get
.head
.mapK(spanToId) should be( .mapK(spanToId) should be(
ImportFromExpr( ImportFromExpr(
NonEmptyList.fromListUnsafe( NonEmptyList.fromListUnsafe(

View File

@ -3,18 +3,20 @@ package aqua.parser.head
import aqua.AquaSpec import aqua.AquaSpec
import aqua.parser.expr.func.ServiceIdExpr import aqua.parser.expr.func.ServiceIdExpr
import aqua.parser.lexer.{LiteralToken, Token} import aqua.parser.lexer.{LiteralToken, Token}
import aqua.parser.lift.LiftParser.Implicits.*
import aqua.types.LiteralType import aqua.types.LiteralType
import cats.Id import cats.Id
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
import aqua.parser.lift.LiftParser.Implicits.*
class ModuleSpec extends AnyFlatSpec with Matchers with AquaSpec { class ModuleSpec extends AnyFlatSpec with Matchers with AquaSpec {
import AquaSpec.* import AquaSpec.*
"module header" should "be parsed" in { "module header" should "be parsed" in {
ModuleExpr.p.parseAll("module MyModule").value.mapK(spanToId) should be( ModuleExpr.p.parseAll("aqua MyModule").value.mapK(spanToId) should be(
ModuleExpr( ModuleExpr(
ModuleExpr.Word[Id](Id(ModuleExpr.Word.Kind.Aqua)),
toAb("MyModule"), toAb("MyModule"),
None, None,
Nil, Nil,
@ -22,13 +24,16 @@ class ModuleSpec extends AnyFlatSpec with Matchers with AquaSpec {
) )
) )
HeadExpr.ast Header.p
.parseAll(s"""module MyModule declares * .parseAll(s"""aqua MyModule declares *
|""".stripMargin) |""".stripMargin)
.value .value
.head .headers
.headOption
.get
.mapK(spanToId) should be( .mapK(spanToId) should be(
ModuleExpr( ModuleExpr(
ModuleExpr.Word[Id](Id(ModuleExpr.Word.Kind.Aqua)),
toAb("MyModule"), toAb("MyModule"),
Some(Token.lift[Id, Unit](())), Some(Token.lift[Id, Unit](())),
Nil, Nil,

13
pnpm-lock.yaml generated
View File

@ -39,9 +39,6 @@ importers:
'@fluencelabs/aqua-api': '@fluencelabs/aqua-api':
specifier: 0.13.4 specifier: 0.13.4
version: link:../api/api-npm version: link:../api/api-npm
'@fluencelabs/aqua-dht':
specifier: 0.2.5
version: 0.2.5
'@fluencelabs/aqua-lib': '@fluencelabs/aqua-lib':
specifier: 0.9.0 specifier: 0.9.0
version: 0.9.0 version: 0.9.0
@ -477,16 +474,6 @@ packages:
'@jridgewell/trace-mapping': 0.3.9 '@jridgewell/trace-mapping': 0.3.9
dev: true dev: true
/@fluencelabs/aqua-dht@0.2.5:
resolution: {integrity: sha512-8jWUCeAftRtafqD6MgC7vkCnLOD6pwJspGHykPbXpg2pKbwANAAzAb/w8XbIScBzbIonZ5N7FfSVlTet383A3w==}
dependencies:
'@fluencelabs/aqua-lib': 0.1.14
dev: true
/@fluencelabs/aqua-lib@0.1.14:
resolution: {integrity: sha512-H2Q4gIvociUxc4J2mwmH0D+mrU2N2Z+enKCHgBCanMVEE2wZDsZ80GTbDKsQjEq+gpqbnJIk8lJBYW6lyvLJTg==}
dev: true
/@fluencelabs/aqua-lib@0.9.0: /@fluencelabs/aqua-lib@0.9.0:
resolution: {integrity: sha512-V0xhc0UXBF6kjfL9Y/agWGQuW+ie2zckj37KWv8Dq4teYuo9N94O4Ynm7XULWHaaWtbWvzFcDcc6nc9qG7gxcQ==} resolution: {integrity: sha512-V0xhc0UXBF6kjfL9Y/agWGQuW+ie2zckj37KWv8Dq4teYuo9N94O4Ynm7XULWHaaWtbWvzFcDcc6nc9qG7gxcQ==}
dev: true dev: true

View File

@ -18,6 +18,7 @@ import cats.syntax.foldable.*
import cats.syntax.functor.* import cats.syntax.functor.*
import cats.syntax.option.* import cats.syntax.option.*
import cats.syntax.semigroup.* import cats.syntax.semigroup.*
import cats.syntax.traverse.*
import cats.syntax.validated.* import cats.syntax.validated.*
import cats.{Comonad, Eval, Monoid} import cats.{Comonad, Eval, Monoid}
@ -104,55 +105,63 @@ class HeaderHandler[S[_]: Comonad, C](using
) )
) )
// Handler for every header expression, will be combined later val handleModule: ModuleExpr[S] => Res[S, C] = {
val onExpr: PartialFunction[HeaderExpr[S], Res[S, C]] = { case ModuleExpr(word, name, declareAll, declareNames, declareCustom) =>
// Module header, like `module A declares *`
case ModuleExpr(name, declareAll, declareNames, declareCustom) =>
val shouldDeclare = declareNames.map(_.value).toSet ++ declareCustom.map(_.value) val shouldDeclare = declareNames.map(_.value).toSet ++ declareCustom.map(_.value)
validNec(
HeaderSem[S, C]( lazy val sem = HeaderSem(
// Save module header info // Save module header info
acm.empty.setModule( acm.empty.setModule(
name.value, name.value,
shouldDeclare shouldDeclare
), ),
(ctx, _) => (ctx, _) =>
// When file is handled, check that all the declarations exists // When file is handled, check that all the declarations exists
if (declareAll.nonEmpty) { if (declareAll.nonEmpty)
validNec( ctx.setModule(name.value, declares = ctx.all).validNec
ctx.setModule(name.value, declares = ctx.all) else
) (
} else declareNames.fproductLeft(_.value) ::: declareCustom.fproductLeft(_.value)
( ).map { case (n, t) =>
declareNames.fproductLeft(_.value) ::: declareCustom.fproductLeft(_.value) ctx
).map { case (n, t) => .pick(n, None, ctx.module.nonEmpty)
ctx .toValidNec(
.pick(n, None, ctx.module.nonEmpty) error(
.toValidNec( t,
error( s"`$n` is expected to be declared, but declaration is not found in the file"
t,
s"`$n` is expected to be declared, but declaration is not found in the file"
)
) )
.void
}.combineAll
.as(
// TODO: why module name and declares is lost? where is it lost?
ctx.setModule(name.value, declares = shouldDeclare)
) )
) .void
}.combineAll.as(
// TODO: why module name and declares is lost? where is it lost?
ctx.setModule(name.value, declares = shouldDeclare)
)
) )
word.value.fold(
module = error(
word,
"Keyword `module` is deprecated, use `aqua` instead"
).invalidNec,
aqua = sem.validNec
)
}
// Handler for every header expression, will be combined later
val onExpr: HeaderExpr[S] => Res[S, C] = {
case m: ModuleExpr[S] =>
error(m.token, "Module header is expected to be at the top").invalidNec
case f @ ImportExpr(_) => case f @ ImportExpr(_) =>
// Import everything from a file // Import everything from a file
resolve(f).map(fc => HeaderSem[S, C](fc, (c, _) => validNec(c))) resolve(f).map(fc => HeaderSem(fc, (c, _) => validNec(c)))
case f @ ImportFromExpr(_, _) => case f @ ImportFromExpr(_, _) =>
// Import, map declarations // Import, map declarations
resolve(f) resolve(f)
.andThen(getFrom(f, _)) .andThen(getFrom(f, _))
.map { ctx => .map { ctx =>
HeaderSem[S, C](ctx, (c, _) => validNec(c)) HeaderSem(ctx, (c, _) => validNec(c))
} }
case f @ UseExpr(_, asModule) => case f @ UseExpr(_, asModule) =>
@ -160,7 +169,7 @@ class HeaderHandler[S[_]: Comonad, C](using
resolve(f) resolve(f)
.andThen(toModule(_, f.token, asModule)) .andThen(toModule(_, f.token, asModule))
.map { fc => .map { fc =>
HeaderSem[S, C](fc, (c, _) => validNec(c)) HeaderSem(fc, (c, _) => validNec(c))
} }
case f @ UseFromExpr(_, _, asModule) => case f @ UseFromExpr(_, _, asModule) =>
@ -169,86 +178,66 @@ class HeaderHandler[S[_]: Comonad, C](using
.andThen(getFrom(f, _)) .andThen(getFrom(f, _))
.andThen(toModule(_, f.token, Some(asModule))) .andThen(toModule(_, f.token, Some(asModule)))
.map { fc => .map { fc =>
HeaderSem[S, C](fc, (c, _) => validNec(c)) HeaderSem(fc, (c, _) => validNec(c))
} }
case ExportExpr(pubs) => case ExportExpr(pubs) =>
// Save exports, finally handle them // Save exports, finally handle them
validNec( HeaderSem(
HeaderSem[S, C]( // Nothing there
// Nothing there picker.blank,
picker.blank, (ctx, initCtx) =>
(ctx, initCtx) => val sumCtx = initCtx |+| ctx
val sumCtx = initCtx |+| ctx
pubs pubs
.map( .map(
_.bimap( _.bimap(
_.bimap(n => (n, n.value), _.map(_.value)), _.bimap(n => (n, n.value), _.map(_.value)),
_.bimap(n => (n, n.value), _.map(_.value)) _.bimap(n => (n, n.value), _.map(_.value))
).merge ).merge
) )
.map { case ((token, name), rename) => .map { case ((token, name), rename) =>
sumCtx sumCtx
.pick(name, rename, declared = false) .pick(name, rename, declared = false)
.as(Map(name -> rename)) .as(Map(name -> rename))
.toValid( .toValid(
error( error(
token, token,
s"File has no $name declaration or import, " + s"File has no $name declaration or import, " +
s"cannot export, available functions: ${sumCtx.funcNames.mkString(", ")}" s"cannot export, available functions: ${sumCtx.funcNames.mkString(", ")}"
)
) )
.ensure(
error(
token,
s"Can not export '$name' as it is an ability"
)
)(_ => !sumCtx.isAbility(name))
.toValidatedNec <* exportFuncChecks(sumCtx, token, name)
}
.prepend(validNec(ctx.exports))
.combineAll
.map(ctx.setExports)
)
)
case HeadExpr(token) =>
// Old file exports everything that it declares
validNec(
HeaderSem[S, C](
acm.empty,
(ctx, initCtx) => {
val sumCtx = initCtx |+| ctx
ctx.funcNames.toList
.traverse_(name =>
// TODO: Provide better token for this error
exportFuncChecks(sumCtx, token, name)
)
.combine(
ctx.definedAbilityNames.toList.traverse_(name =>
// TODO: Provide better token for this error
error(token, s"Can not export '$name' as it is an ability ").invalidNec
) )
) .ensure(
.as( error(
// Export everything token,
ctx.setExports( s"Can not export '$name' as it is an ability"
ctx.all.map(_ -> None).toMap )
) )(_ => !sumCtx.isAbility(name))
) .toValidatedNec <* exportFuncChecks(sumCtx, token, name)
} }
) .prepend(validNec(ctx.exports))
) .combineAll
.map(ctx.setExports)
).validNec
case f: FilenameExpr[S] => case f: FilenameExpr[S] =>
resolve(f).map(fc => HeaderSem[S, C](fc, (c, _) => validNec(c))) resolve(f).map(fc => HeaderSem(fc, (c, _) => validNec(c)))
} }
Cofree val (module, other) =
.cata[Chain, HeaderExpr[S], Res[S, C]](header) { case (expr, children) => header.headers.uncons.collect { case (m: ModuleExpr[S], rest) =>
onExpr.lift.apply(expr).fold(Eval.later(children.combineAll))(combineAnd(children)) (m.some, rest)
} }.getOrElse((none, header.headers))
.value .bimap(
_.toValidNec(
error(
header.begin,
"Missing module header at the top of the file"
)
).andThen(handleModule),
_.foldMap(onExpr)
)
module |+| other
} }
} }

View File

@ -1,6 +1,8 @@
package aqua.semantics package aqua.semantics
import aqua.parser.Ast
import aqua.parser.head.{ExportExpr, FromExpr, HeaderExpr, ModuleExpr} import aqua.parser.head.{ExportExpr, FromExpr, HeaderExpr, ModuleExpr}
import aqua.parser.lexer.Token
import aqua.parser.lexer.{Ability, Name} import aqua.parser.lexer.{Ability, Name}
import aqua.raw.RawContext import aqua.raw.RawContext
import aqua.raw.arrow.{ArrowRaw, FuncRaw} import aqua.raw.arrow.{ArrowRaw, FuncRaw}
@ -11,8 +13,8 @@ import aqua.types.{AbilityType, ArrowType, NilType, ProductType, ScalarType}
import cats.data.{Chain, NonEmptyList, NonEmptyMap, Validated} import cats.data.{Chain, NonEmptyList, NonEmptyMap, Validated}
import cats.free.Cofree import cats.free.Cofree
import cats.{Eval, Id, Monoid}
import cats.syntax.applicative.* import cats.syntax.applicative.*
import cats.{Eval, Id, Monoid}
import org.scalatest.Inside import org.scalatest.Inside
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
@ -23,26 +25,25 @@ class HeaderSpec extends AnyFlatSpec with Matchers with Inside {
val handler = new HeaderHandler[Id, RawContext]() val handler = new HeaderHandler[Id, RawContext]()
def exportHeader(funcName: String): Cofree[Chain, HeaderExpr[Id]] = { def exportHeader(funcName: String): Ast.Head[Id] = {
val exp: FromExpr.NameOrAbAs[Id] = Left((Name(funcName), None)) val exp: FromExpr.NameOrAbAs[Id] = Left((Name(funcName), None))
/** /**
* aqua TestModule * aqua TestModule
* export <funcName> * export <funcName>
*/ */
Cofree( Ast.Head(
ModuleExpr( Token.lift(()),
name = Ability[Id]("TestModule"),
declareAll = None,
declareNames = Nil,
declareCustom = Nil
),
Chain( Chain(
Cofree( ModuleExpr(
ExportExpr(NonEmptyList.of(exp)), word = ModuleExpr.Word[Id](Id(ModuleExpr.Word.Kind.Aqua)),
Chain.empty.pure name = Ability[Id]("TestModule"),
) declareAll = None,
).pure declareNames = Nil,
declareCustom = Nil
),
ExportExpr(NonEmptyList.of(exp))
)
) )
} }

View File

@ -33,6 +33,9 @@ class SemanticsSpec extends AnyFlatSpec with Matchers with Inside {
val semantics = new RawSemantics[Span.S]() val semantics = new RawSemantics[Span.S]()
private def addAqua(script: String) =
if (script.startsWith("aqua")) script else "aqua Test\n" + script
def insideResult(script: String)( def insideResult(script: String)(
test: PartialFunction[ test: PartialFunction[
( (
@ -41,7 +44,7 @@ class SemanticsSpec extends AnyFlatSpec with Matchers with Inside {
), ),
Any Any
] ]
): Unit = inside(parser(script)) { case Validated.Valid(ast) => ): Unit = inside(parser(addAqua(script))) { case Validated.Valid(ast) =>
val init = RawContext.blank.copy( val init = RawContext.blank.copy(
parts = Chain parts = Chain
.fromSeq(ConstantRaw.defaultConstants()) .fromSeq(ConstantRaw.defaultConstants())
@ -60,7 +63,7 @@ class SemanticsSpec extends AnyFlatSpec with Matchers with Inside {
} }
def insideSemErrors(script: String)(test: NonEmptyChain[SemanticError[Span.S]] => Any): Unit = def insideSemErrors(script: String)(test: NonEmptyChain[SemanticError[Span.S]] => Any): Unit =
inside(parser(script)) { case Validated.Valid(ast) => inside(parser(addAqua(script))) { case Validated.Valid(ast) =>
val init = RawContext.blank val init = RawContext.blank
inside(semantics.process(ast, init).value.value) { case Left(errors) => inside(semantics.process(ast, init).value.value) { case Left(errors) =>
test(errors) test(errors)

View File

@ -1,9 +1,9 @@
package aqua.types package aqua.types
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import cats.data.NonEmptyMap import cats.data.NonEmptyMap
import cats.syntax.partialOrder._ import cats.syntax.partialOrder._
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
class IntersectTypesSpec extends AnyFlatSpec with Matchers { class IntersectTypesSpec extends AnyFlatSpec with Matchers {

View File

@ -1,8 +1,8 @@
package aqua.types package aqua.types
import cats.syntax.partialOrder._
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
import cats.syntax.partialOrder._
class UniteTypesSpec extends AnyFlatSpec with Matchers { class UniteTypesSpec extends AnyFlatSpec with Matchers {