UNPKG

rawsql-ts

Version:

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

213 lines 11.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CommandExpressionParser = void 0; const Lexeme_1 = require("../models/Lexeme"); const ValueComponent_1 = require("../models/ValueComponent"); const ValueParser_1 = require("./ValueParser"); class CommandExpressionParser { static parseFromLexeme(lexemes, index) { let idx = index; const current = lexemes[idx]; if (current.value === "case") { // Capture CASE keyword comments before consuming const caseKeywordComments = current.comments; const caseKeywordPositionedComments = current.positionedComments; idx++; return this.parseCaseExpression(lexemes, idx, caseKeywordComments, caseKeywordPositionedComments); } else if (current.value === "case when") { // Capture CASE WHEN keyword comments before consuming const caseWhenKeywordComments = current.comments; const caseWhenKeywordPositionedComments = current.positionedComments; idx++; return this.parseCaseWhenExpression(lexemes, idx, caseWhenKeywordComments, caseWhenKeywordPositionedComments); } return this.parseModifierUnaryExpression(lexemes, idx); } static parseModifierUnaryExpression(lexemes, index) { let idx = index; // Check for modifier unary expression if (idx < lexemes.length && (lexemes[idx].type & Lexeme_1.TokenType.Command)) { const command = lexemes[idx].value; idx++; const result = ValueParser_1.ValueParser.parseFromLexeme(lexemes, idx); return { value: new ValueComponent_1.UnaryExpression(command, result.value), newIndex: result.newIndex }; } throw new Error(`Invalid modifier unary expression at index ${idx}, Lexeme: ${lexemes[idx].value}`); } static parseCaseExpression(lexemes, index, caseKeywordComments, caseKeywordPositionedComments) { let idx = index; const condition = ValueParser_1.ValueParser.parseFromLexeme(lexemes, idx); idx = condition.newIndex; const switchCaseResult = this.parseSwitchCaseArgument(lexemes, idx, []); idx = switchCaseResult.newIndex; // Create CASE expression const result = new ValueComponent_1.CaseExpression(condition.value, switchCaseResult.value); // Assign CASE keyword comments to the CaseExpression (positioned comments only for unified spec) if (caseKeywordPositionedComments && caseKeywordPositionedComments.length > 0) { result.positionedComments = caseKeywordPositionedComments; } else if (caseKeywordComments && caseKeywordComments.length > 0) { // Convert legacy comments to positioned comments for unified spec result.positionedComments = [CommandExpressionParser.convertLegacyToPositioned(caseKeywordComments, 'before')]; } return { value: result, newIndex: idx }; } static parseCaseWhenExpression(lexemes, index, caseWhenKeywordComments, caseWhenKeywordPositionedComments) { let idx = index; // Parse the first WHEN clause const casewhenResult = this.parseCaseConditionValuePair(lexemes, idx); idx = casewhenResult.newIndex; // Add the initial WHEN-THEN pair to the list const caseWhenList = [casewhenResult.value]; // Process remaining WHEN-ELSE-END parts const switchCaseResult = this.parseSwitchCaseArgument(lexemes, idx, caseWhenList); idx = switchCaseResult.newIndex; // Create CASE expression with condition null (uses WHEN conditions instead of a simple CASE) const result = new ValueComponent_1.CaseExpression(null, switchCaseResult.value); // Assign CASE WHEN keyword comments to the CaseExpression (positioned comments only for unified spec) if (caseWhenKeywordPositionedComments && caseWhenKeywordPositionedComments.length > 0) { result.positionedComments = caseWhenKeywordPositionedComments; } else if (caseWhenKeywordComments && caseWhenKeywordComments.length > 0) { // Convert legacy comments to positioned comments for unified spec result.positionedComments = [CommandExpressionParser.convertLegacyToPositioned(caseWhenKeywordComments, 'before')]; } return { value: result, newIndex: idx }; } // parseSwitchCaseArgument method processes the WHEN, ELSE, and END clauses of a CASE expression. static parseSwitchCaseArgument(lexemes, index, initialWhenThenList) { let idx = index; const whenThenList = [...initialWhenThenList]; // Parse all WHEN clauses idx = this.parseAdditionalWhenClauses(lexemes, idx, whenThenList); // Parse optional ELSE clause const { elseValue, elseComments, newIndex: elseIndex } = this.parseElseClause(lexemes, idx); idx = elseIndex; // Parse required END clause const { endComments, newIndex: endIndex } = this.parseEndClause(lexemes, idx); idx = endIndex; if (whenThenList.length === 0) { throw new Error(`The CASE expression requires at least one WHEN clause (index ${idx})`); } // Create SwitchCaseArgument and apply comments directly const switchCaseArg = new ValueComponent_1.SwitchCaseArgument(whenThenList, elseValue); this.applySwitchCaseComments(switchCaseArg, elseComments, endComments); return { value: switchCaseArg, newIndex: idx }; } // Parse additional WHEN clauses static parseAdditionalWhenClauses(lexemes, index, whenThenList) { let idx = index; while (idx < lexemes.length && this.isCommandWithValue(lexemes[idx], "when")) { idx++; const whenResult = this.parseCaseConditionValuePair(lexemes, idx); idx = whenResult.newIndex; whenThenList.push(whenResult.value); } return idx; } // Parse optional ELSE clause static parseElseClause(lexemes, index) { let elseValue = null; let elseComments = null; let idx = index; if (idx < lexemes.length && this.isCommandWithValue(lexemes[idx], "else")) { // Extract comments from ELSE keyword before consuming elseComments = this.extractKeywordComments(lexemes[idx]); idx++; const elseResult = ValueParser_1.ValueParser.parseFromLexeme(lexemes, idx); elseValue = elseResult.value; idx = elseResult.newIndex; } return { elseValue, elseComments, newIndex: idx }; } // Parse required END clause static parseEndClause(lexemes, index) { let idx = index; let endComments = null; if (idx < lexemes.length && this.isCommandWithValue(lexemes[idx], "end")) { // Extract comments from END keyword before consuming endComments = this.extractKeywordComments(lexemes[idx]); idx++; } else { throw new Error(`The CASE expression requires 'end' keyword at the end (index ${idx})`); } return { endComments, newIndex: idx }; } // Extract comments from a keyword token static extractKeywordComments(token) { return { legacy: token.comments, positioned: token.positionedComments }; } // Apply comments to SwitchCaseArgument directly (no collection then assignment) static applySwitchCaseComments(switchCaseArg, elseComments, endComments) { const allPositionedComments = []; const allLegacyComments = []; // Process ELSE comments directly if ((elseComments === null || elseComments === void 0 ? void 0 : elseComments.positioned) && elseComments.positioned.length > 0) { allPositionedComments.push(...elseComments.positioned); } if ((elseComments === null || elseComments === void 0 ? void 0 : elseComments.legacy) && elseComments.legacy.length > 0) { allLegacyComments.push(...elseComments.legacy); } // Process END comments directly if ((endComments === null || endComments === void 0 ? void 0 : endComments.positioned) && endComments.positioned.length > 0) { allPositionedComments.push(...endComments.positioned); } if ((endComments === null || endComments === void 0 ? void 0 : endComments.legacy) && endComments.legacy.length > 0) { allLegacyComments.push(...endComments.legacy); } // Apply positioned comments directly, or convert legacy comments if (allPositionedComments.length > 0) { switchCaseArg.positionedComments = allPositionedComments; } else if (allLegacyComments.length > 0) { switchCaseArg.positionedComments = [CommandExpressionParser.convertLegacyToPositioned(allLegacyComments, 'after')]; } } // Helper method: Check if a lexeme is a Command token with the specified value static isCommandWithValue(lexeme, value) { return ((lexeme.type & Lexeme_1.TokenType.Command) !== 0) && lexeme.value === value; } static parseCaseConditionValuePair(lexemes, index) { let idx = index; const condition = ValueParser_1.ValueParser.parseFromLexeme(lexemes, idx); idx = condition.newIndex; // Check for the existence of the THEN keyword if (idx >= lexemes.length || !(lexemes[idx].type & Lexeme_1.TokenType.Command) || lexemes[idx].value !== "then") { throw new Error(`Expected 'then' after WHEN condition at index ${idx}`); } // Capture THEN keyword comments before consuming const thenKeywordComments = lexemes[idx].comments; const thenKeywordPositionedComments = lexemes[idx].positionedComments; idx++; // Skip the THEN keyword // Parse the value after THEN const value = ValueParser_1.ValueParser.parseFromLexeme(lexemes, idx); idx = value.newIndex; const keyValuePair = new ValueComponent_1.CaseKeyValuePair(condition.value, value.value); // Store THEN keyword comments on the CaseKeyValuePair // Store THEN keyword comments - unified spec: positioned comments only if (thenKeywordPositionedComments && thenKeywordPositionedComments.length > 0) { keyValuePair.positionedComments = thenKeywordPositionedComments; } else if (thenKeywordComments && thenKeywordComments.length > 0) { // Convert legacy comments to positioned comments for unified spec keyValuePair.positionedComments = [CommandExpressionParser.convertLegacyToPositioned(thenKeywordComments, 'after')]; } return { value: keyValuePair, newIndex: idx }; } /** * Convert legacy comments to positioned comments format */ static convertLegacyToPositioned(legacyComments, position = 'before') { return { position, comments: legacyComments }; } } exports.CommandExpressionParser = CommandExpressionParser; //# sourceMappingURL=CommandExpressionParser.js.map