feat(tracing): Introduce function calls tracing [fixes LNG-169] (#732)

* Introduced MetaModel.CallArrowModel

* Fixed ArrowInlinerSpec

* Implemented trace calls injection

* Propagate TracingConfig

* Add tracing flag to js api

* Use detach instead of xor

* Write tests

* ScalarType -> LiteralType

* Add tests

* Fix after rebase

* Fixed tests

* Add tests

* Remove debug prints

* Clean up and add comments

* Fix issues

* Add tracing flag to js api

* Rename liftString -> quote

* Change literal type

* Use CallServiceModel constructor

* Fix js args

* Fixed tests

* Add tracing option to cli
This commit is contained in:
InversionSpaces 2023-06-12 15:28:27 +02:00 committed by GitHub
parent e2f67b3c0f
commit e9c004452e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 1093 additions and 160 deletions

View File

@ -1,14 +1,21 @@
import type {FunctionCallDef, ServiceDef} from "@fluencelabs/fluence/dist/internal/compilerSupport/v3impl/interface"
import type { FunctionCallDef, ServiceDef } from "@fluencelabs/fluence/dist/internal/compilerSupport/v3impl/interface"
export class AquaConfig {
constructor(logLevel: string, constants: string[], noXor: boolean, noRelay: boolean);
constructor(logLevel: string, constants: string[], noXor: boolean, noRelay: boolean, targetType: string);
constructor(
logLevel?: string,
constants?: string[],
noXor?: boolean,
noRelay?: boolean,
targetType?: string,
tracing?: boolean
);
logLevel?: string
constants?: string[]
noXor?: boolean
noRelay?: boolean
targetType?: string
tracing?: boolean
}
export class AquaFunction {
@ -45,8 +52,8 @@ export class Path {
export class Call {
constructor(functionCall: string,
arguments: any,
input: Input | Path);
arguments: any,
input: Input | Path);
functionCall: string
arguments: any

View File

@ -4,7 +4,7 @@ import aqua.api.AquaAPIConfig
import aqua.api.TargetType.*
import aqua.js.{FunctionDefJs, ServiceDefJs}
import aqua.model.transform.TransformConfig
import cats.data.Validated.{Invalid, Valid, invalidNec, validNec}
import cats.data.Validated.{invalidNec, validNec}
import cats.data.{Chain, NonEmptyChain, Validated, ValidatedNec}
import scala.scalajs.js
@ -45,7 +45,9 @@ class AquaConfig(
@JSExport
val noRelay: js.UndefOr[Boolean],
@JSExport
val targetType: js.UndefOr[String]
val targetType: js.UndefOr[String],
@JSExport
val tracing: js.UndefOr[Boolean]
)
object AquaConfig {
@ -62,11 +64,12 @@ object AquaConfig {
.getOrElse(validNec(AirType))
.map { target =>
AquaAPIConfig(
target,
cjs.logLevel.getOrElse("info"),
cjs.constants.map(_.toList).getOrElse(Nil),
cjs.noXor.getOrElse(false),
cjs.noRelay.getOrElse(false)
targetType = target,
logLevel = cjs.logLevel.getOrElse("info"),
constants = cjs.constants.map(_.toList).getOrElse(Nil),
noXor = cjs.noXor.getOrElse(false),
noRelay = cjs.noRelay.getOrElse(false),
tracing = cjs.tracing.getOrElse(false)
)
}
}

View File

@ -9,10 +9,17 @@ case class AquaAPIConfig(
logLevel: String = "info",
constants: List[String] = Nil,
noXor: Boolean = false,
noRelay: Boolean = false
noRelay: Boolean = false,
tracing: Boolean = false
) {
def getTransformConfig: TransformConfig =
if (noRelay) TransformConfig(relayVarName = None, wrapWithXor = !noXor)
else TransformConfig(wrapWithXor = !noXor)
def getTransformConfig: TransformConfig = {
val config = TransformConfig(
wrapWithXor = !noXor,
tracing = Option.when(tracing)(TransformConfig.TracingConfig.default)
)
if (noRelay) config.copy(relayVarName = None)
else config
}
}

View File

@ -7,7 +7,7 @@ import aqua.model.transform.{Transform, TransformConfig}
import aqua.model.{FuncArrow, ValueModel, VarModel}
import aqua.parser.lexer.CallArrowToken
import aqua.parser.lift.Span
import aqua.raw.ops.{Call, CallArrowRawTag, FuncOp, SeqTag}
import aqua.raw.ops.{Call, CallArrowRawTag, SeqTag}
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
import aqua.types.*
import cats.data.Validated.{invalid, invalidNec, invalidNel, validNec, validNel}
@ -18,7 +18,7 @@ import cats.syntax.flatMap.*
import cats.syntax.partialOrder.*
import cats.syntax.show.*
import cats.syntax.traverse.*
import cats.{Id, ~>}
import cats.{~>, Id}
import scala.collection.immutable.SortedMap
import scala.concurrent.ExecutionContext

View File

@ -7,7 +7,7 @@ import aqua.model.transform.{Transform, TransformConfig}
import aqua.model.{FuncArrow, ValueModel, VarModel}
import aqua.parser.lexer.CallArrowToken
import aqua.parser.lift.Span
import aqua.raw.ops.{Call, CallArrowRawTag, FuncOp, SeqTag}
import aqua.raw.ops.{Call, CallArrowRawTag, SeqTag}
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
import aqua.types.*
import cats.data.Validated.{invalid, invalidNec, invalidNel, validNec, validNel}
@ -101,9 +101,10 @@ class RunPreparer(
val returnCodomain = ProductType(results.map(_.`type`))
// arguments is only variables, without literals
val argumentsType = ProductType.labelled(func.args.zip(funcCallable.arrowType.domain.labelledData).collect {
case (VarRaw(name, _), (_, t)) => (name, t)
})
val argumentsType =
ProductType.labelled(func.args.zip(funcCallable.arrowType.domain.labelledData).collect {
case (VarRaw(name, _), (_, t)) => (name, t)
})
FuncArrow(
func.name + "Run",

View File

@ -144,6 +144,12 @@ object AppOpts {
.map(_ => true)
.withDefault(false)
val tracing: Opts[Boolean] =
Opts
.flag("trace", "Generate tace events calls")
.map(_ => true)
.withDefault(false)
val isOldFluenceJs: Opts[Boolean] =
Opts
.flagOption[String](
@ -172,7 +178,7 @@ object AppOpts {
Opts
.flag(
"scheduled",
"Generate air code for script storage. Without error handling wrappers and hops on relay. Will ignore other options"
"Generate air code for script storage. Without error handling wrappers, hops on relay and tracing. Will ignore other options"
)
.map(_ => true)
.withDefault(false)

View File

@ -17,7 +17,7 @@ import cats.syntax.apply.*
import cats.syntax.flatMap.*
import cats.syntax.functor.*
import cats.syntax.traverse.*
import cats.{Functor, Id, Monad, ~>}
import cats.{~>, Functor, Id, Monad}
import com.monovore.decline
import com.monovore.decline.effect.CommandIOApp
import com.monovore.decline.effect.CommandIOApp.printHelp
@ -95,6 +95,7 @@ object AquaCli extends IOApp with Logging {
compileToJs,
noRelay,
noXorWrapper,
tracing,
isOldFluenceJs,
wrapWithOption(helpOpt),
wrapWithOption(versionOpt),
@ -112,6 +113,7 @@ object AquaCli extends IOApp with Logging {
toJs,
noRelayOp,
noXorOp,
tracingOp,
isOldFluenceJsOp,
h,
v,
@ -124,6 +126,7 @@ object AquaCli extends IOApp with Logging {
val toAir = toAirOp || isScheduled
val noXor = noXorOp || isScheduled
val noRelay = noRelayOp || isScheduled
val tracingEnabled = tracingOp && !isScheduled
// if there is `--help` or `--version` flag - show help and version
// otherwise continue program execution
@ -133,7 +136,11 @@ object AquaCli extends IOApp with Logging {
else if (toJs) JavaScriptTarget
else TypescriptTarget
val bc = {
val bc = TransformConfig(wrapWithXor = !noXor, constants = constants)
val bc = TransformConfig(
wrapWithXor = !noXor,
constants = constants,
tracing = Option.when(tracingEnabled)(TransformConfig.TracingConfig.default)
)
bc.copy(relayVarName = bc.relayVarName.filterNot(_ => noRelay))
}
LogFormatter.initLogger(Some(logLevel.compiler))
@ -205,8 +212,8 @@ object AquaCli extends IOApp with Logging {
ExitCode.Error
}
}
.getOrElse{
ConsoleEff[IO].print(h).map{_ =>
.getOrElse {
ConsoleEff[IO].print(h).map { _ =>
// hack to show last string in `help`
println()
ExitCode.Success

View File

@ -2,7 +2,13 @@ package aqua.model.inline
import aqua.model.inline.state.{Arrows, Counter, Exports, Mangler}
import aqua.model.*
import aqua.model.inline.raw.{ApplyFunctorRawInliner, ApplyGateRawInliner, ApplyPropertiesRawInliner, CallArrowRawInliner, CollectionRawInliner}
import aqua.model.inline.raw.{
ApplyFunctorRawInliner,
ApplyGateRawInliner,
ApplyPropertiesRawInliner,
CallArrowRawInliner,
CollectionRawInliner
}
import aqua.raw.ops.*
import aqua.raw.value.*
import aqua.types.{ArrayType, OptionType, StreamType}
@ -86,10 +92,10 @@ object RawValueInliner extends Logging {
case (vv, _) =>
FlattenModel(vv, name).leaf
}
}.map{ predo =>
}.map { predo =>
inline.mergeMode match
case SeqMode =>
SeqModel.wrap((inline.predo.toList ++ predo):_*) :: Nil
SeqModel.wrap((inline.predo.toList ++ predo): _*) :: Nil
case ParMode => inline.predo.toList ::: predo
}
}

View File

@ -10,8 +10,11 @@ import aqua.raw.value.*
import aqua.types.{ArrayType, ArrowType, BoxType, CanonStreamType, StreamType}
import cats.syntax.traverse.*
import cats.syntax.applicative.*
import cats.syntax.functor.*
import cats.syntax.option.*
import cats.instances.list.*
import cats.data.{Chain, State, StateT}
import cats.syntax.show.*
import scribe.{log, Logging}
/**
@ -218,10 +221,10 @@ object TagInliner extends Logging {
collectionToModel(c, Some(assignTo))
case v =>
valueToModel(v, false)
}).flatMap { cd =>
for {
_ <- Exports[S].resolved(assignTo, cd._1)
} yield None -> cd._2
}).flatMap { case (model, prefix) =>
Exports[S]
.resolved(assignTo, model)
.as(None -> prefix)
}
case ClosureTag(arrow, detach) =>

View File

@ -1,7 +1,7 @@
package aqua.model.inline.raw
import aqua.model.inline.Inline.parDesugarPrefixOpt
import aqua.model.{CallServiceModel, FuncArrow, SeqModel, ValueModel, VarModel}
import aqua.model.{CallServiceModel, FuncArrow, MetaModel, SeqModel, ValueModel, VarModel}
import aqua.model.inline.{ArrowInliner, Inline, TagInliner}
import aqua.model.inline.RawValueInliner.{callToModel, valueToModel}
import aqua.model.inline.state.{Arrows, Exports, Mangler}
@ -48,7 +48,10 @@ object CallArrowRawInliner extends RawInliner[CallArrowRaw] with Logging {
}
}
private def resolveFuncArrow[S: Mangler: Exports: Arrows](fn: FuncArrow, call: Call) = {
private def resolveFuncArrow[S: Mangler: Exports: Arrows](
fn: FuncArrow,
call: Call
): State[S, (List[ValueModel], Inline)] = {
logger.trace(Console.YELLOW + s"Call arrow ${fn.funcName}" + Console.RESET)
callToModel(call, false).flatMap { case (cm, p) =>
ArrowInliner
@ -56,13 +59,23 @@ object CallArrowRawInliner extends RawInliner[CallArrowRaw] with Logging {
.map { case (body, vars) =>
vars -> Inline(
ListMap.empty,
Chain.one(SeqModel.wrap(p.toList :+ body: _*))
Chain.one(
// Leave meta information in tree after inlining
MetaModel
.CallArrowModel(fn.funcName)
.wrap(
SeqModel.wrap(p.toList :+ body: _*)
)
)
)
}
}
}
private def resolveArrow[S: Mangler: Exports: Arrows](funcName: String, call: Call) =
private def resolveArrow[S: Mangler: Exports: Arrows](
funcName: String,
call: Call
): State[S, (List[ValueModel], Inline)] =
Arrows[S].arrows.flatMap(arrows =>
arrows.get(funcName) match {
case Some(fn) =>

View File

@ -9,6 +9,8 @@ import cats.syntax.show.*
import cats.data.{Chain, NonEmptyList, NonEmptyMap}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import aqua.raw.value.{CallArrowRaw, ValueRaw}
import aqua.raw.arrow.{ArrowRaw, FuncRaw}
class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
@ -33,7 +35,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
model.equalsOrShowDiff(
CallServiceModel(
LiteralModel("\"dumb_srv_id\"", LiteralType.string),
LiteralModel.quote("dumb_srv_id"),
"dumb",
CallModel(Nil, Nil)
).leaf
@ -113,14 +115,21 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
model.equalsOrShowDiff(
RestrictionModel(streamVar.name, true).wrap(
SeqModel.wrap(
CanonicalizeModel(streamModel, CallModel.Export(canonModel.name, canonModel.`type`)).leaf,
CallServiceModel(
LiteralModel("\"test-service\"", LiteralType.string),
"some-call",
CallModel(canonModel :: Nil, Nil)
).leaf
)
MetaModel
.CallArrowModel("cb")
.wrap(
SeqModel.wrap(
CanonicalizeModel(
streamModel,
CallModel.Export(canonModel.name, canonModel.`type`)
).leaf,
CallServiceModel(
LiteralModel.quote("test-service"),
"some-call",
CallModel(canonModel :: Nil, Nil)
).leaf
)
)
)
) should be(true)
@ -130,7 +139,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
func stream-callback(cb: string -> ()):
records: *string
cb(records!)
*/
*/
ignore /*"arrow inliner"*/ should "pass stream with gate to callback properly" in {
val streamType = StreamType(ScalarType.string)
val streamVar = VarRaw("records", streamType)
@ -210,7 +219,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
model.equalsOrShowDiff(
RestrictionModel(streamVar.name, true).wrap(
CallServiceModel(
LiteralModel("\"test-service\"", LiteralType.string),
LiteralModel.quote("test-service"),
"some-call",
CallModel(streamModel :: Nil, Nil)
).leaf
@ -291,15 +300,19 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
model.equalsOrShowDiff(
SeqModel.wrap(
CallServiceModel(
LiteralModel("\"test-service\"", LiteralType.string),
"get_records",
CallModel(Nil, CallModel.Export(recordsModel.name, recordsModel.`type`) :: Nil)
).leaf,
MetaModel
.CallArrowModel(innerName)
.wrap(
CallServiceModel(
LiteralModel.quote("test-service"),
"get_records",
CallModel(Nil, CallModel.Export(recordsModel.name, recordsModel.`type`) :: Nil)
).leaf
),
SeqModel.wrap(
CanonicalizeModel(recordsModel, CallModel.Export(canonModel.name, canonType)).leaf,
CallServiceModel(
LiteralModel("\"callbackSrv\"", LiteralType.string),
LiteralModel.quote("callbackSrv"),
"response",
CallModel(canonModel :: Nil, Nil)
).leaf
@ -309,6 +322,642 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
}
/**
* service Test("test-service"):
* get_number() -> u16
*
* func inner() -> u16:
* res <- Test.get_number()
* <- res
*
* func outer() -> u16
* res1 <- inner() -- Meta should be left here
* res2 <- Test.get_number()
* res3 <- inner() -- Meta should be left here
* retval = res1 + res2 + res3
* <- retval
*/
"arrow inliner" should "leave meta after function inlining" in {
val innerName = "inner"
val innerRes = VarRaw("res", ScalarType.u16)
val serviceName = "test-service"
val serviceMethod = "get_number"
val serviceCall = (res: VarRaw) =>
CallArrowRawTag
.service(
LiteralRaw.quote(serviceName),
serviceMethod,
Call(Nil, Call.Export(res.name, ScalarType.u16) :: Nil)
)
.leaf
val innerBody = SeqTag.wrap(
serviceCall(innerRes),
ReturnTag(
NonEmptyList.one(
innerRes
)
).leaf
)
val inner = FuncArrow(
funcName = innerName,
body = innerBody,
arrowType = ArrowType(
ProductType(Nil),
ProductType(List(ScalarType.u16))
),
ret = List(innerRes),
capturedArrows = Map.empty,
capturedValues = Map.empty,
capturedTopology = None
)
val outterRes1 = VarRaw("res1", ScalarType.u16)
val outterRes2 = VarRaw("res2", ScalarType.u16)
val outterRes3 = VarRaw("res3", ScalarType.u16)
val outterRetVal = VarRaw("retval", ScalarType.u16)
val innerCall = (res: VarRaw) =>
CallArrowRawTag
.func(
innerName,
Call(Nil, Call.Export(res.name, ScalarType.u16) :: Nil)
)
.leaf
val outerBody = SeqTag.wrap(
innerCall(outterRes1),
serviceCall(outterRes2),
innerCall(outterRes3),
AssignmentTag(
RawBuilder.add(
outterRes1,
RawBuilder.add(
outterRes2,
outterRes3
)
),
outterRetVal.name
).leaf,
ReturnTag(
NonEmptyList
.one(
outterRetVal
)
).leaf
)
val outer = FuncArrow(
funcName = "outer",
body = outerBody,
arrowType = ArrowType(
ProductType(Nil),
ProductType(List(ScalarType.u16))
),
ret = List(outterRetVal),
capturedArrows = Map(innerName -> inner),
capturedValues = Map.empty,
capturedTopology = None
)
val model = ArrowInliner
.callArrow[InliningState](
outer,
CallModel(Nil, Nil)
)
.runA(InliningState())
.value
val serviceCallModel = (res: VarModel) =>
CallServiceModel(
LiteralModel.quote(serviceName),
serviceMethod,
CallModel(Nil, CallModel.Export(res.name, res.`type`) :: Nil)
).leaf
/* WARNING: This naming is unstable */
val res1 = VarModel("res", ScalarType.u16)
val res2 = VarModel("res2", ScalarType.u16)
val res3 = VarModel("res-0", ScalarType.u16)
val tempAdd = VarModel("add-0", ScalarType.u16)
val expected = SeqModel.wrap(
MetaModel
.CallArrowModel(innerName)
.wrap(
serviceCallModel(res1)
),
serviceCallModel(res2),
MetaModel
.CallArrowModel(innerName)
.wrap(
serviceCallModel(res3)
),
SeqModel.wrap(
ModelBuilder.add(res2, res3)(tempAdd).leaf,
ModelBuilder.add(res1, tempAdd)(VarModel("add", ScalarType.u16)).leaf
)
)
model.equalsOrShowDiff(expected) shouldEqual true
}
/**
* func inner() -> u16:
* res = 42
* <- res
*
* func outer() -> u16:
* retval = inner() + inner() + 37
* <- retval
*/
"arrow inliner" should "omit meta if arrow was completely erased" in {
val innerName = "inner"
val innerRes = VarRaw("res", ScalarType.u16)
val innerRet = "42"
val innerBody = SeqTag.wrap(
AssignmentTag(
LiteralRaw(innerRet, ScalarType.u16),
innerRes.name
).leaf,
ReturnTag(
NonEmptyList.one(
innerRes
)
).leaf
)
val inner = FuncArrow(
funcName = innerName,
body = innerBody,
arrowType = ArrowType(
ProductType(Nil),
ProductType(List(ScalarType.u16))
),
ret = List(innerRes),
capturedArrows = Map.empty,
capturedValues = Map.empty,
capturedTopology = None
)
val innerCall = CallArrowRaw(
ability = None,
name = innerName,
arguments = Nil,
baseType = ArrowType(
domain = NilType,
codomain = ProductType(List(ScalarType.u16))
),
serviceId = None
)
val outerAdd = "37"
val outterRetVal = VarRaw("retval", ScalarType.u16)
val outerBody = SeqTag.wrap(
AssignmentTag(
RawBuilder.add(
innerCall,
RawBuilder.add(
innerCall,
LiteralRaw(outerAdd, ScalarType.u16)
)
),
outterRetVal.name
).leaf,
ReturnTag(
NonEmptyList
.one(
outterRetVal
)
).leaf
)
val outer = FuncArrow(
funcName = "outer",
body = outerBody,
arrowType = ArrowType(
ProductType(Nil),
ProductType(List(ScalarType.u16))
),
ret = List(outterRetVal),
capturedArrows = Map(innerName -> inner),
capturedValues = Map.empty,
capturedTopology = None
)
val model = ArrowInliner
.callArrow[InliningState](
outer,
CallModel(Nil, Nil)
)
.runA(InliningState())
.value
/* WARNING: This naming is unstable */
val tempAdd0 = VarModel("add-0", ScalarType.u16)
val tempAdd = VarModel("add", ScalarType.u16)
val expected = SeqModel.wrap(
ModelBuilder
.add(
LiteralModel(innerRet, ScalarType.u16),
LiteralModel(outerAdd, ScalarType.u16)
)(tempAdd0)
.leaf,
ModelBuilder
.add(
LiteralModel(innerRet, ScalarType.u16),
tempAdd0
)(tempAdd)
.leaf
)
model.equalsOrShowDiff(expected) shouldEqual true
}
/**
* func inner(arg: u16) -> u16 -> u16:
* closure = (x: u16) -> u16:
* retval = x + arg
* <- retval
* <- closure
*
* func outer() -> u16:
* c <- inner(42)
* retval = 37 + c(1) + c(2)
* <- retval
*/
"arrow inliner" should "leave meta after returned closure inlining" in {
val innerName = "inner"
val closureName = "closure"
val closureArg = VarRaw(
"x",
ScalarType.u16
)
val innerArg = VarRaw(
"arg",
ScalarType.u16
)
val closureRes = VarRaw(
"retval",
ScalarType.u16
)
val closureType = ArrowType(
domain = ProductType(List(closureArg.`type`)),
codomain = ProductType(List(ScalarType.u16))
)
val closureTypeLablled = closureType.copy(
domain = ProductType.labelled(List(closureArg.name -> closureArg.`type`))
)
val innerRes = VarRaw(
closureName,
closureTypeLablled
)
val innerType = ArrowType(
domain = ProductType.labelled(List(innerArg.name -> innerArg.`type`)),
codomain = ProductType(List(closureType))
)
val closureBody = SeqTag.wrap(
AssignmentTag(
RawBuilder.add(
closureArg,
innerArg
),
closureRes.name
).leaf,
ReturnTag(
NonEmptyList.one(closureRes)
).leaf
)
val closureFunc = FuncRaw(
name = closureName,
arrow = ArrowRaw(
`type` = closureTypeLablled,
ret = List(closureRes),
body = closureBody
)
)
val innerBody = SeqTag.wrap(
ClosureTag(
func = closureFunc,
detach = false
).leaf,
ReturnTag(
NonEmptyList.one(innerRes)
).leaf
)
val inner = FuncArrow(
funcName = innerName,
body = innerBody,
arrowType = innerType,
ret = List(innerRes),
capturedArrows = Map.empty,
capturedValues = Map.empty,
capturedTopology = None
)
val outterClosure = VarRaw(
"c",
closureType
)
val outterRes = VarRaw(
"retval",
ScalarType.u16
)
val innerCall =
CallArrowRawTag(
List(Call.Export(outterClosure.name, outterClosure.`type`)),
CallArrowRaw(
ability = None,
name = innerName,
arguments = List(LiteralRaw("42", LiteralType.number)),
baseType = innerType,
serviceId = None
)
).leaf
val closureCall = (i: String) =>
CallArrowRaw(
ability = None,
name = outterClosure.name,
arguments = List(LiteralRaw(i, LiteralType.number)),
baseType = closureType,
serviceId = None
)
val outerBody = SeqTag.wrap(
innerCall,
AssignmentTag(
RawBuilder.add(
RawBuilder.add(
LiteralRaw("37", LiteralType.number),
closureCall("1")
),
closureCall("2")
),
outterRes.name
).leaf,
ReturnTag(
NonEmptyList
.one(
outterRes
)
).leaf
)
val outer = FuncArrow(
funcName = "outer",
body = outerBody,
arrowType = ArrowType(
ProductType(Nil),
ProductType(List(ScalarType.u16))
),
ret = List(outterRes),
capturedArrows = Map(innerName -> inner),
capturedValues = Map.empty,
capturedTopology = None
)
val model = ArrowInliner
.callArrow[InliningState](
outer,
CallModel(Nil, Nil)
)
.runA(InliningState())
.value
val closureCallModel = (x: String, o: VarModel) =>
MetaModel
.CallArrowModel(closureName)
.wrap(
ApplyTopologyModel(closureName)
.wrap(
ModelBuilder
.add(
LiteralModel(x, LiteralType.number),
LiteralModel("42", LiteralType.number)
)(o)
.leaf
)
)
/* WARNING: This naming is unstable */
val tempAdd0 = VarModel("add-0", ScalarType.u16)
val tempAdd1 = VarModel("add-1", ScalarType.u16)
val tempAdd2 = VarModel("add-2", ScalarType.u16)
val tempAdd = VarModel("add", ScalarType.u16)
val expected = SeqModel.wrap(
MetaModel
.CallArrowModel(innerName)
.wrap(
CaptureTopologyModel(closureName).leaf
),
SeqModel.wrap(
ParModel.wrap(
SeqModel.wrap(
closureCallModel("1", tempAdd1),
ModelBuilder
.add(
LiteralModel("37", LiteralType.number),
tempAdd1
)(tempAdd0)
.leaf
),
closureCallModel("2", tempAdd2)
),
ModelBuilder
.add(
tempAdd0,
tempAdd2
)(tempAdd)
.leaf
)
)
model.equalsOrShowDiff(expected) shouldEqual true
}
/**
* func inner() -> () -> u16:
* closure = func () -> u16:
* <- 42
* <- closure
*
* func outer() -> u16:
* c <- inner()
* retval = 37 + c() + c()
* <- retval
*/
"arrow inliner" should "omit meta if returned closure was completely erased" in {
val innerName = "inner"
val closureName = "closure"
val closureRes = LiteralRaw(
"42",
LiteralType.number
)
val closureType = ArrowType(
domain = NilType,
codomain = ProductType(List(ScalarType.u16))
)
val innerRes = VarRaw(
closureName,
closureType
)
val innerType = ArrowType(
domain = NilType,
codomain = ProductType(List(closureType))
)
val closureBody = SeqTag.wrap(
ReturnTag(
NonEmptyList.one(closureRes)
).leaf
)
val closureFunc = FuncRaw(
name = closureName,
arrow = ArrowRaw(
`type` = closureType,
ret = List(closureRes),
body = closureBody
)
)
val innerBody = SeqTag.wrap(
ClosureTag(
func = closureFunc,
detach = true
).leaf,
ReturnTag(
NonEmptyList.one(innerRes)
).leaf
)
val inner = FuncArrow(
funcName = innerName,
body = innerBody,
arrowType = innerType,
ret = List(innerRes),
capturedArrows = Map.empty,
capturedValues = Map.empty,
capturedTopology = None
)
val outterClosure = VarRaw(
"c",
closureType
)
val outterRes = VarRaw(
"retval",
ScalarType.u16
)
val innerCall =
CallArrowRawTag(
List(Call.Export(outterClosure.name, outterClosure.`type`)),
CallArrowRaw(
ability = None,
name = innerName,
arguments = Nil,
baseType = innerType,
serviceId = None
)
).leaf
val closureCall =
CallArrowRaw(
ability = None,
name = outterClosure.name,
arguments = Nil,
baseType = closureType,
serviceId = None
)
val outerBody = SeqTag.wrap(
innerCall,
AssignmentTag(
RawBuilder.add(
RawBuilder.add(
LiteralRaw("37", LiteralType.number),
closureCall
),
closureCall
),
outterRes.name
).leaf,
ReturnTag(
NonEmptyList
.one(
outterRes
)
).leaf
)
val outer = FuncArrow(
funcName = "outer",
body = outerBody,
arrowType = ArrowType(
ProductType(Nil),
ProductType(List(ScalarType.u16))
),
ret = List(outterRes),
capturedArrows = Map(innerName -> inner),
capturedValues = Map.empty,
capturedTopology = None
)
val model = ArrowInliner
.callArrow[InliningState](
outer,
CallModel(Nil, Nil)
)
.runA(InliningState())
.value
/* WARNING: This naming is unstable */
val tempAdd0 = VarModel("add-0", ScalarType.u16)
val tempAdd = VarModel("add", ScalarType.u16)
val number = (v: String) =>
LiteralModel(
v,
LiteralType.number
)
val expected = SeqModel.wrap(
ModelBuilder
.add(
number("37"),
number("42")
)(tempAdd0)
.leaf,
ModelBuilder
.add(
tempAdd0,
number("42")
)(tempAdd)
.leaf
)
model.equalsOrShowDiff(expected) shouldEqual true
}
/*
data Prod:
value: string
@ -322,6 +971,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
OpHa.identity(v)
*/
"arrow inliner" should "hold lambda" in {
val innerName = "inner"
// lambda that will be assigned to another variable
val objectVarLambda =
@ -348,7 +998,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
// function where we assign object lambda to value and call service
val inner =
FuncArrow(
"inner",
innerName,
SeqTag.wrap(
AssignmentTag(
objectVarLambda,
@ -399,18 +1049,22 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
model.equalsOrShowDiff(
SeqModel.wrap(
CallServiceModel(
LiteralModel("\"getSrv\"", LiteralType.string),
LiteralModel.quote("getSrv"),
"getObj",
CallModel(Nil, CallModel.Export(objectVar.name, objectVar.`type`) :: Nil)
).leaf,
SeqModel.wrap(
FlattenModel(ValueModel.fromRaw(objectVarLambda), flattenObject.name).leaf,
CallServiceModel(
LiteralModel("\"callbackSrv\"", LiteralType.string),
"response",
CallModel(ValueModel.fromRaw(flattenObject) :: Nil, Nil)
).leaf
)
MetaModel
.CallArrowModel(innerName)
.wrap(
SeqModel.wrap(
FlattenModel(ValueModel.fromRaw(objectVarLambda), flattenObject.name).leaf,
CallServiceModel(
LiteralModel.quote("callbackSrv"),
"response",
CallModel(ValueModel.fromRaw(flattenObject) :: Nil, Nil)
).leaf
)
)
)
) should be(true)
@ -421,6 +1075,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
join nodes[idx]
*/
"arrow inliner" should "not rename value in index array lambda" in {
val innerName = "inner"
// lambda that will be assigned to another variable
val argArray = VarRaw(
@ -453,7 +1108,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
// function where we assign object lambda to value and call service
val inner =
FuncArrow(
"inner",
innerName,
JoinTag(NonEmptyList.one(arrIdx)).leaf,
ArrowType(
ProductType.labelled(
@ -495,12 +1150,12 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
model.equalsOrShowDiff(
SeqModel.wrap(
CallServiceModel(
LiteralModel("\"getSrv\"", LiteralType.string),
LiteralModel.quote("getSrv"),
"getArr",
CallModel(Nil, CallModel.Export(argArray.name, argArray.`type`) :: Nil)
).leaf,
CallServiceModel(
LiteralModel("\"getSrv\"", LiteralType.string),
LiteralModel.quote("getSrv"),
"getIdx",
CallModel(Nil, CallModel.Export(idxVar.name, idxVar.`type`) :: Nil)
).leaf

View File

@ -24,7 +24,13 @@ class CopyInlinerSpec extends AnyFlatSpec with Matchers {
val length = FunctorRaw("length", ScalarType.u32)
val lengthValue = VarRaw("l", arrType).withProperty(length)
val getField = CallArrowRaw(None, "get_field", Nil, ArrowType(NilType, UnlabeledConsType(ScalarType.string, NilType)), Option(LiteralRaw("\"serv\"", ScalarType.string)))
val getField = CallArrowRaw(
None,
"get_field",
Nil,
ArrowType(NilType, UnlabeledConsType(ScalarType.string, NilType)),
Option(LiteralRaw.quote("serv"))
)
val copyRaw =
IntoCopyRaw(structType, NonEmptyMap.of("field1" -> lengthValue, "field2" -> getField))
@ -44,7 +50,7 @@ class CopyInlinerSpec extends AnyFlatSpec with Matchers {
ParModel.wrap(
SeqModel.wrap(
FlattenModel(VarModel("l", arrType), "l_to_functor").leaf,
FlattenModel(VarModel("l_to_functor", arrType, Chain.one(lengthModel)), "l_length").leaf,
FlattenModel(VarModel("l_to_functor", arrType, Chain.one(lengthModel)), "l_length").leaf
),
CallServiceModel(
"serv",

View File

@ -29,7 +29,7 @@ class MakeStructInlinerSpec extends AnyFlatSpec with Matchers {
"get_field",
Nil,
ArrowType(NilType, UnlabeledConsType(ScalarType.string, NilType)),
Option(LiteralRaw("\"serv\"", ScalarType.string))
Option(LiteralRaw.quote("serv"))
)
val makeStruct =

View File

@ -0,0 +1,14 @@
package aqua.model.inline
import aqua.model.{CallModel, CallServiceModel, LiteralModel, ValueModel, VarModel}
object ModelBuilder {
def add(l: ValueModel, r: ValueModel)(o: VarModel): CallServiceModel =
CallServiceModel(
serviceId = "math",
funcName = "add",
args = List(l, r),
result = o
)
}

View File

@ -0,0 +1,21 @@
package aqua.model.inline
import aqua.raw.value.{CallArrowRaw, LiteralRaw, ValueRaw}
import aqua.types.{ArrowType, ProductType, ScalarType}
object RawBuilder {
def add(l: ValueRaw, r: ValueRaw): ValueRaw =
CallArrowRaw(
ability = Some("math"),
name = "add",
arguments = List(l, r),
baseType = ArrowType(
ProductType(List(ScalarType.i64, ScalarType.i64)),
ProductType(
List(l.`type` `` r.`type`)
)
),
serviceId = Some(LiteralRaw.quote("math"))
)
}

View File

@ -172,7 +172,8 @@ case class CollectionRaw(values: NonEmptyList[ValueRaw], boxType: BoxType) exten
copy(values = values.map(_.renameVars(map)))
}
case class MakeStructRaw(fields: NonEmptyMap[String, ValueRaw], structType: StructType) extends ValueRaw {
case class MakeStructRaw(fields: NonEmptyMap[String, ValueRaw], structType: StructType)
extends ValueRaw {
override def baseType: Type = structType

View File

@ -1,7 +1,7 @@
package aqua.model
import aqua.raw.arrow.FuncRaw
import aqua.raw.ops.{CallArrowRawTag, FuncOp}
import aqua.raw.ops.CallArrowRawTag
import aqua.raw.value.ValueRaw
import aqua.raw.value.CallArrowRaw
import aqua.raw.{ConstantRaw, RawContext, RawPart, ServiceRaw, TypeRaw}

View File

@ -2,7 +2,7 @@ package aqua.model
import aqua.raw.Raw
import aqua.raw.arrow.FuncRaw
import aqua.raw.ops.{FuncOp, RawTag}
import aqua.raw.ops.RawTag
import aqua.raw.value.ValueRaw
import aqua.types.{ArrowType, Type}

View File

@ -39,6 +39,26 @@ object OpModel extends TreeNodeCompanion[OpModel] {
}
}
/**
* Meta information embedded in a tree
*/
enum MetaModel extends OpModel {
/**
* Wraps subtree that was produced after inlining arrow
*
* @param name Name of arrow inlined
*/
case CallArrowModel(name: String)
override def wrap(children: Tree*): Tree =
// NOTE: Consider leaving some meta info if call is completely erased?
children.filter(_.head != EmptyModel) match {
case Nil => EmptyModel.leaf
case filtered => super.wrap(filtered: _*)
}
}
sealed trait NoExecModel extends OpModel
sealed trait ForceExecModel extends OpModel
@ -95,9 +115,14 @@ case class MatchMismatchModel(left: ValueModel, right: ValueModel, shouldMatch:
left.usesVarNames ++ right.usesVarNames
}
case class ForModel(item: String, iterable: ValueModel, mode: Option[ForModel.Mode] = Some(ForModel.NullMode)) extends SeqGroupModel {
case class ForModel(
item: String,
iterable: ValueModel,
mode: Option[ForModel.Mode] = Some(ForModel.NullMode)
) extends SeqGroupModel {
override def toString: String = s"for $item <- $iterable${mode.map(m => " " + m.toString).getOrElse("")}"
override def toString: String =
s"for $item <- $iterable${mode.map(m => " " + m.toString).getOrElse("")}"
override def restrictsVarNames: Set[String] = Set(item)
@ -142,9 +167,15 @@ case class CallServiceModel(serviceId: ValueModel, funcName: String, call: CallM
}
object CallServiceModel {
def apply(serviceId: String, funcName: String, args: List[ValueModel], result: VarModel): CallServiceModel =
def apply(
serviceId: String,
funcName: String,
args: List[ValueModel],
result: VarModel
): CallServiceModel =
CallServiceModel(
LiteralModel(s"\"$serviceId\"", ScalarType.string),
LiteralModel.quote(serviceId),
funcName,
CallModel(
args,

View File

@ -47,6 +47,8 @@ case class LiteralModel(value: String, `type`: Type) extends ValueModel {
object LiteralModel {
def fromRaw(raw: LiteralRaw): LiteralModel = LiteralModel(raw.value, raw.baseType)
def quote(str: String): LiteralModel = LiteralModel(s"\"$str\"", LiteralType.string)
}
sealed trait PropertyModel {

View File

@ -16,7 +16,9 @@ import aqua.types.ScalarType
import cats.Eval
import cats.data.Chain
import cats.free.Cofree
import cats.syntax.option.*
import scribe.Logging
import aqua.model.transform.TransformConfig.TracingConfig
// API for transforming RawTag to Res
object Transform extends Logging {
@ -87,6 +89,11 @@ object Transform extends Logging {
callable = initCallable
)
val tracing = Tracing(
enabledConfig = conf.tracing,
initCallable = initCallable
)
val argsProvider: ArgsProvider = ArgsFromService(
dataServiceId = conf.dataSrvId,
names = relayVar.toList ::: func.arrowType.domain.labelledData
@ -110,9 +117,10 @@ object Transform extends Logging {
// Pre transform and inline the function
model <- funcToModelTree(func, preTransformer)
// Post transform the function
postModel = errorsCatcher.transform(model)
errorsModel = errorsCatcher.transform(model)
tracingModel <- tracing(errorsModel)
// Resolve topology
resolved <- Topology.resolve(postModel)
resolved <- Topology.resolve(tracingModel)
// Clear the tree
result = clear(resolved)
} yield FuncRes(

View File

@ -16,6 +16,7 @@ case class TransformConfig(
respFuncName: String = "response",
relayVarName: Option[String] = Some("-relay-"),
wrapWithXor: Boolean = true,
tracing: Option[TransformConfig.TracingConfig] = None,
constants: List[ConstantRaw] = Nil
) {
@ -29,3 +30,15 @@ case class TransformConfig(
val constantsList: List[ConstantRaw] =
ConstantRaw.defaultConstants(relayVarName) ::: constants
}
object TransformConfig {
final case class TracingConfig(
serviceId: String = "tracingSrv",
serviceFuncName: String = "tracingEvent"
)
object TracingConfig {
lazy val default = TracingConfig()
}
}

View File

@ -0,0 +1,34 @@
package aqua.model.transform.funcop
import aqua.model.OpModel
import cats.data.Chain
import cats.free.Cofree
import cats.Eval
/**
* Base type for [[OpModel.Tree]] -> [[OpModel.Tree]] transformation
*/
trait OpTransform {
/**
* Transformation step
* (node, child results) => node result
*/
def folder: OpTransform.OpFolder
def apply(tree: OpModel.Tree): Eval[OpModel.Tree] =
Cofree.cata[Chain, OpModel, OpModel.Tree](tree)((op, children) =>
folder
.lift(op, children)
.getOrElse(
Eval.now(
op.wrap(children.toList: _*)
)
)
)
}
object OpTransform {
type OpFolder = PartialFunction[(OpModel, Chain[OpModel.Tree]), Eval[OpModel.Tree]]
}

View File

@ -0,0 +1,86 @@
package aqua.model.transform.funcop
import cats.data.Chain
import cats.Eval
import aqua.model.{
CallModel,
CallServiceModel,
LiteralModel,
MetaModel,
OpModel,
SeqModel,
ValueModel
}
import aqua.model.transform.pre.InitPeerCallable
import aqua.model.ParModel
import aqua.model.DetachModel
import aqua.model.transform.TransformConfig.TracingConfig
final case class Tracing(
enabledConfig: Option[TracingConfig],
initCallable: InitPeerCallable
) extends OpTransform {
import Tracing.*
private def getChild(children: Chain[OpModel.Tree]): OpModel.Tree =
children.headOption
.filter(_ => children.length == 1)
.getOrElse(
SeqModel.wrap(children.toList: _*)
)
override def folder: OpTransform.OpFolder = {
case (MetaModel.CallArrowModel(arrowName), children) =>
val child = getChild(children)
Eval.now(
enabledConfig
.map(traceCallModel(_, arrowName))
.fold(child)(traceCall =>
/* seq:
detach: call tracing enter
<call-arrow-code>
detach: call tracing exit */
SeqModel.wrap(
DetachModel.wrap(
initCallable.onInitPeer.wrap(
traceCall(Event.Enter)
)
),
child,
DetachModel.wrap(
initCallable.onInitPeer.wrap(
traceCall(Event.Exit)
)
)
)
)
)
}
}
object Tracing {
enum Event {
case Enter, Exit
def toArg: ValueModel = LiteralModel.quote(this match {
case Enter => "enter"
case Exit => "exit"
})
}
def traceCallModel(config: TracingConfig, arrowName: String)(
event: Event
): OpModel.Tree =
CallServiceModel(
LiteralModel.quote(config.serviceId),
config.serviceFuncName,
CallModel(
args = List(LiteralModel.quote(arrowName), event.toArg),
exportTo = Nil
)
).leaf
}

View File

@ -2,7 +2,7 @@ package aqua.model.transform.pre
import aqua.model.FuncArrow
import aqua.model.ArgsCall
import aqua.raw.ops.{Call, CallArrowRawTag, FuncOp, RawTag, SeqTag}
import aqua.raw.ops.{Call, CallArrowRawTag, RawTag, SeqTag}
import aqua.raw.value.{ValueRaw, VarRaw}
import aqua.types.*
import cats.syntax.show.*

View File

@ -16,16 +16,16 @@ trait TreeNodeCompanion[T <: TreeNode[T]] {
type Tree = Cofree[Chain, T]
private def showOffset(what: Tree, offset: Int): String = {
val spaces = " " * offset
val spaces = "| " * offset
spaces + what.head.show + what.tail.map {
case ch if ch.nonEmpty =>
" :\n" + ch.toList.map(showOffset(_, offset + 1)).mkString("") + "\n"
" :\n" + ch.toList.map(showOffset(_, offset + 1)).mkString("")
case ch => "\n"
}.value
}
private def showDiffOffset(what: (Tree, Tree), offset: Int): String = {
val spaces = " " * offset
val spaces = "| " * offset
val head =
if (what._1.head == what._2.head) what._1.head.show
else {

View File

@ -23,6 +23,7 @@ import cats.Reducible
import cats.data.Validated.{Invalid, Valid}
import cats.kernel.Monoid
import cats.syntax.applicative.*
import cats.syntax.option.*
import cats.syntax.apply.*
import cats.syntax.flatMap.*
import cats.syntax.functor.*
@ -59,9 +60,7 @@ class RawSemantics[S[_]](implicit p: Picker[RawContext]) extends Semantics[S, Ra
.map { case (state, ctx) =>
NonEmptyChain
.fromChain(state.errors)
.fold[ValidatedNec[SemanticError[S], RawContext]](
Valid(ctx)
)(Invalid(_))
.toInvalid(ctx)
}
// TODO: return as Eval
.value

View File

@ -1,7 +1,7 @@
package aqua.semantics.expr.func
import aqua.raw.Raw
import aqua.raw.ops.{AbilityIdTag, FuncOp}
import aqua.raw.ops.AbilityIdTag
import aqua.parser.expr.func.AbilityIdExpr
import aqua.semantics.Prog
import aqua.semantics.rules.ValuesAlgebra

View File

@ -133,13 +133,15 @@ class ArrowSem[S[_]](val expr: ArrowExpr[S]) extends AnyVal {
(
SeqTag.wrap(
bodyAcc :: CanonicalizeTag(
bodyAcc,
CanonicalizeTag(
VarRaw(streamName, streamType),
Call.Export(canonReturnVar.name, canonReturnVar.`type`)
).leaf :: FlattenTag(
).leaf,
FlattenTag(
canonReturnVar,
returnVar.name
).leaf :: Nil: _*
).leaf
),
returnAcc :+ returnVar,
idx + 1

View File

@ -3,7 +3,7 @@ package aqua.semantics.expr.func
import aqua.raw.Raw
import aqua.types.ArrowType
import aqua.raw.value.CallArrowRaw
import aqua.raw.ops.{AssignmentTag, ClosureTag, FuncOp}
import aqua.raw.ops.{AssignmentTag, ClosureTag}
import aqua.parser.expr.func.AssignmentExpr
import aqua.raw.arrow.FuncRaw
import aqua.semantics.Prog

View File

@ -16,34 +16,42 @@ import cats.syntax.flatMap.*
import cats.syntax.functor.*
import cats.syntax.traverse.*
import cats.{Monad, Traverse}
import aqua.raw.value.CallArrowRaw
class CallArrowSem[S[_]](val expr: CallArrowExpr[S]) extends AnyVal {
import expr.*
private def getExports[Alg[_]: Monad](callArrow: CallArrowRaw)(implicit
N: NamesAlgebra[S, Alg],
T: TypesAlgebra[S, Alg]
): Alg[List[Call.Export]] =
(variables zip callArrow.baseType.codomain.toList).traverse { case (v, t) =>
N.read(v, mustBeDefined = false).flatMap {
case Some(stream @ StreamType(st)) =>
T.ensureTypeMatches(v, st, t).as(Call.Export(v.value, stream))
case _ =>
N.define(v, t).as(Call.Export(v.value, t))
}
}
private def toModel[Alg[_]: Monad](implicit
N: NamesAlgebra[S, Alg],
A: AbilitiesAlgebra[S, Alg],
T: TypesAlgebra[S, Alg],
V: ValuesAlgebra[S, Alg]
): Alg[Option[FuncOp]] = V.callArrowToRaw(callArrow).flatMap {
case None => None.pure[Alg]
case Some(car) =>
): Alg[Option[FuncOp]] = for {
callArrowRaw <- V.callArrowToRaw(callArrow)
maybeOp <- callArrowRaw.traverse(car =>
variables
.drop(car.baseType.codomain.length)
.headOption
.fold(
(variables zip car.baseType.codomain.toList).traverse { case (v, t) =>
N.read(v, mustBeDefined = false).flatMap {
case Some(stream @ StreamType(st)) =>
T.ensureTypeMatches(v, st, t).as(Call.Export(v.value, stream))
case _ =>
N.define(v, t).as(Call.Export(v.value, t))
}
}
)(T.expectNoExport(_).as(Nil))
.map(maybeExport => Some(CallArrowRawTag(maybeExport, car).funcOpLeaf))
}
.fold(getExports(car))(
T.expectNoExport(_).as(Nil)
)
.map(maybeExports => CallArrowRawTag(maybeExports, car).funcOpLeaf)
)
} yield maybeOp
def program[Alg[_]: Monad](implicit
N: NamesAlgebra[S, Alg],

View File

@ -1,7 +1,7 @@
package aqua.semantics.expr.func
import aqua.raw.Raw
import aqua.raw.ops.{ClosureTag, FuncOp}
import aqua.raw.ops.ClosureTag
import aqua.raw.arrow.{ArrowRaw, FuncRaw}
import aqua.parser.expr.FuncExpr
import aqua.parser.expr.func.ClosureExpr

View File

@ -1,6 +1,6 @@
package aqua.semantics.expr.func
import aqua.raw.ops.{DeclareStreamTag, FuncOp}
import aqua.raw.ops.DeclareStreamTag
import aqua.parser.expr.func.DeclareStreamExpr
import aqua.raw.Raw
import aqua.raw.value.VarRaw

View File

@ -2,7 +2,7 @@ package aqua.semantics.expr.func
import aqua.parser.expr.func.JoinExpr
import aqua.raw.Raw
import aqua.raw.ops.{FuncOp, JoinTag}
import aqua.raw.ops.JoinTag
import aqua.semantics.Prog
import aqua.semantics.rules.ValuesAlgebra
import aqua.semantics.rules.types.TypesAlgebra

View File

@ -1,6 +1,6 @@
package aqua.semantics.expr.func
import aqua.raw.ops.{Call, FuncOp, PushToStreamTag}
import aqua.raw.ops.{Call, PushToStreamTag}
import aqua.parser.expr.func.PushToStreamExpr
import aqua.parser.lexer.Token
import aqua.raw.Raw

View File

@ -1,6 +1,6 @@
package aqua.semantics.expr.func
import aqua.raw.ops.{FuncOp, ReturnTag}
import aqua.raw.ops.ReturnTag
import aqua.parser.expr.func.ReturnExpr
import aqua.raw.Raw
import aqua.semantics.Prog

View File

@ -14,6 +14,7 @@ import cats.syntax.apply.*
import cats.syntax.flatMap.*
import cats.syntax.functor.*
import cats.syntax.traverse.*
import cats.syntax.option.*
import cats.instances.list.*
import cats.data.{NonEmptyList, NonEmptyMap}
@ -209,18 +210,18 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
}
def callArrowToRaw(ca: CallArrowToken[S]): Alg[Option[CallArrowRaw]] =
ca.ability
def callArrowToRaw(ca: CallArrowToken[S]): Alg[Option[CallArrowRaw]] = for {
raw <- ca.ability
.fold(
N.readArrow(ca.funcName)
.map(
_.map(
_.map(bt =>
CallArrowRaw(
None,
ca.funcName.value,
Nil,
_,
None
ability = None,
name = ca.funcName.value,
arguments = Nil,
baseType = bt,
serviceId = None
)
)
)
@ -228,49 +229,48 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
(A.getArrow(ab, ca.funcName), A.getServiceId(ab)).mapN {
case (Some(at), Right(sid)) =>
// Service call, actually
Some(
CallArrowRaw(
Some(ab.value),
ca.funcName.value,
Nil,
at,
Some(sid)
)
)
CallArrowRaw(
ability = Some(ab.value),
name = ca.funcName.value,
arguments = Nil,
baseType = at,
serviceId = Some(sid)
).some
case (Some(at), Left(true)) =>
// Ability function call, actually
Some(
CallArrowRaw(
Some(ab.value),
ca.funcName.value,
Nil,
at,
None
)
)
case _ => None
CallArrowRaw(
ability = Some(ab.value),
name = ca.funcName.value,
arguments = Nil,
baseType = at,
serviceId = None
).some
case _ => none
}
)
.flatMap {
case Some(r) =>
val arr = r.baseType
T.checkArgumentsNumber(ca.funcName, arr.domain.length, ca.args.length).flatMap {
case false => Option.empty[CallArrowRaw].pure[Alg]
case true =>
(ca.args zip arr.domain.toList).traverse { case (tkn, tp) =>
valueToRaw(tkn).flatMap {
case Some(v) => T.ensureTypeMatches(tkn, tp, v.`type`).map(Option.when(_)(v))
case None => None.pure[Alg]
}
}.map(_.toList.flatten).map {
case args: List[ValueRaw] if args.length == arr.domain.length =>
Some(r.copy(arguments = args))
case _ => None
}
}
case None => Option.empty[CallArrowRaw].pure[Alg]
}
result <- raw.flatTraverse(r =>
val arr = r.baseType
for {
argsCheck <- T.checkArgumentsNumber(ca.funcName, arr.domain.length, ca.args.length)
args <- Option
.when(argsCheck)(ca.args zip arr.domain.toList)
.traverse(
_.flatTraverse { case (tkn, tp) =>
for {
maybeValueRaw <- valueToRaw(tkn)
checked <- maybeValueRaw.flatTraverse(v =>
T.ensureTypeMatches(tkn, tp, v.`type`)
.map(Option.when(_)(v))
)
} yield checked.toList
}
)
result = args
.filter(_.length == arr.domain.length)
.map(args => r.copy(arguments = args))
} yield result
)
} yield result
def checkArguments(token: Token[S], arr: ArrowType, args: List[ValueToken[S]]): Alg[Boolean] =
// TODO: do we really need to check this?

View File

@ -169,7 +169,7 @@ object ScalarType {
}
case class LiteralType private (oneOf: Set[ScalarType], name: String) extends DataType {
override def toString: String = name
override def toString: String = s"$name:lt"
}
object LiteralType {