All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pl.touk.nussknacker.engine.compile.nodecompilation.BuiltInNodeCompiler.scala Maven / Gradle / Ivy

The newest version!
package pl.touk.nussknacker.engine.compile.nodecompilation

import cats.data.Validated.{Invalid, Valid}
import cats.data.ValidatedNel
import cats.implicits.{catsSyntaxTuple2Semigroupal, toFoldableOps, toTraverseOps}
import cats.instances.list._
import pl.touk.nussknacker.engine.api.NodeId
import pl.touk.nussknacker.engine.api.context._
import pl.touk.nussknacker.engine.api.expression.ExpressionTypingInfo
import pl.touk.nussknacker.engine.api.parameter.ParameterName
import pl.touk.nussknacker.engine.api.typed.typing.{Typed, TypingResult, Unknown}
import pl.touk.nussknacker.engine.compile._
import pl.touk.nussknacker.engine.compile.nodecompilation.BaseComponentValidationHelper._
import pl.touk.nussknacker.engine.compile.nodecompilation.BuiltInNodeCompiler._
import pl.touk.nussknacker.engine.compile.nodecompilation.NodeCompiler.NodeCompilationResult
import pl.touk.nussknacker.engine.compiledgraph
import pl.touk.nussknacker.engine.expression.parse.{CompiledExpression, TypedExpression}
import pl.touk.nussknacker.engine.graph.expression.NodeExpressionId.DefaultExpressionIdParamName
import pl.touk.nussknacker.engine.graph.expression._
import pl.touk.nussknacker.engine.graph.node
import pl.touk.nussknacker.engine.graph.node._

class BuiltInNodeCompiler(expressionCompiler: ExpressionCompiler) {

  def compileVariable(variable: Variable, ctx: ValidationContext)(
      implicit nodeId: NodeId
  ): NodeCompilationResult[CompiledExpression] = {
    val (validTypedExpression, nodeCompilation) =
      compileExpression(
        variable.value,
        ctx,
        expectedType = Unknown,
        paramName = DefaultExpressionIdParamName,
        outputVar = Some(OutputVar.variable(variable.varName))
      )

    val additionalValidationResult = validateVariableValue(validTypedExpression, DefaultExpressionIdParamName)

    combineErrors(nodeCompilation, additionalValidationResult)
  }

  def compileFilter(filter: Filter, ctx: ValidationContext)(
      implicit nodeId: NodeId
  ): NodeCompilationResult[CompiledExpression] = {
    val (validTypedExpression, nodeCompilation) =
      compileExpression(
        filter.expression,
        ctx,
        expectedType = Typed[Boolean],
        paramName = DefaultExpressionIdParamName,
        outputVar = None
      )

    val additionalValidationResult = validateBoolean(validTypedExpression, DefaultExpressionIdParamName)

    combineErrors(nodeCompilation, additionalValidationResult)
  }

  def compileSwitch(
      expressionRaw: Option[(String, Expression)],
      choices: List[(String, Expression)],
      ctx: ValidationContext
  )(
      implicit nodeId: NodeId
  ): NodeCompilationResult[(Option[CompiledExpression], List[CompiledExpression])] = {

    // the frontend uses empty string to delete deprecated expression.
    val expression = expressionRaw.filterNot(_._1.isEmpty)

    val expressionCompilation = expression.map { case (output, expression) =>
      compileExpression(
        expr = expression,
        ctx = ctx,
        expectedType = Unknown,
        paramName = NodeExpressionId.DefaultExpressionIdParamName,
        outputVar = Some(OutputVar.switch(output))
      )._2
    }
    val objExpression = expressionCompilation.map(_.compiledObject).sequence

    val caseCtx = expressionCompilation.flatMap(_.validationContext.toOption).getOrElse(ctx)

    val (additionalValidations, caseExpressions) = choices.map { case (outEdge, caseExpr) =>
      val outEdgeParamName = ParameterName(outEdge)
      val (validTypedExpression, nodeCompilation) =
        compileExpression(caseExpr, caseCtx, Typed[Boolean], outEdgeParamName, None)
      val validation     = validateBoolean(validTypedExpression, outEdgeParamName)
      val caseExpression = nodeCompilation
      (validation, caseExpression)
    }.unzip

    val expressionTypingInfos = caseExpressions
      .map(_.expressionTypingInfo)
      .foldLeft(expressionCompilation.map(_.expressionTypingInfo).getOrElse(Map.empty)) {
        _ ++ _
      }

    val objCases = caseExpressions.map(_.compiledObject).sequence

    val compilationResult = NodeCompilationResult(
      expressionTypingInfos,
      None,
      expressionCompilation.map(_.validationContext).getOrElse(Valid(ctx)),
      objExpression.product(objCases),
      expressionCompilation.flatMap(_.expressionType)
    )

    combineErrors(compilationResult, additionalValidations.combineAll)
  }

  def compileFields(
      fields: List[pl.touk.nussknacker.engine.graph.variable.Field],
      ctx: ValidationContext,
      outputVar: Option[OutputVar]
  )(implicit nodeId: NodeId): NodeCompilationResult[List[compiledgraph.variable.Field]] = {

    val (compiledRecord, indexedFields) = {
      val compiledFields = fields.zipWithIndex.map { case (field, index) =>
        val compiledField = expressionCompiler
          .compile(field.expression, Some(ParameterName(node.recordValueFieldName(index))), ctx, Unknown)
          .map(result =>
            CompiledIndexedRecordField(compiledgraph.variable.Field(field.name, result.expression), index, result)
          )
        val indexedKeys = IndexedRecordKey(field.name, index)
        (compiledField, indexedKeys)
      }
      (compiledFields.map(_._1).sequence, compiledFields.map(_._2))
    }

    val typedObject = compiledRecord match {
      case Valid(fields) =>
        Typed.record(fields.map(f => (f.field.name, typedExprToTypingResult(Some(f.typedExpression)))))
      case Invalid(_) => Unknown
    }

    val fieldsTypingInfo: Map[String, ExpressionTypingInfo] = compiledRecord match {
      case Valid(fields) =>
        fields.flatMap(f => typedExprToTypingInfo(Some(f.typedExpression), ParameterName(f.field.name))).toMap
      case Invalid(_) => Map.empty
    }

    val compiledFields = compiledRecord.map(_.map(_.field))

    val compilationResult = NodeCompilationResult(
      expressionTypingInfo = fieldsTypingInfo,
      parameters = None,
      validationContext = outputVar.map(ctx.withVariable(_, typedObject)).getOrElse(Valid(ctx)),
      compiledObject = compiledFields,
      expressionType = Some(typedObject)
    )

    val additionalValidationResult = RecordValidator.validate(compiledRecord, indexedFields)

    combineErrors(compilationResult, additionalValidationResult)
  }

  private def compileExpression(
      expr: Expression,
      ctx: ValidationContext,
      expectedType: TypingResult,
      paramName: ParameterName,
      outputVar: Option[OutputVar]
  )(
      implicit nodeId: NodeId
  ): (ValidatedNel[ProcessCompilationError, TypedExpression], NodeCompilationResult[CompiledExpression]) = {
    val validTypedExpression = expressionCompiler
      .compile(expr, Some(paramName), ctx, expectedType)

    val typingResult = typedExprToTypingResult(validTypedExpression.toOption)

    val nodeCompilation: NodeCompilationResult[CompiledExpression] = NodeCompilationResult(
      expressionTypingInfo = typedExprToTypingInfo(validTypedExpression.toOption, paramName),
      parameters = None,
      validationContext = outputVar.map(ctx.withVariable(_, typingResult)).getOrElse(Valid(ctx)),
      compiledObject = validTypedExpression.map(_.expression),
      expressionType = Some(typingResult)
    )

    (validTypedExpression, nodeCompilation)

  }

}

object BuiltInNodeCompiler {

  private def typedExprToTypingResult(expr: Option[TypedExpression]) = {
    expr.map(_.returnType).getOrElse(Unknown)
  }

  private def typedExprToTypingInfo(expr: Option[TypedExpression], parameterName: ParameterName) = {
    expr.map(te => (parameterName.value, te.typingInfo)).toMap
  }

  private def combineErrors[T](
      compilationResult: NodeCompilationResult[T],
      additionalValidationResult: ValidatedNel[ProcessCompilationError, Unit]
  ): NodeCompilationResult[T] = {
    val newCompiledObject = (compilationResult.compiledObject, additionalValidationResult).mapN { case (result, _) =>
      result
    }
    compilationResult.copy(compiledObject = newCompiledObject)
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy