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 { export class AquaConfig {
constructor(logLevel: string, constants: string[], noXor: boolean, noRelay: boolean); constructor(
constructor(logLevel: string, constants: string[], noXor: boolean, noRelay: boolean, targetType: string); logLevel?: string,
constants?: string[],
noXor?: boolean,
noRelay?: boolean,
targetType?: string,
tracing?: boolean
);
logLevel?: string logLevel?: string
constants?: string[] constants?: string[]
noXor?: boolean noXor?: boolean
noRelay?: boolean noRelay?: boolean
targetType?: string targetType?: string
tracing?: boolean
} }
export class AquaFunction { export class AquaFunction {
@ -45,8 +52,8 @@ export class Path {
export class Call { export class Call {
constructor(functionCall: string, constructor(functionCall: string,
arguments: any, arguments: any,
input: Input | Path); input: Input | Path);
functionCall: string functionCall: string
arguments: any arguments: any

View File

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

View File

@ -9,10 +9,17 @@ case class AquaAPIConfig(
logLevel: String = "info", logLevel: String = "info",
constants: List[String] = Nil, constants: List[String] = Nil,
noXor: Boolean = false, noXor: Boolean = false,
noRelay: Boolean = false noRelay: Boolean = false,
tracing: Boolean = false
) { ) {
def getTransformConfig: TransformConfig = def getTransformConfig: TransformConfig = {
if (noRelay) TransformConfig(relayVarName = None, wrapWithXor = !noXor) val config = TransformConfig(
else TransformConfig(wrapWithXor = !noXor) 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.model.{FuncArrow, ValueModel, VarModel}
import aqua.parser.lexer.CallArrowToken import aqua.parser.lexer.CallArrowToken
import aqua.parser.lift.Span 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.raw.value.{LiteralRaw, ValueRaw, VarRaw}
import aqua.types.* import aqua.types.*
import cats.data.Validated.{invalid, invalidNec, invalidNel, validNec, validNel} import cats.data.Validated.{invalid, invalidNec, invalidNel, validNec, validNel}
@ -18,7 +18,7 @@ import cats.syntax.flatMap.*
import cats.syntax.partialOrder.* import cats.syntax.partialOrder.*
import cats.syntax.show.* import cats.syntax.show.*
import cats.syntax.traverse.* import cats.syntax.traverse.*
import cats.{Id, ~>} import cats.{~>, Id}
import scala.collection.immutable.SortedMap import scala.collection.immutable.SortedMap
import scala.concurrent.ExecutionContext import scala.concurrent.ExecutionContext

View File

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

View File

@ -144,6 +144,12 @@ object AppOpts {
.map(_ => true) .map(_ => true)
.withDefault(false) .withDefault(false)
val tracing: Opts[Boolean] =
Opts
.flag("trace", "Generate tace events calls")
.map(_ => true)
.withDefault(false)
val isOldFluenceJs: Opts[Boolean] = val isOldFluenceJs: Opts[Boolean] =
Opts Opts
.flagOption[String]( .flagOption[String](
@ -172,7 +178,7 @@ object AppOpts {
Opts Opts
.flag( .flag(
"scheduled", "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) .map(_ => true)
.withDefault(false) .withDefault(false)

View File

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

View File

@ -2,7 +2,13 @@ package aqua.model.inline
import aqua.model.inline.state.{Arrows, Counter, Exports, Mangler} import aqua.model.inline.state.{Arrows, Counter, Exports, Mangler}
import aqua.model.* 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.ops.*
import aqua.raw.value.* import aqua.raw.value.*
import aqua.types.{ArrayType, OptionType, StreamType} import aqua.types.{ArrayType, OptionType, StreamType}
@ -86,10 +92,10 @@ object RawValueInliner extends Logging {
case (vv, _) => case (vv, _) =>
FlattenModel(vv, name).leaf FlattenModel(vv, name).leaf
} }
}.map{ predo => }.map { predo =>
inline.mergeMode match inline.mergeMode match
case SeqMode => case SeqMode =>
SeqModel.wrap((inline.predo.toList ++ predo):_*) :: Nil SeqModel.wrap((inline.predo.toList ++ predo): _*) :: Nil
case ParMode => inline.predo.toList ::: predo 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 aqua.types.{ArrayType, ArrowType, BoxType, CanonStreamType, StreamType}
import cats.syntax.traverse.* import cats.syntax.traverse.*
import cats.syntax.applicative.* import cats.syntax.applicative.*
import cats.syntax.functor.*
import cats.syntax.option.*
import cats.instances.list.* import cats.instances.list.*
import cats.data.{Chain, State, StateT} import cats.data.{Chain, State, StateT}
import cats.syntax.show.*
import scribe.{log, Logging} import scribe.{log, Logging}
/** /**
@ -218,10 +221,10 @@ object TagInliner extends Logging {
collectionToModel(c, Some(assignTo)) collectionToModel(c, Some(assignTo))
case v => case v =>
valueToModel(v, false) valueToModel(v, false)
}).flatMap { cd => }).flatMap { case (model, prefix) =>
for { Exports[S]
_ <- Exports[S].resolved(assignTo, cd._1) .resolved(assignTo, model)
} yield None -> cd._2 .as(None -> prefix)
} }
case ClosureTag(arrow, detach) => case ClosureTag(arrow, detach) =>

View File

@ -1,7 +1,7 @@
package aqua.model.inline.raw package aqua.model.inline.raw
import aqua.model.inline.Inline.parDesugarPrefixOpt 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.{ArrowInliner, Inline, TagInliner}
import aqua.model.inline.RawValueInliner.{callToModel, valueToModel} import aqua.model.inline.RawValueInliner.{callToModel, valueToModel}
import aqua.model.inline.state.{Arrows, Exports, Mangler} 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) logger.trace(Console.YELLOW + s"Call arrow ${fn.funcName}" + Console.RESET)
callToModel(call, false).flatMap { case (cm, p) => callToModel(call, false).flatMap { case (cm, p) =>
ArrowInliner ArrowInliner
@ -56,13 +59,23 @@ object CallArrowRawInliner extends RawInliner[CallArrowRaw] with Logging {
.map { case (body, vars) => .map { case (body, vars) =>
vars -> Inline( vars -> Inline(
ListMap.empty, 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[S].arrows.flatMap(arrows =>
arrows.get(funcName) match { arrows.get(funcName) match {
case Some(fn) => case Some(fn) =>

View File

@ -9,6 +9,8 @@ import cats.syntax.show.*
import cats.data.{Chain, NonEmptyList, NonEmptyMap} import cats.data.{Chain, NonEmptyList, NonEmptyMap}
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
import aqua.raw.value.{CallArrowRaw, ValueRaw}
import aqua.raw.arrow.{ArrowRaw, FuncRaw}
class ArrowInlinerSpec extends AnyFlatSpec with Matchers { class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
@ -33,7 +35,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
model.equalsOrShowDiff( model.equalsOrShowDiff(
CallServiceModel( CallServiceModel(
LiteralModel("\"dumb_srv_id\"", LiteralType.string), LiteralModel.quote("dumb_srv_id"),
"dumb", "dumb",
CallModel(Nil, Nil) CallModel(Nil, Nil)
).leaf ).leaf
@ -113,14 +115,21 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
model.equalsOrShowDiff( model.equalsOrShowDiff(
RestrictionModel(streamVar.name, true).wrap( RestrictionModel(streamVar.name, true).wrap(
SeqModel.wrap( MetaModel
CanonicalizeModel(streamModel, CallModel.Export(canonModel.name, canonModel.`type`)).leaf, .CallArrowModel("cb")
CallServiceModel( .wrap(
LiteralModel("\"test-service\"", LiteralType.string), SeqModel.wrap(
"some-call", CanonicalizeModel(
CallModel(canonModel :: Nil, Nil) streamModel,
).leaf CallModel.Export(canonModel.name, canonModel.`type`)
) ).leaf,
CallServiceModel(
LiteralModel.quote("test-service"),
"some-call",
CallModel(canonModel :: Nil, Nil)
).leaf
)
)
) )
) should be(true) ) should be(true)
@ -130,7 +139,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
func stream-callback(cb: string -> ()): func stream-callback(cb: string -> ()):
records: *string records: *string
cb(records!) cb(records!)
*/ */
ignore /*"arrow inliner"*/ should "pass stream with gate to callback properly" in { ignore /*"arrow inliner"*/ should "pass stream with gate to callback properly" in {
val streamType = StreamType(ScalarType.string) val streamType = StreamType(ScalarType.string)
val streamVar = VarRaw("records", streamType) val streamVar = VarRaw("records", streamType)
@ -210,7 +219,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
model.equalsOrShowDiff( model.equalsOrShowDiff(
RestrictionModel(streamVar.name, true).wrap( RestrictionModel(streamVar.name, true).wrap(
CallServiceModel( CallServiceModel(
LiteralModel("\"test-service\"", LiteralType.string), LiteralModel.quote("test-service"),
"some-call", "some-call",
CallModel(streamModel :: Nil, Nil) CallModel(streamModel :: Nil, Nil)
).leaf ).leaf
@ -291,15 +300,19 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
model.equalsOrShowDiff( model.equalsOrShowDiff(
SeqModel.wrap( SeqModel.wrap(
CallServiceModel( MetaModel
LiteralModel("\"test-service\"", LiteralType.string), .CallArrowModel(innerName)
"get_records", .wrap(
CallModel(Nil, CallModel.Export(recordsModel.name, recordsModel.`type`) :: Nil) CallServiceModel(
).leaf, LiteralModel.quote("test-service"),
"get_records",
CallModel(Nil, CallModel.Export(recordsModel.name, recordsModel.`type`) :: Nil)
).leaf
),
SeqModel.wrap( SeqModel.wrap(
CanonicalizeModel(recordsModel, CallModel.Export(canonModel.name, canonType)).leaf, CanonicalizeModel(recordsModel, CallModel.Export(canonModel.name, canonType)).leaf,
CallServiceModel( CallServiceModel(
LiteralModel("\"callbackSrv\"", LiteralType.string), LiteralModel.quote("callbackSrv"),
"response", "response",
CallModel(canonModel :: Nil, Nil) CallModel(canonModel :: Nil, Nil)
).leaf ).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: data Prod:
value: string value: string
@ -322,6 +971,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
OpHa.identity(v) OpHa.identity(v)
*/ */
"arrow inliner" should "hold lambda" in { "arrow inliner" should "hold lambda" in {
val innerName = "inner"
// lambda that will be assigned to another variable // lambda that will be assigned to another variable
val objectVarLambda = val objectVarLambda =
@ -348,7 +998,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
// function where we assign object lambda to value and call service // function where we assign object lambda to value and call service
val inner = val inner =
FuncArrow( FuncArrow(
"inner", innerName,
SeqTag.wrap( SeqTag.wrap(
AssignmentTag( AssignmentTag(
objectVarLambda, objectVarLambda,
@ -399,18 +1049,22 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
model.equalsOrShowDiff( model.equalsOrShowDiff(
SeqModel.wrap( SeqModel.wrap(
CallServiceModel( CallServiceModel(
LiteralModel("\"getSrv\"", LiteralType.string), LiteralModel.quote("getSrv"),
"getObj", "getObj",
CallModel(Nil, CallModel.Export(objectVar.name, objectVar.`type`) :: Nil) CallModel(Nil, CallModel.Export(objectVar.name, objectVar.`type`) :: Nil)
).leaf, ).leaf,
SeqModel.wrap( MetaModel
FlattenModel(ValueModel.fromRaw(objectVarLambda), flattenObject.name).leaf, .CallArrowModel(innerName)
CallServiceModel( .wrap(
LiteralModel("\"callbackSrv\"", LiteralType.string), SeqModel.wrap(
"response", FlattenModel(ValueModel.fromRaw(objectVarLambda), flattenObject.name).leaf,
CallModel(ValueModel.fromRaw(flattenObject) :: Nil, Nil) CallServiceModel(
).leaf LiteralModel.quote("callbackSrv"),
) "response",
CallModel(ValueModel.fromRaw(flattenObject) :: Nil, Nil)
).leaf
)
)
) )
) should be(true) ) should be(true)
@ -421,6 +1075,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
join nodes[idx] join nodes[idx]
*/ */
"arrow inliner" should "not rename value in index array lambda" in { "arrow inliner" should "not rename value in index array lambda" in {
val innerName = "inner"
// lambda that will be assigned to another variable // lambda that will be assigned to another variable
val argArray = VarRaw( val argArray = VarRaw(
@ -453,7 +1108,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
// function where we assign object lambda to value and call service // function where we assign object lambda to value and call service
val inner = val inner =
FuncArrow( FuncArrow(
"inner", innerName,
JoinTag(NonEmptyList.one(arrIdx)).leaf, JoinTag(NonEmptyList.one(arrIdx)).leaf,
ArrowType( ArrowType(
ProductType.labelled( ProductType.labelled(
@ -495,12 +1150,12 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
model.equalsOrShowDiff( model.equalsOrShowDiff(
SeqModel.wrap( SeqModel.wrap(
CallServiceModel( CallServiceModel(
LiteralModel("\"getSrv\"", LiteralType.string), LiteralModel.quote("getSrv"),
"getArr", "getArr",
CallModel(Nil, CallModel.Export(argArray.name, argArray.`type`) :: Nil) CallModel(Nil, CallModel.Export(argArray.name, argArray.`type`) :: Nil)
).leaf, ).leaf,
CallServiceModel( CallServiceModel(
LiteralModel("\"getSrv\"", LiteralType.string), LiteralModel.quote("getSrv"),
"getIdx", "getIdx",
CallModel(Nil, CallModel.Export(idxVar.name, idxVar.`type`) :: Nil) CallModel(Nil, CallModel.Export(idxVar.name, idxVar.`type`) :: Nil)
).leaf ).leaf

View File

@ -24,7 +24,13 @@ class CopyInlinerSpec extends AnyFlatSpec with Matchers {
val length = FunctorRaw("length", ScalarType.u32) val length = FunctorRaw("length", ScalarType.u32)
val lengthValue = VarRaw("l", arrType).withProperty(length) 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 = val copyRaw =
IntoCopyRaw(structType, NonEmptyMap.of("field1" -> lengthValue, "field2" -> getField)) IntoCopyRaw(structType, NonEmptyMap.of("field1" -> lengthValue, "field2" -> getField))
@ -44,7 +50,7 @@ class CopyInlinerSpec extends AnyFlatSpec with Matchers {
ParModel.wrap( ParModel.wrap(
SeqModel.wrap( SeqModel.wrap(
FlattenModel(VarModel("l", arrType), "l_to_functor").leaf, 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( CallServiceModel(
"serv", "serv",

View File

@ -29,7 +29,7 @@ class MakeStructInlinerSpec extends AnyFlatSpec with Matchers {
"get_field", "get_field",
Nil, Nil,
ArrowType(NilType, UnlabeledConsType(ScalarType.string, NilType)), ArrowType(NilType, UnlabeledConsType(ScalarType.string, NilType)),
Option(LiteralRaw("\"serv\"", ScalarType.string)) Option(LiteralRaw.quote("serv"))
) )
val makeStruct = 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))) 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 override def baseType: Type = structType

View File

@ -1,7 +1,7 @@
package aqua.model package aqua.model
import aqua.raw.arrow.FuncRaw 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.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}

View File

@ -2,7 +2,7 @@ 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.{FuncOp, RawTag} import aqua.raw.ops.RawTag
import aqua.raw.value.ValueRaw import aqua.raw.value.ValueRaw
import aqua.types.{ArrowType, Type} 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 NoExecModel extends OpModel
sealed trait ForceExecModel extends OpModel sealed trait ForceExecModel extends OpModel
@ -95,9 +115,14 @@ case class MatchMismatchModel(left: ValueModel, right: ValueModel, shouldMatch:
left.usesVarNames ++ right.usesVarNames 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) override def restrictsVarNames: Set[String] = Set(item)
@ -142,9 +167,15 @@ case class CallServiceModel(serviceId: ValueModel, funcName: String, call: CallM
} }
object CallServiceModel { 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( CallServiceModel(
LiteralModel(s"\"$serviceId\"", ScalarType.string), LiteralModel.quote(serviceId),
funcName, funcName,
CallModel( CallModel(
args, args,

View File

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

View File

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

View File

@ -16,6 +16,7 @@ case class TransformConfig(
respFuncName: String = "response", respFuncName: String = "response",
relayVarName: Option[String] = Some("-relay-"), relayVarName: Option[String] = Some("-relay-"),
wrapWithXor: Boolean = true, wrapWithXor: Boolean = true,
tracing: Option[TransformConfig.TracingConfig] = None,
constants: List[ConstantRaw] = Nil constants: List[ConstantRaw] = Nil
) { ) {
@ -29,3 +30,15 @@ case class TransformConfig(
val constantsList: List[ConstantRaw] = val constantsList: List[ConstantRaw] =
ConstantRaw.defaultConstants(relayVarName) ::: constants 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.FuncArrow
import aqua.model.ArgsCall 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.raw.value.{ValueRaw, VarRaw}
import aqua.types.* import aqua.types.*
import cats.syntax.show.* import cats.syntax.show.*

View File

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

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@ package aqua.semantics.expr.func
import aqua.raw.Raw import aqua.raw.Raw
import aqua.types.ArrowType import aqua.types.ArrowType
import aqua.raw.value.CallArrowRaw 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.parser.expr.func.AssignmentExpr
import aqua.raw.arrow.FuncRaw import aqua.raw.arrow.FuncRaw
import aqua.semantics.Prog import aqua.semantics.Prog

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
package aqua.semantics.expr.func 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.expr.func.PushToStreamExpr
import aqua.parser.lexer.Token import aqua.parser.lexer.Token
import aqua.raw.Raw import aqua.raw.Raw

View File

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

View File

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