preExecute method

  1. @override
FunctionCallPreExecutionResult? preExecute(
  1. DartBlockArbiter arbiter
)
override

Implementation

@override
FunctionCallPreExecutionResult? preExecute(DartBlockArbiter arbiter) {
  final DartBlockFunction? customFunction = arbiter.retrieveCustomFunction(
    customFunctionName,
  );
  if (customFunction == null) {
    throw UndefinedCustomFunctionException(customFunctionName);
  }

  if (customFunction.parameters.length != arguments.length) {
    throw CustomFunctionArgumentsCountException(
      customFunctionName,
      customFunction.parameters.length,
      arguments.length,
    );
  }
  List<VariableDeclarationStatement> parameterDeclarations = [];
  for (var (idx, argument) in arguments.indexed) {
    /// Get the concrete value in the preExecute step, as the variable scope
    /// (Environment) has not yet changed.
    final concreteValue = argument.getValue(arbiter);
    final expectedParameter = customFunction.parameters[idx];

    final (isCorrectType, givenWrongType) = arbiter
        .verifyDataTypeOfConcreteValue(
          expectedParameter.dataType,
          concreteValue,
        );
    if (isCorrectType) {
      /// Re-convert the concrete value back to a wrapper NeoValue object,
      /// based on the expected data type of the corresponding function parameter.
      DartBlockValue? passedValue;
      if (concreteValue != null) {
        switch (expectedParameter.dataType) {
          case DartBlockDataType.integerType:
          case DartBlockDataType.doubleType:
            if (concreteValue is num) {
              passedValue = DartBlockAlgebraicExpression.fromConstant(
                concreteValue,
              );
            }
            break;
          case DartBlockDataType.booleanType:
            if (concreteValue is bool) {
              passedValue = DartBlockBooleanExpression.fromConstant(
                concreteValue,
              );
            }
            break;
          case DartBlockDataType.stringType:
            if (concreteValue is String) {
              passedValue = DartBlockConcatenationValue.init([
                DartBlockStringValue.init(concreteValue),
              ]);
            }
            break;
        }
      }
      parameterDeclarations.add(
        VariableDeclarationStatement.init(
          expectedParameter.name,
          expectedParameter.dataType,

          /// Do NOT use argument here, as it will cause issues in certain cases, e.g.,
          /// if the value contains a NeoVariable which only exists in the current
          /// variable scope. Remember that the variable scope changes to a new,
          /// isolated one after preExecute, hence, we use passedValue here.
          passedValue,
        ),
      );
    } else {
      throw CustomFunctionMissingArgumentException(
        customFunctionName,
        expectedParameter,
        idx,
        givenWrongType!,
      );
    }
  }

  return FunctionCallPreExecutionResult(
    customFunction,
    parameterDeclarations,
  );
}