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.transform.{Transform, TransformConfig}
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.raw.ops.Call
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.lift.FileSpan.F
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.ops.Call
import aqua.raw.value.ValueRaw

View File

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

View File

@ -37,4 +37,4 @@ func bugLng317() -> []string:
worker_job = WorkerJob(runOnSingleWorker = clos())
subnet_job <- disjoint_run{worker_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.{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.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.{~>, Comonad, Monad}
import cats.syntax.validated.*
import cats.{Comonad, Monad, ~>}
import scribe.Logging
// 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
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
}.value.traverse { case (filename, token) =>
}.traverse { case (filename, token) =>
sources
.resolveImport(id, filename)
.map(

View File

@ -89,7 +89,7 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
val src = Map(
"index.aqua" ->
"""module Foo declares X
"""aqua Foo declares 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 {
val src = Map(
"index.aqua" ->
"""service Op("op"):
"""aqua Test
|
|export exec
|
|service Op("op"):
| identity(s: 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 {
val src = Map(
"index.aqua" ->
"""service Op("op"):
"""aqua HopBackTest
|
|export exec
|
|service Op("op"):
| call(s: string)
|
|func exec(peers: []string):
@ -288,7 +296,7 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
val src = Map(
"index.aqua" ->
"""module Import
"""aqua Import
|import foobar from "export2.aqua"
|
|use foo as f from "export2.aqua" as Exp
@ -307,7 +315,7 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
)
val imports = Map(
"export2.aqua" ->
"""module Export declares foobar, foo
"""aqua Export declares foobar, foo
|
|func bar() -> string:
| <- " I am MyFooBar bar"
@ -323,7 +331,7 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
|
|""".stripMargin,
"../gen/OneMore.aqua" ->
"""
"""aqua Test declares OneMore
|service OneMore:
| more_call()
| consume(s: string)
@ -379,7 +387,10 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
it should "optimize math inside stream join" in {
val src = Map(
"main.aqua" -> """
"main.aqua" -> """aqua Test
|
|export main
|
|func main(i: i32):
| stream: *string
| stream <<- "a"
@ -434,8 +445,7 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
it should "allow returning and passing services as abilities" in {
val src = Map(
"main.aqua" -> """
|aqua Test
"main.aqua" -> """aqua 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
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
use DECLARE_CONST, decl_bar from "imports_exports/declare.aqua" as Declare
service SomeService("wed"):
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:
value: string

View File

@ -1,6 +1,11 @@
aqua CallArrow
export passFunctionAsArg, reproArgsBug426
import "println.aqua"
import "@fluencelabs/aqua-lib/builtin.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
func passFunctionAsArg(node: string, str: string, c: string -> string):

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,7 @@
aqua Complex
export TestS, doStuff
import "helloWorld.aqua"
import "println.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"
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
alias PeerId : string

View File

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

View File

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

View File

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

View File

@ -1,3 +1,7 @@
aqua Func
export TestSrv, testFunc
service TestSrv("test-service-id"):
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

View File

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

View File

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

View File

@ -1,3 +1,7 @@
aqua If
export ifElseCall, ifElseNumCall, ifCorrectXorWrap, bugLNG69
import "println.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
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"

View File

@ -1,5 +1,5 @@
-- exports3.aqua
module Export3 declares *
aqua Export3 declares *
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"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,7 @@
aqua Join
export joinIdxLocal, joinIdxRelay, joinIdx
import "@fluencelabs/aqua-lib/builtin.aqua"
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"
service GetStr("multiret-test"):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,7 @@
aqua ParSeq
export testParSeq
import "@fluencelabs/aqua-lib/builtin.aqua"
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"
service AquaDHT("test-dht"):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
module Ret declares *
aqua Ret declares *
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:
str: *string

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,7 @@
aqua TryOtherwise
export tryOtherwiseTest
service Unexisted("unex"):
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": {},
"devDependencies": {
"@fluencelabs/aqua-api": "0.13.4",
"@fluencelabs/aqua-dht": "0.2.5",
"@fluencelabs/aqua-lib": "0.9.0",
"@types/jest": "29.5.2",
"@types/node": "18.19.3",

View File

@ -3,29 +3,27 @@ package aqua
import aqua.files.FileModuleId
import aqua.parser.lift.{FileSpan, Span}
import aqua.parser.{Ast, Parser, ParserError}
import cats.data.*
import cats.parse.LocationMap
import cats.{~>, Comonad, Eval, Monad, Monoid, Order}
import cats.{Comonad, Eval, Monad, Monoid, Order, ~>}
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 =>
{ source =>
{
logger.trace(s"creating parser for $id...")
val nat = new (Span.S ~> FileSpan.F) {
override def apply[A](span: Span.S[A]): FileSpan.F[A] = {
(
FileSpan(id.file.absolute.toString, Eval.later(LocationMap(source)), span._1),
span._2
)
}
source => {
logger.trace(s"creating parser for $id...")
val nat = new (Span.S ~> FileSpan.F) {
override def apply[A](span: Span.S[A]): FileSpan.F[A] = {
(
FileSpan(id.file.absolute.toString, Eval.later(LocationMap(source)), span._1),
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 cats.data.Validated.{Invalid, Valid}
import cats.data.{NonEmptyChain, ValidatedNec}
import cats.syntax.applicative.*
import cats.syntax.apply.*
import cats.syntax.flatMap.*
import cats.syntax.functor.*
import cats.syntax.foldable.*
import cats.syntax.either.*
import cats.syntax.flatMap.*
import cats.syntax.foldable.*
import cats.syntax.functor.*
import cats.syntax.reducible.*
import cats.data.{NonEmptyChain, ValidatedNec}
import monocle.Lens
import monocle.macros.GenLens
class LspSemantics[S[_]] extends Semantics[S, LspContext[S]] {
private def getImportTokens(ast: Ast[S]): List[LiteralToken[S]] =
ast.collectHead {
ast.head.collect {
case ImportExpr(fn) => fn
case ImportFromExpr(_, fn) => fn
case UseExpr(fn, _) => fn
case UseFromExpr(_, fn, _) => fn
}.value.toList
}.toList
/**
* Process the AST and return the semantics result.

View File

@ -108,22 +108,18 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
def compile(
src: Map[String, String],
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
.compileToLsp[Id, String, String, Span.S](
aquaSource(src, imports),
id => txt => Parser.parse(Parser.parserSchema)(txt),
AquaCompilerConf(ConstantRaw.defaultConstants(None))
)
.leftMap { errors =>
println(errors)
errors
}
}
it should "return right tokens" in {
val main =
"""module Import
"""aqua Import
|
|import foo, strFunc, num from "export2.aqua"
|
|import "../gen/OneMore.aqua"
@ -156,7 +152,7 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
)
val firstImport =
"""module Export declares strFunc, num, foo
"""aqua Export declares strFunc, num, foo
|
|func absb() -> string:
| <- "ff"
@ -173,7 +169,8 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
|""".stripMargin
val secondImport =
"""
"""aqua Export declares OneMore
|
|service OneMore:
| more_call()
| 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
// OneMore service
res.checkTokenLoc(secondImport, "OneMore", 0, serviceType) shouldBe true
res.checkTokenLoc(secondImport, "OneMore", 1, serviceType) shouldBe true
res.checkTokenLoc(
secondImport,
"more_call",
@ -265,7 +262,7 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
ProductType(ScalarType.u32 :: Nil)
)
) 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
res.checkTokenLoc(

View File

@ -1,43 +1,48 @@
package aqua.parser
import aqua.helpers.tree.Tree
import aqua.parser.expr.*
import aqua.parser.head.{HeadExpr, HeaderExpr}
import aqua.parser.lift.{LiftParser, Span}
import aqua.parser.lift.LiftParser.*
import aqua.parser.head.HeaderExpr
import aqua.parser.lexer.Token
import cats.data.{Chain, Validated, ValidatedNec}
import cats.syntax.flatMap.*
import cats.data.Chain
import cats.free.Cofree
import cats.{Comonad, Eval}
import cats.~>
import cats.Show
import cats.syntax.flatMap.*
import cats.syntax.show.*
import cats.{Comonad, ~>}
import cats.{Eval, Show}
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] =
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 {
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]]
given [S[_]]: Show[Ast[S]] with {
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)
s"$head\n$body"

View File

@ -1,13 +1,15 @@
package aqua.parser
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.Span.S
import aqua.parser.lift.{LiftParser, Span}
import cats.data.{Validated, ValidatedNec}
import cats.free.Cofree
import cats.parse.{Parser as P, Parser0 as P0}
import cats.syntax.validated.*
import cats.{Comonad, ~>}
object Parser extends scribe.Logging {
@ -15,7 +17,7 @@ object Parser extends scribe.Logging {
def parserSchema: P0[ValidatedNec[ParserError[Span.S], Ast[Span.S]]] = {
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, _))
}
logger.trace("schema created")
@ -24,19 +26,15 @@ object Parser extends scribe.Logging {
def parse[S[_]: LiftParser: Comonad](
p: P0[ValidatedNec[ParserError[S], Ast[S]]]
)(source: String): ValidatedNec[ParserError[S], Ast[S]] = {
p.parseAll(source) match {
case Right(value) => value
case Left(e) => Validated.invalidNec(LexerError(e.wrapErr))
}
}
)(source: String): ValidatedNec[ParserError[S], Ast[S]] =
p.parseAll(source).left.map(e => LexerError(e.wrapErr).invalidNec).merge
def natParser[S[_]: LiftParser: Comonad, K[_]: Comonad](
p: P0[ValidatedNec[ParserError[S], Ast[S]]],
nat: S ~> K
)(source: String): ValidatedNec[ParserError[K], Ast[K]] =
parse[S](p)(source).bimap(
parse(p)(source).bimap(
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.lexer.Token
import aqua.parser.lexer.Token.*
import aqua.parser.lift.{LiftParser, Span}
import aqua.parser.lift.LiftParser.*
import aqua.parser.lift.{LiftParser, Span}
import aqua.parser.{Expr, ParserError}
import cats.data.{Chain, NonEmptyChain, NonEmptyList, Validated, ValidatedNec}
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.~>
@ -24,7 +26,9 @@ object RootExpr extends Expr.Companion {
def validChildren: List[Expr.Lexem] =
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) {
case ((errs, trees), Validated.Valid(tree)) => (errs, trees :+ tree)
case ((errs, trees), Validated.Invalid(err)) => (errs ++ err.toChain, trees)
@ -54,12 +58,11 @@ object RootExpr extends Expr.Companion {
empty.backtrack | ast
override val ast: P[ValidatedNec[ParserError[Span.S], Tree[Span.S]]] =
parserSchema
.map { case (point, (errs, trees)) =>
parserSchema.map { case (point, (errs, trees)) =>
NonEmptyChain
.fromChain(errs)
.fold[ValidatedNec[ParserError[Span.S], Tree[Span.S]]](
Validated.validNec(Cofree(RootExpr[Span.S](point), Eval.now(trees)))
)(Validated.invalid)
.toInvalid(
Cofree(RootExpr(point), Eval.now(trees))
)
}
}

View File

@ -3,13 +3,14 @@ package aqua.parser.head
import aqua.parser.lexer.Token.*
import aqua.parser.lexer.{LiteralToken, Token, ValueToken}
import aqua.parser.lift.LiftParser
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import cats.Comonad
import cats.data.NonEmptyList
import cats.parse.Parser
import cats.syntax.either.*
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] {
@ -20,7 +21,7 @@ case class ExportExpr[F[_]](pubs: NonEmptyList[FromExpr.NameOrAbAs[F]]) extends
copy(FromExpr.mapK(pubs)(fk))
}
object ExportExpr extends HeaderExpr.Leaf {
object ExportExpr extends HeaderExpr.Companion {
override val p: Parser[ExportExpr[Span.S]] =
(`_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.{P0ToSpan, PToSpan}
import cats.{Comonad, Eval}
import cats.Show
import cats.data.Chain
import cats.free.Cofree
import cats.Show
import cats.parse.Parser as P
import cats.{Comonad, Eval}
import cats.~>
trait HeaderExpr[S[_]] {
@ -23,14 +23,6 @@ object HeaderExpr {
trait Companion {
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 {

View File

@ -3,11 +3,12 @@ package aqua.parser.head
import aqua.parser.lexer.Token._
import aqua.parser.lexer.{LiteralToken, ValueToken}
import aqua.parser.lift.LiftParser
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import cats.Comonad
import cats.parse.Parser
import cats.~>
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
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}"
}
object ImportExpr extends HeaderExpr.Leaf {
object ImportExpr extends HeaderExpr.Companion {
override val p: Parser[HeaderExpr[Span.S]] =
`import` *> ` ` *> ValueToken.string.map(ImportExpr(_))

View File

@ -3,12 +3,13 @@ package aqua.parser.head
import aqua.parser.lexer.Token.*
import aqua.parser.lexer.{LiteralToken, ValueToken}
import aqua.parser.lift.LiftParser
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import cats.Comonad
import cats.data.NonEmptyList
import cats.parse.Parser
import cats.~>
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
case class ImportFromExpr[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}"
}
object ImportFromExpr extends HeaderExpr.Leaf {
object ImportFromExpr extends HeaderExpr.Companion {
override val p: Parser[HeaderExpr[Span.S]] =
(`import` *> FromExpr.importFrom.surroundedBy(` `) ~ ValueToken.string).map {

View File

@ -1,17 +1,21 @@
package aqua.parser.head
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.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.{P0ToSpan, PToSpan}
import cats.Comonad
import cats.parse.Parser
import cats.syntax.comonad.*
import cats.syntax.functor.*
import cats.~>
case class ModuleExpr[F[_]](
word: ModuleExpr.Word[F],
name: Ability[F],
declareAll: Option[Token[F]],
declareNames: List[Name[F]],
@ -21,40 +25,75 @@ case class ModuleExpr[F[_]](
override def mapK[K[_]: Comonad](fk: F ~> K): ModuleExpr[K] =
copy(
name.mapK(fk),
declareAll.map(_.mapK(fk)),
declareNames.map(_.mapK(fk)),
declareCustom.map(_.mapK(fk))
word = word.mapK(fk),
name = name.mapK(fk),
declareAll = declareAll.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]]
val nameOrAb: Parser[NameOrAb[Span.S]] =
private val nameOrAb: Parser[NameOrAb[Span.S]] =
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)
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(_))
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]] =
((`module` | `aqua-word`) *> ` ` *> Ability.dotted ~
(` declares ` *> nameOrAbListOrAll).?).map {
case (name, None) =>
ModuleExpr(name, None, Nil, Nil)
case (name, Some(Left(exportMembers))) =>
(
(` *`.with1 *> moduleWord) ~
(` ` *> Ability.dotted) ~
(` declares ` *> nameOrAbListOrAll).?
).map {
case ((word, name), None) =>
ModuleExpr(word, name, None, Nil, Nil)
case ((word, name), Some(Left(exportMembers))) =>
ModuleExpr(
word,
name,
None,
exportMembers.collect { case Left(x) => x },
exportMembers.collect { case Right(x) => x }
)
case (name, Some(Right(point))) =>
case ((word, name), Some(Right(point))) =>
ModuleExpr(
word,
name,
Some(point),
Nil,

View File

@ -3,11 +3,12 @@ package aqua.parser.head
import aqua.parser.lexer.Token.*
import aqua.parser.lexer.{Ability, LiteralToken, ValueToken}
import aqua.parser.lift.LiftParser
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import cats.Comonad
import cats.parse.Parser
import cats.~>
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
case class UseExpr[F[_]](
filename: LiteralToken[F],
@ -21,7 +22,7 @@ case class UseExpr[F[_]](
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]] =
(`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.{Ability, LiteralToken, Name, ValueToken}
import aqua.parser.lift.LiftParser
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import cats.Comonad
import cats.data.NonEmptyList
import cats.parse.Parser
import cats.~>
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
case class UseFromExpr[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}"
}
object UseFromExpr extends HeaderExpr.Leaf {
object UseFromExpr extends HeaderExpr.Companion {
override val p: Parser[UseFromExpr[Span.S]] =
(`use` *> FromExpr.importFrom.surroundedBy(

View File

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

View File

@ -2,7 +2,6 @@ package aqua.parser.lift
import cats.parse.{LocationMap, Parser => P, Parser0}
import cats.{Comonad, Eval}
import scala.language.implicitConversions
// 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, _))
override def hashCode(): Int = (name, span).hashCode()
override def equals(obj: Any): Boolean = {
obj match {
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))
}
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 {
val script =
"""func f() -> string:
"""aqua Test
|
|func f() -> string:
| closure = (s: string) -> string:
| LocalSrv.inside()
| 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 {
import AquaSpec.{given, *}
private val parser = Parser.spanParser
private val parser = RootExpr.ast0
"func header" should "parse" in {
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 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))
}
@ -311,8 +311,7 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with Inside with Inspectors
|""".stripMargin
inside(parser.parseAll(script).value) { case Valid(ast) =>
ast
.cata[Int]((expr, results) =>
Cofree.cata[Chain, Expr[Span.S], Int](ast)((expr, results) =>
// Count `if`s inside the tree
Eval.later(results.sumAll + (expr match {
case IfExpr(_) => 1

View File

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

View File

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

13
pnpm-lock.yaml generated
View File

@ -39,9 +39,6 @@ importers:
'@fluencelabs/aqua-api':
specifier: 0.13.4
version: link:../api/api-npm
'@fluencelabs/aqua-dht':
specifier: 0.2.5
version: 0.2.5
'@fluencelabs/aqua-lib':
specifier: 0.9.0
version: 0.9.0
@ -477,16 +474,6 @@ packages:
'@jridgewell/trace-mapping': 0.3.9
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:
resolution: {integrity: sha512-V0xhc0UXBF6kjfL9Y/agWGQuW+ie2zckj37KWv8Dq4teYuo9N94O4Ynm7XULWHaaWtbWvzFcDcc6nc9qG7gxcQ==}
dev: true

View File

@ -18,6 +18,7 @@ import cats.syntax.foldable.*
import cats.syntax.functor.*
import cats.syntax.option.*
import cats.syntax.semigroup.*
import cats.syntax.traverse.*
import cats.syntax.validated.*
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 onExpr: PartialFunction[HeaderExpr[S], Res[S, C]] = {
// Module header, like `module A declares *`
case ModuleExpr(name, declareAll, declareNames, declareCustom) =>
val handleModule: ModuleExpr[S] => Res[S, C] = {
case ModuleExpr(word, name, declareAll, declareNames, declareCustom) =>
val shouldDeclare = declareNames.map(_.value).toSet ++ declareCustom.map(_.value)
validNec(
HeaderSem[S, C](
// Save module header info
acm.empty.setModule(
name.value,
shouldDeclare
),
(ctx, _) =>
// When file is handled, check that all the declarations exists
if (declareAll.nonEmpty) {
validNec(
ctx.setModule(name.value, declares = ctx.all)
)
} else
(
declareNames.fproductLeft(_.value) ::: declareCustom.fproductLeft(_.value)
).map { case (n, t) =>
ctx
.pick(n, None, ctx.module.nonEmpty)
.toValidNec(
error(
t,
s"`$n` is expected to be declared, but declaration is not found in the file"
)
lazy val sem = HeaderSem(
// Save module header info
acm.empty.setModule(
name.value,
shouldDeclare
),
(ctx, _) =>
// When file is handled, check that all the declarations exists
if (declareAll.nonEmpty)
ctx.setModule(name.value, declares = ctx.all).validNec
else
(
declareNames.fproductLeft(_.value) ::: declareCustom.fproductLeft(_.value)
).map { case (n, t) =>
ctx
.pick(n, None, ctx.module.nonEmpty)
.toValidNec(
error(
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(_) =>
// 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(_, _) =>
// Import, map declarations
resolve(f)
.andThen(getFrom(f, _))
.map { ctx =>
HeaderSem[S, C](ctx, (c, _) => validNec(c))
HeaderSem(ctx, (c, _) => validNec(c))
}
case f @ UseExpr(_, asModule) =>
@ -160,7 +169,7 @@ class HeaderHandler[S[_]: Comonad, C](using
resolve(f)
.andThen(toModule(_, f.token, asModule))
.map { fc =>
HeaderSem[S, C](fc, (c, _) => validNec(c))
HeaderSem(fc, (c, _) => validNec(c))
}
case f @ UseFromExpr(_, _, asModule) =>
@ -169,86 +178,66 @@ class HeaderHandler[S[_]: Comonad, C](using
.andThen(getFrom(f, _))
.andThen(toModule(_, f.token, Some(asModule)))
.map { fc =>
HeaderSem[S, C](fc, (c, _) => validNec(c))
HeaderSem(fc, (c, _) => validNec(c))
}
case ExportExpr(pubs) =>
// Save exports, finally handle them
validNec(
HeaderSem[S, C](
// Nothing there
picker.blank,
(ctx, initCtx) =>
val sumCtx = initCtx |+| ctx
HeaderSem(
// Nothing there
picker.blank,
(ctx, initCtx) =>
val sumCtx = initCtx |+| ctx
pubs
.map(
_.bimap(
_.bimap(n => (n, n.value), _.map(_.value)),
_.bimap(n => (n, n.value), _.map(_.value))
).merge
)
.map { case ((token, name), rename) =>
sumCtx
.pick(name, rename, declared = false)
.as(Map(name -> rename))
.toValid(
error(
token,
s"File has no $name declaration or import, " +
s"cannot export, available functions: ${sumCtx.funcNames.mkString(", ")}"
)
pubs
.map(
_.bimap(
_.bimap(n => (n, n.value), _.map(_.value)),
_.bimap(n => (n, n.value), _.map(_.value))
).merge
)
.map { case ((token, name), rename) =>
sumCtx
.pick(name, rename, declared = false)
.as(Map(name -> rename))
.toValid(
error(
token,
s"File has no $name declaration or import, " +
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
)
)
.as(
// Export everything
ctx.setExports(
ctx.all.map(_ -> None).toMap
)
)
}
)
)
.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)
).validNec
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
.cata[Chain, HeaderExpr[S], Res[S, C]](header) { case (expr, children) =>
onExpr.lift.apply(expr).fold(Eval.later(children.combineAll))(combineAnd(children))
}
.value
val (module, other) =
header.headers.uncons.collect { case (m: ModuleExpr[S], rest) =>
(m.some, rest)
}.getOrElse((none, header.headers))
.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
import aqua.parser.Ast
import aqua.parser.head.{ExportExpr, FromExpr, HeaderExpr, ModuleExpr}
import aqua.parser.lexer.Token
import aqua.parser.lexer.{Ability, Name}
import aqua.raw.RawContext
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.free.Cofree
import cats.{Eval, Id, Monoid}
import cats.syntax.applicative.*
import cats.{Eval, Id, Monoid}
import org.scalatest.Inside
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
@ -23,26 +25,25 @@ class HeaderSpec extends AnyFlatSpec with Matchers with Inside {
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))
/**
* aqua TestModule
* export <funcName>
*/
Cofree(
ModuleExpr(
name = Ability[Id]("TestModule"),
declareAll = None,
declareNames = Nil,
declareCustom = Nil
),
Ast.Head(
Token.lift(()),
Chain(
Cofree(
ExportExpr(NonEmptyList.of(exp)),
Chain.empty.pure
)
).pure
ModuleExpr(
word = ModuleExpr.Word[Id](Id(ModuleExpr.Word.Kind.Aqua)),
name = Ability[Id]("TestModule"),
declareAll = None,
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]()
private def addAqua(script: String) =
if (script.startsWith("aqua")) script else "aqua Test\n" + script
def insideResult(script: String)(
test: PartialFunction[
(
@ -41,7 +44,7 @@ class SemanticsSpec extends AnyFlatSpec with Matchers with Inside {
),
Any
]
): Unit = inside(parser(script)) { case Validated.Valid(ast) =>
): Unit = inside(parser(addAqua(script))) { case Validated.Valid(ast) =>
val init = RawContext.blank.copy(
parts = Chain
.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 =
inside(parser(script)) { case Validated.Valid(ast) =>
inside(parser(addAqua(script))) { case Validated.Valid(ast) =>
val init = RawContext.blank
inside(semantics.process(ast, init).value.value) { case Left(errors) =>
test(errors)

View File

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

View File

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