From a25694501f7291f9653628ef4f6ec7fa0468bd39 Mon Sep 17 00:00:00 2001 From: vadimkibana Date: Thu, 29 Aug 2024 13:28:06 +0200 Subject: [PATCH] support argument precedence in basic pretty printer --- .../__tests__/basic_pretty_printer.test.ts | 32 ++++++++++++++++++- .../src/pretty_print/basic_pretty_printer.ts | 29 ++++++++++++++++- .../pretty_print/wrapping_pretty_printer.ts | 1 + 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/packages/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts b/packages/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts index 0afb4e8e42ce4..d6509a1e1601b 100644 --- a/packages/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts +++ b/packages/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts @@ -211,7 +211,7 @@ describe('single line query', () => { }); }); - describe('binary expression expression', () => { + describe('binary expression', () => { test('arithmetic expression', () => { const { text } = reprint('ROW 1 + 2'); @@ -235,6 +235,36 @@ describe('single line query', () => { expect(text).toBe('FROM a | WHERE a LIKE "b"'); }); + + test('inserts brackets where necessary due precedence', () => { + const { text } = reprint('FROM a | WHERE (1 + 2) * 3'); + + expect(text).toBe('FROM a | WHERE (1 + 2) * 3'); + }); + + test('inserts brackets where necessary due precedence - 2', () => { + const { text } = reprint('FROM a | WHERE (1 + 2) * (3 - 4)'); + + expect(text).toBe('FROM a | WHERE (1 + 2) * (3 - 4)'); + }); + + test('inserts brackets where necessary due precedence - 3', () => { + const { text } = reprint('FROM a | WHERE (1 + 2) * (3 - 4) / (5 + 6 + 7)'); + + expect(text).toBe('FROM a | WHERE (1 + 2) * (3 - 4) / (5 + 6 + 7)'); + }); + + test('inserts brackets where necessary due precedence - 4', () => { + const { text } = reprint('FROM a | WHERE (1 + (1 + 2)) * ((3 - 4) / (5 + 6 + 7))'); + + expect(text).toBe('FROM a | WHERE (1 + 1 + 2) * (3 - 4) / (5 + 6 + 7)'); + }); + + test('inserts brackets where necessary due precedence - 5', () => { + const { text } = reprint('FROM a | WHERE (1 + (1 + 2)) * (((3 - 4) / (5 + 6 + 7)) + 1)'); + + expect(text).toBe('FROM a | WHERE (1 + 1 + 2) * ((3 - 4) / (5 + 6 + 7) + 1)'); + }); }); }); diff --git a/packages/kbn-esql-ast/src/pretty_print/basic_pretty_printer.ts b/packages/kbn-esql-ast/src/pretty_print/basic_pretty_printer.ts index 0aa3ccd608cc6..b6adf29a75cc9 100644 --- a/packages/kbn-esql-ast/src/pretty_print/basic_pretty_printer.ts +++ b/packages/kbn-esql-ast/src/pretty_print/basic_pretty_printer.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { binaryExpressionGroup } from '../ast/helpers'; import { ESQLAstCommand } from '../types'; import { ESQLAstExpressionNode, ESQLAstQueryNode, Visitor } from '../visitor'; import { LeafPrinter } from './leaf_printer'; @@ -135,10 +136,12 @@ export class BasicPrettyPrinter { .on('visitExpression', (ctx) => { return ''; }) + .on('visitSourceExpression', (ctx) => LeafPrinter.source(ctx.node)) .on('visitColumnExpression', (ctx) => LeafPrinter.column(ctx.node)) .on('visitLiteralExpression', (ctx) => LeafPrinter.literal(ctx.node)) .on('visitTimeIntervalLiteralExpression', (ctx) => LeafPrinter.timeInterval(ctx.node)) + .on('visitInlineCastExpression', (ctx) => { const value = ctx.value(); const wrapInBrackets = @@ -154,6 +157,7 @@ export class BasicPrettyPrinter { return `${valueFormatted}::${ctx.node.castType}`; }) + .on('visitListLiteralExpression', (ctx) => { let elements = ''; @@ -163,6 +167,7 @@ export class BasicPrettyPrinter { return `[${elements}]`; }) + .on('visitFunctionCallExpression', (ctx) => { const opts = this.opts; const node = ctx.node; @@ -183,7 +188,25 @@ export class BasicPrettyPrinter { case 'binary-expression': { operator = this.keyword(operator); - return `${ctx.visitArgument(0)} ${operator} ${ctx.visitArgument(1)}`; + const group = binaryExpressionGroup(ctx.node); + const [left, right] = ctx.arguments(); + const groupLeft = binaryExpressionGroup(left); + const groupRight = binaryExpressionGroup(right); + + let leftFormatted = ctx.visitArgument(0); + let rightFormatted = ctx.visitArgument(1); + + if (groupLeft && groupLeft < group) { + leftFormatted = `(${leftFormatted})`; + } + + if (groupRight && groupRight < group) { + rightFormatted = `(${rightFormatted})`; + } + + const formatted = `${leftFormatted} ${operator} ${rightFormatted}`; + + return formatted; } default: { if (opts.lowercaseFunctions) { @@ -200,9 +223,11 @@ export class BasicPrettyPrinter { } } }) + .on('visitRenameExpression', (ctx) => { return `${ctx.visitArgument(0)} ${this.keyword('AS')} ${ctx.visitArgument(1)}`; }) + .on('visitCommandOption', (ctx) => { const opts = this.opts; const option = opts.lowercaseOptions ? ctx.node.name : ctx.node.name.toUpperCase(); @@ -218,6 +243,7 @@ export class BasicPrettyPrinter { return optionFormatted; }) + .on('visitCommand', (ctx) => { const opts = this.opts; const cmd = opts.lowercaseCommands ? ctx.node.name : ctx.node.name.toUpperCase(); @@ -239,6 +265,7 @@ export class BasicPrettyPrinter { return cmdFormatted; }) + .on('visitQuery', (ctx) => { const opts = this.opts; const cmdSeparator = opts.multiline ? `\n${opts.pipeTab ?? ' '}| ` : ' | '; diff --git a/packages/kbn-esql-ast/src/pretty_print/wrapping_pretty_printer.ts b/packages/kbn-esql-ast/src/pretty_print/wrapping_pretty_printer.ts index 24381fbcda1a8..d4c2fe5cc20eb 100644 --- a/packages/kbn-esql-ast/src/pretty_print/wrapping_pretty_printer.ts +++ b/packages/kbn-esql-ast/src/pretty_print/wrapping_pretty_printer.ts @@ -332,6 +332,7 @@ export class WrappingPrettyPrinter { .on('visitRenameExpression', (ctx, inp: Input): Output => { const operator = this.keyword('AS'); + return this.visitBinaryExpression(ctx, operator, inp); })