UNPKG

rawsql-ts

Version:

[beta]High-performance SQL parser and AST analyzer written in TypeScript. Provides fast parsing and advanced transformation capabilities.

210 lines 10.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FunctionExpressionParser = void 0; const Lexeme_1 = require("../models/Lexeme"); const ValueComponent_1 = require("../models/ValueComponent"); const OverExpressionParser_1 = require("./OverExpressionParser"); const ValueParser_1 = require("./ValueParser"); const FullNameParser_1 = require("./FullNameParser"); const SelectQueryParser_1 = require("./SelectQueryParser"); class FunctionExpressionParser { /** * Parse ARRAY expressions - handles both ARRAY[...] (literal) and ARRAY(...) (query) syntax * @param lexemes Array of lexemes to parse * @param index Current parsing index * @returns Parsed array expression and new index */ static parseArrayExpression(lexemes, index) { let idx = index; // Check if this is array literal (ARRAY[...]) or function call (ARRAY(...)) if (idx + 1 < lexemes.length && (lexemes[idx + 1].type & Lexeme_1.TokenType.OpenBracket)) { idx++; const arg = ValueParser_1.ValueParser.parseArgument(Lexeme_1.TokenType.OpenBracket, Lexeme_1.TokenType.CloseBracket, lexemes, idx); idx = arg.newIndex; const value = new ValueComponent_1.ArrayExpression(arg.value); return { value, newIndex: idx }; } else if (idx + 1 < lexemes.length && (lexemes[idx + 1].type & Lexeme_1.TokenType.OpenParen)) { idx++; idx++; // Skip the opening parenthesis const arg = SelectQueryParser_1.SelectQueryParser.parseFromLexeme(lexemes, idx); idx = arg.newIndex; idx++; // Skip the closing parenthesis const value = new ValueComponent_1.ArrayQueryExpression(arg.value); return { value, newIndex: idx }; } throw new Error(`Invalid ARRAY syntax at index ${idx}, expected ARRAY[... or ARRAY(...)`); } static parseFromLexeme(lexemes, index) { let idx = index; const current = lexemes[idx]; if (current.value === "array") { return this.parseArrayExpression(lexemes, idx); } else if (current.value === "substring" || current.value === "overlay") { return this.parseKeywordFunction(lexemes, idx, [ { key: "from", required: false }, { key: "for", required: false } ]); } else if (current.value === "cast") { return this.parseKeywordFunction(lexemes, idx, [ { key: "as", required: true } ]); } else if (current.value === "trim") { return this.parseKeywordFunction(lexemes, idx, [ { key: "from", required: false } ]); } return this.parseFunctionCall(lexemes, idx); } static tryParseBinaryExpression(lexemes, index, left, allowAndOperator = true) { let idx = index; // If the next element is an operator, process it as a binary expression if (idx < lexemes.length && (lexemes[idx].type & Lexeme_1.TokenType.Operator)) { if (!allowAndOperator && lexemes[idx].value === "and") { // Handle special case for "and" operator return null; } const operator = lexemes[idx].value; idx++; // between if (operator === "between") { return this.parseBetweenExpression(lexemes, idx, left, false); } else if (operator === "not between") { return this.parseBetweenExpression(lexemes, idx, left, true); } // :: if (operator === "::") { const typeValue = this.parseTypeValue(lexemes, idx); idx = typeValue.newIndex; const exp = new ValueComponent_1.CastExpression(left, typeValue.value); return { value: exp, newIndex: idx }; } // Get the right-hand side value const rightResult = ValueParser_1.ValueParser.parseFromLexeme(lexemes, idx); idx = rightResult.newIndex; // Create binary expression const value = new ValueComponent_1.BinaryExpression(left, operator, rightResult.value); return { value, newIndex: idx }; } return null; } static parseBetweenExpression(lexemes, index, value, negated) { let idx = index; const lower = ValueParser_1.ValueParser.parseFromLexeme(lexemes, idx, false); idx = lower.newIndex; if (idx < lexemes.length && (lexemes[idx].type & Lexeme_1.TokenType.Operator) && lexemes[idx].value !== "and") { throw new Error(`Expected 'and' after 'between' at index ${idx}`); } idx++; const upper = ValueParser_1.ValueParser.parseFromLexeme(lexemes, idx); idx = upper.newIndex; const result = new ValueComponent_1.BetweenExpression(value, lower.value, upper.value, negated); return { value: result, newIndex: idx }; } static parseFunctionCall(lexemes, index) { let idx = index; // Parse namespaced function name (e.g., myschema.myfunc, dbo.util.myfunc) // Use FullNameParser to get namespaces and function name const fullNameResult = FullNameParser_1.FullNameParser.parseFromLexeme(lexemes, idx); const namespaces = fullNameResult.namespaces; const name = fullNameResult.name; idx = fullNameResult.newIndex; if (idx < lexemes.length && (lexemes[idx].type & Lexeme_1.TokenType.OpenParen)) { // General argument parsing const arg = ValueParser_1.ValueParser.parseArgument(Lexeme_1.TokenType.OpenParen, Lexeme_1.TokenType.CloseParen, lexemes, idx); idx = arg.newIndex; if (idx < lexemes.length && lexemes[idx].value === "over") { const over = OverExpressionParser_1.OverExpressionParser.parseFromLexeme(lexemes, idx); idx = over.newIndex; const value = new ValueComponent_1.FunctionCall(namespaces, name.name, arg.value, over.value); return { value, newIndex: idx }; } else { const value = new ValueComponent_1.FunctionCall(namespaces, name.name, arg.value, null); return { value, newIndex: idx }; } } else { throw new Error(`Expected opening parenthesis after function name '${name.name}' at index ${idx}`); } } static parseKeywordFunction(lexemes, index, keywords) { let idx = index; // Parse function name and namespaces at the beginning for consistent usage const fullNameResult = FullNameParser_1.FullNameParser.parseFromLexeme(lexemes, idx); const namespaces = fullNameResult.namespaces; const name = fullNameResult.name; idx = fullNameResult.newIndex; if (idx < lexemes.length && (lexemes[idx].type & Lexeme_1.TokenType.OpenParen)) { idx++; const input = ValueParser_1.ValueParser.parseFromLexeme(lexemes, idx); let arg = input.value; idx = input.newIndex; // Delegate to the standard function parser if parsing by comma if (idx < lexemes.length && (lexemes[idx].type & Lexeme_1.TokenType.Comma)) { return this.parseFunctionCall(lexemes, index); } // Check for required/optional keywords in function arguments for (const { key, required } of keywords) { if (idx < lexemes.length && (lexemes[idx].type & Lexeme_1.TokenType.Command) && lexemes[idx].value === key) { idx++; if (idx < lexemes.length && (lexemes[idx].type & Lexeme_1.TokenType.Type)) { const typeValue = this.parseTypeValue(lexemes, idx); arg = new ValueComponent_1.BinaryExpression(arg, key, typeValue.value); idx = typeValue.newIndex; } else { const right = ValueParser_1.ValueParser.parseFromLexeme(lexemes, idx); arg = new ValueComponent_1.BinaryExpression(arg, key, right.value); idx = right.newIndex; } } else if (required) { throw new Error(`Keyword '${key}' is required at index ${idx}`); } } if (idx < lexemes.length && (lexemes[idx].type & Lexeme_1.TokenType.CloseParen)) { idx++; // Use the previously parsed namespaces and function name for consistency if (idx < lexemes.length && lexemes[idx].value === "over") { idx++; const over = OverExpressionParser_1.OverExpressionParser.parseFromLexeme(lexemes, idx); idx = over.newIndex; const value = new ValueComponent_1.FunctionCall(namespaces, name.name, arg, over.value); return { value, newIndex: idx }; } else { const value = new ValueComponent_1.FunctionCall(namespaces, name.name, arg, null); return { value, newIndex: idx }; } } else { throw new Error(`Missing closing parenthesis for function '${name.name}' at index ${idx}`); } } else { throw new Error(`Missing opening parenthesis for function '${name.name}' at index ${idx}`); } } static parseTypeValue(lexemes, index) { let idx = index; const { namespaces, name, newIndex } = FullNameParser_1.FullNameParser.parseFromLexeme(lexemes, idx); idx = newIndex; if (idx < lexemes.length && (lexemes[idx].type & Lexeme_1.TokenType.OpenParen)) { const arg = ValueParser_1.ValueParser.parseArgument(Lexeme_1.TokenType.OpenParen, Lexeme_1.TokenType.CloseParen, lexemes, idx); idx = arg.newIndex; const value = new ValueComponent_1.TypeValue(namespaces, new ValueComponent_1.RawString(name.name), arg.value); return { value, newIndex: idx }; } else { const value = new ValueComponent_1.TypeValue(namespaces, new ValueComponent_1.RawString(name.name)); return { value, newIndex: idx }; } } } exports.FunctionExpressionParser = FunctionExpressionParser; //# sourceMappingURL=FunctionExpressionParser.js.map