mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-04 22:50:18 +00:00
feat(compiler): Services as abilities [fixes LNG-206] (#873)
* Refactor ServiceSem * Refactor AbilityIdSem * Remove tokens from state * Refactor * Add mangler * Fix tests * Refactor valueToRaw * ServiceIdTag * AbilityId -> ServiceId * Add ServiceType * Fix defineServiceType * Refactor resolveArrowDef * Refactor TypesHelper * Add ServiceIdTag inlining * Implement resolution * Add service as ability passing * Fix importing services * Fix cli * Implement default service * Remove println * Fix capture * Add integration test * Fix id * Fix test * Fix test * Refactor test * Do not resolve id * Refactor FuncArrow creation * Refactor FuncArrow wrapper creation * Add named arguments * Add comment * ensureIsString -> valueToStringRaw, refactor OnSem * Resolve services as abilities * Add name to varNames * Remove service hack * Capture services, do not rename captured * Rename arrows along with values * Fix CallArrowRaw.map * Fix unit tests * Remove service case * Refactor abilities state * Propagate rootServiceIds * Remove unused * Add comments * Refactor * Refactor * Add test --------- Co-authored-by: Dima <dmitry.shakhtarin@fluence.ai>
This commit is contained in:
parent
f8b5017918
commit
6be2a3d5da
@ -10,6 +10,7 @@ import aqua.raw.{RawContext, RawPart}
|
|||||||
import aqua.res.AquaRes
|
import aqua.res.AquaRes
|
||||||
import aqua.semantics.{CompilerState, Semantics}
|
import aqua.semantics.{CompilerState, Semantics}
|
||||||
import aqua.semantics.header.{HeaderHandler, HeaderSem, Picker}
|
import aqua.semantics.header.{HeaderHandler, HeaderSem, Picker}
|
||||||
|
|
||||||
import cats.data.*
|
import cats.data.*
|
||||||
import cats.data.Validated.{validNec, Invalid, Valid}
|
import cats.data.Validated.{validNec, Invalid, Valid}
|
||||||
import cats.parse.Parser0
|
import cats.parse.Parser0
|
||||||
|
@ -153,6 +153,7 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers {
|
|||||||
val canonResult = VarModel("-" + results.name + "-fix-0", CanonStreamType(resultsType.element))
|
val canonResult = VarModel("-" + results.name + "-fix-0", CanonStreamType(resultsType.element))
|
||||||
val flatResult = VarModel("-results-flat-0", ArrayType(ScalarType.string))
|
val flatResult = VarModel("-results-flat-0", ArrayType(ScalarType.string))
|
||||||
val initPeer = LiteralModel.fromRaw(ValueRaw.InitPeerId)
|
val initPeer = LiteralModel.fromRaw(ValueRaw.InitPeerId)
|
||||||
|
val retVar = VarModel("ret", ScalarType.string)
|
||||||
|
|
||||||
val expected =
|
val expected =
|
||||||
SeqRes.wrap(
|
SeqRes.wrap(
|
||||||
@ -173,10 +174,11 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers {
|
|||||||
"identity",
|
"identity",
|
||||||
CallRes(
|
CallRes(
|
||||||
LiteralModel.fromRaw(LiteralRaw.quote("hahahahah")) :: Nil,
|
LiteralModel.fromRaw(LiteralRaw.quote("hahahahah")) :: Nil,
|
||||||
Some(CallModel.Export(results.name, results.`type`))
|
Some(CallModel.Export(retVar.name, retVar.`type`))
|
||||||
),
|
),
|
||||||
peer
|
peer
|
||||||
).leaf,
|
).leaf,
|
||||||
|
ApRes(retVar, CallModel.Export(results.name, results.`type`)).leaf,
|
||||||
through(ValueModel.fromRaw(relay)),
|
through(ValueModel.fromRaw(relay)),
|
||||||
through(initPeer)
|
through(initPeer)
|
||||||
),
|
),
|
||||||
|
95
integration-tests/aqua/examples/servicesAsAbilities.aqua
Normal file
95
integration-tests/aqua/examples/servicesAsAbilities.aqua
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
aqua Test
|
||||||
|
|
||||||
|
export test, testCapture, TestService
|
||||||
|
|
||||||
|
service TestService("default-id"):
|
||||||
|
getId() -> string
|
||||||
|
concatId(s: string) -> string
|
||||||
|
|
||||||
|
ability MatchingAbility:
|
||||||
|
getId() -> string
|
||||||
|
concatId(s: string) -> string
|
||||||
|
|
||||||
|
func acceptClosure(closure: string -> string, arg: string) -> string:
|
||||||
|
<- closure(arg)
|
||||||
|
|
||||||
|
func acceptAbility{MatchingAbility}(arg: string) -> string:
|
||||||
|
<- MatchingAbility.concatId(arg)
|
||||||
|
|
||||||
|
func test() -> []string:
|
||||||
|
result: *string
|
||||||
|
|
||||||
|
-- Test service
|
||||||
|
result <- TestService.concatId("call")
|
||||||
|
capture = TestService.concatId
|
||||||
|
result <- capture("capture call")
|
||||||
|
result <- acceptClosure(TestService.concatId, "accept closure call")
|
||||||
|
result <- acceptAbility{TestService}("accept ability call")
|
||||||
|
|
||||||
|
-- Test renamed service
|
||||||
|
Renamed = TestService
|
||||||
|
result <- Renamed.concatId("call")
|
||||||
|
captureRenamed = Renamed.concatId
|
||||||
|
result <- captureRenamed("capture call")
|
||||||
|
result <- acceptClosure(Renamed.concatId, "accept closure call")
|
||||||
|
result <- acceptAbility{Renamed}("accept ability call")
|
||||||
|
|
||||||
|
-- Test resolved service
|
||||||
|
TestService "resolved-id-1"
|
||||||
|
result <- TestService.concatId("call")
|
||||||
|
captureResolved = TestService.concatId
|
||||||
|
result <- captureResolved("capture call")
|
||||||
|
result <- acceptClosure(TestService.concatId, "accept closure call")
|
||||||
|
result <- acceptAbility{TestService}("accept ability call")
|
||||||
|
|
||||||
|
-- Test renamed resolved service
|
||||||
|
Renamed1 = TestService
|
||||||
|
result <- Renamed1.concatId("call")
|
||||||
|
captureRenamed1 = Renamed1.concatId
|
||||||
|
result <- captureRenamed1("capture call")
|
||||||
|
result <- acceptClosure(Renamed1.concatId, "accept closure call")
|
||||||
|
result <- acceptAbility{Renamed1}("accept ability call")
|
||||||
|
|
||||||
|
-- Test renamed service again (should save id)
|
||||||
|
result <- Renamed.concatId("call")
|
||||||
|
captureRenamedAgain = Renamed.concatId
|
||||||
|
result <- captureRenamedAgain("capture call")
|
||||||
|
result <- acceptClosure(Renamed.concatId, "accept closure call")
|
||||||
|
result <- acceptAbility{Renamed}("accept ability call")
|
||||||
|
|
||||||
|
-- Test resolved in scope service
|
||||||
|
for i <- ["iter-id-1", "iter-id-2"]:
|
||||||
|
TestService i
|
||||||
|
RenamedI = TestService
|
||||||
|
result <- RenamedI.concatId("call")
|
||||||
|
captureI = RenamedI.concatId
|
||||||
|
result <- captureI("capture call")
|
||||||
|
result <- acceptClosure(RenamedI.concatId, "accept closure call")
|
||||||
|
result <- acceptAbility{RenamedI}("accept ability call")
|
||||||
|
|
||||||
|
-- Test resolved service again (should save id)
|
||||||
|
result <- TestService.concatId("call")
|
||||||
|
captureAgain = TestService.concatId
|
||||||
|
result <- captureAgain("capture call")
|
||||||
|
result <- acceptClosure(TestService.concatId, "accept closure call")
|
||||||
|
result <- acceptAbility{TestService}("accept ability call")
|
||||||
|
|
||||||
|
-- Test re resolved service in same scope
|
||||||
|
TestService "resolved-id-2"
|
||||||
|
result <- TestService.concatId("call")
|
||||||
|
captureReResolved = TestService.concatId
|
||||||
|
result <- captureReResolved("capture call")
|
||||||
|
result <- acceptClosure(TestService.concatId, "accept closure call")
|
||||||
|
result <- acceptAbility{TestService}("accept ability call")
|
||||||
|
|
||||||
|
<- result
|
||||||
|
|
||||||
|
func callCapture{MatchingAbility}() -> string, string:
|
||||||
|
TestService "resolved-id-in-capture"
|
||||||
|
res1 <- TestService.concatId("in capture")
|
||||||
|
res2 <- MatchingAbility.concatId("in capture")
|
||||||
|
<- res1, res2
|
||||||
|
|
||||||
|
func testCapture() -> string, string:
|
||||||
|
res1, res2 <- callCapture{TestService}()
|
||||||
|
<- res1, res2
|
File diff suppressed because it is too large
Load Diff
71
integration-tests/src/examples/servicesAsAbilities.ts
Normal file
71
integration-tests/src/examples/servicesAsAbilities.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import {
|
||||||
|
test,
|
||||||
|
testCapture,
|
||||||
|
registerTestService,
|
||||||
|
} from "../compiled/examples/servicesAsAbilities.js";
|
||||||
|
|
||||||
|
const serviceIds = {
|
||||||
|
default: "default-id",
|
||||||
|
resolved1: "resolved-id-1",
|
||||||
|
resolved2: "resolved-id-2",
|
||||||
|
iter1: "iter-id-1",
|
||||||
|
iter2: "iter-id-2",
|
||||||
|
};
|
||||||
|
|
||||||
|
const serviceIdsSequence = [
|
||||||
|
serviceIds.default,
|
||||||
|
serviceIds.default,
|
||||||
|
serviceIds.resolved1,
|
||||||
|
serviceIds.resolved1,
|
||||||
|
serviceIds.default,
|
||||||
|
serviceIds.iter1,
|
||||||
|
serviceIds.iter2,
|
||||||
|
serviceIds.resolved1,
|
||||||
|
serviceIds.resolved2,
|
||||||
|
];
|
||||||
|
|
||||||
|
const msgs = [
|
||||||
|
"call",
|
||||||
|
"capture call",
|
||||||
|
"accept closure call",
|
||||||
|
"accept ability call",
|
||||||
|
];
|
||||||
|
|
||||||
|
export const expectedServiceResults = serviceIdsSequence.flatMap((id) =>
|
||||||
|
msgs.map((msg) => `${id}: ${msg}`),
|
||||||
|
);
|
||||||
|
|
||||||
|
export async function servicesAsAbilitiesCall() {
|
||||||
|
Object.entries(serviceIds).forEach(([_key, id], _idx) =>
|
||||||
|
registerTestService(id, {
|
||||||
|
concatId: (s: string) => {
|
||||||
|
return `${id}: ${s}`;
|
||||||
|
},
|
||||||
|
getId: () => {
|
||||||
|
return id;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return await test();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const expectedServiceCaptureResults = [
|
||||||
|
"resolved-id-in-capture: in capture",
|
||||||
|
"default-id: in capture",
|
||||||
|
];
|
||||||
|
|
||||||
|
export async function servicesAsAbilitiesCaptureCall() {
|
||||||
|
["resolved-id-in-capture", "default-id"].forEach((id) =>
|
||||||
|
registerTestService(id, {
|
||||||
|
concatId: (s: string) => {
|
||||||
|
return `${id}: ${s}`;
|
||||||
|
},
|
||||||
|
getId: () => {
|
||||||
|
return id;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return await testCapture();
|
||||||
|
}
|
@ -5,10 +5,11 @@ import aqua.model.*
|
|||||||
import aqua.model.inline.state.{Arrows, Exports, Mangler}
|
import aqua.model.inline.state.{Arrows, Exports, Mangler}
|
||||||
import aqua.raw.ops.RawTag
|
import aqua.raw.ops.RawTag
|
||||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||||
import aqua.types.{AbilityType, ArrowType, BoxType, StreamType, Type}
|
import aqua.types.{AbilityType, ArrowType, BoxType, NamedType, StreamType, Type}
|
||||||
|
|
||||||
import cats.data.StateT
|
import cats.data.StateT
|
||||||
import cats.data.{Chain, IndexedStateT, State}
|
import cats.data.{Chain, IndexedStateT, State}
|
||||||
|
import cats.syntax.functor.*
|
||||||
import cats.syntax.applicative.*
|
import cats.syntax.applicative.*
|
||||||
import cats.syntax.bifunctor.*
|
import cats.syntax.bifunctor.*
|
||||||
import cats.syntax.foldable.*
|
import cats.syntax.foldable.*
|
||||||
@ -123,22 +124,22 @@ object ArrowInliner extends Logging {
|
|||||||
/**
|
/**
|
||||||
* Get ability fields (vars or arrows) from exports
|
* Get ability fields (vars or arrows) from exports
|
||||||
*
|
*
|
||||||
* @param abilityName ability current name in state
|
* @param name ability current name in state
|
||||||
* @param abilityNewName ability new name (for renaming)
|
* @param newName ability new name (for renaming)
|
||||||
* @param abilityType ability type
|
* @param type ability type
|
||||||
* @param exports exports state to resolve fields
|
* @param exports exports state to resolve fields
|
||||||
* @param fields fields selector
|
* @param fields fields selector
|
||||||
* @return resolved ability fields (renamed if necessary)
|
* @return resolved ability fields (renamed if necessary)
|
||||||
*/
|
*/
|
||||||
private def getAbilityFields[T <: Type](
|
private def getAbilityFields[T <: Type](
|
||||||
abilityName: String,
|
name: String,
|
||||||
abilityNewName: Option[String],
|
newName: Option[String],
|
||||||
abilityType: AbilityType,
|
`type`: NamedType,
|
||||||
exports: Map[String, ValueModel]
|
exports: Map[String, ValueModel]
|
||||||
)(fields: AbilityType => Map[String, T]): Map[String, ValueModel] =
|
)(fields: NamedType => Map[String, T]): Map[String, ValueModel] =
|
||||||
fields(abilityType).flatMap { case (fName, _) =>
|
fields(`type`).flatMap { case (fName, _) =>
|
||||||
val fullName = AbilityType.fullName(abilityName, fName)
|
val fullName = AbilityType.fullName(name, fName)
|
||||||
val newFullName = AbilityType.fullName(abilityNewName.getOrElse(abilityName), fName)
|
val newFullName = AbilityType.fullName(newName.getOrElse(name), fName)
|
||||||
|
|
||||||
Exports
|
Exports
|
||||||
.getLastValue(fullName, exports)
|
.getLastValue(fullName, exports)
|
||||||
@ -179,24 +180,24 @@ object ArrowInliner extends Logging {
|
|||||||
/**
|
/**
|
||||||
* Get ability arrows from arrows
|
* Get ability arrows from arrows
|
||||||
*
|
*
|
||||||
* @param abilityName ability current name in state
|
* @param name ability current name in state
|
||||||
* @param abilityNewName ability new name (for renaming)
|
* @param newName ability new name (for renaming)
|
||||||
* @param abilityType ability type
|
* @param type ability type
|
||||||
* @param exports exports state to resolve fields
|
* @param exports exports state to resolve fields
|
||||||
* @param arrows arrows state to resolve arrows
|
* @param arrows arrows state to resolve arrows
|
||||||
* @return resolved ability arrows (renamed if necessary)
|
* @return resolved ability arrows (renamed if necessary)
|
||||||
*/
|
*/
|
||||||
private def getAbilityArrows(
|
private def getAbilityArrows(
|
||||||
abilityName: String,
|
name: String,
|
||||||
abilityNewName: Option[String],
|
newName: Option[String],
|
||||||
abilityType: AbilityType,
|
`type`: NamedType,
|
||||||
exports: Map[String, ValueModel],
|
exports: Map[String, ValueModel],
|
||||||
arrows: Map[String, FuncArrow]
|
arrows: Map[String, FuncArrow]
|
||||||
): Map[String, FuncArrow] = {
|
): Map[String, FuncArrow] = {
|
||||||
val get = getAbilityFields(
|
val get = getAbilityFields(
|
||||||
abilityName,
|
name,
|
||||||
abilityNewName,
|
newName,
|
||||||
abilityType,
|
`type`,
|
||||||
exports
|
exports
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -210,12 +211,12 @@ object ArrowInliner extends Logging {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private def getAbilityArrows[S: Arrows: Exports](
|
private def getAbilityArrows[S: Arrows: Exports](
|
||||||
abilityName: String,
|
name: String,
|
||||||
abilityType: AbilityType
|
`type`: NamedType
|
||||||
): State[S, Map[String, FuncArrow]] = for {
|
): State[S, Map[String, FuncArrow]] = for {
|
||||||
exports <- Exports[S].exports
|
exports <- Exports[S].exports
|
||||||
arrows <- Arrows[S].arrows
|
arrows <- Arrows[S].arrows
|
||||||
} yield getAbilityArrows(abilityName, None, abilityType, exports, arrows)
|
} yield getAbilityArrows(name, None, `type`, exports, arrows)
|
||||||
|
|
||||||
final case class Renamed[T](
|
final case class Renamed[T](
|
||||||
renames: Map[String, String],
|
renames: Map[String, String],
|
||||||
@ -276,7 +277,22 @@ object ArrowInliner extends Logging {
|
|||||||
* to avoid collisions, then resolve them in context.
|
* to avoid collisions, then resolve them in context.
|
||||||
*/
|
*/
|
||||||
capturedValues <- findNewNames(fn.capturedValues)
|
capturedValues <- findNewNames(fn.capturedValues)
|
||||||
capturedArrows <- findNewNames(fn.capturedArrows)
|
/**
|
||||||
|
* If arrow correspond to a value,
|
||||||
|
* rename in accordingly to the value
|
||||||
|
*/
|
||||||
|
capturedArrowValues = fn.capturedArrows.flatMap { case (arrowName, arrow) =>
|
||||||
|
capturedValues.renames
|
||||||
|
.get(arrowName)
|
||||||
|
.orElse(fn.capturedValues.get(arrowName).as(arrowName))
|
||||||
|
.map(_ -> arrow)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Rename arrows that are not values
|
||||||
|
*/
|
||||||
|
capturedArrows <- findNewNames(fn.capturedArrows.filterNot { case (arrowName, _) =>
|
||||||
|
capturedArrowValues.contains(arrowName)
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function defines variables inside its body.
|
* Function defines variables inside its body.
|
||||||
@ -301,7 +317,12 @@ object ArrowInliner extends Logging {
|
|||||||
defineRenames
|
defineRenames
|
||||||
)
|
)
|
||||||
|
|
||||||
arrowsResolved = arrows ++ capturedArrows.renamed
|
/**
|
||||||
|
* TODO: Optimize resolve.
|
||||||
|
* It seems that resolving whole `exports`
|
||||||
|
* and `arrows` is not necessary.
|
||||||
|
*/
|
||||||
|
arrowsResolved = arrows ++ capturedArrowValues ++ capturedArrows.renamed
|
||||||
exportsResolved = exports ++ data.renamed ++ capturedValues.renamed
|
exportsResolved = exports ++ data.renamed ++ capturedValues.renamed
|
||||||
|
|
||||||
tree = fn.body.rename(renaming)
|
tree = fn.body.rename(renaming)
|
||||||
@ -322,12 +343,13 @@ object ArrowInliner extends Logging {
|
|||||||
|
|
||||||
exports <- Exports[S].exports
|
exports <- Exports[S].exports
|
||||||
streams <- getOutsideStreamNames
|
streams <- getOutsideStreamNames
|
||||||
|
arrows = passArrows ++ arrowsFromAbilities
|
||||||
|
|
||||||
inlineResult <- Exports[S].scope(
|
inlineResult <- Exports[S].scope(
|
||||||
Arrows[S].scope(
|
Arrows[S].scope(
|
||||||
for {
|
for {
|
||||||
// Process renamings, prepare environment
|
// Process renamings, prepare environment
|
||||||
fn <- ArrowInliner.prelude(arrow, call, exports, passArrows ++ arrowsFromAbilities)
|
fn <- ArrowInliner.prelude(arrow, call, exports, arrows)
|
||||||
inlineResult <- ArrowInliner.inline(fn, call, streams)
|
inlineResult <- ArrowInliner.inline(fn, call, streams)
|
||||||
} yield inlineResult
|
} yield inlineResult
|
||||||
)
|
)
|
||||||
|
@ -413,6 +413,49 @@ object TagInliner extends Logging {
|
|||||||
} yield TagInlined.Empty(prefix = prefix)
|
} yield TagInlined.Empty(prefix = prefix)
|
||||||
case _ => none
|
case _ => none
|
||||||
|
|
||||||
|
case ServiceIdTag(id, serviceType, name) =>
|
||||||
|
for {
|
||||||
|
idm <- valueToModel(id)
|
||||||
|
(idModel, idPrefix) = idm
|
||||||
|
|
||||||
|
// Make `FuncArrow` wrappers for service methods
|
||||||
|
methods <- serviceType.fields.toSortedMap.toList.traverse {
|
||||||
|
case (methodName, methodType) =>
|
||||||
|
for {
|
||||||
|
arrowName <- Mangler[S].findAndForbidName(s"$name-$methodName")
|
||||||
|
fn = FuncArrow.fromServiceMethod(
|
||||||
|
arrowName,
|
||||||
|
serviceType.name,
|
||||||
|
methodName,
|
||||||
|
methodType,
|
||||||
|
idModel
|
||||||
|
)
|
||||||
|
} yield methodName -> fn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve wrappers in arrows
|
||||||
|
_ <- Arrows[S].resolved(
|
||||||
|
methods.map { case (_, fn) =>
|
||||||
|
fn.funcName -> fn
|
||||||
|
}.toMap
|
||||||
|
)
|
||||||
|
|
||||||
|
// Resolve wrappers in exports
|
||||||
|
_ <- methods.traverse { case (methodName, fn) =>
|
||||||
|
Exports[S].resolveAbilityField(
|
||||||
|
name,
|
||||||
|
methodName,
|
||||||
|
VarModel(fn.funcName, fn.arrowType)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve service in exports
|
||||||
|
_ <- Exports[S].resolved(
|
||||||
|
name,
|
||||||
|
VarModel(name, serviceType)
|
||||||
|
)
|
||||||
|
} yield TagInlined.Empty(prefix = idPrefix)
|
||||||
|
|
||||||
case _: SeqGroupTag => pure(SeqModel)
|
case _: SeqGroupTag => pure(SeqModel)
|
||||||
case ParTag.Detach => pure(DetachModel)
|
case ParTag.Detach => pure(DetachModel)
|
||||||
case _: ParGroupTag => pure(ParModel)
|
case _: ParGroupTag => pure(ParModel)
|
||||||
|
@ -59,65 +59,57 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi
|
|||||||
|
|
||||||
private def unfoldAbilityProperty[S: Mangler: Exports: Arrows](
|
private def unfoldAbilityProperty[S: Mangler: Exports: Arrows](
|
||||||
varModel: VarModel,
|
varModel: VarModel,
|
||||||
abilityType: AbilityType,
|
abilityType: NamedType,
|
||||||
p: PropertyRaw
|
p: PropertyRaw
|
||||||
): State[S, (VarModel, Inline)] = {
|
): State[S, (VarModel, Inline)] = p match {
|
||||||
p match {
|
case IntoArrowRaw(arrowName, t, arguments) =>
|
||||||
case IntoArrowRaw(arrowName, t, arguments) =>
|
val arrowType = abilityType.fields
|
||||||
val arrowType = abilityType.fields
|
.lookup(arrowName)
|
||||||
.lookup(arrowName)
|
.collect { case at @ ArrowType(_, _) =>
|
||||||
.collect { case at @ ArrowType(_, _) =>
|
at
|
||||||
at
|
}
|
||||||
}
|
.getOrElse {
|
||||||
.getOrElse {
|
logger.error(s"Inlining, cannot find arrow $arrowName in $varModel")
|
||||||
logger.error(s"Inlining, cannot find arrow $arrowName in ability $varModel")
|
ArrowType(NilType, NilType)
|
||||||
ArrowType(NilType, NilType)
|
}
|
||||||
}
|
for {
|
||||||
for {
|
callArrow <- CallArrowRawInliner(
|
||||||
callArrow <- CallArrowRawInliner(
|
CallArrowRaw.func(
|
||||||
CallArrowRaw(
|
funcName = AbilityType.fullName(varModel.name, arrowName),
|
||||||
None,
|
baseType = arrowType,
|
||||||
AbilityType.fullName(varModel.name, arrowName),
|
arguments = arguments
|
||||||
arguments,
|
|
||||||
arrowType,
|
|
||||||
None
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
result <- callArrow match {
|
)
|
||||||
case (vm: VarModel, inl) =>
|
result <- callArrow match {
|
||||||
State.pure((vm, inl))
|
case (vm: VarModel, inl) =>
|
||||||
case (lm: LiteralModel, inl) =>
|
State.pure((vm, inl))
|
||||||
flatLiteralWithProperties(lm, inl, Chain.empty).flatMap { case (vm, inline) =>
|
case (lm: LiteralModel, inl) =>
|
||||||
Exports[S].resolved(vm.name, vm).map(_ => (vm, inline))
|
flatLiteralWithProperties(lm, inl, Chain.empty).flatMap { case (vm, inline) =>
|
||||||
}
|
Exports[S].resolved(vm.name, vm).map(_ => (vm, inline))
|
||||||
}
|
}
|
||||||
} yield {
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
case IntoFieldRaw(fieldName, at @ AbilityType(abName, fields)) =>
|
} yield result
|
||||||
(VarModel(AbilityType.fullName(varModel.name, fieldName), at), Inline.empty).pure
|
case IntoFieldRaw(fieldName, at @ AbilityType(abName, fields)) =>
|
||||||
case IntoFieldRaw(fieldName, t) =>
|
(VarModel(AbilityType.fullName(varModel.name, fieldName), at), Inline.empty).pure
|
||||||
for {
|
case IntoFieldRaw(fieldName, t) =>
|
||||||
abilityField <- Exports[S].getAbilityField(varModel.name, fieldName)
|
for {
|
||||||
result <- abilityField match {
|
abilityField <- Exports[S].getAbilityField(varModel.name, fieldName)
|
||||||
case Some(vm: VarModel) =>
|
result <- abilityField match {
|
||||||
State.pure((vm, Inline.empty))
|
case Some(vm: VarModel) =>
|
||||||
case Some(lm: LiteralModel) =>
|
State.pure((vm, Inline.empty))
|
||||||
flatLiteralWithProperties(lm, Inline.empty, Chain.empty)
|
case Some(lm: LiteralModel) =>
|
||||||
case _ =>
|
flatLiteralWithProperties(lm, Inline.empty, Chain.empty)
|
||||||
Exports[S].getKeys.flatMap { keys =>
|
case _ =>
|
||||||
logger.error(
|
Exports[S].getKeys.flatMap { keys =>
|
||||||
s"Inlining, cannot find field ${AbilityType
|
logger.error(
|
||||||
.fullName(varModel.name, fieldName)} in ability $varModel. Available: $keys"
|
s"Inlining, cannot find field ${AbilityType
|
||||||
)
|
.fullName(varModel.name, fieldName)} in ability $varModel. Available: $keys"
|
||||||
flatLiteralWithProperties(LiteralModel.quote(""), Inline.empty, Chain.empty)
|
)
|
||||||
}
|
flatLiteralWithProperties(LiteralModel.quote(""), Inline.empty, Chain.empty)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
||||||
} yield {
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
}
|
} yield result
|
||||||
}
|
}
|
||||||
|
|
||||||
private[inline] def unfoldProperty[S: Mangler: Exports: Arrows](
|
private[inline] def unfoldProperty[S: Mangler: Exports: Arrows](
|
||||||
@ -284,9 +276,9 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi
|
|||||||
case (_, _) =>
|
case (_, _) =>
|
||||||
unfold(raw).flatMap {
|
unfold(raw).flatMap {
|
||||||
case (vm: VarModel, prevInline) =>
|
case (vm: VarModel, prevInline) =>
|
||||||
unfoldProperties(prevInline, vm, properties, propertiesAllowed).map { case (v, i) =>
|
unfoldProperties(prevInline, vm, properties, propertiesAllowed)
|
||||||
v -> i
|
// To coerce types
|
||||||
}
|
.map(identity)
|
||||||
case (l: LiteralModel, inline) =>
|
case (l: LiteralModel, inline) =>
|
||||||
flatLiteralWithProperties(
|
flatLiteralWithProperties(
|
||||||
l,
|
l,
|
||||||
|
@ -32,12 +32,8 @@ trait Arrows[S] extends Scoped[S] {
|
|||||||
for {
|
for {
|
||||||
exps <- Exports[S].exports
|
exps <- Exports[S].exports
|
||||||
arrs <- arrows
|
arrs <- arrows
|
||||||
// _ = println(s"Resolved arrow: ${arrow.name}")
|
|
||||||
// _ = println(s"Captured var names: ${arrow.capturedVars}")
|
|
||||||
captuedVars = exps.filterKeys(arrow.capturedVars).toMap
|
captuedVars = exps.filterKeys(arrow.capturedVars).toMap
|
||||||
capturedArrows = arrs.filterKeys(arrow.capturedVars).toMap
|
capturedArrows = arrs.filterKeys(arrow.capturedVars).toMap
|
||||||
// _ = println(s"Captured vars: ${captuedVars}")
|
|
||||||
// _ = println(s"Captured arrows: ${capturedArrows}")
|
|
||||||
funcArrow = FuncArrow.fromRaw(arrow, capturedArrows, captuedVars, topology)
|
funcArrow = FuncArrow.fromRaw(arrow, capturedArrows, captuedVars, topology)
|
||||||
_ <- save(arrow.name, funcArrow)
|
_ <- save(arrow.name, funcArrow)
|
||||||
} yield ()
|
} yield ()
|
||||||
|
@ -2,7 +2,8 @@ package aqua.model.inline.state
|
|||||||
|
|
||||||
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
||||||
import aqua.model.ValueModel.Ability
|
import aqua.model.ValueModel.Ability
|
||||||
import aqua.types.AbilityType
|
import aqua.types.{AbilityType, NamedType}
|
||||||
|
|
||||||
import cats.data.{NonEmptyList, State}
|
import cats.data.{NonEmptyList, State}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -122,7 +123,7 @@ trait Exports[S] extends Scoped[S] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object Exports {
|
object Exports {
|
||||||
def apply[S](implicit exports: Exports[S]): Exports[S] = exports
|
def apply[S](using exports: Exports[S]): Exports[S] = exports
|
||||||
|
|
||||||
// Get last linked VarModel
|
// Get last linked VarModel
|
||||||
def getLastValue(name: String, state: Map[String, ValueModel]): Option[ValueModel] = {
|
def getLastValue(name: String, state: Map[String, ValueModel]): Option[ValueModel] = {
|
||||||
@ -143,7 +144,7 @@ object Exports {
|
|||||||
private def getAbilityPairs(
|
private def getAbilityPairs(
|
||||||
oldName: String,
|
oldName: String,
|
||||||
newName: String,
|
newName: String,
|
||||||
at: AbilityType,
|
at: NamedType,
|
||||||
state: Map[String, ValueModel]
|
state: Map[String, ValueModel]
|
||||||
): NonEmptyList[(String, ValueModel)] = {
|
): NonEmptyList[(String, ValueModel)] = {
|
||||||
at.fields.toNel.flatMap {
|
at.fields.toNel.flatMap {
|
||||||
|
@ -6,16 +6,16 @@ import aqua.types.{ArrowType, ProductType, ScalarType}
|
|||||||
object RawBuilder {
|
object RawBuilder {
|
||||||
|
|
||||||
def add(l: ValueRaw, r: ValueRaw): ValueRaw =
|
def add(l: ValueRaw, r: ValueRaw): ValueRaw =
|
||||||
CallArrowRaw(
|
CallArrowRaw.service(
|
||||||
ability = Some("math"),
|
abilityName = "math",
|
||||||
name = "add",
|
serviceId = LiteralRaw.quote("math"),
|
||||||
arguments = List(l, r),
|
funcName = "add",
|
||||||
baseType = ArrowType(
|
baseType = ArrowType(
|
||||||
ProductType(List(ScalarType.i64, ScalarType.i64)),
|
ProductType(List(ScalarType.i64, ScalarType.i64)),
|
||||||
ProductType(
|
ProductType(
|
||||||
List(l.`type` `∪` r.`type`)
|
List(l.`type` `∪` r.`type`)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
serviceId = Some(LiteralRaw.quote("math"))
|
arguments = List(l, r)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package aqua.raw
|
|||||||
|
|
||||||
import aqua.raw.arrow.FuncRaw
|
import aqua.raw.arrow.FuncRaw
|
||||||
import aqua.raw.value.ValueRaw
|
import aqua.raw.value.ValueRaw
|
||||||
import aqua.types.{StructType, Type, AbilityType}
|
import aqua.types.{AbilityType, StructType, Type}
|
||||||
|
|
||||||
import cats.Monoid
|
import cats.Monoid
|
||||||
import cats.Semigroup
|
import cats.Semigroup
|
||||||
@ -61,8 +61,9 @@ case class RawContext(
|
|||||||
all(_.services)
|
all(_.services)
|
||||||
|
|
||||||
lazy val types: Map[String, Type] =
|
lazy val types: Map[String, Type] =
|
||||||
collectPartsMap { case t: TypeRaw =>
|
collectPartsMap {
|
||||||
t.`type`
|
case t: TypeRaw => t.`type`
|
||||||
|
case s: ServiceRaw => s.`type`
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy val allTypes: Map[String, Type] =
|
lazy val allTypes: Map[String, Type] =
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
package aqua.raw
|
package aqua.raw
|
||||||
|
|
||||||
import aqua.types.{ArrowType, StructType}
|
import aqua.types.ServiceType
|
||||||
import cats.data.NonEmptyMap
|
|
||||||
import aqua.raw.value.ValueRaw
|
import aqua.raw.value.ValueRaw
|
||||||
|
|
||||||
case class ServiceRaw(
|
case class ServiceRaw(
|
||||||
name: String,
|
name: String,
|
||||||
arrows: NonEmptyMap[String, ArrowType],
|
`type`: ServiceType,
|
||||||
defaultId: Option[ValueRaw]
|
defaultId: Option[ValueRaw]
|
||||||
) extends RawPart {
|
) extends RawPart {
|
||||||
def rawPartType: StructType = StructType(name, arrows)
|
def rawPartType: ServiceType = `type`
|
||||||
|
|
||||||
override def rename(s: String): RawPart = copy(name = s)
|
override def rename(s: String): RawPart = copy(name = s)
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import aqua.raw.arrow.FuncRaw
|
|||||||
import aqua.raw.ops.RawTag.Tree
|
import aqua.raw.ops.RawTag.Tree
|
||||||
import aqua.raw.value.{CallArrowRaw, ValueRaw}
|
import aqua.raw.value.{CallArrowRaw, ValueRaw}
|
||||||
import aqua.tree.{TreeNode, TreeNodeCompanion}
|
import aqua.tree.{TreeNode, TreeNodeCompanion}
|
||||||
import aqua.types.{ArrowType, DataType}
|
import aqua.types.{ArrowType, DataType, ServiceType}
|
||||||
|
|
||||||
import cats.Show
|
import cats.Show
|
||||||
import cats.data.{Chain, NonEmptyList}
|
import cats.data.{Chain, NonEmptyList}
|
||||||
@ -20,7 +20,7 @@ sealed trait RawTag extends TreeNode[RawTag] {
|
|||||||
def restrictsVarNames: Set[String] = Set.empty
|
def restrictsVarNames: Set[String] = Set.empty
|
||||||
|
|
||||||
// All variable names introduced by this tag
|
// All variable names introduced by this tag
|
||||||
def definesVarNames: Set[String] = exportsVarNames ++ restrictsVarNames
|
final def definesVarNames: Set[String] = exportsVarNames ++ restrictsVarNames
|
||||||
|
|
||||||
// Variable names used by this tag (not introduced by it)
|
// Variable names used by this tag (not introduced by it)
|
||||||
def usesVarNames: Set[String] = Set.empty
|
def usesVarNames: Set[String] = Set.empty
|
||||||
@ -208,6 +208,22 @@ case class CallArrowRawTag(
|
|||||||
|
|
||||||
object CallArrowRawTag {
|
object CallArrowRawTag {
|
||||||
|
|
||||||
|
def ability(
|
||||||
|
abilityName: String,
|
||||||
|
funcName: String,
|
||||||
|
call: Call,
|
||||||
|
arrowType: ArrowType
|
||||||
|
): CallArrowRawTag =
|
||||||
|
CallArrowRawTag(
|
||||||
|
call.exportTo,
|
||||||
|
CallArrowRaw.ability(
|
||||||
|
abilityName,
|
||||||
|
funcName,
|
||||||
|
arrowType,
|
||||||
|
call.args
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def service(
|
def service(
|
||||||
serviceId: ValueRaw,
|
serviceId: ValueRaw,
|
||||||
fnName: String,
|
fnName: String,
|
||||||
@ -231,12 +247,10 @@ object CallArrowRawTag {
|
|||||||
def func(fnName: String, call: Call): CallArrowRawTag =
|
def func(fnName: String, call: Call): CallArrowRawTag =
|
||||||
CallArrowRawTag(
|
CallArrowRawTag(
|
||||||
call.exportTo,
|
call.exportTo,
|
||||||
CallArrowRaw(
|
CallArrowRaw.func(
|
||||||
None,
|
funcName = fnName,
|
||||||
fnName,
|
baseType = call.arrowType,
|
||||||
call.args,
|
arguments = call.args
|
||||||
call.arrowType,
|
|
||||||
None
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -307,15 +321,27 @@ object EmptyTag extends NoExecTag {
|
|||||||
override def mapValues(f: ValueRaw => ValueRaw): RawTag = this
|
override def mapValues(f: ValueRaw => ValueRaw): RawTag = this
|
||||||
}
|
}
|
||||||
|
|
||||||
case class AbilityIdTag(
|
/**
|
||||||
|
* Tag for `Service "id"` expression.
|
||||||
|
* For each such expression new ability
|
||||||
|
* is created with unique name (@p name).
|
||||||
|
*
|
||||||
|
* @param value value of service ID
|
||||||
|
* @param serviceType type of service
|
||||||
|
* @param name **rename** of service
|
||||||
|
*/
|
||||||
|
case class ServiceIdTag(
|
||||||
value: ValueRaw,
|
value: ValueRaw,
|
||||||
service: String
|
serviceType: ServiceType,
|
||||||
|
name: String
|
||||||
) extends NoExecTag {
|
) extends NoExecTag {
|
||||||
|
|
||||||
override def usesVarNames: Set[String] = value.varNames
|
override def usesVarNames: Set[String] = value.varNames
|
||||||
|
|
||||||
|
override def exportsVarNames: Set[String] = Set(name)
|
||||||
|
|
||||||
override def mapValues(f: ValueRaw => ValueRaw): RawTag =
|
override def mapValues(f: ValueRaw => ValueRaw): RawTag =
|
||||||
AbilityIdTag(value.map(f), service)
|
ServiceIdTag(value.map(f), serviceType, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
case class PushToStreamTag(operand: ValueRaw, exportTo: Call.Export) extends RawTag {
|
case class PushToStreamTag(operand: ValueRaw, exportTo: Call.Export) extends RawTag {
|
||||||
|
@ -250,12 +250,14 @@ case class CallArrowRaw(
|
|||||||
override def map(f: ValueRaw => ValueRaw): ValueRaw =
|
override def map(f: ValueRaw => ValueRaw): ValueRaw =
|
||||||
f(
|
f(
|
||||||
copy(
|
copy(
|
||||||
arguments = arguments.map(f),
|
arguments = arguments.map(_.map(f)),
|
||||||
serviceId = serviceId.map(f)
|
serviceId = serviceId.map(_.map(f))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
override def varNames: Set[String] = arguments.flatMap(_.varNames).toSet
|
override def varNames: Set[String] = name.some
|
||||||
|
.filterNot(_ => ability.isDefined || serviceId.isDefined)
|
||||||
|
.toSet ++ arguments.flatMap(_.varNames).toSet
|
||||||
|
|
||||||
override def renameVars(map: Map[String, String]): ValueRaw =
|
override def renameVars(map: Map[String, String]): ValueRaw =
|
||||||
copy(
|
copy(
|
||||||
|
@ -11,7 +11,7 @@ object ServiceRes {
|
|||||||
def fromModel(sm: ServiceModel): ServiceRes =
|
def fromModel(sm: ServiceModel): ServiceRes =
|
||||||
ServiceRes(
|
ServiceRes(
|
||||||
name = sm.name,
|
name = sm.name,
|
||||||
members = sm.arrows.toNel.toList,
|
members = sm.`type`.arrows.toList,
|
||||||
defaultId = sm.defaultId.collect {
|
defaultId = sm.defaultId.collect {
|
||||||
case LiteralModel(value, t) if ScalarType.string.acceptsValueOf(t) =>
|
case LiteralModel(value, t) if ScalarType.string.acceptsValueOf(t) =>
|
||||||
value
|
value
|
||||||
|
@ -5,15 +5,19 @@ import aqua.raw.ops.CallArrowRawTag
|
|||||||
import aqua.raw.value.ValueRaw
|
import aqua.raw.value.ValueRaw
|
||||||
import aqua.raw.value.CallArrowRaw
|
import aqua.raw.value.CallArrowRaw
|
||||||
import aqua.raw.{ConstantRaw, RawContext, RawPart, ServiceRaw, TypeRaw}
|
import aqua.raw.{ConstantRaw, RawContext, RawPart, ServiceRaw, TypeRaw}
|
||||||
import aqua.types.{StructType, Type}
|
import aqua.types.{AbilityType, StructType, Type}
|
||||||
|
|
||||||
import cats.Monoid
|
import cats.Monoid
|
||||||
import cats.data.NonEmptyMap
|
import cats.data.NonEmptyMap
|
||||||
import cats.data.Chain
|
import cats.data.Chain
|
||||||
import cats.kernel.Semigroup
|
import cats.kernel.Semigroup
|
||||||
import cats.syntax.functor.*
|
import cats.syntax.functor.*
|
||||||
|
import cats.syntax.foldable.*
|
||||||
|
import cats.syntax.traverse.*
|
||||||
|
import cats.syntax.bifunctor.*
|
||||||
import cats.syntax.monoid.*
|
import cats.syntax.monoid.*
|
||||||
|
import cats.syntax.option.*
|
||||||
import scribe.Logging
|
import scribe.Logging
|
||||||
|
|
||||||
import scala.collection.immutable.SortedMap
|
import scala.collection.immutable.SortedMap
|
||||||
|
|
||||||
case class AquaContext(
|
case class AquaContext(
|
||||||
@ -26,31 +30,76 @@ case class AquaContext(
|
|||||||
services: Map[String, ServiceModel]
|
services: Map[String, ServiceModel]
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private def prefixFirst[T](prefix: String, pair: (String, T)): (String, T) =
|
|
||||||
(prefix + pair._1, pair._2)
|
|
||||||
|
|
||||||
// TODO: it's a duplicate
|
// TODO: it's a duplicate
|
||||||
private def all[T](what: AquaContext => Map[String, T], prefix: String = ""): Map[String, T] =
|
private def all[T](
|
||||||
abilities
|
what: AquaContext => Map[String, T],
|
||||||
.foldLeft(what(this)) { case (ts, (k, v)) =>
|
prefix: String = ""
|
||||||
ts ++ v.all(what, k + ".")
|
): Map[String, T] = (
|
||||||
}
|
what(this) ++ abilities.toList.foldMap { case (k, v) =>
|
||||||
.map(prefixFirst(prefix, _))
|
v.all(what, k + ".").toList
|
||||||
|
}
|
||||||
|
).map(_.leftMap(prefix + _)).toMap
|
||||||
|
|
||||||
lazy val allValues: Map[String, ValueModel] =
|
lazy val allValues: Map[String, ValueModel] =
|
||||||
all(_.values)
|
all(_.values) ++
|
||||||
|
/**
|
||||||
|
* Add values from services that have default ID
|
||||||
|
* So that they will be available in functions.
|
||||||
|
*/
|
||||||
|
services.flatMap { case (srvName, srv) =>
|
||||||
|
srv.defaultId.toList.flatMap(_ =>
|
||||||
|
srv.`type`.arrows.map { case (arrowName, arrowType) =>
|
||||||
|
val fullName = AbilityType.fullName(srvName, arrowName)
|
||||||
|
|
||||||
|
fullName -> VarModel(
|
||||||
|
fullName,
|
||||||
|
arrowType
|
||||||
|
)
|
||||||
|
}.updated(
|
||||||
|
srvName,
|
||||||
|
VarModel(
|
||||||
|
srvName,
|
||||||
|
srv.`type`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
lazy val allFuncs: Map[String, FuncArrow] =
|
lazy val allFuncs: Map[String, FuncArrow] =
|
||||||
all(_.funcs)
|
all(_.funcs) ++
|
||||||
|
/**
|
||||||
|
* Add functions from services that have default ID
|
||||||
|
* So that they will be available in functions.
|
||||||
|
*/
|
||||||
|
services.flatMap { case (srvName, srv) =>
|
||||||
|
srv.defaultId.toList.flatMap(id =>
|
||||||
|
srv.`type`.arrows.map { case (arrowName, arrowType) =>
|
||||||
|
val fullName = AbilityType.fullName(srvName, arrowName)
|
||||||
|
|
||||||
|
fullName -> FuncArrow.fromServiceMethod(
|
||||||
|
fullName,
|
||||||
|
srvName,
|
||||||
|
arrowName,
|
||||||
|
arrowType,
|
||||||
|
id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private def pickOne[T](
|
private def pickOne[T](
|
||||||
name: String,
|
name: String,
|
||||||
newName: String,
|
newName: String,
|
||||||
ctx: Map[String, T],
|
ctx: Map[String, T],
|
||||||
add: (AquaContext, Map[String, T]) => AquaContext
|
add: (AquaContext, Map[String, T]) => AquaContext
|
||||||
): AquaContext = {
|
): AquaContext = ctx
|
||||||
ctx.get(name).fold(AquaContext.blank)(t => add(AquaContext.blank, Map(newName -> t)))
|
.get(name)
|
||||||
}
|
.fold(AquaContext.blank)(t =>
|
||||||
|
add(
|
||||||
|
AquaContext.blank,
|
||||||
|
Map(newName -> t)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def pick(name: String, maybeRename: Option[String]): AquaContext = {
|
def pick(name: String, maybeRename: Option[String]): AquaContext = {
|
||||||
val newName = maybeRename.getOrElse(name)
|
val newName = maybeRename.getOrElse(name)
|
||||||
@ -68,7 +117,7 @@ object AquaContext extends Logging {
|
|||||||
lazy val size: Long = data.size
|
lazy val size: Long = data.size
|
||||||
|
|
||||||
def get(ctx: RawContext): Option[AquaContext] =
|
def get(ctx: RawContext): Option[AquaContext] =
|
||||||
data.find(_._1 eq ctx).map(_._2)
|
data.collectFirst { case (rawCtx, aquaCtx) if rawCtx eq ctx => aquaCtx }
|
||||||
|
|
||||||
def updated(ctx: RawContext, aCtx: AquaContext): Cache = copy(data :+ (ctx -> aCtx))
|
def updated(ctx: RawContext, aCtx: AquaContext): Cache = copy(data :+ (ctx -> aCtx))
|
||||||
}
|
}
|
||||||
@ -91,11 +140,10 @@ object AquaContext extends Logging {
|
|||||||
x.services ++ y.services
|
x.services ++ y.services
|
||||||
)
|
)
|
||||||
|
|
||||||
//
|
|
||||||
def fromService(sm: ServiceRaw, serviceId: ValueRaw): AquaContext =
|
def fromService(sm: ServiceRaw, serviceId: ValueRaw): AquaContext =
|
||||||
blank.copy(
|
blank.copy(
|
||||||
module = Some(sm.name),
|
module = Some(sm.name),
|
||||||
funcs = sm.arrows.toSortedMap.map { case (fnName, arrowType) =>
|
funcs = sm.`type`.arrows.map { case (fnName, arrowType) =>
|
||||||
val (args, call, ret) = ArgsCall.arrowToArgsCallRet(arrowType)
|
val (args, call, ret) = ArgsCall.arrowToArgsCallRet(arrowType)
|
||||||
fnName ->
|
fnName ->
|
||||||
FuncArrow(
|
FuncArrow(
|
||||||
@ -192,13 +240,16 @@ object AquaContext extends Logging {
|
|||||||
logger.trace("Adding service " + m.name)
|
logger.trace("Adding service " + m.name)
|
||||||
val (pctx, pcache) = fromRawContext(partContext, ctxCache)
|
val (pctx, pcache) = fromRawContext(partContext, ctxCache)
|
||||||
logger.trace("Got " + m.name + " from raw")
|
logger.trace("Got " + m.name + " from raw")
|
||||||
val id = m.defaultId.map(ValueModel.fromRaw).map(_.resolveWith(pctx.allValues))
|
val id = m.defaultId
|
||||||
val srv = ServiceModel(m.name, m.arrows, id)
|
.map(ValueModel.fromRaw)
|
||||||
|
.map(_.resolveWith(pctx.allValues))
|
||||||
|
val srv = ServiceModel(m.name, m.`type`, id)
|
||||||
val add =
|
val add =
|
||||||
blank
|
blank
|
||||||
.copy(
|
.copy(
|
||||||
abilities =
|
abilities = m.defaultId
|
||||||
m.defaultId.fold(Map.empty)(id => Map(m.name -> fromService(m, id))),
|
.map(id => Map(m.name -> fromService(m, id)))
|
||||||
|
.orEmpty,
|
||||||
services = Map(m.name -> srv)
|
services = Map(m.name -> srv)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package aqua.model
|
package aqua.model
|
||||||
|
|
||||||
import aqua.model.{ValueModel, VarModel}
|
import aqua.model.{ValueModel, VarModel}
|
||||||
|
import aqua.model.ValueModel.Ability
|
||||||
import aqua.raw.ops.Call
|
import aqua.raw.ops.Call
|
||||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||||
import aqua.types.*
|
import aqua.types.*
|
||||||
@ -39,11 +40,11 @@ case class ArgsCall(args: ProductType, callWith: List[ValueModel]) {
|
|||||||
}.toMap
|
}.toMap
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ability arguments as mapping
|
* Ability and service arguments as mapping
|
||||||
* Name of argument -> (variable passed in the call, ability type)
|
* Name of argument -> (variable passed in the call, type)
|
||||||
*/
|
*/
|
||||||
lazy val abilityArgs: Map[String, (VarModel, AbilityType)] =
|
lazy val abilityArgs: Map[String, (VarModel, NamedType)] =
|
||||||
zipped.collect { case ((name, _), vr @ VarModel(_, t @ AbilityType(_, _), _)) =>
|
zipped.collect { case ((name, _), vr @ Ability(_, t, _)) =>
|
||||||
name -> (vr, t)
|
name -> (vr, t)
|
||||||
}.toMap
|
}.toMap
|
||||||
|
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
package aqua.model
|
package aqua.model
|
||||||
|
|
||||||
import aqua.raw.ops.Call
|
import aqua.raw.ops.Call
|
||||||
import aqua.types.{ArrowType, AbilityType, Type}
|
import aqua.types.{ArrowType, NamedType, Type}
|
||||||
|
import aqua.model.ValueModel.{Ability, Arrow}
|
||||||
|
|
||||||
// TODO docs
|
// TODO docs
|
||||||
case class CallModel(args: List[ValueModel], exportTo: List[CallModel.Export]) {
|
case class CallModel(args: List[ValueModel], exportTo: List[CallModel.Export]) {
|
||||||
override def toString: String = s"[${args.mkString(" ")}] ${exportTo.mkString(" ")}"
|
override def toString: String = s"[${args.mkString(" ")}] ${exportTo.mkString(" ")}"
|
||||||
|
|
||||||
def arrowArgNames: Set[String] = args.collect { case VarModel(m, _: ArrowType, _) =>
|
def arrowArgNames: Set[String] = args.collect { case Arrow(m, _) =>
|
||||||
m
|
m
|
||||||
}.toSet
|
}.toSet
|
||||||
|
|
||||||
def abilityArgs: List[(String, AbilityType)] = args.collect { case VarModel(m, t: AbilityType, _) =>
|
def abilityArgs: List[(String, NamedType)] = args.collect { case Ability(m, t, _) =>
|
||||||
(m, t)
|
(m, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,9 +2,9 @@ package aqua.model
|
|||||||
|
|
||||||
import aqua.raw.Raw
|
import aqua.raw.Raw
|
||||||
import aqua.raw.arrow.FuncRaw
|
import aqua.raw.arrow.FuncRaw
|
||||||
import aqua.raw.ops.RawTag
|
import aqua.raw.ops.{Call, CallArrowRawTag, RawTag}
|
||||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||||
import aqua.types.{ArrowType, Type}
|
import aqua.types.{ArrowType, ServiceType, Type}
|
||||||
|
|
||||||
case class FuncArrow(
|
case class FuncArrow(
|
||||||
funcName: String,
|
funcName: String,
|
||||||
@ -42,4 +42,49 @@ object FuncArrow {
|
|||||||
constants,
|
constants,
|
||||||
topology
|
topology
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create function - wrapper around a service method
|
||||||
|
*
|
||||||
|
* @param funcName name of the function
|
||||||
|
* @param serviceName name of the service
|
||||||
|
* @param methodName name of the service method to wrap
|
||||||
|
* @param methodType type of the service method to wrap
|
||||||
|
* @param idValue resolved value of the service id
|
||||||
|
* @return `FuncArrow` wrapper for the service method
|
||||||
|
*/
|
||||||
|
def fromServiceMethod(
|
||||||
|
funcName: String,
|
||||||
|
serviceName: String,
|
||||||
|
methodName: String,
|
||||||
|
methodType: ArrowType,
|
||||||
|
idValue: ValueModel
|
||||||
|
): FuncArrow = {
|
||||||
|
val id = VarRaw("id", idValue.`type`)
|
||||||
|
val retVar = methodType.res.map(t => VarRaw("ret", t))
|
||||||
|
|
||||||
|
val call = Call(
|
||||||
|
methodType.domain.toLabelledList().map(VarRaw.apply),
|
||||||
|
retVar.map(r => Call.Export(r.name, r.`type`)).toList
|
||||||
|
)
|
||||||
|
val body = CallArrowRawTag.service(
|
||||||
|
serviceId = id,
|
||||||
|
fnName = methodName,
|
||||||
|
call = call,
|
||||||
|
name = serviceName,
|
||||||
|
arrowType = methodType
|
||||||
|
)
|
||||||
|
|
||||||
|
FuncArrow(
|
||||||
|
funcName = funcName,
|
||||||
|
body = body.leaf,
|
||||||
|
arrowType = methodType,
|
||||||
|
ret = retVar.toList,
|
||||||
|
capturedArrows = Map.empty,
|
||||||
|
capturedValues = Map(
|
||||||
|
id.name -> idValue
|
||||||
|
),
|
||||||
|
capturedTopology = None
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package aqua.model
|
package aqua.model
|
||||||
|
|
||||||
import aqua.types.ArrowType
|
import aqua.types.ServiceType
|
||||||
import cats.data.NonEmptyMap
|
|
||||||
|
|
||||||
case class ServiceModel(
|
case class ServiceModel(
|
||||||
name: String,
|
name: String,
|
||||||
arrows: NonEmptyMap[String, ArrowType],
|
`type`: ServiceType,
|
||||||
defaultId: Option[ValueModel]
|
defaultId: Option[ValueModel]
|
||||||
)
|
)
|
||||||
|
@ -51,11 +51,21 @@ object ValueModel {
|
|||||||
case _ => ???
|
case _ => ???
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object Arrow {
|
||||||
|
|
||||||
|
def unapply(vm: VarModel): Option[(String, ArrowType)] =
|
||||||
|
vm match {
|
||||||
|
case VarModel(name, t: ArrowType, _) =>
|
||||||
|
(name, t).some
|
||||||
|
case _ => none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
object Ability {
|
object Ability {
|
||||||
|
|
||||||
def unapply(vm: VarModel): Option[(String, AbilityType, Chain[PropertyModel])] =
|
def unapply(vm: VarModel): Option[(String, NamedType, Chain[PropertyModel])] =
|
||||||
vm match {
|
vm match {
|
||||||
case VarModel(name, t@AbilityType(_, _), properties) =>
|
case VarModel(name, t: (AbilityType | ServiceType), properties) =>
|
||||||
(name, t, properties).some
|
(name, t, properties).some
|
||||||
case _ => none
|
case _ => none
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ case class ArrowExpr[F[_]](arrowTypeExpr: ArrowTypeToken[F])
|
|||||||
object ArrowExpr extends Expr.AndIndented {
|
object ArrowExpr extends Expr.AndIndented {
|
||||||
|
|
||||||
val funcChildren: List[Expr.Lexem] =
|
val funcChildren: List[Expr.Lexem] =
|
||||||
AbilityIdExpr ::
|
ServiceIdExpr ::
|
||||||
PushToStreamExpr ::
|
PushToStreamExpr ::
|
||||||
ForExpr ::
|
ForExpr ::
|
||||||
Expr.defer(OnExpr) ::
|
Expr.defer(OnExpr) ::
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package aqua.parser.expr.func
|
package aqua.parser.expr.func
|
||||||
|
|
||||||
import aqua.parser.Expr
|
import aqua.parser.Expr
|
||||||
import aqua.parser.expr.func.AbilityIdExpr
|
import aqua.parser.expr.func.ServiceIdExpr
|
||||||
import aqua.parser.lexer.Token.*
|
import aqua.parser.lexer.Token.*
|
||||||
import aqua.parser.lexer.{NamedTypeToken, ValueToken}
|
import aqua.parser.lexer.{NamedTypeToken, ValueToken}
|
||||||
import aqua.parser.lift.LiftParser
|
import aqua.parser.lift.LiftParser
|
||||||
@ -10,19 +10,19 @@ import cats.{~>, Comonad}
|
|||||||
import aqua.parser.lift.Span
|
import aqua.parser.lift.Span
|
||||||
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
|
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
|
||||||
|
|
||||||
case class AbilityIdExpr[F[_]](ability: NamedTypeToken[F], id: ValueToken[F])
|
case class ServiceIdExpr[F[_]](service: NamedTypeToken[F], id: ValueToken[F])
|
||||||
extends Expr[F](AbilityIdExpr, ability) {
|
extends Expr[F](ServiceIdExpr, service) {
|
||||||
|
|
||||||
def mapK[K[_]: Comonad](fk: F ~> K): AbilityIdExpr[K] =
|
def mapK[K[_]: Comonad](fk: F ~> K): ServiceIdExpr[K] =
|
||||||
copy(ability.copy(fk(ability.name)), id.mapK(fk))
|
copy(service.copy(fk(service.name)), id.mapK(fk))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object AbilityIdExpr extends Expr.Leaf {
|
object ServiceIdExpr extends Expr.Leaf {
|
||||||
|
|
||||||
override val p: P[AbilityIdExpr[Span.S]] =
|
override val p: P[ServiceIdExpr[Span.S]] =
|
||||||
((NamedTypeToken.dotted <* ` `) ~ ValueToken.`value`).map { case (ability, id) =>
|
((NamedTypeToken.dotted <* ` `) ~ ValueToken.`value`).map { case (ability, id) =>
|
||||||
AbilityIdExpr(ability, id)
|
ServiceIdExpr(ability, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -118,8 +118,8 @@ trait AquaSpec extends EitherValues {
|
|||||||
def parseUse(str: String): UseFromExpr[Id] =
|
def parseUse(str: String): UseFromExpr[Id] =
|
||||||
UseFromExpr.p.parseAll(str).value.mapK(spanToId)
|
UseFromExpr.p.parseAll(str).value.mapK(spanToId)
|
||||||
|
|
||||||
def parseAbId(str: String): AbilityIdExpr[Id] =
|
def parseServiceId(str: String): ServiceIdExpr[Id] =
|
||||||
AbilityIdExpr.p.parseAll(str).value.mapK(spanToId)
|
ServiceIdExpr.p.parseAll(str).value.mapK(spanToId)
|
||||||
|
|
||||||
def parseOn(str: String): OnExpr[Id] =
|
def parseOn(str: String): OnExpr[Id] =
|
||||||
OnExpr.p.parseAll(str).value.mapK(spanToId)
|
OnExpr.p.parseAll(str).value.mapK(spanToId)
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package aqua.parser
|
package aqua.parser
|
||||||
|
|
||||||
import aqua.AquaSpec
|
import aqua.AquaSpec
|
||||||
import aqua.parser.expr.func.AbilityIdExpr
|
import aqua.parser.expr.func.ServiceIdExpr
|
||||||
import aqua.parser.lexer.LiteralToken
|
import aqua.parser.lexer.LiteralToken
|
||||||
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
|
||||||
@ -12,20 +13,20 @@ class AbilityIdExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
|||||||
import AquaSpec._
|
import AquaSpec._
|
||||||
|
|
||||||
"abilities" should "be parsed" in {
|
"abilities" should "be parsed" in {
|
||||||
parseAbId("Ab a") should be(
|
parseServiceId("Ab a") should be(
|
||||||
AbilityIdExpr[Id](toNamedType("Ab"), toVar("a"))
|
ServiceIdExpr[Id](toNamedType("Ab"), toVar("a"))
|
||||||
)
|
)
|
||||||
|
|
||||||
parseAbId("Ab \"a\"") should be(
|
parseServiceId("Ab \"a\"") should be(
|
||||||
AbilityIdExpr[Id](toNamedType("Ab"), LiteralToken[Id]("\"a\"", LiteralType.string))
|
ServiceIdExpr[Id](toNamedType("Ab"), LiteralToken[Id]("\"a\"", LiteralType.string))
|
||||||
)
|
)
|
||||||
|
|
||||||
parseAbId("Ab 1") should be(
|
parseServiceId("Ab 1") should be(
|
||||||
AbilityIdExpr[Id](toNamedType("Ab"), toNumber(1))
|
ServiceIdExpr[Id](toNamedType("Ab"), toNumber(1))
|
||||||
)
|
)
|
||||||
|
|
||||||
parseAbId("Ab a.id") should be(
|
parseServiceId("Ab a.id") should be(
|
||||||
AbilityIdExpr[Id](toNamedType("Ab"), toVarLambda("a", List("id")))
|
ServiceIdExpr[Id](toNamedType("Ab"), toVarLambda("a", List("id")))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,17 +2,13 @@ package aqua.parser
|
|||||||
|
|
||||||
import aqua.AquaSpec
|
import aqua.AquaSpec
|
||||||
import aqua.parser.expr.*
|
import aqua.parser.expr.*
|
||||||
import aqua.parser.expr.func.{AbilityIdExpr, ArrowExpr, CallArrowExpr, IfExpr, OnExpr, ReturnExpr}
|
import aqua.parser.expr.func.*
|
||||||
import aqua.parser.lexer.{
|
import aqua.parser.lexer.*
|
||||||
ArrowTypeToken,
|
|
||||||
BasicTypeToken,
|
|
||||||
CallArrowToken,
|
|
||||||
LiteralToken,
|
|
||||||
Token,
|
|
||||||
VarToken
|
|
||||||
}
|
|
||||||
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
||||||
|
import aqua.parser.lift.Span
|
||||||
|
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
|
||||||
import aqua.types.ScalarType.*
|
import aqua.types.ScalarType.*
|
||||||
|
|
||||||
import cats.Id
|
import cats.Id
|
||||||
import cats.data.{Chain, NonEmptyList}
|
import cats.data.{Chain, NonEmptyList}
|
||||||
import cats.data.Chain.*
|
import cats.data.Chain.*
|
||||||
@ -27,11 +23,6 @@ import cats.Eval
|
|||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import scala.language.implicitConversions
|
import scala.language.implicitConversions
|
||||||
import aqua.parser.lift.Span
|
|
||||||
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
|
|
||||||
import aqua.parser.lexer.PropertyToken
|
|
||||||
import aqua.parser.lexer.IntoArrow
|
|
||||||
import aqua.parser.expr.func.AssignmentExpr
|
|
||||||
|
|
||||||
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, *}
|
||||||
@ -123,7 +114,7 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with Inside with Inspectors
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
ifBody(1).head.mapK(spanToId) should be(AbilityIdExpr(toNamedType("Peer"), toStr("some id")))
|
ifBody(1).head.mapK(spanToId) should be(ServiceIdExpr(toNamedType("Peer"), toStr("some id")))
|
||||||
ifBody(2).head.mapK(spanToId) should be(
|
ifBody(2).head.mapK(spanToId) should be(
|
||||||
CallArrowExpr(Nil, CallArrowToken("call", List(toBool(true))))
|
CallArrowExpr(Nil, CallArrowToken("call", List(toBool(true))))
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package aqua.parser.head
|
package aqua.parser.head
|
||||||
|
|
||||||
import aqua.AquaSpec
|
import aqua.AquaSpec
|
||||||
import aqua.parser.expr.func.AbilityIdExpr
|
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
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package aqua.parser.head
|
package aqua.parser.head
|
||||||
|
|
||||||
import aqua.AquaSpec
|
import aqua.AquaSpec
|
||||||
import aqua.parser.expr.func.AbilityIdExpr
|
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
|
||||||
@ -23,8 +23,7 @@ class ImportFromSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
HeadExpr
|
HeadExpr.ast
|
||||||
.ast
|
|
||||||
.parseAll(s"""import MyModule, func as fn from "file.aqua"
|
.parseAll(s"""import MyModule, func as fn from "file.aqua"
|
||||||
|""".stripMargin)
|
|""".stripMargin)
|
||||||
.value
|
.value
|
||||||
@ -32,7 +31,8 @@ class ImportFromSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
|||||||
.value
|
.value
|
||||||
.headOption
|
.headOption
|
||||||
.get
|
.get
|
||||||
.head.mapK(spanToId) should be(
|
.head
|
||||||
|
.mapK(spanToId) should be(
|
||||||
ImportFromExpr(
|
ImportFromExpr(
|
||||||
NonEmptyList.fromListUnsafe(
|
NonEmptyList.fromListUnsafe(
|
||||||
Right(toAb("MyModule") -> None) :: Left(toName("func") -> Some(toName("fn"))) :: Nil
|
Right(toAb("MyModule") -> None) :: Left(toName("func") -> Some(toName("fn"))) :: Nil
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package aqua.parser.head
|
package aqua.parser.head
|
||||||
|
|
||||||
import aqua.AquaSpec
|
import aqua.AquaSpec
|
||||||
import aqua.parser.expr.func.AbilityIdExpr
|
import aqua.parser.expr.func.ServiceIdExpr
|
||||||
import aqua.parser.lexer.{LiteralToken, Token}
|
import aqua.parser.lexer.{LiteralToken, Token}
|
||||||
import aqua.types.LiteralType
|
import aqua.types.LiteralType
|
||||||
import cats.Id
|
import cats.Id
|
||||||
@ -22,12 +22,12 @@ class ModuleSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
HeadExpr
|
HeadExpr.ast
|
||||||
.ast
|
|
||||||
.parseAll(s"""module MyModule declares *
|
.parseAll(s"""module MyModule declares *
|
||||||
|""".stripMargin)
|
|""".stripMargin)
|
||||||
.value
|
.value
|
||||||
.head.mapK(spanToId) should be(
|
.head
|
||||||
|
.mapK(spanToId) should be(
|
||||||
ModuleExpr(
|
ModuleExpr(
|
||||||
toAb("MyModule"),
|
toAb("MyModule"),
|
||||||
Some(Token.lift[Id, Unit](())),
|
Some(Token.lift[Id, Unit](())),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package aqua.parser.head
|
package aqua.parser.head
|
||||||
|
|
||||||
import aqua.AquaSpec
|
import aqua.AquaSpec
|
||||||
import aqua.parser.expr.func.AbilityIdExpr
|
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
|
||||||
|
@ -8,6 +8,7 @@ import aqua.semantics.rules.definitions.DefinitionsState
|
|||||||
import aqua.semantics.rules.locations.LocationsState
|
import aqua.semantics.rules.locations.LocationsState
|
||||||
import aqua.semantics.rules.names.NamesState
|
import aqua.semantics.rules.names.NamesState
|
||||||
import aqua.semantics.rules.types.TypesState
|
import aqua.semantics.rules.types.TypesState
|
||||||
|
import aqua.semantics.rules.mangler.ManglerState
|
||||||
import aqua.semantics.rules.errors.ReportErrors
|
import aqua.semantics.rules.errors.ReportErrors
|
||||||
|
|
||||||
import cats.Semigroup
|
import cats.Semigroup
|
||||||
@ -19,6 +20,7 @@ import monocle.macros.GenLens
|
|||||||
|
|
||||||
case class CompilerState[S[_]](
|
case class CompilerState[S[_]](
|
||||||
errors: Chain[SemanticError[S]] = Chain.empty[SemanticError[S]],
|
errors: Chain[SemanticError[S]] = Chain.empty[SemanticError[S]],
|
||||||
|
mangler: ManglerState = ManglerState(),
|
||||||
names: NamesState[S] = NamesState[S](),
|
names: NamesState[S] = NamesState[S](),
|
||||||
abilities: AbilitiesState[S] = AbilitiesState[S](),
|
abilities: AbilitiesState[S] = AbilitiesState[S](),
|
||||||
types: TypesState[S] = TypesState[S](),
|
types: TypesState[S] = TypesState[S](),
|
||||||
@ -42,6 +44,9 @@ object CompilerState {
|
|||||||
given [S[_]]: Lens[CompilerState[S], AbilitiesState[S]] =
|
given [S[_]]: Lens[CompilerState[S], AbilitiesState[S]] =
|
||||||
GenLens[CompilerState[S]](_.abilities)
|
GenLens[CompilerState[S]](_.abilities)
|
||||||
|
|
||||||
|
given [S[_]]: Lens[CompilerState[S], ManglerState] =
|
||||||
|
GenLens[CompilerState[S]](_.mangler)
|
||||||
|
|
||||||
given [S[_]]: Lens[CompilerState[S], TypesState[S]] =
|
given [S[_]]: Lens[CompilerState[S], TypesState[S]] =
|
||||||
GenLens[CompilerState[S]](_.types)
|
GenLens[CompilerState[S]](_.types)
|
||||||
|
|
||||||
@ -60,7 +65,7 @@ object CompilerState {
|
|||||||
st.focus(_.errors).modify(_.append(RulesViolated(token, hints)))
|
st.focus(_.errors).modify(_.append(RulesViolated(token, hints)))
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit def compilerStateMonoid[S[_]]: Monoid[St[S]] = new Monoid[St[S]] {
|
given [S[_]]: Monoid[St[S]] with {
|
||||||
override def empty: St[S] = State.pure(Raw.Empty("compiler state monoid empty"))
|
override def empty: St[S] = State.pure(Raw.Empty("compiler state monoid empty"))
|
||||||
|
|
||||||
override def combine(x: St[S], y: St[S]): St[S] = for {
|
override def combine(x: St[S], y: St[S]): St[S] = for {
|
||||||
@ -69,6 +74,7 @@ object CompilerState {
|
|||||||
_ <- State.set(
|
_ <- State.set(
|
||||||
CompilerState[S](
|
CompilerState[S](
|
||||||
a.errors ++ b.errors,
|
a.errors ++ b.errors,
|
||||||
|
a.mangler |+| b.mangler,
|
||||||
a.names |+| b.names,
|
a.names |+| b.names,
|
||||||
a.abilities |+| b.abilities,
|
a.abilities |+| b.abilities,
|
||||||
a.types |+| b.types,
|
a.types |+| b.types,
|
||||||
|
@ -27,7 +27,7 @@ object ExprSem {
|
|||||||
L: LocationsAlgebra[S, G]
|
L: LocationsAlgebra[S, G]
|
||||||
): Prog[G, Raw] =
|
): Prog[G, Raw] =
|
||||||
expr match {
|
expr match {
|
||||||
case expr: AbilityIdExpr[S] => new AbilityIdSem(expr).program[G]
|
case expr: ServiceIdExpr[S] => new ServiceIdSem(expr).program[G]
|
||||||
case expr: AssignmentExpr[S] => new AssignmentSem(expr).program[G]
|
case expr: AssignmentExpr[S] => new AssignmentSem(expr).program[G]
|
||||||
case expr: PushToStreamExpr[S] => new PushToStreamSem(expr).program[G]
|
case expr: PushToStreamExpr[S] => new PushToStreamSem(expr).program[G]
|
||||||
case expr: AliasExpr[S] => new AliasSem(expr).program[G]
|
case expr: AliasExpr[S] => new AliasSem(expr).program[G]
|
||||||
|
@ -8,15 +8,11 @@ import aqua.raw.{Raw, RawContext, RawPart}
|
|||||||
import aqua.semantics.header.Picker
|
import aqua.semantics.header.Picker
|
||||||
import aqua.semantics.header.Picker.*
|
import aqua.semantics.header.Picker.*
|
||||||
import aqua.semantics.rules.abilities.{AbilitiesAlgebra, AbilitiesInterpreter, AbilitiesState}
|
import aqua.semantics.rules.abilities.{AbilitiesAlgebra, AbilitiesInterpreter, AbilitiesState}
|
||||||
import aqua.semantics.rules.definitions.{
|
import aqua.semantics.rules.definitions.{DefinitionsAlgebra, DefinitionsInterpreter}
|
||||||
DefinitionsAlgebra,
|
import aqua.semantics.rules.locations.{DummyLocationsInterpreter, LocationsAlgebra}
|
||||||
DefinitionsInterpreter,
|
import aqua.semantics.rules.names.{NamesAlgebra, NamesInterpreter}
|
||||||
DefinitionsState
|
import aqua.semantics.rules.mangler.{ManglerAlgebra, ManglerInterpreter}
|
||||||
}
|
import aqua.semantics.rules.types.{TypesAlgebra, TypesInterpreter}
|
||||||
import aqua.semantics.rules.locations.{DummyLocationsInterpreter, LocationsAlgebra, LocationsState}
|
|
||||||
import aqua.semantics.rules.names.{NamesAlgebra, NamesInterpreter, NamesState}
|
|
||||||
import aqua.semantics.rules.types.{TypesAlgebra, TypesInterpreter, TypesState}
|
|
||||||
import aqua.semantics.rules.ValuesAlgebra
|
|
||||||
import aqua.semantics.rules.errors.ReportErrors
|
import aqua.semantics.rules.errors.ReportErrors
|
||||||
import aqua.semantics.rules.errors.ErrorsAlgebra
|
import aqua.semantics.rules.errors.ErrorsAlgebra
|
||||||
import aqua.raw.ops.*
|
import aqua.raw.ops.*
|
||||||
@ -306,17 +302,19 @@ object RawSemantics extends Logging {
|
|||||||
|
|
||||||
type Interpreter[S[_], A] = State[CompilerState[S], A]
|
type Interpreter[S[_], A] = State[CompilerState[S], A]
|
||||||
|
|
||||||
def transpile[S[_]](
|
def transpile[S[_]](ast: Ast[S])(using
|
||||||
ast: Ast[S]
|
LocationsAlgebra[S, Interpreter[S, *]]
|
||||||
)(implicit locations: LocationsAlgebra[S, Interpreter[S, *]]): Interpreter[S, Raw] = {
|
): Interpreter[S, Raw] = {
|
||||||
|
|
||||||
implicit val typesInterpreter: TypesInterpreter[S, CompilerState[S]] =
|
given TypesAlgebra[S, Interpreter[S, *]] =
|
||||||
new TypesInterpreter[S, CompilerState[S]]
|
new TypesInterpreter[S, CompilerState[S]]
|
||||||
implicit val abilitiesInterpreter: AbilitiesInterpreter[S, CompilerState[S]] =
|
given ManglerAlgebra[Interpreter[S, *]] =
|
||||||
|
new ManglerInterpreter[CompilerState[S]]
|
||||||
|
given AbilitiesAlgebra[S, Interpreter[S, *]] =
|
||||||
new AbilitiesInterpreter[S, CompilerState[S]]
|
new AbilitiesInterpreter[S, CompilerState[S]]
|
||||||
implicit val namesInterpreter: NamesInterpreter[S, CompilerState[S]] =
|
given NamesAlgebra[S, Interpreter[S, *]] =
|
||||||
new NamesInterpreter[S, CompilerState[S]]
|
new NamesInterpreter[S, CompilerState[S]]
|
||||||
implicit val definitionsInterpreter: DefinitionsInterpreter[S, CompilerState[S]] =
|
given DefinitionsAlgebra[S, Interpreter[S, *]] =
|
||||||
new DefinitionsInterpreter[S, CompilerState[S]]
|
new DefinitionsInterpreter[S, CompilerState[S]]
|
||||||
|
|
||||||
ast
|
ast
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package aqua.semantics.expr
|
package aqua.semantics.expr
|
||||||
|
|
||||||
import aqua.parser.expr.AbilityExpr
|
import aqua.parser.expr.AbilityExpr
|
||||||
import aqua.raw.{Raw, ServiceRaw, TypeRaw}
|
import aqua.raw.{Raw, TypeRaw}
|
||||||
import aqua.parser.lexer.{Name, NamedTypeToken}
|
import aqua.parser.lexer.{Name, NamedTypeToken}
|
||||||
import aqua.raw.{Raw, ServiceRaw}
|
|
||||||
import aqua.semantics.Prog
|
import aqua.semantics.Prog
|
||||||
import aqua.semantics.rules.ValuesAlgebra
|
import aqua.semantics.rules.ValuesAlgebra
|
||||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||||
@ -11,6 +10,7 @@ import aqua.semantics.rules.definitions.DefinitionsAlgebra
|
|||||||
import aqua.semantics.rules.names.NamesAlgebra
|
import aqua.semantics.rules.names.NamesAlgebra
|
||||||
import aqua.semantics.rules.types.TypesAlgebra
|
import aqua.semantics.rules.types.TypesAlgebra
|
||||||
import aqua.types.{AbilityType, ArrowType, Type}
|
import aqua.types.{AbilityType, ArrowType, Type}
|
||||||
|
|
||||||
import cats.syntax.apply.*
|
import cats.syntax.apply.*
|
||||||
import cats.syntax.flatMap.*
|
import cats.syntax.flatMap.*
|
||||||
import cats.syntax.functor.*
|
import cats.syntax.functor.*
|
||||||
|
@ -8,49 +8,72 @@ import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
|||||||
import aqua.semantics.rules.definitions.DefinitionsAlgebra
|
import aqua.semantics.rules.definitions.DefinitionsAlgebra
|
||||||
import aqua.semantics.rules.names.NamesAlgebra
|
import aqua.semantics.rules.names.NamesAlgebra
|
||||||
import aqua.semantics.rules.types.TypesAlgebra
|
import aqua.semantics.rules.types.TypesAlgebra
|
||||||
|
|
||||||
|
import cats.data.EitherT
|
||||||
|
import cats.syntax.either.*
|
||||||
|
import cats.syntax.option.*
|
||||||
import cats.syntax.apply.*
|
import cats.syntax.apply.*
|
||||||
import cats.syntax.flatMap.*
|
import cats.syntax.flatMap.*
|
||||||
import cats.syntax.functor.*
|
import cats.syntax.functor.*
|
||||||
|
import cats.syntax.traverse.*
|
||||||
|
import cats.syntax.foldable.*
|
||||||
import cats.syntax.applicative.*
|
import cats.syntax.applicative.*
|
||||||
import cats.Monad
|
import cats.Monad
|
||||||
|
|
||||||
class ServiceSem[S[_]](val expr: ServiceExpr[S]) extends AnyVal {
|
class ServiceSem[S[_]](val expr: ServiceExpr[S]) extends AnyVal {
|
||||||
|
|
||||||
def program[Alg[_]: Monad](implicit
|
private def define[Alg[_]: Monad](using
|
||||||
A: AbilitiesAlgebra[S, Alg],
|
A: AbilitiesAlgebra[S, Alg],
|
||||||
N: NamesAlgebra[S, Alg],
|
N: NamesAlgebra[S, Alg],
|
||||||
T: TypesAlgebra[S, Alg],
|
T: TypesAlgebra[S, Alg],
|
||||||
V: ValuesAlgebra[S, Alg],
|
V: ValuesAlgebra[S, Alg],
|
||||||
D: DefinitionsAlgebra[S, Alg]
|
D: DefinitionsAlgebra[S, Alg]
|
||||||
): Prog[Alg, Raw] =
|
): EitherT[Alg, Raw, ServiceRaw] = for {
|
||||||
Prog.after(
|
arrows <- EitherT.fromOptionF(
|
||||||
_ =>
|
// TODO: Move to purgeDefs here, allow not only arrows
|
||||||
D.purgeArrows(expr.name).flatMap {
|
// from parsing, throw errors here
|
||||||
case Some(nel) =>
|
D.purgeArrows(expr.name),
|
||||||
val arrows = nel.map(kv => kv._1.value -> (kv._1, kv._2)).toNem
|
Raw.error("Service has no arrows")
|
||||||
for {
|
|
||||||
defaultId <- expr.id
|
|
||||||
.map(v => V.valueToRaw(v))
|
|
||||||
.getOrElse(None.pure[Alg])
|
|
||||||
defineResult <- A.defineService(
|
|
||||||
expr.name,
|
|
||||||
arrows,
|
|
||||||
defaultId
|
|
||||||
)
|
|
||||||
_ <- (expr.id zip defaultId)
|
|
||||||
.fold(().pure[Alg])(idV =>
|
|
||||||
(V.ensureIsString(idV._1) >> A.setServiceId(expr.name, idV._1, idV._2)).map(_ =>
|
|
||||||
()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} yield
|
|
||||||
if (defineResult) {
|
|
||||||
ServiceRaw(expr.name.value, arrows.map(_._2), defaultId)
|
|
||||||
} else Raw.empty("Service not created due to validation errors")
|
|
||||||
|
|
||||||
case None =>
|
|
||||||
Raw.error("Service has no arrows, fails").pure[Alg]
|
|
||||||
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
arrowsByName = arrows.map { case (name, arrow) =>
|
||||||
|
name.value -> (name, arrow)
|
||||||
|
}.toNem
|
||||||
|
defaultId <- expr.id.traverse(id =>
|
||||||
|
EitherT.fromOptionF(
|
||||||
|
V.valueToStringRaw(id),
|
||||||
|
Raw.error("Failed to resolve default service id")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
serviceType <- EitherT.fromOptionF(
|
||||||
|
T.defineServiceType(expr.name, arrowsByName.toSortedMap.toMap),
|
||||||
|
Raw.error("Failed to define service type")
|
||||||
|
)
|
||||||
|
arrowsDefs = arrows.map { case (name, _) => name.value -> name }.toNem
|
||||||
|
_ <- EitherT(
|
||||||
|
A.defineService(
|
||||||
|
expr.name,
|
||||||
|
arrowsDefs,
|
||||||
|
defaultId
|
||||||
|
).map(defined =>
|
||||||
|
Raw
|
||||||
|
.error("Service not created due to validation errors")
|
||||||
|
.asLeft
|
||||||
|
.whenA(!defined)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} yield ServiceRaw(
|
||||||
|
expr.name.value,
|
||||||
|
serviceType,
|
||||||
|
defaultId
|
||||||
|
)
|
||||||
|
|
||||||
|
def program[Alg[_]: Monad](using
|
||||||
|
A: AbilitiesAlgebra[S, Alg],
|
||||||
|
N: NamesAlgebra[S, Alg],
|
||||||
|
T: TypesAlgebra[S, Alg],
|
||||||
|
V: ValuesAlgebra[S, Alg],
|
||||||
|
D: DefinitionsAlgebra[S, Alg]
|
||||||
|
): Prog[Alg, Raw] = Prog.after_(
|
||||||
|
define.value.map(_.merge)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
package aqua.semantics.expr.func
|
|
||||||
|
|
||||||
import aqua.raw.Raw
|
|
||||||
import aqua.raw.ops.AbilityIdTag
|
|
||||||
import aqua.parser.expr.func.AbilityIdExpr
|
|
||||||
import aqua.semantics.Prog
|
|
||||||
import aqua.semantics.rules.ValuesAlgebra
|
|
||||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
|
||||||
import cats.Monad
|
|
||||||
import cats.syntax.applicative.*
|
|
||||||
import cats.syntax.flatMap.*
|
|
||||||
import cats.syntax.functor.*
|
|
||||||
|
|
||||||
class AbilityIdSem[S[_]](val expr: AbilityIdExpr[S]) extends AnyVal {
|
|
||||||
|
|
||||||
def program[Alg[_]: Monad](implicit
|
|
||||||
A: AbilitiesAlgebra[S, Alg],
|
|
||||||
V: ValuesAlgebra[S, Alg]
|
|
||||||
): Prog[Alg, Raw] =
|
|
||||||
V.ensureIsString(expr.id) >> V.valueToRaw(
|
|
||||||
expr.id
|
|
||||||
) >>= {
|
|
||||||
case Some(id) =>
|
|
||||||
A.setServiceId(expr.ability, expr.id, id) as (AbilityIdTag(
|
|
||||||
id,
|
|
||||||
expr.ability.value
|
|
||||||
).funcOpLeaf: Raw)
|
|
||||||
case _ => Raw.error("Cannot resolve ability ID").pure[Alg]
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,11 +13,13 @@ import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
|||||||
import aqua.semantics.rules.locations.LocationsAlgebra
|
import aqua.semantics.rules.locations.LocationsAlgebra
|
||||||
import aqua.semantics.rules.names.NamesAlgebra
|
import aqua.semantics.rules.names.NamesAlgebra
|
||||||
import aqua.semantics.rules.types.TypesAlgebra
|
import aqua.semantics.rules.types.TypesAlgebra
|
||||||
import aqua.types.{ArrayType, ArrowType, CanonStreamType, ProductType, StreamType, Type}
|
import aqua.types.*
|
||||||
|
|
||||||
import cats.Eval
|
import cats.Eval
|
||||||
import cats.data.{Chain, NonEmptyList}
|
import cats.data.{Chain, NonEmptyList}
|
||||||
import cats.free.{Cofree, Free}
|
import cats.free.{Cofree, Free}
|
||||||
|
import cats.data.OptionT
|
||||||
|
import cats.syntax.show.*
|
||||||
import cats.syntax.applicative.*
|
import cats.syntax.applicative.*
|
||||||
import cats.syntax.apply.*
|
import cats.syntax.apply.*
|
||||||
import cats.syntax.foldable.*
|
import cats.syntax.foldable.*
|
||||||
@ -64,16 +66,15 @@ class ArrowSem[S[_]](val expr: ArrowExpr[S]) extends AnyVal {
|
|||||||
res = bodyGen match {
|
res = bodyGen match {
|
||||||
case FuncOp(bodyModel) =>
|
case FuncOp(bodyModel) =>
|
||||||
// TODO: wrap with local on...via...
|
// TODO: wrap with local on...via...
|
||||||
|
|
||||||
val retsAndArgs = retValues zip funcArrow.codomain.toList
|
val retsAndArgs = retValues zip funcArrow.codomain.toList
|
||||||
|
|
||||||
val argNames = funcArrow.domain.labelledData.map { case (name, _) => name }
|
val dataArgsNames = funcArrow.domain.labelledData.map { case (name, _) => name }
|
||||||
val streamsThatReturnAsStreams = retsAndArgs.collect {
|
val streamsThatReturnAsStreams = retsAndArgs.collect {
|
||||||
case (VarRaw(n, StreamType(_)), StreamType(_)) => n
|
case (VarRaw(n, StreamType(_)), StreamType(_)) => n
|
||||||
}.toSet
|
}.toSet
|
||||||
|
|
||||||
// Remove arguments, and values returned as streams
|
// Remove arguments, and values returned as streams
|
||||||
val localStreams = streamsInScope -- argNames -- streamsThatReturnAsStreams
|
val localStreams = streamsInScope -- dataArgsNames -- streamsThatReturnAsStreams
|
||||||
|
|
||||||
// process stream that returns as not streams and all Apply*Raw
|
// process stream that returns as not streams and all Apply*Raw
|
||||||
val (bodyRets, retVals) = retsAndArgs.mapWithIndex {
|
val (bodyRets, retVals) = retsAndArgs.mapWithIndex {
|
||||||
|
@ -10,7 +10,9 @@ import aqua.semantics.rules.ValuesAlgebra
|
|||||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||||
import aqua.semantics.rules.types.TypesAlgebra
|
import aqua.semantics.rules.types.TypesAlgebra
|
||||||
import aqua.types.{BoxType, OptionType, ScalarType}
|
import aqua.types.{BoxType, OptionType, ScalarType}
|
||||||
|
|
||||||
import cats.data.Chain
|
import cats.data.Chain
|
||||||
|
import cats.data.OptionT
|
||||||
import cats.syntax.applicative.*
|
import cats.syntax.applicative.*
|
||||||
import cats.syntax.apply.*
|
import cats.syntax.apply.*
|
||||||
import cats.syntax.flatMap.*
|
import cats.syntax.flatMap.*
|
||||||
@ -49,24 +51,25 @@ class OnSem[S[_]](val expr: OnExpr[S]) extends AnyVal {
|
|||||||
|
|
||||||
object OnSem {
|
object OnSem {
|
||||||
|
|
||||||
def beforeOn[S[_], Alg[_]: Monad](peerId: ValueToken[S], via: List[ValueToken[S]])(implicit
|
def beforeOn[S[_], Alg[_]: Monad](
|
||||||
|
peerId: ValueToken[S],
|
||||||
|
via: List[ValueToken[S]]
|
||||||
|
)(using
|
||||||
V: ValuesAlgebra[S, Alg],
|
V: ValuesAlgebra[S, Alg],
|
||||||
T: TypesAlgebra[S, Alg],
|
T: TypesAlgebra[S, Alg],
|
||||||
A: AbilitiesAlgebra[S, Alg]
|
A: AbilitiesAlgebra[S, Alg]
|
||||||
): Alg[List[ValueRaw]] =
|
): Alg[List[ValueRaw]] =
|
||||||
|
// TODO: Remove ensureIsString, use valueToStringRaw
|
||||||
V.ensureIsString(peerId) *> via
|
V.ensureIsString(peerId) *> via
|
||||||
.traverse(v =>
|
.traverse(v =>
|
||||||
V.valueToRaw(v).flatTap {
|
OptionT(V.valueToRaw(v)).filterF { vm =>
|
||||||
case Some(vm) =>
|
val expectedType = vm.`type` match {
|
||||||
vm.`type` match {
|
case _: BoxType => OptionType(ScalarType.string)
|
||||||
case _: BoxType =>
|
case _ => ScalarType.string
|
||||||
T.ensureTypeMatches(v, OptionType(ScalarType.string), vm.`type`)
|
}
|
||||||
case _ =>
|
|
||||||
T.ensureTypeMatches(v, ScalarType.string, vm.`type`)
|
T.ensureTypeMatches(v, expectedType, vm.`type`)
|
||||||
}
|
|
||||||
case None => false.pure[Alg]
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.map(_.flatten)
|
.getOrElse(List.empty)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
package aqua.semantics.expr.func
|
||||||
|
|
||||||
|
import aqua.raw.Raw
|
||||||
|
import aqua.raw.ops.ServiceIdTag
|
||||||
|
import aqua.parser.expr.func.ServiceIdExpr
|
||||||
|
import aqua.semantics.Prog
|
||||||
|
import aqua.semantics.rules.ValuesAlgebra
|
||||||
|
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||||
|
import aqua.semantics.rules.types.TypesAlgebra
|
||||||
|
import aqua.semantics.rules.names.NamesAlgebra
|
||||||
|
|
||||||
|
import cats.Monad
|
||||||
|
import cats.data.EitherT
|
||||||
|
import cats.syntax.either.*
|
||||||
|
import cats.syntax.applicative.*
|
||||||
|
import cats.syntax.flatMap.*
|
||||||
|
import cats.syntax.functor.*
|
||||||
|
|
||||||
|
class ServiceIdSem[S[_]](val expr: ServiceIdExpr[S]) extends AnyVal {
|
||||||
|
|
||||||
|
def program[Alg[_]: Monad](using
|
||||||
|
A: AbilitiesAlgebra[S, Alg],
|
||||||
|
V: ValuesAlgebra[S, Alg],
|
||||||
|
N: NamesAlgebra[S, Alg],
|
||||||
|
T: TypesAlgebra[S, Alg]
|
||||||
|
): Prog[Alg, Raw] = (
|
||||||
|
for {
|
||||||
|
id <- EitherT.fromOptionF(
|
||||||
|
V.valueToStringRaw(expr.id),
|
||||||
|
Raw.error("Can not resolve service ID")
|
||||||
|
)
|
||||||
|
serviceType <- EitherT.fromOptionF(
|
||||||
|
T.resolveServiceType(expr.service),
|
||||||
|
Raw.error("Can not resolve service type")
|
||||||
|
)
|
||||||
|
name <- EitherT.fromOptionF(
|
||||||
|
A.renameService(expr.service),
|
||||||
|
Raw.error("Can not set service ID")
|
||||||
|
)
|
||||||
|
_ <- EitherT.liftF(
|
||||||
|
N.derive(
|
||||||
|
expr.service.asName.rename(name),
|
||||||
|
serviceType,
|
||||||
|
id.varNames
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} yield ServiceIdTag(id, serviceType, name).funcOpLeaf
|
||||||
|
).value.map(_.merge)
|
||||||
|
}
|
@ -11,6 +11,7 @@ import aqua.semantics.rules.errors.ErrorsAlgebra
|
|||||||
import aqua.types.*
|
import aqua.types.*
|
||||||
|
|
||||||
import cats.Monad
|
import cats.Monad
|
||||||
|
import cats.data.OptionT
|
||||||
import cats.data.Chain
|
import cats.data.Chain
|
||||||
import cats.syntax.applicative.*
|
import cats.syntax.applicative.*
|
||||||
import cats.syntax.apply.*
|
import cats.syntax.apply.*
|
||||||
@ -33,23 +34,6 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](using
|
|||||||
A: AbilitiesAlgebra[S, Alg]
|
A: AbilitiesAlgebra[S, Alg]
|
||||||
) extends Logging {
|
) extends Logging {
|
||||||
|
|
||||||
def ensureIsString(v: ValueToken[S]): Alg[Boolean] =
|
|
||||||
ensureTypeMatches(v, LiteralType.string)
|
|
||||||
|
|
||||||
def ensureTypeMatches(v: ValueToken[S], expected: Type): Alg[Boolean] =
|
|
||||||
resolveType(v).flatMap {
|
|
||||||
case Some(vt) =>
|
|
||||||
T.ensureTypeMatches(
|
|
||||||
v,
|
|
||||||
expected,
|
|
||||||
vt
|
|
||||||
)
|
|
||||||
case None => false.pure[Alg]
|
|
||||||
}
|
|
||||||
|
|
||||||
def resolveType(v: ValueToken[S]): Alg[Option[Type]] =
|
|
||||||
valueToRaw(v).map(_.map(_.`type`))
|
|
||||||
|
|
||||||
private def resolveSingleProperty(rootType: Type, op: PropertyOp[S]): Alg[Option[PropertyRaw]] =
|
private def resolveSingleProperty(rootType: Type, op: PropertyOp[S]): Alg[Option[PropertyRaw]] =
|
||||||
op match {
|
op match {
|
||||||
case op: IntoField[S] =>
|
case op: IntoField[S] =>
|
||||||
@ -83,11 +67,20 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](using
|
|||||||
LiteralRaw(l.value, t).some.pure[Alg]
|
LiteralRaw(l.value, t).some.pure[Alg]
|
||||||
|
|
||||||
case VarToken(name) =>
|
case VarToken(name) =>
|
||||||
N.read(name).flatMap {
|
N.read(name, mustBeDefined = false).flatMap {
|
||||||
case Some(t) =>
|
case Some(t) =>
|
||||||
VarRaw(name.value, t).some.pure[Alg]
|
VarRaw(name.value, t).some.pure
|
||||||
case None =>
|
case None =>
|
||||||
None.pure[Alg]
|
(for {
|
||||||
|
t <- OptionT(
|
||||||
|
T.getType(name.value)
|
||||||
|
).collect { case st: ServiceType => st }
|
||||||
|
// A hack to report name error, better to refactor
|
||||||
|
.flatTapNone(N.read(name))
|
||||||
|
rename <- OptionT(
|
||||||
|
A.getServiceRename(name.asTypeToken)
|
||||||
|
)
|
||||||
|
} yield VarRaw(rename, t)).value
|
||||||
}
|
}
|
||||||
|
|
||||||
case prop @ PropertyToken(value, properties) =>
|
case prop @ PropertyToken(value, properties) =>
|
||||||
@ -107,35 +100,30 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](using
|
|||||||
)(valueToRaw)
|
)(valueToRaw)
|
||||||
|
|
||||||
case dvt @ NamedValueToken(typeName, fields) =>
|
case dvt @ NamedValueToken(typeName, fields) =>
|
||||||
T.resolveType(typeName).flatMap {
|
(for {
|
||||||
case Some(resolvedType) =>
|
resolvedType <- OptionT(T.resolveType(typeName))
|
||||||
for {
|
fieldsGiven <- fields.traverse(value => OptionT(valueToRaw(value)))
|
||||||
fieldsRawOp: NonEmptyMap[String, Option[ValueRaw]] <- fields.traverse(valueToRaw)
|
fieldsGivenTypes = fieldsGiven.map(_.`type`)
|
||||||
fieldsRaw: List[(String, ValueRaw)] = fieldsRawOp.toSortedMap.toList.collect {
|
generated <- OptionT.fromOption(
|
||||||
case (n, Some(vr)) => n -> vr
|
resolvedType match {
|
||||||
}
|
case struct: StructType =>
|
||||||
rawFields = NonEmptyMap.fromMap(SortedMap.from(fieldsRaw))
|
(
|
||||||
typeFromFieldsWithData = rawFields
|
struct.copy(fields = fieldsGivenTypes),
|
||||||
.map(rf =>
|
MakeStructRaw(fieldsGiven, struct)
|
||||||
resolvedType match {
|
).some
|
||||||
case struct @ StructType(_, _) =>
|
case ability: AbilityType =>
|
||||||
(
|
(
|
||||||
StructType(typeName.value, rf.map(_.`type`)),
|
ability.copy(fields = fieldsGivenTypes),
|
||||||
Some(MakeStructRaw(rf, struct))
|
AbilityRaw(fieldsGiven, ability)
|
||||||
)
|
).some
|
||||||
case scope @ AbilityType(_, _) =>
|
case _ => none
|
||||||
(
|
}
|
||||||
AbilityType(typeName.value, rf.map(_.`type`)),
|
)
|
||||||
Some(AbilityRaw(rf, scope))
|
(genType, genData) = generated
|
||||||
)
|
data <- OptionT.whenM(
|
||||||
}
|
T.ensureTypeMatches(dvt, resolvedType, genType)
|
||||||
)
|
)(genData.pure)
|
||||||
.getOrElse(BottomType -> None)
|
} yield data).value
|
||||||
(typeFromFields, data) = typeFromFieldsWithData
|
|
||||||
isTypesCompatible <- T.ensureTypeMatches(dvt, resolvedType, typeFromFields)
|
|
||||||
} yield data.filter(_ => isTypesCompatible)
|
|
||||||
case _ => None.pure[Alg]
|
|
||||||
}
|
|
||||||
|
|
||||||
case ct @ CollectionToken(_, values) =>
|
case ct @ CollectionToken(_, values) =>
|
||||||
for {
|
for {
|
||||||
@ -295,15 +283,15 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](using
|
|||||||
} yield Option.when(
|
} yield Option.when(
|
||||||
leftChecked.isDefined && rightChecked.isDefined
|
leftChecked.isDefined && rightChecked.isDefined
|
||||||
)(
|
)(
|
||||||
CallArrowRaw(
|
CallArrowRaw.service(
|
||||||
ability = Some(id),
|
abilityName = id,
|
||||||
name = fn,
|
serviceId = LiteralRaw.quote(id),
|
||||||
arguments = leftRaw :: rightRaw :: Nil,
|
funcName = fn,
|
||||||
baseType = ArrowType(
|
baseType = ArrowType(
|
||||||
ProductType(lType :: rType :: Nil),
|
ProductType(lType :: rType :: Nil),
|
||||||
ProductType(resType :: Nil)
|
ProductType(resType :: Nil)
|
||||||
),
|
),
|
||||||
serviceId = Some(LiteralRaw.quote(id))
|
arguments = leftRaw :: rightRaw :: Nil
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -321,9 +309,24 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](using
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def valueToTypedRaw(v: ValueToken[S], expectedType: Type): Alg[Option[ValueRaw]] =
|
||||||
|
OptionT(valueToRaw(v))
|
||||||
|
.flatMap(raw =>
|
||||||
|
OptionT.whenM(
|
||||||
|
T.ensureTypeMatches(v, expectedType, raw.`type`)
|
||||||
|
)(raw.pure)
|
||||||
|
)
|
||||||
|
.value
|
||||||
|
|
||||||
|
def valueToStringRaw(v: ValueToken[S]): Alg[Option[ValueRaw]] =
|
||||||
|
valueToTypedRaw(v, LiteralType.string)
|
||||||
|
|
||||||
|
def ensureIsString(v: ValueToken[S]): Alg[Boolean] =
|
||||||
|
valueToStringRaw(v).map(_.isDefined)
|
||||||
|
|
||||||
private def callArrowFromAbility(
|
private def callArrowFromAbility(
|
||||||
ab: Name[S],
|
ab: Name[S],
|
||||||
at: AbilityType,
|
at: NamedType,
|
||||||
funcName: Name[S]
|
funcName: Name[S]
|
||||||
): Option[CallArrowRaw] = at.arrows
|
): Option[CallArrowRaw] = at.arrows
|
||||||
.get(funcName.value)
|
.get(funcName.value)
|
||||||
@ -351,24 +354,23 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](using
|
|||||||
)
|
)
|
||||||
)(ab =>
|
)(ab =>
|
||||||
N.read(ab.asName, mustBeDefined = false).flatMap {
|
N.read(ab.asName, mustBeDefined = false).flatMap {
|
||||||
case Some(at: AbilityType) =>
|
case Some(nt: (AbilityType | ServiceType)) =>
|
||||||
callArrowFromAbility(ab.asName, at, callArrow.funcName).pure
|
callArrowFromAbility(ab.asName, nt, callArrow.funcName).pure
|
||||||
case _ =>
|
case _ =>
|
||||||
T.getType(ab.value).flatMap {
|
T.getType(ab.value).flatMap {
|
||||||
case Some(at: AbilityType) =>
|
case Some(st: ServiceType) =>
|
||||||
callArrowFromAbility(ab.asName, at, callArrow.funcName).pure
|
OptionT(A.getServiceRename(ab))
|
||||||
|
.subflatMap(rename =>
|
||||||
|
callArrowFromAbility(
|
||||||
|
ab.asName.rename(rename),
|
||||||
|
st,
|
||||||
|
callArrow.funcName
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.value
|
||||||
case _ =>
|
case _ =>
|
||||||
(A.getArrow(ab, callArrow.funcName), A.getServiceId(ab)).mapN {
|
A.getArrow(ab, callArrow.funcName).map {
|
||||||
case (Some(at), Right(sid)) =>
|
case Some(at) =>
|
||||||
CallArrowRaw
|
|
||||||
.service(
|
|
||||||
abilityName = ab.value,
|
|
||||||
serviceId = sid,
|
|
||||||
funcName = callArrow.funcName.value,
|
|
||||||
baseType = at
|
|
||||||
)
|
|
||||||
.some
|
|
||||||
case (Some(at), Left(true)) =>
|
|
||||||
CallArrowRaw
|
CallArrowRaw
|
||||||
.ability(
|
.ability(
|
||||||
abilityName = ab.value,
|
abilityName = ab.value,
|
||||||
|
@ -2,7 +2,8 @@ package aqua.semantics.rules.abilities
|
|||||||
|
|
||||||
import aqua.parser.lexer.{Name, NamedTypeToken, Token, ValueToken}
|
import aqua.parser.lexer.{Name, NamedTypeToken, Token, ValueToken}
|
||||||
import aqua.raw.value.ValueRaw
|
import aqua.raw.value.ValueRaw
|
||||||
import aqua.types.ArrowType
|
import aqua.types.{ArrowType, ServiceType}
|
||||||
|
|
||||||
import cats.InjectK
|
import cats.InjectK
|
||||||
import cats.data.{NonEmptyList, NonEmptyMap}
|
import cats.data.{NonEmptyList, NonEmptyMap}
|
||||||
|
|
||||||
@ -10,15 +11,15 @@ trait AbilitiesAlgebra[S[_], Alg[_]] {
|
|||||||
|
|
||||||
def defineService(
|
def defineService(
|
||||||
name: NamedTypeToken[S],
|
name: NamedTypeToken[S],
|
||||||
arrows: NonEmptyMap[String, (Name[S], ArrowType)],
|
arrowDefs: NonEmptyMap[String, Name[S]],
|
||||||
defaultId: Option[ValueRaw]
|
defaultId: Option[ValueRaw]
|
||||||
): Alg[Boolean]
|
): Alg[Boolean]
|
||||||
|
|
||||||
def getArrow(name: NamedTypeToken[S], arrow: Name[S]): Alg[Option[ArrowType]]
|
def getArrow(name: NamedTypeToken[S], arrow: Name[S]): Alg[Option[ArrowType]]
|
||||||
|
|
||||||
def setServiceId(name: NamedTypeToken[S], id: ValueToken[S], vm: ValueRaw): Alg[Boolean]
|
def renameService(name: NamedTypeToken[S]): Alg[Option[String]]
|
||||||
|
|
||||||
def getServiceId(name: NamedTypeToken[S]): Alg[Either[Boolean, ValueRaw]]
|
def getServiceRename(name: NamedTypeToken[S]): Alg[Option[String]]
|
||||||
|
|
||||||
def beginScope(token: Token[S]): Alg[Unit]
|
def beginScope(token: Token[S]): Alg[Unit]
|
||||||
|
|
||||||
|
@ -5,27 +5,31 @@ import aqua.raw.value.ValueRaw
|
|||||||
import aqua.raw.{RawContext, ServiceRaw}
|
import aqua.raw.{RawContext, ServiceRaw}
|
||||||
import aqua.semantics.Levenshtein
|
import aqua.semantics.Levenshtein
|
||||||
import aqua.semantics.rules.errors.ReportErrors
|
import aqua.semantics.rules.errors.ReportErrors
|
||||||
|
import aqua.semantics.rules.mangler.ManglerAlgebra
|
||||||
import aqua.semantics.rules.locations.LocationsAlgebra
|
import aqua.semantics.rules.locations.LocationsAlgebra
|
||||||
import aqua.semantics.rules.{abilities, StackInterpreter}
|
import aqua.semantics.rules.{abilities, StackInterpreter}
|
||||||
import aqua.types.ArrowType
|
import aqua.types.{ArrowType, ServiceType}
|
||||||
|
|
||||||
import cats.data.{NonEmptyMap, State}
|
import cats.data.{NonEmptyMap, State}
|
||||||
import cats.syntax.functor.*
|
import cats.syntax.functor.*
|
||||||
|
import cats.syntax.apply.*
|
||||||
import cats.syntax.foldable.*
|
import cats.syntax.foldable.*
|
||||||
import cats.syntax.traverse.*
|
import cats.syntax.traverse.*
|
||||||
import cats.syntax.applicative.*
|
import cats.syntax.applicative.*
|
||||||
|
import cats.syntax.option.*
|
||||||
import monocle.Lens
|
import monocle.Lens
|
||||||
import monocle.macros.GenLens
|
import monocle.macros.GenLens
|
||||||
|
|
||||||
class AbilitiesInterpreter[S[_], X](implicit
|
class AbilitiesInterpreter[S[_], X](using
|
||||||
lens: Lens[X, AbilitiesState[S]],
|
lens: Lens[X, AbilitiesState[S]],
|
||||||
error: ReportErrors[S, X],
|
error: ReportErrors[S, X],
|
||||||
|
mangler: ManglerAlgebra[State[X, *]],
|
||||||
locations: LocationsAlgebra[S, State[X, *]]
|
locations: LocationsAlgebra[S, State[X, *]]
|
||||||
) extends AbilitiesAlgebra[S, State[X, *]] {
|
) extends AbilitiesAlgebra[S, State[X, *]] {
|
||||||
|
|
||||||
type SX[A] = State[X, A]
|
type SX[A] = State[X, A]
|
||||||
|
|
||||||
val stackInt = new StackInterpreter[S, X, AbilitiesState[S], AbilitiesState.Frame[S]](
|
private val stackInt = new StackInterpreter[S, X, AbilitiesState[S], AbilitiesState.Frame[S]](
|
||||||
GenLens[AbilitiesState[S]](_.stack)
|
GenLens[AbilitiesState[S]](_.stack)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -33,11 +37,11 @@ class AbilitiesInterpreter[S[_], X](implicit
|
|||||||
|
|
||||||
override def defineService(
|
override def defineService(
|
||||||
name: NamedTypeToken[S],
|
name: NamedTypeToken[S],
|
||||||
arrows: NonEmptyMap[String, (Name[S], ArrowType)],
|
arrowDefs: NonEmptyMap[String, Name[S]],
|
||||||
defaultId: Option[ValueRaw]
|
defaultId: Option[ValueRaw]
|
||||||
): SX[Boolean] =
|
): SX[Boolean] =
|
||||||
getService(name.value).flatMap {
|
serviceExists(name.value).flatMap {
|
||||||
case Some(_) =>
|
case true =>
|
||||||
getState
|
getState
|
||||||
.map(_.definitions.get(name.value).exists(_ == name))
|
.map(_.definitions.get(name.value).exists(_ == name))
|
||||||
.flatMap(exists =>
|
.flatMap(exists =>
|
||||||
@ -47,23 +51,14 @@ class AbilitiesInterpreter[S[_], X](implicit
|
|||||||
).whenA(!exists)
|
).whenA(!exists)
|
||||||
)
|
)
|
||||||
.as(false)
|
.as(false)
|
||||||
case None =>
|
case false =>
|
||||||
for {
|
for {
|
||||||
_ <- arrows.toNel.traverse_ { case (_, (n, arr)) =>
|
_ <- modify(_.defineService(name, defaultId))
|
||||||
report(n, "Service functions cannot have multiple results")
|
// TODO: Is it used?
|
||||||
.whenA(arr.codomain.length > 1)
|
|
||||||
}
|
|
||||||
_ <- modify(s =>
|
|
||||||
s.copy(
|
|
||||||
services = s.services
|
|
||||||
.updated(name.value, ServiceRaw(name.value, arrows.map(_._2), defaultId)),
|
|
||||||
definitions = s.definitions.updated(name.value, name)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
_ <- locations.addTokenWithFields(
|
_ <- locations.addTokenWithFields(
|
||||||
name.value,
|
name.value,
|
||||||
name,
|
name,
|
||||||
arrows.toNel.toList.map(t => t._1 -> t._2._1)
|
arrowDefs.toNel.toList
|
||||||
)
|
)
|
||||||
} yield true
|
} yield true
|
||||||
}
|
}
|
||||||
@ -74,83 +69,50 @@ class AbilitiesInterpreter[S[_], X](implicit
|
|||||||
}
|
}
|
||||||
|
|
||||||
override def getArrow(name: NamedTypeToken[S], arrow: Name[S]): SX[Option[ArrowType]] =
|
override def getArrow(name: NamedTypeToken[S], arrow: Name[S]): SX[Option[ArrowType]] =
|
||||||
getService(name.value).map(_.map(_.arrows)).flatMap {
|
getAbility(name.value).flatMap {
|
||||||
case Some(arrows) =>
|
case Some(abCtx) =>
|
||||||
arrows(arrow.value)
|
abCtx.funcs
|
||||||
|
.get(arrow.value)
|
||||||
.fold(
|
.fold(
|
||||||
report(
|
report(
|
||||||
arrow,
|
arrow,
|
||||||
Levenshtein.genMessage(
|
Levenshtein.genMessage(
|
||||||
s"Service is found, but arrow '${arrow.value}' isn't found in scope",
|
s"Ability is found, but arrow '${arrow.value}' isn't found in scope",
|
||||||
arrow.value,
|
arrow.value,
|
||||||
arrows.value.keys.toNonEmptyList.toList
|
abCtx.funcs.keys.toList
|
||||||
)
|
)
|
||||||
).as(Option.empty[ArrowType])
|
).as(none)
|
||||||
)(a => addServiceArrowLocation(name, arrow).as(Some(a)))
|
) { fn =>
|
||||||
case None =>
|
// TODO: add name and arrow separately
|
||||||
getAbility(name.value).flatMap {
|
// TODO: find tokens somewhere
|
||||||
case Some(abCtx) =>
|
addServiceArrowLocation(name, arrow).as(fn.arrow.`type`.some)
|
||||||
abCtx.funcs
|
|
||||||
.get(arrow.value)
|
|
||||||
.fold(
|
|
||||||
report(
|
|
||||||
arrow,
|
|
||||||
Levenshtein.genMessage(
|
|
||||||
s"Ability is found, but arrow '${arrow.value}' isn't found in scope",
|
|
||||||
arrow.value,
|
|
||||||
abCtx.funcs.keys.toList
|
|
||||||
)
|
|
||||||
).as(Option.empty[ArrowType])
|
|
||||||
) { fn =>
|
|
||||||
// TODO: add name and arrow separately
|
|
||||||
// TODO: find tokens somewhere
|
|
||||||
addServiceArrowLocation(name, arrow).as(Some(fn.arrow.`type`))
|
|
||||||
}
|
|
||||||
case None =>
|
|
||||||
report(name, "Ability with this name is undefined").as(Option.empty[ArrowType])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override def setServiceId(name: NamedTypeToken[S], id: ValueToken[S], vm: ValueRaw): SX[Boolean] =
|
|
||||||
getService(name.value).flatMap {
|
|
||||||
case Some(_) =>
|
|
||||||
mapStackHeadM(
|
|
||||||
modify(st => st.copy(rootServiceIds = st.rootServiceIds.updated(name.value, id -> vm)))
|
|
||||||
.as(true)
|
|
||||||
)(h => (h.copy(serviceIds = h.serviceIds.updated(name.value, id -> vm)) -> true).pure)
|
|
||||||
case None =>
|
|
||||||
report(name, "Service with this name is not registered, can't set its ID").as(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override def getServiceId(name: NamedTypeToken[S]): SX[Either[Boolean, ValueRaw]] =
|
|
||||||
getService(name.value).flatMap {
|
|
||||||
case Some(_) =>
|
|
||||||
getState.flatMap(st =>
|
|
||||||
st.stack
|
|
||||||
.flatMap(_.serviceIds.get(name.value).map(_._2))
|
|
||||||
.headOption orElse st.rootServiceIds
|
|
||||||
.get(
|
|
||||||
name.value
|
|
||||||
)
|
|
||||||
.map(_._2) orElse st.services.get(name.value).flatMap(_.defaultId) match {
|
|
||||||
case None =>
|
|
||||||
report(
|
|
||||||
name,
|
|
||||||
s"Service ID unresolved, use `${name.value} id` expression to set it"
|
|
||||||
)
|
|
||||||
.as(Left[Boolean, ValueRaw](false))
|
|
||||||
|
|
||||||
case Some(v) => State.pure(Right(v))
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
case None =>
|
case None =>
|
||||||
getAbility(name.value).flatMap {
|
report(name, "Ability with this name is undefined").as(none)
|
||||||
case Some(_) => State.pure(Left[Boolean, ValueRaw](true))
|
}
|
||||||
case None =>
|
|
||||||
report(name, "Ability with this name is undefined").as(
|
override def renameService(name: NamedTypeToken[S]): SX[Option[String]] =
|
||||||
Left[Boolean, ValueRaw](false)
|
serviceExists(name.value).flatMap {
|
||||||
)
|
case true =>
|
||||||
}
|
mapStackHeadM(
|
||||||
|
name.value.pure
|
||||||
|
)(h =>
|
||||||
|
mangler
|
||||||
|
.rename(name.value)
|
||||||
|
.map(newName => h.setServiceRename(name.value, newName) -> newName)
|
||||||
|
).map(_.some)
|
||||||
|
case false =>
|
||||||
|
report(name, "Service with this name is not registered").as(none)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def getServiceRename(name: NamedTypeToken[S]): State[X, Option[String]] =
|
||||||
|
(
|
||||||
|
serviceExists(name.value),
|
||||||
|
getState.map(_.getServiceRename(name.value))
|
||||||
|
).flatMapN {
|
||||||
|
case (true, Some(rename)) => rename.some.pure
|
||||||
|
case (false, _) => report(name, "Service with this name is undefined").as(none)
|
||||||
|
case (_, None) => report(name, "Service ID is undefined").as(none)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def beginScope(token: Token[S]): SX[Unit] =
|
override def beginScope(token: Token[S]): SX[Unit] =
|
||||||
@ -158,8 +120,8 @@ class AbilitiesInterpreter[S[_], X](implicit
|
|||||||
|
|
||||||
override def endScope(): SX[Unit] = stackInt.endScope
|
override def endScope(): SX[Unit] = stackInt.endScope
|
||||||
|
|
||||||
private def getService(name: String): SX[Option[ServiceRaw]] =
|
private def serviceExists(name: String): SX[Boolean] =
|
||||||
getState.map(_.services.get(name))
|
getState.map(_.services(name))
|
||||||
|
|
||||||
private def getAbility(name: String): SX[Option[RawContext]] =
|
private def getAbility(name: String): SX[Option[RawContext]] =
|
||||||
getState.map(_.abilities.get(name))
|
getState.map(_.abilities.get(name))
|
||||||
|
@ -4,54 +4,81 @@ import aqua.raw.{RawContext, ServiceRaw}
|
|||||||
import aqua.raw.value.ValueRaw
|
import aqua.raw.value.ValueRaw
|
||||||
import aqua.parser.lexer.{Name, NamedTypeToken, Token, ValueToken}
|
import aqua.parser.lexer.{Name, NamedTypeToken, Token, ValueToken}
|
||||||
import aqua.types.ArrowType
|
import aqua.types.ArrowType
|
||||||
|
|
||||||
import cats.Monoid
|
import cats.Monoid
|
||||||
|
import cats.syntax.foldable.*
|
||||||
|
import cats.syntax.functor.*
|
||||||
import cats.data.NonEmptyList
|
import cats.data.NonEmptyList
|
||||||
|
import aqua.parser.lexer.Token.name
|
||||||
|
|
||||||
case class AbilitiesState[S[_]](
|
case class AbilitiesState[S[_]](
|
||||||
stack: List[AbilitiesState.Frame[S]] = Nil,
|
stack: List[AbilitiesState.Frame[S]] = Nil,
|
||||||
services: Map[String, ServiceRaw] = Map.empty,
|
services: Set[String] = Set.empty,
|
||||||
abilities: Map[String, RawContext] = Map.empty,
|
abilities: Map[String, RawContext] = Map.empty,
|
||||||
rootServiceIds: Map[String, (ValueToken[S], ValueRaw)] =
|
rootServiceIds: Map[String, ValueRaw] = Map(),
|
||||||
Map.empty[String, (ValueToken[S], ValueRaw)],
|
definitions: Map[String, NamedTypeToken[S]] = Map()
|
||||||
definitions: Map[String, NamedTypeToken[S]] = Map.empty[String, NamedTypeToken[S]]
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
def purgeArrows: Option[(NonEmptyList[(Name[S], ArrowType)], AbilitiesState[S])] =
|
def defineService(name: NamedTypeToken[S], defaultId: Option[ValueRaw]): AbilitiesState[S] =
|
||||||
stack match {
|
copy(
|
||||||
case sc :: tail =>
|
services = services + name.value,
|
||||||
NonEmptyList
|
definitions = definitions.updated(name.value, name),
|
||||||
.fromList(sc.arrows.values.toList)
|
rootServiceIds = rootServiceIds ++ defaultId.map(name.value -> _)
|
||||||
.map(_ -> copy[S](sc.copy(arrows = Map.empty) :: tail))
|
)
|
||||||
case _ => None
|
|
||||||
}
|
def getServiceRename(name: String): Option[String] =
|
||||||
|
stack.collectFirstSome(_.getServiceRename(name)) orElse
|
||||||
|
// Suppose that services without id
|
||||||
|
// resolved in scope are not renamed
|
||||||
|
rootServiceIds.get(name).as(name)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object AbilitiesState {
|
object AbilitiesState {
|
||||||
|
|
||||||
case class Frame[S[_]](
|
case class Frame[S[_]](
|
||||||
token: Token[S],
|
token: Token[S],
|
||||||
arrows: Map[String, (Name[S], ArrowType)] = Map.empty[String, (Name[S], ArrowType)],
|
services: Map[String, Frame.ServiceState] = Map()
|
||||||
serviceIds: Map[String, (ValueToken[S], ValueRaw)] =
|
) {
|
||||||
Map.empty[String, (ValueToken[S], ValueRaw)]
|
|
||||||
)
|
|
||||||
|
|
||||||
implicit def abilitiesStateMonoid[S[_]]: Monoid[AbilitiesState[S]] =
|
def setServiceRename(name: String, rename: String): Frame[S] =
|
||||||
new Monoid[AbilitiesState[S]] {
|
copy(services =
|
||||||
override def empty: AbilitiesState[S] = AbilitiesState()
|
services.updated(
|
||||||
|
name,
|
||||||
override def combine(x: AbilitiesState[S], y: AbilitiesState[S]): AbilitiesState[S] =
|
Frame.ServiceState(rename)
|
||||||
AbilitiesState(
|
|
||||||
Nil,
|
|
||||||
x.services ++ y.services,
|
|
||||||
x.abilities ++ y.abilities,
|
|
||||||
x.rootServiceIds ++ y.rootServiceIds,
|
|
||||||
x.definitions ++ y.definitions
|
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
|
|
||||||
|
def getServiceRename(name: String): Option[String] =
|
||||||
|
services.get(name).map(_.rename)
|
||||||
|
}
|
||||||
|
|
||||||
|
object Frame {
|
||||||
|
|
||||||
|
final case class ServiceState(
|
||||||
|
rename: String
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
given [S[_]]: Monoid[AbilitiesState[S]] with {
|
||||||
|
override def empty: AbilitiesState[S] = AbilitiesState()
|
||||||
|
|
||||||
|
override def combine(x: AbilitiesState[S], y: AbilitiesState[S]): AbilitiesState[S] =
|
||||||
|
AbilitiesState(
|
||||||
|
Nil,
|
||||||
|
x.services ++ y.services,
|
||||||
|
x.abilities ++ y.abilities,
|
||||||
|
x.rootServiceIds ++ y.rootServiceIds,
|
||||||
|
x.definitions ++ y.definitions
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
def init[S[_]](context: RawContext): AbilitiesState[S] =
|
def init[S[_]](context: RawContext): AbilitiesState[S] =
|
||||||
AbilitiesState(
|
AbilitiesState(
|
||||||
services = context.allServices,
|
services = context.allServices.keySet,
|
||||||
|
rootServiceIds = context.allServices.flatMap { case (name, service) =>
|
||||||
|
service.defaultId.map(name -> _)
|
||||||
|
},
|
||||||
abilities = context.abilities // TODO is it the right way to collect abilities? Why?
|
abilities = context.abilities // TODO is it the right way to collect abilities? Why?
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package aqua.semantics.rules.mangler
|
||||||
|
|
||||||
|
trait ManglerAlgebra[Alg[_]] {
|
||||||
|
def rename(name: String): Alg[String]
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package aqua.semantics.rules.mangler
|
||||||
|
|
||||||
|
import cats.data.State
|
||||||
|
import monocle.Lens
|
||||||
|
import monocle.macros.GenLens
|
||||||
|
|
||||||
|
class ManglerInterpreter[X](using
|
||||||
|
lens: Lens[X, ManglerState]
|
||||||
|
) extends ManglerAlgebra[State[X, *]] {
|
||||||
|
|
||||||
|
override def rename(name: String): State[X, String] =
|
||||||
|
for {
|
||||||
|
s <- get
|
||||||
|
newName = LazyList
|
||||||
|
.from(0)
|
||||||
|
.map(i => s"$name-$i")
|
||||||
|
.dropWhile(s.isForbidden)
|
||||||
|
.head
|
||||||
|
_ <- modify(_.forbid(newName))
|
||||||
|
} yield newName
|
||||||
|
|
||||||
|
private lazy val get: State[X, ManglerState] =
|
||||||
|
State.get[X].map(lens.get)
|
||||||
|
|
||||||
|
private def modify(f: ManglerState => ManglerState): State[X, Unit] =
|
||||||
|
State.modify[X](lens.modify(f))
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package aqua.semantics.rules.mangler
|
||||||
|
|
||||||
|
import cats.kernel.Monoid
|
||||||
|
|
||||||
|
final case class ManglerState(
|
||||||
|
forbidden: Set[String] = Set.empty
|
||||||
|
) {
|
||||||
|
|
||||||
|
def isForbidden(name: String): Boolean =
|
||||||
|
forbidden.contains(name)
|
||||||
|
|
||||||
|
def forbid(name: String): ManglerState =
|
||||||
|
copy(forbidden = forbidden + name)
|
||||||
|
}
|
||||||
|
|
||||||
|
object ManglerState {
|
||||||
|
|
||||||
|
given Monoid[ManglerState] with {
|
||||||
|
override val empty: ManglerState = ManglerState()
|
||||||
|
|
||||||
|
override def combine(x: ManglerState, y: ManglerState): ManglerState =
|
||||||
|
ManglerState(forbidden = x.forbidden ++ y.forbidden)
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@ package aqua.semantics.rules.types
|
|||||||
|
|
||||||
import aqua.parser.lexer.*
|
import aqua.parser.lexer.*
|
||||||
import aqua.raw.value.{PropertyRaw, ValueRaw}
|
import aqua.raw.value.{PropertyRaw, ValueRaw}
|
||||||
import aqua.types.{AbilityType, ArrowType, NamedType, StructType, Type}
|
import aqua.types.*
|
||||||
|
|
||||||
import cats.data.NonEmptyMap
|
import cats.data.NonEmptyMap
|
||||||
import cats.data.NonEmptyList
|
import cats.data.NonEmptyList
|
||||||
@ -15,11 +15,18 @@ trait TypesAlgebra[S[_], Alg[_]] {
|
|||||||
|
|
||||||
def resolveArrowDef(arrowDef: ArrowTypeToken[S]): Alg[Option[ArrowType]]
|
def resolveArrowDef(arrowDef: ArrowTypeToken[S]): Alg[Option[ArrowType]]
|
||||||
|
|
||||||
|
def resolveServiceType(name: NamedTypeToken[S]): Alg[Option[ServiceType]]
|
||||||
|
|
||||||
def defineAbilityType(
|
def defineAbilityType(
|
||||||
name: NamedTypeToken[S],
|
name: NamedTypeToken[S],
|
||||||
fields: Map[String, (Name[S], Type)]
|
fields: Map[String, (Name[S], Type)]
|
||||||
): Alg[Option[AbilityType]]
|
): Alg[Option[AbilityType]]
|
||||||
|
|
||||||
|
def defineServiceType(
|
||||||
|
name: NamedTypeToken[S],
|
||||||
|
fields: Map[String, (Name[S], Type)]
|
||||||
|
): Alg[Option[ServiceType]]
|
||||||
|
|
||||||
def defineStructType(
|
def defineStructType(
|
||||||
name: NamedTypeToken[S],
|
name: NamedTypeToken[S],
|
||||||
fields: Map[String, (Name[S], Type)]
|
fields: Map[String, (Name[S], Type)]
|
||||||
|
@ -13,16 +13,17 @@ import aqua.raw.value.{
|
|||||||
import aqua.semantics.rules.locations.LocationsAlgebra
|
import aqua.semantics.rules.locations.LocationsAlgebra
|
||||||
import aqua.semantics.rules.StackInterpreter
|
import aqua.semantics.rules.StackInterpreter
|
||||||
import aqua.semantics.rules.errors.ReportErrors
|
import aqua.semantics.rules.errors.ReportErrors
|
||||||
|
import aqua.semantics.rules.types.TypesStateHelper.{TypeResolution, TypeResolutionError}
|
||||||
import aqua.types.*
|
import aqua.types.*
|
||||||
|
|
||||||
import cats.data.Validated.{Invalid, Valid}
|
import cats.data.Validated.{Invalid, Valid}
|
||||||
import cats.data.{Chain, NonEmptyList, NonEmptyMap, State}
|
import cats.data.{Chain, NonEmptyList, NonEmptyMap, OptionT, State}
|
||||||
import cats.instances.list.*
|
|
||||||
import cats.syntax.applicative.*
|
import cats.syntax.applicative.*
|
||||||
import cats.syntax.apply.*
|
import cats.syntax.apply.*
|
||||||
import cats.syntax.flatMap.*
|
import cats.syntax.flatMap.*
|
||||||
import cats.syntax.functor.*
|
import cats.syntax.functor.*
|
||||||
import cats.syntax.traverse.*
|
import cats.syntax.traverse.*
|
||||||
|
import cats.syntax.foldable.*
|
||||||
import cats.{~>, Applicative}
|
import cats.{~>, Applicative}
|
||||||
import cats.syntax.option.*
|
import cats.syntax.option.*
|
||||||
import monocle.Lens
|
import monocle.Lens
|
||||||
@ -44,18 +45,12 @@ class TypesInterpreter[S[_], X](implicit
|
|||||||
|
|
||||||
type ST[A] = State[X, A]
|
type ST[A] = State[X, A]
|
||||||
|
|
||||||
val resolver: (TypesState[S], NamedTypeToken[S]) => Option[
|
|
||||||
(Type, List[(Token[S], NamedTypeToken[S])])
|
|
||||||
] = { (state, ctt) =>
|
|
||||||
state.strict.get(ctt.value).map(t => (t, state.definitions.get(ctt.value).toList.map(ctt -> _)))
|
|
||||||
}
|
|
||||||
|
|
||||||
override def getType(name: String): State[X, Option[Type]] =
|
override def getType(name: String): State[X, Option[Type]] =
|
||||||
getState.map(st => st.strict.get(name))
|
getState.map(st => st.strict.get(name))
|
||||||
|
|
||||||
override def resolveType(token: TypeToken[S]): State[X, Option[Type]] =
|
override def resolveType(token: TypeToken[S]): State[X, Option[Type]] =
|
||||||
getState.map(st => TypesStateHelper.resolveTypeToken(token, st, resolver)).flatMap {
|
getState.map(TypesStateHelper.resolveTypeToken(token)).flatMap {
|
||||||
case Some((typ, tokens)) =>
|
case Some(TypeResolution(typ, tokens)) =>
|
||||||
val tokensLocs = tokens.map { case (t, n) => n.value -> t }
|
val tokensLocs = tokens.map { case (t, n) => n.value -> t }
|
||||||
locations.pointLocations(tokensLocs).as(typ.some)
|
locations.pointLocations(tokensLocs).as(typ.some)
|
||||||
case None =>
|
case None =>
|
||||||
@ -64,88 +59,111 @@ class TypesInterpreter[S[_], X](implicit
|
|||||||
}
|
}
|
||||||
|
|
||||||
override def resolveArrowDef(arrowDef: ArrowTypeToken[S]): State[X, Option[ArrowType]] =
|
override def resolveArrowDef(arrowDef: ArrowTypeToken[S]): State[X, Option[ArrowType]] =
|
||||||
getState.map(st => TypesStateHelper.resolveArrowDef(arrowDef, st, resolver)).flatMap {
|
getState.map(TypesStateHelper.resolveArrowDef(arrowDef)).flatMap {
|
||||||
case Valid(t) =>
|
case Valid(TypeResolution(tt, tokens)) =>
|
||||||
val (tt, tokens) = t
|
val tokensLocs = tokens.map { case (t, n) => n.value -> t }
|
||||||
val tokensLocs = tokens.map { case (t, n) =>
|
locations.pointLocations(tokensLocs).as(tt.some)
|
||||||
n.value -> t
|
|
||||||
}
|
|
||||||
locations.pointLocations(tokensLocs).map(_ => Some(tt))
|
|
||||||
case Invalid(errs) =>
|
case Invalid(errs) =>
|
||||||
errs
|
errs.traverse_ { case TypeResolutionError(token, hint) =>
|
||||||
.foldLeft[ST[Option[ArrowType]]](State.pure(None)) { case (n, (tkn, hint)) =>
|
report(token, hint)
|
||||||
report(tkn, hint) >> n
|
}.as(none)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def resolveServiceType(name: NamedTypeToken[S]): State[X, Option[ServiceType]] =
|
||||||
|
resolveType(name).flatMap {
|
||||||
|
case Some(serviceType: ServiceType) =>
|
||||||
|
serviceType.some.pure
|
||||||
|
case Some(t) =>
|
||||||
|
report(name, s"Type `$t` is not a service").as(none)
|
||||||
|
case None =>
|
||||||
|
report(name, s"Type `${name.value}` is not defined").as(none)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def defineAbilityType(
|
override def defineAbilityType(
|
||||||
name: NamedTypeToken[S],
|
name: NamedTypeToken[S],
|
||||||
fields: Map[String, (Name[S], Type)]
|
fields: Map[String, (Name[S], Type)]
|
||||||
): State[X, Option[AbilityType]] =
|
): State[X, Option[AbilityType]] =
|
||||||
getState.map(_.definitions.get(name.value)).flatMap {
|
ensureNameNotDefined(name.value, name, ifDefined = none) {
|
||||||
case Some(_) => report(name, s"Ability `${name.value}` was already defined").as(none)
|
val types = fields.view.mapValues { case (_, t) => t }.toMap
|
||||||
case None =>
|
NonEmptyMap
|
||||||
val types = fields.view.mapValues { case (_, t) => t }.toMap
|
.fromMap(SortedMap.from(types))
|
||||||
NonEmptyMap
|
.fold(report(name, s"Ability `${name.value}` has no fields").as(none))(nonEmptyFields =>
|
||||||
.fromMap(SortedMap.from(types))
|
val `type` = AbilityType(name.value, nonEmptyFields)
|
||||||
.fold(report(name, s"Ability `${name.value}` has no fields").as(none))(nonEmptyFields =>
|
|
||||||
val `type` = AbilityType(name.value, nonEmptyFields)
|
modify(_.defineType(name, `type`)).as(`type`.some)
|
||||||
modify { st =>
|
)
|
||||||
st.copy(
|
|
||||||
strict = st.strict.updated(name.value, `type`),
|
|
||||||
definitions = st.definitions.updated(name.value, name)
|
|
||||||
)
|
|
||||||
}.as(`type`.some)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def defineServiceType(
|
||||||
|
name: NamedTypeToken[S],
|
||||||
|
fields: Map[String, (Name[S], Type)]
|
||||||
|
): State[X, Option[ServiceType]] =
|
||||||
|
ensureNameNotDefined(name.value, name, ifDefined = none)(
|
||||||
|
fields.toList.traverse {
|
||||||
|
case (field, (fieldName, t: ArrowType)) =>
|
||||||
|
OptionT
|
||||||
|
.when(t.codomain.length <= 1)(field -> t)
|
||||||
|
.flatTapNone(
|
||||||
|
report(fieldName, "Service functions cannot have multiple results")
|
||||||
|
)
|
||||||
|
case (field, (fieldName, t)) =>
|
||||||
|
OptionT(
|
||||||
|
report(
|
||||||
|
fieldName,
|
||||||
|
s"Field '$field' has unacceptable for service field type '$t'"
|
||||||
|
).as(none)
|
||||||
|
)
|
||||||
|
}.flatMapF(arrows =>
|
||||||
|
NonEmptyMap
|
||||||
|
.fromMap(SortedMap.from(arrows))
|
||||||
|
.fold(
|
||||||
|
report(name, s"Service `${name.value}` has no fields").as(none)
|
||||||
|
)(_.some.pure)
|
||||||
|
).semiflatMap(nonEmptyArrows =>
|
||||||
|
val `type` = ServiceType(name.value, nonEmptyArrows)
|
||||||
|
|
||||||
|
modify(_.defineType(name, `type`)).as(`type`)
|
||||||
|
).value
|
||||||
|
)
|
||||||
|
|
||||||
override def defineStructType(
|
override def defineStructType(
|
||||||
name: NamedTypeToken[S],
|
name: NamedTypeToken[S],
|
||||||
fields: Map[String, (Name[S], Type)]
|
fields: Map[String, (Name[S], Type)]
|
||||||
): State[X, Option[StructType]] =
|
): State[X, Option[StructType]] =
|
||||||
getState.map(_.definitions.get(name.value)).flatMap {
|
ensureNameNotDefined(name.value, name, ifDefined = none)(
|
||||||
case Some(_) => report(name, s"Data `${name.value}` was already defined").as(none)
|
fields.toList.traverse {
|
||||||
case None =>
|
case (field, (fieldName, t: DataType)) =>
|
||||||
fields.toList.traverse {
|
t match {
|
||||||
case (field, (fieldName, t: DataType)) =>
|
case _: StreamType => report(fieldName, s"Field '$field' has stream type").as(none)
|
||||||
t match {
|
case _ => (field -> t).some.pure[ST]
|
||||||
case _: StreamType => report(fieldName, s"Field '$field' has stream type").as(none)
|
}
|
||||||
case _ => (field -> t).some.pure[ST]
|
case (field, (fieldName, t)) =>
|
||||||
}
|
report(
|
||||||
case (field, (fieldName, t)) =>
|
fieldName,
|
||||||
report(
|
s"Field '$field' has unacceptable for struct field type '$t'"
|
||||||
fieldName,
|
).as(none)
|
||||||
s"Field '$field' has unacceptable for struct field type '$t'"
|
}.map(_.sequence.map(_.toMap))
|
||||||
).as(none)
|
.flatMap(
|
||||||
}.map(_.sequence.map(_.toMap))
|
_.map(SortedMap.from)
|
||||||
.flatMap(
|
.flatMap(NonEmptyMap.fromMap)
|
||||||
_.map(SortedMap.from)
|
.fold(
|
||||||
.flatMap(NonEmptyMap.fromMap)
|
report(name, s"Struct `${name.value}` has no fields").as(none)
|
||||||
.fold(
|
)(nonEmptyFields =>
|
||||||
report(name, s"Struct `${name.value}` has no fields").as(none)
|
val `type` = StructType(name.value, nonEmptyFields)
|
||||||
)(nonEmptyFields =>
|
|
||||||
val `type` = StructType(name.value, nonEmptyFields)
|
modify(_.defineType(name, `type`)).as(`type`.some)
|
||||||
modify { st =>
|
)
|
||||||
st.copy(
|
)
|
||||||
strict = st.strict.updated(name.value, `type`),
|
)
|
||||||
definitions = st.definitions.updated(name.value, name)
|
|
||||||
)
|
|
||||||
}.as(`type`.some)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override def defineAlias(name: NamedTypeToken[S], target: Type): State[X, Boolean] =
|
override def defineAlias(name: NamedTypeToken[S], target: Type): State[X, Boolean] =
|
||||||
getState.map(_.definitions.get(name.value)).flatMap {
|
getState.map(_.definitions.get(name.value)).flatMap {
|
||||||
case Some(n) if n == name => State.pure(false)
|
case Some(n) if n == name => State.pure(false)
|
||||||
case Some(_) => report(name, s"Type `${name.value}` was already defined").as(false)
|
case Some(_) => report(name, s"Type `${name.value}` was already defined").as(false)
|
||||||
case None =>
|
case None =>
|
||||||
modify(st =>
|
modify(_.defineType(name, target))
|
||||||
st.copy(
|
.productL(locations.addToken(name.value, name))
|
||||||
strict = st.strict.updated(name.value, target),
|
.as(true)
|
||||||
definitions = st.definitions.updated(name.value, name)
|
|
||||||
)
|
|
||||||
).flatMap(_ => locations.addToken(name.value, name)).as(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override def resolveField(rootT: Type, op: IntoField[S]): State[X, Option[PropertyRaw]] = {
|
override def resolveField(rootT: Type, op: IntoField[S]): State[X, Option[PropertyRaw]] = {
|
||||||
@ -465,4 +483,22 @@ class TypesInterpreter[S[_], X](implicit
|
|||||||
).as(frame -> Nil)
|
).as(frame -> Nil)
|
||||||
else (frame -> frame.retVals.getOrElse(Nil)).pure
|
else (frame -> frame.retVals.getOrElse(Nil)).pure
|
||||||
) <* stack.endScope
|
) <* stack.endScope
|
||||||
|
|
||||||
|
private def ensureNameNotDefined[A](
|
||||||
|
name: String,
|
||||||
|
token: Token[S],
|
||||||
|
ifDefined: => A
|
||||||
|
)(
|
||||||
|
ifNotDefined: => State[X, A]
|
||||||
|
): State[X, A] = getState
|
||||||
|
.map(_.definitions.get(name))
|
||||||
|
.flatMap {
|
||||||
|
case Some(_) =>
|
||||||
|
// TODO: Point to both locations here
|
||||||
|
report(
|
||||||
|
token,
|
||||||
|
s"Name `${name}` was already defined here"
|
||||||
|
).as(ifDefined)
|
||||||
|
case None => ifNotDefined
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,129 +9,139 @@ import cats.data.Validated.{Invalid, Valid}
|
|||||||
import cats.data.{Chain, NonEmptyChain, ValidatedNec}
|
import cats.data.{Chain, NonEmptyChain, ValidatedNec}
|
||||||
import cats.kernel.Monoid
|
import cats.kernel.Monoid
|
||||||
import cats.syntax.option.*
|
import cats.syntax.option.*
|
||||||
|
import cats.syntax.traverse.*
|
||||||
|
import cats.syntax.validated.*
|
||||||
|
import cats.syntax.apply.*
|
||||||
|
import cats.syntax.bifunctor.*
|
||||||
|
import cats.syntax.functor.*
|
||||||
|
import cats.syntax.apply.*
|
||||||
|
|
||||||
case class TypesState[S[_]](
|
case class TypesState[S[_]](
|
||||||
fields: Map[String, (Name[S], Type)] = Map.empty[String, (Name[S], Type)],
|
fields: Map[String, (Name[S], Type)] = Map(),
|
||||||
strict: Map[String, Type] = Map.empty[String, Type],
|
strict: Map[String, Type] = Map.empty,
|
||||||
definitions: Map[String, NamedTypeToken[S]] = Map.empty[String, NamedTypeToken[S]],
|
definitions: Map[String, NamedTypeToken[S]] = Map(),
|
||||||
stack: List[TypesState.Frame[S]] = Nil
|
stack: List[TypesState.Frame[S]] = Nil
|
||||||
) {
|
) {
|
||||||
def isDefined(t: String): Boolean = strict.contains(t)
|
def isDefined(t: String): Boolean = strict.contains(t)
|
||||||
|
|
||||||
|
def defineType(name: NamedTypeToken[S], `type`: Type): TypesState[S] =
|
||||||
|
copy(
|
||||||
|
strict = strict.updated(name.value, `type`),
|
||||||
|
definitions = definitions.updated(name.value, name)
|
||||||
|
)
|
||||||
|
|
||||||
|
def getType(name: String): Option[Type] =
|
||||||
|
strict.get(name)
|
||||||
|
|
||||||
|
def getTypeDefinition(name: String): Option[NamedTypeToken[S]] =
|
||||||
|
definitions.get(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
object TypesStateHelper {
|
object TypesStateHelper {
|
||||||
|
|
||||||
// TODO: an ugly return type, refactoring
|
final case class TypeResolution[S[_], +T](
|
||||||
// Returns type and a token with its definition
|
`type`: T,
|
||||||
def resolveTypeToken[S[_]](
|
definitions: List[(Token[S], NamedTypeToken[S])]
|
||||||
tt: TypeToken[S],
|
)
|
||||||
state: TypesState[S],
|
|
||||||
resolver: (
|
final case class TypeResolutionError[S[_]](
|
||||||
TypesState[S],
|
token: Token[S],
|
||||||
NamedTypeToken[S]
|
hint: String
|
||||||
) => Option[(Type, List[(Token[S], NamedTypeToken[S])])]
|
)
|
||||||
): Option[(Type, List[(Token[S], NamedTypeToken[S])])] =
|
|
||||||
|
def resolveTypeToken[S[_]](tt: TypeToken[S])(
|
||||||
|
state: TypesState[S]
|
||||||
|
): Option[TypeResolution[S, Type]] =
|
||||||
tt match {
|
tt match {
|
||||||
case TopBottomToken(_, isTop) =>
|
case TopBottomToken(_, isTop) =>
|
||||||
(if (isTop) TopType else BottomType, Nil).some
|
val `type` = if (isTop) TopType else BottomType
|
||||||
|
|
||||||
|
TypeResolution(`type`, Nil).some
|
||||||
case ArrayTypeToken(_, dtt) =>
|
case ArrayTypeToken(_, dtt) =>
|
||||||
resolveTypeToken(dtt, state, resolver).collect { case (it: DataType, t) =>
|
resolveTypeToken(dtt)(state).collect { case TypeResolution(it: DataType, t) =>
|
||||||
(ArrayType(it), t)
|
TypeResolution(ArrayType(it), t)
|
||||||
}
|
}
|
||||||
case StreamTypeToken(_, dtt) =>
|
case StreamTypeToken(_, dtt) =>
|
||||||
resolveTypeToken(dtt, state, resolver).collect { case (it: DataType, t) =>
|
resolveTypeToken(dtt)(state).collect { case TypeResolution(it: DataType, t) =>
|
||||||
(StreamType(it), t)
|
TypeResolution(StreamType(it), t)
|
||||||
}
|
}
|
||||||
case OptionTypeToken(_, dtt) =>
|
case OptionTypeToken(_, dtt) =>
|
||||||
resolveTypeToken(dtt, state, resolver).collect { case (it: DataType, t) =>
|
resolveTypeToken(dtt)(state).collect { case TypeResolution(it: DataType, t) =>
|
||||||
(OptionType(it), t)
|
TypeResolution(OptionType(it), t)
|
||||||
}
|
|
||||||
case ctt: NamedTypeToken[S] =>
|
|
||||||
resolver(state, ctt)
|
|
||||||
case btt: BasicTypeToken[S] => (btt.value, Nil).some
|
|
||||||
case ArrowTypeToken(_, args, res) =>
|
|
||||||
val strictArgs =
|
|
||||||
args.map(_._2).map(resolveTypeToken(_, state, resolver)).collect {
|
|
||||||
case Some((dt: DataType, t)) =>
|
|
||||||
(dt, t)
|
|
||||||
}
|
|
||||||
val strictRes =
|
|
||||||
res.map(resolveTypeToken(_, state, resolver)).collect { case Some((dt: DataType, t)) =>
|
|
||||||
(dt, t)
|
|
||||||
}
|
|
||||||
Option.when(strictRes.length == res.length && strictArgs.length == args.length) {
|
|
||||||
val (sArgs, argTokens) = strictArgs.unzip
|
|
||||||
val (sRes, resTokens) = strictRes.unzip
|
|
||||||
(ArrowType(ProductType(sArgs), ProductType(sRes)), argTokens.flatten ++ resTokens.flatten)
|
|
||||||
}
|
}
|
||||||
|
case ntt: NamedTypeToken[S] =>
|
||||||
|
val defs = state
|
||||||
|
.getTypeDefinition(ntt.value)
|
||||||
|
.toList
|
||||||
|
.map(ntt -> _)
|
||||||
|
|
||||||
|
state
|
||||||
|
.getType(ntt.value)
|
||||||
|
.map(typ => TypeResolution(typ, defs))
|
||||||
|
case btt: BasicTypeToken[S] =>
|
||||||
|
TypeResolution(btt.value, Nil).some
|
||||||
|
case att: ArrowTypeToken[S] =>
|
||||||
|
resolveArrowDef(att)(state).toOption
|
||||||
}
|
}
|
||||||
|
|
||||||
def resolveArrowDef[S[_]](
|
def resolveArrowDef[S[_]](arrowTypeToken: ArrowTypeToken[S])(
|
||||||
arrowTypeToken: ArrowTypeToken[S],
|
state: TypesState[S]
|
||||||
state: TypesState[S],
|
): ValidatedNec[TypeResolutionError[S], TypeResolution[S, ArrowType]] = {
|
||||||
resolver: (
|
val res = arrowTypeToken.res.traverse(typeToken =>
|
||||||
TypesState[S],
|
resolveTypeToken(typeToken)(state)
|
||||||
NamedTypeToken[S]
|
.toValidNec(
|
||||||
) => Option[(Type, List[(Token[S], NamedTypeToken[S])])]
|
TypeResolutionError(
|
||||||
): ValidatedNec[(Token[S], String), (ArrowType, List[(Token[S], NamedTypeToken[S])])] = {
|
typeToken,
|
||||||
val resType = arrowTypeToken.res.map(resolveTypeToken(_, state, resolver))
|
"Can not resolve the result type"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val args = arrowTypeToken.args.traverse { case (argName, typeToken) =>
|
||||||
|
resolveTypeToken(typeToken)(state)
|
||||||
|
.toValidNec(
|
||||||
|
TypeResolutionError(
|
||||||
|
typeToken,
|
||||||
|
"Can not resolve the argument type"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.map(argName.map(_.value) -> _)
|
||||||
|
}
|
||||||
|
|
||||||
NonEmptyChain
|
(args, res).mapN { (args, res) =>
|
||||||
.fromChain(Chain.fromSeq(arrowTypeToken.res.zip(resType).collect { case (dt, None) =>
|
val (argsLabeledTypes, argsTokens) =
|
||||||
dt -> "Cannot resolve the result type"
|
args.map { case lbl -> TypeResolution(typ, tkn) =>
|
||||||
}))
|
(lbl, typ) -> tkn
|
||||||
.fold[ValidatedNec[(Token[S], String), (ArrowType, List[(Token[S], NamedTypeToken[S])])]] {
|
}.unzip.map(_.flatten)
|
||||||
val (errs, argTypes) = arrowTypeToken.args.map { (argName, tt) =>
|
val (resTypes, resTokens) =
|
||||||
resolveTypeToken(tt, state, resolver)
|
res.map { case TypeResolution(typ, tkn) =>
|
||||||
.toRight(tt -> s"Type unresolved")
|
typ -> tkn
|
||||||
.map(argName.map(_.value) -> _)
|
}.unzip.map(_.flatten)
|
||||||
}
|
|
||||||
.foldLeft[
|
|
||||||
(
|
|
||||||
Chain[(Token[S], String)],
|
|
||||||
Chain[(Option[String], (Type, List[(Token[S], NamedTypeToken[S])]))]
|
|
||||||
)
|
|
||||||
](
|
|
||||||
(
|
|
||||||
Chain.empty,
|
|
||||||
Chain.empty[(Option[String], (Type, List[(Token[S], NamedTypeToken[S])]))]
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
case ((errs, argTypes), Right(at)) => (errs, argTypes.append(at))
|
|
||||||
case ((errs, argTypes), Left(e)) => (errs.append(e), argTypes)
|
|
||||||
}
|
|
||||||
|
|
||||||
NonEmptyChain
|
val typ = ArrowType(
|
||||||
.fromChain(errs)
|
ProductType.maybeLabelled(argsLabeledTypes),
|
||||||
.fold[ValidatedNec[
|
ProductType(resTypes)
|
||||||
(Token[S], String),
|
)
|
||||||
(ArrowType, List[(Token[S], NamedTypeToken[S])])
|
val defs = (argsTokens ++ resTokens)
|
||||||
]](
|
|
||||||
Valid {
|
TypeResolution(typ, defs)
|
||||||
val (labels, types) = argTypes.toList.unzip
|
}
|
||||||
val (resTypes, resTokens) = resType.flatten.unzip
|
|
||||||
(
|
|
||||||
ArrowType(
|
|
||||||
ProductType.maybeLabelled(labels.zip(types.map(_._1))),
|
|
||||||
ProductType(resTypes)
|
|
||||||
),
|
|
||||||
types.flatMap(_._2) ++ resTokens.flatten
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)(Invalid(_))
|
|
||||||
}(Invalid(_))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object TypesState {
|
object TypesState {
|
||||||
|
|
||||||
|
final case class TypeDefinition[S[_]](
|
||||||
|
token: NamedTypeToken[S],
|
||||||
|
`type`: Type
|
||||||
|
)
|
||||||
|
|
||||||
case class Frame[S[_]](
|
case class Frame[S[_]](
|
||||||
token: ArrowTypeToken[S],
|
token: ArrowTypeToken[S],
|
||||||
arrowType: ArrowType,
|
arrowType: ArrowType,
|
||||||
retVals: Option[List[ValueRaw]]
|
retVals: Option[List[ValueRaw]]
|
||||||
)
|
)
|
||||||
|
|
||||||
implicit def typesStateMonoid[S[_]]: Monoid[TypesState[S]] = new Monoid[TypesState[S]] {
|
given [S[_]]: Monoid[TypesState[S]] with {
|
||||||
override def empty: TypesState[S] = TypesState()
|
override def empty: TypesState[S] = TypesState()
|
||||||
|
|
||||||
override def combine(x: TypesState[S], y: TypesState[S]): TypesState[S] =
|
override def combine(x: TypesState[S], y: TypesState[S]): TypesState[S] =
|
||||||
|
@ -15,7 +15,8 @@ import org.scalatest.flatspec.AnyFlatSpec
|
|||||||
import org.scalatest.matchers.should.Matchers
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
|
||||||
class ArrowSemSpec extends AnyFlatSpec with Matchers with EitherValues {
|
class ArrowSemSpec extends AnyFlatSpec with Matchers with EitherValues {
|
||||||
import Utils.*
|
|
||||||
|
import Utils.{given, *}
|
||||||
|
|
||||||
def program(arrowStr: String): Prog[State[CompilerState[cats.Id], *], Raw] = {
|
def program(arrowStr: String): Prog[State[CompilerState[cats.Id], *], Raw] = {
|
||||||
import CompilerState.*
|
import CompilerState.*
|
||||||
|
@ -29,7 +29,7 @@ import org.scalatest.matchers.should.Matchers
|
|||||||
|
|
||||||
class ClosureSemSpec extends AnyFlatSpec with Matchers {
|
class ClosureSemSpec extends AnyFlatSpec with Matchers {
|
||||||
|
|
||||||
import Utils.*
|
import Utils.{given, *}
|
||||||
|
|
||||||
val program: Prog[State[CompilerState[cats.Id], *], Raw] = {
|
val program: Prog[State[CompilerState[cats.Id], *], Raw] = {
|
||||||
import CompilerState.*
|
import CompilerState.*
|
||||||
|
@ -87,11 +87,10 @@ class SemanticsSpec extends AnyFlatSpec with Matchers with Inside {
|
|||||||
|
|
||||||
def testServiceCallStr(str: String) =
|
def testServiceCallStr(str: String) =
|
||||||
CallArrowRawTag
|
CallArrowRawTag
|
||||||
.service(
|
.ability(
|
||||||
serviceId = LiteralRaw.quote("test"),
|
abilityName = "Test",
|
||||||
fnName = "testCallStr",
|
funcName = "testCallStr",
|
||||||
call = Call(LiteralRaw.quote(str) :: Nil, Nil),
|
call = Call(LiteralRaw.quote(str) :: Nil, Nil),
|
||||||
name = "Test",
|
|
||||||
arrowType = ArrowType(
|
arrowType = ArrowType(
|
||||||
ProductType.labelled(("s" -> ScalarType.string) :: Nil),
|
ProductType.labelled(("s" -> ScalarType.string) :: Nil),
|
||||||
ProductType(ScalarType.string :: Nil)
|
ProductType(ScalarType.string :: Nil)
|
||||||
@ -137,11 +136,10 @@ class SemanticsSpec extends AnyFlatSpec with Matchers with Inside {
|
|||||||
insideBody(script) { body =>
|
insideBody(script) { body =>
|
||||||
val arrowType = ArrowType(NilType, ConsType.cons(ScalarType.string, NilType))
|
val arrowType = ArrowType(NilType, ConsType.cons(ScalarType.string, NilType))
|
||||||
val serviceCall = CallArrowRawTag
|
val serviceCall = CallArrowRawTag
|
||||||
.service(
|
.ability(
|
||||||
serviceId = LiteralRaw.quote("srv1"),
|
abilityName = "A",
|
||||||
fnName = "fn1",
|
funcName = "fn1",
|
||||||
call = emptyCall,
|
call = emptyCall,
|
||||||
name = "A",
|
|
||||||
arrowType = arrowType
|
arrowType = arrowType
|
||||||
)
|
)
|
||||||
.leaf
|
.leaf
|
||||||
|
@ -6,45 +6,35 @@ import aqua.parser.lift.Span
|
|||||||
import aqua.raw.{Raw, RawContext}
|
import aqua.raw.{Raw, RawContext}
|
||||||
import aqua.semantics.expr.func.ClosureSem
|
import aqua.semantics.expr.func.ClosureSem
|
||||||
import aqua.semantics.rules.errors.ReportErrors
|
import aqua.semantics.rules.errors.ReportErrors
|
||||||
import aqua.semantics.rules.abilities.{AbilitiesInterpreter, AbilitiesState}
|
import aqua.semantics.rules.abilities.{AbilitiesAlgebra, AbilitiesInterpreter, AbilitiesState}
|
||||||
import aqua.semantics.rules.locations.DummyLocationsInterpreter
|
import aqua.semantics.rules.locations.{DummyLocationsInterpreter, LocationsAlgebra}
|
||||||
import aqua.semantics.rules.names.{NamesInterpreter, NamesState}
|
import aqua.semantics.rules.names.{NamesAlgebra, NamesInterpreter, NamesState}
|
||||||
import aqua.semantics.rules.types.{TypesInterpreter, TypesState}
|
import aqua.semantics.rules.types.{TypesAlgebra, TypesInterpreter, TypesState}
|
||||||
import aqua.types.*
|
import aqua.types.*
|
||||||
|
|
||||||
import cats.data.State
|
import cats.data.State
|
||||||
import cats.{~>, Id}
|
import cats.{~>, Id}
|
||||||
import monocle.Lens
|
import monocle.Lens
|
||||||
import monocle.macros.GenLens
|
import monocle.macros.GenLens
|
||||||
import monocle.syntax.all.*
|
import monocle.syntax.all.*
|
||||||
|
import aqua.semantics.rules.mangler.ManglerAlgebra
|
||||||
|
import aqua.semantics.rules.mangler.ManglerInterpreter
|
||||||
|
|
||||||
object Utils {
|
object Utils {
|
||||||
|
|
||||||
implicit val re: ReportErrors[Id, CompilerState[Id]] = new ReportErrors[Id, CompilerState[Id]] {
|
given ManglerAlgebra[State[CompilerState[Id], *]] =
|
||||||
|
new ManglerInterpreter[CompilerState[Id]]
|
||||||
|
|
||||||
override def apply(
|
given LocationsAlgebra[Id, State[CompilerState[Id], *]] =
|
||||||
st: CompilerState[Id],
|
|
||||||
token: Token[Id],
|
|
||||||
hints: List[String]
|
|
||||||
): CompilerState[Id] =
|
|
||||||
st.focus(_.errors).modify(_.append(RulesViolated(token, hints)))
|
|
||||||
}
|
|
||||||
|
|
||||||
implicit val locationsInterpreter: DummyLocationsInterpreter[Id, CompilerState[Id]] =
|
|
||||||
new DummyLocationsInterpreter[Id, CompilerState[Id]]()
|
new DummyLocationsInterpreter[Id, CompilerState[Id]]()
|
||||||
|
|
||||||
implicit val ns: Lens[CompilerState[Id], NamesState[Id]] = GenLens[CompilerState[Id]](_.names)
|
given NamesAlgebra[Id, State[CompilerState[Id], *]] =
|
||||||
|
|
||||||
implicit val as: Lens[CompilerState[Id], AbilitiesState[Id]] =
|
|
||||||
GenLens[CompilerState[Id]](_.abilities)
|
|
||||||
implicit val ts: Lens[CompilerState[Id], TypesState[Id]] = GenLens[CompilerState[Id]](_.types)
|
|
||||||
|
|
||||||
implicit val alg: NamesInterpreter[Id, CompilerState[Id]] =
|
|
||||||
new NamesInterpreter[Id, CompilerState[Id]]
|
new NamesInterpreter[Id, CompilerState[Id]]
|
||||||
|
|
||||||
implicit val typesInterpreter: TypesInterpreter[Id, CompilerState[Id]] =
|
given TypesAlgebra[Id, State[CompilerState[Id], *]] =
|
||||||
new TypesInterpreter[Id, CompilerState[Id]]
|
new TypesInterpreter[Id, CompilerState[Id]]
|
||||||
|
|
||||||
implicit val abilitiesInterpreter: AbilitiesInterpreter[Id, CompilerState[Id]] =
|
given AbilitiesAlgebra[Id, State[CompilerState[Id], *]] =
|
||||||
new AbilitiesInterpreter[Id, CompilerState[Id]]
|
new AbilitiesInterpreter[Id, CompilerState[Id]]
|
||||||
|
|
||||||
def spanToId: Span.S ~> Id = new (Span.S ~> Id) {
|
def spanToId: Span.S ~> Id = new (Span.S ~> Id) {
|
||||||
|
@ -1,32 +1,17 @@
|
|||||||
package aqua.semantics
|
package aqua.semantics
|
||||||
|
|
||||||
import aqua.semantics.rules.ValuesAlgebra
|
import aqua.semantics.rules.ValuesAlgebra
|
||||||
import aqua.semantics.rules.names.NamesState
|
import aqua.semantics.rules.abilities.{AbilitiesAlgebra, AbilitiesInterpreter, AbilitiesState}
|
||||||
import aqua.semantics.rules.abilities.AbilitiesState
|
import aqua.semantics.rules.names.{NamesAlgebra, NamesInterpreter, NamesState}
|
||||||
import aqua.semantics.rules.types.TypesState
|
import aqua.semantics.rules.definitions.{DefinitionsAlgebra, DefinitionsInterpreter}
|
||||||
import aqua.semantics.rules.types.TypesAlgebra
|
import aqua.semantics.rules.types.{TypesAlgebra, TypesInterpreter, TypesState}
|
||||||
import aqua.semantics.rules.abilities.AbilitiesInterpreter
|
import aqua.semantics.rules.locations.{DummyLocationsInterpreter, LocationsAlgebra}
|
||||||
import aqua.semantics.rules.names.NamesAlgebra
|
import aqua.semantics.rules.mangler.{ManglerAlgebra, ManglerInterpreter}
|
||||||
import aqua.semantics.rules.definitions.DefinitionsAlgebra
|
|
||||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
|
||||||
import aqua.semantics.rules.names.NamesInterpreter
|
|
||||||
import aqua.semantics.rules.definitions.DefinitionsInterpreter
|
|
||||||
import aqua.semantics.rules.types.TypesInterpreter
|
|
||||||
import aqua.semantics.rules.locations.LocationsAlgebra
|
|
||||||
import aqua.semantics.rules.locations.DummyLocationsInterpreter
|
|
||||||
import aqua.raw.value.{ApplyBinaryOpRaw, LiteralRaw}
|
import aqua.raw.value.{ApplyBinaryOpRaw, LiteralRaw}
|
||||||
import aqua.raw.RawContext
|
import aqua.raw.RawContext
|
||||||
import aqua.types.*
|
import aqua.types.*
|
||||||
import aqua.parser.lexer.{
|
import aqua.parser.lexer.*
|
||||||
CollectionToken,
|
import aqua.raw.value.*
|
||||||
InfixToken,
|
|
||||||
LiteralToken,
|
|
||||||
Name,
|
|
||||||
PrefixToken,
|
|
||||||
ValueToken,
|
|
||||||
VarToken
|
|
||||||
}
|
|
||||||
import aqua.raw.value.ApplyUnaryOpRaw
|
|
||||||
import aqua.parser.lexer.ValueToken.string
|
import aqua.parser.lexer.ValueToken.string
|
||||||
|
|
||||||
import org.scalatest.flatspec.AnyFlatSpec
|
import org.scalatest.flatspec.AnyFlatSpec
|
||||||
@ -50,6 +35,8 @@ class ValuesAlgebraSpec extends AnyFlatSpec with Matchers with Inside {
|
|||||||
given LocationsAlgebra[Id, Interpreter] =
|
given LocationsAlgebra[Id, Interpreter] =
|
||||||
new DummyLocationsInterpreter[Id, CompilerState[Id]]
|
new DummyLocationsInterpreter[Id, CompilerState[Id]]
|
||||||
|
|
||||||
|
given ManglerAlgebra[Interpreter] =
|
||||||
|
new ManglerInterpreter[CompilerState[Id]]
|
||||||
given TypesAlgebra[Id, Interpreter] =
|
given TypesAlgebra[Id, Interpreter] =
|
||||||
new TypesInterpreter[Id, CompilerState[Id]]
|
new TypesInterpreter[Id, CompilerState[Id]]
|
||||||
given AbilitiesAlgebra[Id, Interpreter] =
|
given AbilitiesAlgebra[Id, Interpreter] =
|
||||||
|
@ -243,31 +243,19 @@ case class OptionType(element: Type) extends BoxType {
|
|||||||
sealed trait NamedType extends Type {
|
sealed trait NamedType extends Type {
|
||||||
def name: String
|
def name: String
|
||||||
def fields: NonEmptyMap[String, Type]
|
def fields: NonEmptyMap[String, Type]
|
||||||
}
|
|
||||||
|
|
||||||
// Struct is an unordered collection of labelled types
|
|
||||||
// TODO: Make fields type `DataType`
|
|
||||||
case class StructType(name: String, fields: NonEmptyMap[String, Type])
|
|
||||||
extends DataType with NamedType {
|
|
||||||
|
|
||||||
override def toString: String =
|
|
||||||
s"$name{${fields.map(_.toString).toNel.toList.map(kv => kv._1 + ": " + kv._2).mkString(", ")}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ability is an unordered collection of labelled types and arrows
|
|
||||||
case class AbilityType(name: String, fields: NonEmptyMap[String, Type]) extends NamedType {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all arrows defined in this ability and its sub-abilities.
|
* Get all arrows defined in this type and its sub-abilities.
|
||||||
* Paths to arrows are returned **without** ability name
|
* Paths to arrows are returned **without** type name
|
||||||
* to allow renaming on call site.
|
* to allow renaming on call site.
|
||||||
*/
|
*/
|
||||||
lazy val arrows: Map[String, ArrowType] = {
|
lazy val arrows: Map[String, ArrowType] = {
|
||||||
def getArrowsEval(path: Option[String], ability: AbilityType): Eval[List[(String, ArrowType)]] =
|
def getArrowsEval(path: Option[String], nt: NamedType): Eval[List[(String, ArrowType)]] =
|
||||||
ability.fields.toNel.toList.flatTraverse {
|
nt.fields.toNel.toList.flatTraverse {
|
||||||
case (abName, abType: AbilityType) =>
|
// sub-arrows could be in abilities or services
|
||||||
val newPath = path.fold(abName)(AbilityType.fullName(_, abName))
|
case (innerName, innerType: (ServiceType | AbilityType)) =>
|
||||||
getArrowsEval(newPath.some, abType)
|
val newPath = path.fold(innerName)(AbilityType.fullName(_, innerName))
|
||||||
|
getArrowsEval(newPath.some, innerType)
|
||||||
case (aName, aType: ArrowType) =>
|
case (aName, aType: ArrowType) =>
|
||||||
val newPath = path.fold(aName)(AbilityType.fullName(_, aName))
|
val newPath = path.fold(aName)(AbilityType.fullName(_, aName))
|
||||||
List(newPath -> aType).pure
|
List(newPath -> aType).pure
|
||||||
@ -278,16 +266,17 @@ case class AbilityType(name: String, fields: NonEmptyMap[String, Type]) extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all abilities defined in this ability and its sub-abilities.
|
* Get all abilities defined in this type and its sub-abilities.
|
||||||
* Paths to abilities are returned **without** ability name
|
* Paths to abilities are returned **without** type name
|
||||||
* to allow renaming on call site.
|
* to allow renaming on call site.
|
||||||
*/
|
*/
|
||||||
lazy val abilities: Map[String, AbilityType] = {
|
lazy val abilities: Map[String, AbilityType] = {
|
||||||
def getAbilitiesEval(
|
def getAbilitiesEval(
|
||||||
path: Option[String],
|
path: Option[String],
|
||||||
ability: AbilityType
|
nt: NamedType
|
||||||
): Eval[List[(String, AbilityType)]] =
|
): Eval[List[(String, AbilityType)]] =
|
||||||
ability.fields.toNel.toList.flatTraverse {
|
nt.fields.toNel.toList.flatTraverse {
|
||||||
|
// sub-abilities could be only in abilities
|
||||||
case (abName, abType: AbilityType) =>
|
case (abName, abType: AbilityType) =>
|
||||||
val fullName = path.fold(abName)(AbilityType.fullName(_, abName))
|
val fullName = path.fold(abName)(AbilityType.fullName(_, abName))
|
||||||
getAbilitiesEval(fullName.some, abType).map(
|
getAbilitiesEval(fullName.some, abType).map(
|
||||||
@ -300,16 +289,17 @@ case class AbilityType(name: String, fields: NonEmptyMap[String, Type]) extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all variables defined in this ability and its sub-abilities.
|
* Get all variables defined in this type and its sub-abilities.
|
||||||
* Paths to variables are returned **without** ability name
|
* Paths to variables are returned **without** type name
|
||||||
* to allow renaming on call site.
|
* to allow renaming on call site.
|
||||||
*/
|
*/
|
||||||
lazy val variables: Map[String, DataType] = {
|
lazy val variables: Map[String, DataType] = {
|
||||||
def getVariablesEval(
|
def getVariablesEval(
|
||||||
path: Option[String],
|
path: Option[String],
|
||||||
ability: AbilityType
|
nt: NamedType
|
||||||
): Eval[List[(String, DataType)]] =
|
): Eval[List[(String, DataType)]] =
|
||||||
ability.fields.toNel.toList.flatTraverse {
|
nt.fields.toNel.toList.flatTraverse {
|
||||||
|
// sub-variables could be only in abilities
|
||||||
case (abName, abType: AbilityType) =>
|
case (abName, abType: AbilityType) =>
|
||||||
val newPath = path.fold(abName)(AbilityType.fullName(_, abName))
|
val newPath = path.fold(abName)(AbilityType.fullName(_, abName))
|
||||||
getVariablesEval(newPath.some, abType)
|
getVariablesEval(newPath.some, abType)
|
||||||
@ -321,6 +311,25 @@ case class AbilityType(name: String, fields: NonEmptyMap[String, Type]) extends
|
|||||||
|
|
||||||
getVariablesEval(None, this).value.toMap
|
getVariablesEval(None, this).value.toMap
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Struct is an unordered collection of labelled types
|
||||||
|
// TODO: Make fields type `DataType`
|
||||||
|
case class StructType(name: String, fields: NonEmptyMap[String, Type])
|
||||||
|
extends DataType with NamedType {
|
||||||
|
|
||||||
|
override def toString: String =
|
||||||
|
s"$name{${fields.map(_.toString).toNel.toList.map(kv => kv._1 + ": " + kv._2).mkString(", ")}}"
|
||||||
|
}
|
||||||
|
|
||||||
|
case class ServiceType(name: String, fields: NonEmptyMap[String, ArrowType]) extends NamedType {
|
||||||
|
|
||||||
|
override def toString: String =
|
||||||
|
s"service $name{${fields.map(_.toString).toNel.toList.map(kv => kv._1 + ": " + kv._2).mkString(", ")}}"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ability is an unordered collection of labelled types and arrows
|
||||||
|
case class AbilityType(name: String, fields: NonEmptyMap[String, Type]) extends NamedType {
|
||||||
|
|
||||||
override def toString: String =
|
override def toString: String =
|
||||||
s"ability $name{${fields.map(_.toString).toNel.toList.map(kv => kv._1 + ": " + kv._2).mkString(", ")}}"
|
s"ability $name{${fields.map(_.toString).toNel.toList.map(kv => kv._1 + ": " + kv._2).mkString(", ")}}"
|
||||||
|
@ -170,7 +170,7 @@ class TypeSpec extends AnyFlatSpec with Matchers {
|
|||||||
accepts(four, one) should be(false)
|
accepts(four, one) should be(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
"labeled types" should "create correc labels" in {
|
"labeled types" should "create correct labels" in {
|
||||||
val cons = LabeledConsType(
|
val cons = LabeledConsType(
|
||||||
"arg1",
|
"arg1",
|
||||||
ArrowType(
|
ArrowType(
|
||||||
|
Loading…
Reference in New Issue
Block a user