function parseExpression()

in libs/designer-ui/src/lib/workflow/languageservice/workflowlanguageservice.ts [372:438]


function parseExpression(value: string, position: Position, templateFunctions: Record<string, FunctionDefinition>): ExpressionInfo | null {
  // parsing multi-line values
  if (position.lineNumber > 1) {
    // eslint-disable-next-line no-param-reassign
    value = value.split('\n')[position.lineNumber - 1];
  }
  const caretPosition = position.column - 1;

  const scanner = new ExpressionScanner(value, /*prefetch*/ false);

  let previousToken: ExpressionToken | undefined;
  let currentToken: ExpressionToken | undefined;
  const identifiersInfo: IdentifierTokenInfo[] = [];

  try {
    previousToken = currentToken = scanner.getNextToken();
    while (currentToken !== null && currentToken.type !== ExpressionTokenType.EndOfData) {
      const { type, startPosition, endPosition } = currentToken;

      if (caretPosition >= startPosition && caretPosition < endPosition) {
        break;
      }

      if (type === ExpressionTokenType.LeftParenthesis) {
        if (previousToken.type === ExpressionTokenType.Identifier) {
          identifiersInfo.push({
            name: previousToken.value,
            argumentsCovered: 0,
          });
        }
      } else if (type === ExpressionTokenType.Comma) {
        identifiersInfo[identifiersInfo.length - 1].argumentsCovered += 1;
      } else if (type === ExpressionTokenType.RightParenthesis) {
        identifiersInfo.pop();
      }

      previousToken = currentToken;
      currentToken = scanner.getNextToken();
    }
  } catch {
    // Note: Exceptions thrown by the scanner and proceed with recognized tokens.
  }

  if (!identifiersInfo.length) {
    return null;
  }

  const hasEmptyArgument =
    currentToken?.type === ExpressionTokenType.RightParenthesis && previousToken?.type === ExpressionTokenType.LeftParenthesis;
  const token = identifiersInfo.pop();
  if (!token) {
    return null;
  }
  const identifierName = token?.name;
  const argumentsCovered = token?.argumentsCovered;
  const templateFunction = getPropertyValue(templateFunctions, identifierName);

  if (!templateFunction) {
    return null;
  }

  return {
    templateFunction,
    argumentsCovered,
    hasEmptyArgument,
  };
}