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
JavaScript
"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