mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-17 23:47:12 +00:00
chore: updated grammer for value, added parsetree for finding current context
This commit is contained in:
parent
e2498863e7
commit
3da7ce5f03
@ -14,10 +14,10 @@ import { Color } from '@signozhq/design-tokens';
|
||||
import { copilot } from '@uiw/codemirror-theme-copilot';
|
||||
import CodeMirror, {
|
||||
EditorView,
|
||||
Extension,
|
||||
keymap,
|
||||
Extension,
|
||||
} from '@uiw/react-codemirror';
|
||||
import { Button, Card, Collapse, Popover, Tag } from 'antd';
|
||||
import { Button, Card, Collapse, Popover, Space, Tag, Typography } from 'antd';
|
||||
import { getKeySuggestions } from 'api/querySuggestions/getKeySuggestions';
|
||||
import { getValueSuggestions } from 'api/querySuggestions/getValueSuggestion';
|
||||
import cx from 'classnames';
|
||||
@ -196,7 +196,6 @@ function QuerySearch({
|
||||
return op.toUpperCase() === 'IN' || op.toUpperCase() === 'NOT IN';
|
||||
};
|
||||
|
||||
// Helper function to format value based on operator type and value type
|
||||
const formatValueForOperator = (
|
||||
value: string,
|
||||
operatorToken: string | undefined,
|
||||
@ -215,7 +214,10 @@ function QuerySearch({
|
||||
|
||||
// If we're already inside bracket list for IN operator and it's a string value
|
||||
// just wrap in quotes but not brackets (we're already in brackets)
|
||||
if (type === 'value' || type === 'keyword') {
|
||||
if (
|
||||
(type === 'value' || type === 'keyword') &&
|
||||
!/^[a-zA-Z0-9_][a-zA-Z0-9_.\[\]]*$/.test(value)
|
||||
) {
|
||||
return wrapStringValueInQuotes(value);
|
||||
}
|
||||
|
||||
@ -1054,7 +1056,7 @@ function QuerySearch({
|
||||
</Collapse>
|
||||
</Card>
|
||||
)}
|
||||
{/*
|
||||
|
||||
{queryContext && (
|
||||
<Card size="small" title="Current Context" className="query-context">
|
||||
<div className="context-details">
|
||||
@ -1097,7 +1099,7 @@ function QuerySearch({
|
||||
</Space>
|
||||
</div>
|
||||
</Card>
|
||||
)} */}
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -2,25 +2,23 @@
|
||||
|
||||
import { ParseTreeListener } from 'antlr4';
|
||||
|
||||
import {
|
||||
AndExpressionContext,
|
||||
ArrayContext,
|
||||
ComparisonContext,
|
||||
ExpressionContext,
|
||||
FullTextContext,
|
||||
FunctionCallContext,
|
||||
FunctionParamContext,
|
||||
FunctionParamListContext,
|
||||
InClauseContext,
|
||||
KeyContext,
|
||||
NotInClauseContext,
|
||||
OrExpressionContext,
|
||||
PrimaryContext,
|
||||
QueryContext,
|
||||
UnaryExpressionContext,
|
||||
ValueContext,
|
||||
ValueListContext,
|
||||
} from './FilterQueryParser';
|
||||
import { QueryContext } from './FilterQueryParser';
|
||||
import { ExpressionContext } from './FilterQueryParser';
|
||||
import { OrExpressionContext } from './FilterQueryParser';
|
||||
import { AndExpressionContext } from './FilterQueryParser';
|
||||
import { UnaryExpressionContext } from './FilterQueryParser';
|
||||
import { PrimaryContext } from './FilterQueryParser';
|
||||
import { ComparisonContext } from './FilterQueryParser';
|
||||
import { InClauseContext } from './FilterQueryParser';
|
||||
import { NotInClauseContext } from './FilterQueryParser';
|
||||
import { ValueListContext } from './FilterQueryParser';
|
||||
import { FullTextContext } from './FilterQueryParser';
|
||||
import { FunctionCallContext } from './FilterQueryParser';
|
||||
import { FunctionParamListContext } from './FilterQueryParser';
|
||||
import { FunctionParamContext } from './FilterQueryParser';
|
||||
import { ArrayContext } from './FilterQueryParser';
|
||||
import { ValueContext } from './FilterQueryParser';
|
||||
import { KeyContext } from './FilterQueryParser';
|
||||
|
||||
/**
|
||||
* This interface defines a complete listener for a parse tree produced by
|
||||
@ -32,199 +30,166 @@ export default class FilterQueryListener extends ParseTreeListener {
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterQuery?: (ctx: QueryContext) => void;
|
||||
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.query`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitQuery?: (ctx: QueryContext) => void;
|
||||
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.expression`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterExpression?: (ctx: ExpressionContext) => void;
|
||||
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.expression`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitExpression?: (ctx: ExpressionContext) => void;
|
||||
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.orExpression`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterOrExpression?: (ctx: OrExpressionContext) => void;
|
||||
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.orExpression`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitOrExpression?: (ctx: OrExpressionContext) => void;
|
||||
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.andExpression`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterAndExpression?: (ctx: AndExpressionContext) => void;
|
||||
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.andExpression`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitAndExpression?: (ctx: AndExpressionContext) => void;
|
||||
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.unaryExpression`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterUnaryExpression?: (ctx: UnaryExpressionContext) => void;
|
||||
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.unaryExpression`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitUnaryExpression?: (ctx: UnaryExpressionContext) => void;
|
||||
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.primary`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterPrimary?: (ctx: PrimaryContext) => void;
|
||||
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.primary`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitPrimary?: (ctx: PrimaryContext) => void;
|
||||
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.comparison`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterComparison?: (ctx: ComparisonContext) => void;
|
||||
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.comparison`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitComparison?: (ctx: ComparisonContext) => void;
|
||||
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.inClause`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterInClause?: (ctx: InClauseContext) => void;
|
||||
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.inClause`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitInClause?: (ctx: InClauseContext) => void;
|
||||
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.notInClause`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterNotInClause?: (ctx: NotInClauseContext) => void;
|
||||
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.notInClause`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitNotInClause?: (ctx: NotInClauseContext) => void;
|
||||
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.valueList`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterValueList?: (ctx: ValueListContext) => void;
|
||||
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.valueList`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitValueList?: (ctx: ValueListContext) => void;
|
||||
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.fullText`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterFullText?: (ctx: FullTextContext) => void;
|
||||
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.fullText`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitFullText?: (ctx: FullTextContext) => void;
|
||||
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.functionCall`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterFunctionCall?: (ctx: FunctionCallContext) => void;
|
||||
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.functionCall`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitFunctionCall?: (ctx: FunctionCallContext) => void;
|
||||
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.functionParamList`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterFunctionParamList?: (ctx: FunctionParamListContext) => void;
|
||||
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.functionParamList`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitFunctionParamList?: (ctx: FunctionParamListContext) => void;
|
||||
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.functionParam`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterFunctionParam?: (ctx: FunctionParamContext) => void;
|
||||
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.functionParam`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitFunctionParam?: (ctx: FunctionParamContext) => void;
|
||||
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.array`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterArray?: (ctx: ArrayContext) => void;
|
||||
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.array`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitArray?: (ctx: ArrayContext) => void;
|
||||
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.value`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterValue?: (ctx: ValueContext) => void;
|
||||
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.value`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitValue?: (ctx: ValueContext) => void;
|
||||
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.key`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterKey?: (ctx: KeyContext) => void;
|
||||
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.key`.
|
||||
* @param ctx the parse tree
|
||||
|
||||
@ -1022,17 +1022,15 @@ export default class FilterQueryParser extends Parser {
|
||||
try {
|
||||
this.state = 202;
|
||||
this._errHandler.sync(this);
|
||||
switch (this._input.LA(1)) {
|
||||
case 37:
|
||||
switch (this._interp.adaptivePredict(this._input, 12, this._ctx)) {
|
||||
case 1:
|
||||
this.enterOuterAlt(localctx, 1);
|
||||
{
|
||||
this.state = 199;
|
||||
this.key();
|
||||
}
|
||||
break;
|
||||
case 34:
|
||||
case 35:
|
||||
case 36:
|
||||
case 2:
|
||||
this.enterOuterAlt(localctx, 2);
|
||||
{
|
||||
this.state = 200;
|
||||
@ -1046,8 +1044,6 @@ export default class FilterQueryParser extends Parser {
|
||||
this.array();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new NoViableAltException(this);
|
||||
}
|
||||
} catch (re) {
|
||||
if (re instanceof RecognitionException) {
|
||||
@ -1099,7 +1095,7 @@ export default class FilterQueryParser extends Parser {
|
||||
{
|
||||
this.state = 208;
|
||||
_la = this._input.LA(1);
|
||||
if (!(((_la - 34) & ~0x1f) === 0 && ((1 << (_la - 34)) & 7) !== 0)) {
|
||||
if (!(((_la - 34) & ~0x1f) === 0 && ((1 << (_la - 34)) & 15) !== 0)) {
|
||||
this._errHandler.recoverInline(this);
|
||||
} else {
|
||||
this._errHandler.reportMatch(this);
|
||||
@ -1639,7 +1635,7 @@ export default class FilterQueryParser extends Parser {
|
||||
1,
|
||||
0,
|
||||
34,
|
||||
36,
|
||||
37,
|
||||
227,
|
||||
0,
|
||||
34,
|
||||
@ -3809,6 +3805,9 @@ export class ValueContext extends ParserRuleContext {
|
||||
public BOOL(): TerminalNode {
|
||||
return this.getToken(FilterQueryParser.BOOL, 0);
|
||||
}
|
||||
public KEY(): TerminalNode {
|
||||
return this.getToken(FilterQueryParser.KEY, 0);
|
||||
}
|
||||
public get ruleIndex(): number {
|
||||
return FilterQueryParser.RULE_value;
|
||||
}
|
||||
|
||||
@ -2,25 +2,23 @@
|
||||
|
||||
import { ParseTreeVisitor } from 'antlr4';
|
||||
|
||||
import {
|
||||
AndExpressionContext,
|
||||
ArrayContext,
|
||||
ComparisonContext,
|
||||
ExpressionContext,
|
||||
FullTextContext,
|
||||
FunctionCallContext,
|
||||
FunctionParamContext,
|
||||
FunctionParamListContext,
|
||||
InClauseContext,
|
||||
KeyContext,
|
||||
NotInClauseContext,
|
||||
OrExpressionContext,
|
||||
PrimaryContext,
|
||||
QueryContext,
|
||||
UnaryExpressionContext,
|
||||
ValueContext,
|
||||
ValueListContext,
|
||||
} from './FilterQueryParser';
|
||||
import { QueryContext } from './FilterQueryParser';
|
||||
import { ExpressionContext } from './FilterQueryParser';
|
||||
import { OrExpressionContext } from './FilterQueryParser';
|
||||
import { AndExpressionContext } from './FilterQueryParser';
|
||||
import { UnaryExpressionContext } from './FilterQueryParser';
|
||||
import { PrimaryContext } from './FilterQueryParser';
|
||||
import { ComparisonContext } from './FilterQueryParser';
|
||||
import { InClauseContext } from './FilterQueryParser';
|
||||
import { NotInClauseContext } from './FilterQueryParser';
|
||||
import { ValueListContext } from './FilterQueryParser';
|
||||
import { FullTextContext } from './FilterQueryParser';
|
||||
import { FunctionCallContext } from './FilterQueryParser';
|
||||
import { FunctionParamListContext } from './FilterQueryParser';
|
||||
import { FunctionParamContext } from './FilterQueryParser';
|
||||
import { ArrayContext } from './FilterQueryParser';
|
||||
import { ValueContext } from './FilterQueryParser';
|
||||
import { KeyContext } from './FilterQueryParser';
|
||||
|
||||
/**
|
||||
* This interface defines a complete generic visitor for a parse tree produced
|
||||
@ -38,112 +36,96 @@ export default class FilterQueryVisitor<
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitQuery?: (ctx: QueryContext) => Result;
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.expression`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitExpression?: (ctx: ExpressionContext) => Result;
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.orExpression`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitOrExpression?: (ctx: OrExpressionContext) => Result;
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.andExpression`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitAndExpression?: (ctx: AndExpressionContext) => Result;
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.unaryExpression`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitUnaryExpression?: (ctx: UnaryExpressionContext) => Result;
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.primary`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitPrimary?: (ctx: PrimaryContext) => Result;
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.comparison`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitComparison?: (ctx: ComparisonContext) => Result;
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.inClause`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitInClause?: (ctx: InClauseContext) => Result;
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.notInClause`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitNotInClause?: (ctx: NotInClauseContext) => Result;
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.valueList`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitValueList?: (ctx: ValueListContext) => Result;
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.fullText`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitFullText?: (ctx: FullTextContext) => Result;
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.functionCall`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitFunctionCall?: (ctx: FunctionCallContext) => Result;
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.functionParamList`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitFunctionParamList?: (ctx: FunctionParamListContext) => Result;
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.functionParam`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitFunctionParam?: (ctx: FunctionParamContext) => Result;
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.array`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitArray?: (ctx: ArrayContext) => Result;
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.value`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitValue?: (ctx: ValueContext) => Result;
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.key`.
|
||||
* @param ctx the parse tree
|
||||
|
||||
94
frontend/src/parser/analyzeQuery.ts
Normal file
94
frontend/src/parser/analyzeQuery.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import FilterQueryLexer from './FilterQueryLexer';
|
||||
import FilterQueryParser from './FilterQueryParser';
|
||||
import { ParseTreeWalker, CharStreams, CommonTokenStream, Token } from 'antlr4';
|
||||
import { isOperatorToken } from 'utils/tokenUtils';
|
||||
import FilterQueryListener from './FilterQueryListener';
|
||||
|
||||
import {
|
||||
KeyContext,
|
||||
ValueContext,
|
||||
ComparisonContext,
|
||||
} from './FilterQueryParser';
|
||||
import { IToken } from 'types/antlrQueryTypes';
|
||||
|
||||
// 👇 Define the token classification
|
||||
type TokenClassification = 'Key' | 'Value' | 'Operator';
|
||||
|
||||
interface TokenInfo {
|
||||
text: string;
|
||||
startIndex: number;
|
||||
stopIndex: number;
|
||||
type: TokenClassification;
|
||||
}
|
||||
|
||||
// 👇 Custom listener to walk the parse tree
|
||||
class TypeTrackingListener implements FilterQueryListener {
|
||||
public tokens: TokenInfo[] = [];
|
||||
|
||||
enterKey(ctx: KeyContext) {
|
||||
const token = ctx.KEY().symbol;
|
||||
this.tokens.push({
|
||||
text: token.text!,
|
||||
startIndex: token.start,
|
||||
stopIndex: token.stop,
|
||||
type: 'Key',
|
||||
});
|
||||
}
|
||||
|
||||
enterValue(ctx: ValueContext) {
|
||||
const token = ctx.start;
|
||||
this.tokens.push({
|
||||
text: token.text!,
|
||||
startIndex: token.start,
|
||||
stopIndex: token.stop,
|
||||
type: 'Value',
|
||||
});
|
||||
}
|
||||
|
||||
enterComparison(ctx: ComparisonContext) {
|
||||
const children = ctx.children || [];
|
||||
for (const child of children) {
|
||||
const token = (child as any).symbol;
|
||||
if (token && isOperatorToken(token.type)) {
|
||||
this.tokens.push({
|
||||
text: token.text!,
|
||||
startIndex: token.start,
|
||||
stopIndex: token.stop,
|
||||
type: 'Operator',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Required no-op stubs
|
||||
enterEveryRule() {}
|
||||
exitEveryRule() {}
|
||||
exitKey() {}
|
||||
exitValue() {}
|
||||
exitComparison() {}
|
||||
visitTerminal() {}
|
||||
visitErrorNode() {}
|
||||
}
|
||||
|
||||
// 👇 Analyze function
|
||||
export function analyzeQuery(input: string, lastToken: IToken) {
|
||||
input = input.trim();
|
||||
const chars = CharStreams.fromString(input);
|
||||
const lexer = new FilterQueryLexer(chars);
|
||||
const tokens = new CommonTokenStream(lexer);
|
||||
const parser = new FilterQueryParser(tokens);
|
||||
|
||||
const tree = parser.query();
|
||||
|
||||
const listener = new TypeTrackingListener();
|
||||
ParseTreeWalker.DEFAULT.walk(listener, tree);
|
||||
|
||||
const currentToken = listener.tokens.find(
|
||||
(token) =>
|
||||
token.text === lastToken.text &&
|
||||
token.startIndex === lastToken.start &&
|
||||
token.stopIndex === lastToken.stop,
|
||||
);
|
||||
|
||||
return currentToken;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@ -93,6 +93,7 @@ value
|
||||
: QUOTED_TEXT
|
||||
| NUMBER
|
||||
| BOOL
|
||||
| KEY
|
||||
;
|
||||
|
||||
key
|
||||
|
||||
@ -3,6 +3,16 @@
|
||||
import { CharStreams, CommonTokenStream, Token } from 'antlr4';
|
||||
import FilterQueryLexer from 'parser/FilterQueryLexer';
|
||||
import { IQueryContext, IQueryPair, IToken } from 'types/antlrQueryTypes';
|
||||
import { analyzeQuery } from 'parser/analyzeQuery';
|
||||
import {
|
||||
isBracketToken,
|
||||
isConjunctionToken,
|
||||
isFunctionToken,
|
||||
isKeyToken,
|
||||
isMultiValueOperator,
|
||||
isOperatorToken,
|
||||
isValueToken,
|
||||
} from './tokenUtils';
|
||||
|
||||
// Function to normalize multiple spaces to single spaces when not in quotes
|
||||
function normalizeSpaces(query: string): string {
|
||||
@ -69,7 +79,8 @@ export function createContext(
|
||||
|
||||
// Helper to determine token type for context
|
||||
function determineTokenContext(
|
||||
tokenType: number,
|
||||
token: IToken,
|
||||
query: string,
|
||||
): {
|
||||
isInKey: boolean;
|
||||
isInOperator: boolean;
|
||||
@ -78,57 +89,49 @@ function determineTokenContext(
|
||||
isInConjunction: boolean;
|
||||
isInParenthesis: boolean;
|
||||
} {
|
||||
// Key context
|
||||
const isInKey = tokenType === FilterQueryLexer.KEY;
|
||||
let isInKey: boolean = false;
|
||||
let isInOperator: boolean = false;
|
||||
let isInValue: boolean = false;
|
||||
let isInFunction: boolean = false;
|
||||
let isInConjunction: boolean = false;
|
||||
let isInParenthesis: boolean = false;
|
||||
|
||||
// Operator context
|
||||
const isInOperator = [
|
||||
FilterQueryLexer.EQUALS,
|
||||
FilterQueryLexer.NOT_EQUALS,
|
||||
FilterQueryLexer.NEQ,
|
||||
FilterQueryLexer.LT,
|
||||
FilterQueryLexer.LE,
|
||||
FilterQueryLexer.GT,
|
||||
FilterQueryLexer.GE,
|
||||
FilterQueryLexer.LIKE,
|
||||
FilterQueryLexer.NOT_LIKE,
|
||||
FilterQueryLexer.ILIKE,
|
||||
FilterQueryLexer.NOT_ILIKE,
|
||||
FilterQueryLexer.BETWEEN,
|
||||
FilterQueryLexer.EXISTS,
|
||||
FilterQueryLexer.REGEXP,
|
||||
FilterQueryLexer.CONTAINS,
|
||||
FilterQueryLexer.IN,
|
||||
FilterQueryLexer.NOT,
|
||||
].includes(tokenType);
|
||||
const tokenType = token.type;
|
||||
const currentTokenContext = analyzeQuery(query, token);
|
||||
|
||||
// Value context
|
||||
const isInValue = [
|
||||
FilterQueryLexer.QUOTED_TEXT,
|
||||
FilterQueryLexer.NUMBER,
|
||||
FilterQueryLexer.BOOL,
|
||||
].includes(tokenType);
|
||||
if (!currentTokenContext) {
|
||||
// Key context
|
||||
isInKey = isKeyToken(tokenType);
|
||||
|
||||
// Operator context
|
||||
isInOperator = isOperatorToken(tokenType);
|
||||
|
||||
// Value context
|
||||
isInValue = isValueToken(tokenType);
|
||||
} else {
|
||||
switch (currentTokenContext.type) {
|
||||
case 'Operator':
|
||||
isInOperator = true;
|
||||
break;
|
||||
case 'Value':
|
||||
isInValue = true;
|
||||
break;
|
||||
case 'Key':
|
||||
isInKey = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Function context
|
||||
const isInFunction = [
|
||||
FilterQueryLexer.HAS,
|
||||
FilterQueryLexer.HASANY,
|
||||
FilterQueryLexer.HASALL,
|
||||
FilterQueryLexer.HASNONE,
|
||||
].includes(tokenType);
|
||||
isInFunction = isFunctionToken(tokenType);
|
||||
|
||||
// Conjunction context
|
||||
const isInConjunction = [FilterQueryLexer.AND, FilterQueryLexer.OR].includes(
|
||||
tokenType,
|
||||
);
|
||||
isInConjunction = isConjunctionToken(tokenType);
|
||||
|
||||
// Parenthesis context
|
||||
const isInParenthesis = [
|
||||
FilterQueryLexer.LPAREN,
|
||||
FilterQueryLexer.RPAREN,
|
||||
FilterQueryLexer.LBRACK,
|
||||
FilterQueryLexer.RBRACK,
|
||||
].includes(tokenType);
|
||||
isInParenthesis = isBracketToken(tokenType);
|
||||
|
||||
return {
|
||||
isInKey,
|
||||
@ -140,61 +143,6 @@ function determineTokenContext(
|
||||
};
|
||||
}
|
||||
|
||||
// Helper function to check if a token is an operator
|
||||
function isOperatorToken(tokenType: number): boolean {
|
||||
return [
|
||||
FilterQueryLexer.EQUALS,
|
||||
FilterQueryLexer.NOT_EQUALS,
|
||||
FilterQueryLexer.NEQ,
|
||||
FilterQueryLexer.LT,
|
||||
FilterQueryLexer.LE,
|
||||
FilterQueryLexer.GT,
|
||||
FilterQueryLexer.GE,
|
||||
FilterQueryLexer.LIKE,
|
||||
FilterQueryLexer.NOT_LIKE,
|
||||
FilterQueryLexer.ILIKE,
|
||||
FilterQueryLexer.NOT_ILIKE,
|
||||
FilterQueryLexer.BETWEEN,
|
||||
FilterQueryLexer.EXISTS,
|
||||
FilterQueryLexer.REGEXP,
|
||||
FilterQueryLexer.CONTAINS,
|
||||
FilterQueryLexer.IN,
|
||||
FilterQueryLexer.NOT,
|
||||
].includes(tokenType);
|
||||
}
|
||||
|
||||
// Helper function to check if a token is a value
|
||||
function isValueToken(tokenType: number): boolean {
|
||||
return [
|
||||
FilterQueryLexer.QUOTED_TEXT,
|
||||
FilterQueryLexer.NUMBER,
|
||||
FilterQueryLexer.BOOL,
|
||||
].includes(tokenType);
|
||||
}
|
||||
|
||||
// Helper function to check if a token is a conjunction
|
||||
function isConjunctionToken(tokenType: number): boolean {
|
||||
return [FilterQueryLexer.AND, FilterQueryLexer.OR].includes(tokenType);
|
||||
}
|
||||
|
||||
// Helper function to check if a token is a bracket
|
||||
function isBracketToken(tokenType: number): boolean {
|
||||
return [
|
||||
FilterQueryLexer.LPAREN,
|
||||
FilterQueryLexer.RPAREN,
|
||||
FilterQueryLexer.LBRACK,
|
||||
FilterQueryLexer.RBRACK,
|
||||
].includes(tokenType);
|
||||
}
|
||||
|
||||
// Helper function to check if an operator typically uses bracket values (multi-value operators)
|
||||
function isMultiValueOperator(operatorToken?: string): boolean {
|
||||
if (!operatorToken) return false;
|
||||
|
||||
const upperOp = operatorToken.toUpperCase();
|
||||
return upperOp === 'IN' || upperOp === 'NOT IN';
|
||||
}
|
||||
|
||||
// Function to determine token context boundaries more precisely
|
||||
function determineContextBoundaries(
|
||||
query: string,
|
||||
@ -888,7 +836,7 @@ export function getQueryContextAtCursor(
|
||||
lastTokenBeforeCursor &&
|
||||
(isAtSpace || isAfterSpace || isTransitionPoint)
|
||||
) {
|
||||
const lastTokenContext = determineTokenContext(lastTokenBeforeCursor.type);
|
||||
const lastTokenContext = determineTokenContext(lastTokenBeforeCursor, input);
|
||||
|
||||
// Apply the context progression logic: key → operator → value → conjunction → key
|
||||
if (lastTokenContext.isInKey) {
|
||||
@ -984,7 +932,7 @@ export function getQueryContextAtCursor(
|
||||
// FIXED: Consider the case where the cursor is at the end of a token
|
||||
// with no space yet (user is actively typing)
|
||||
if (exactToken && adjustedCursorIndex === exactToken.stop + 1) {
|
||||
const tokenContext = determineTokenContext(exactToken.type);
|
||||
const tokenContext = determineTokenContext(exactToken, input);
|
||||
|
||||
// When the cursor is at the end of a token, return the current token context
|
||||
return {
|
||||
@ -1011,7 +959,7 @@ export function getQueryContextAtCursor(
|
||||
|
||||
// Regular token-based context detection (when cursor is directly on a token)
|
||||
if (exactToken?.channel === 0) {
|
||||
const tokenContext = determineTokenContext(exactToken.type);
|
||||
const tokenContext = determineTokenContext(exactToken, input);
|
||||
|
||||
// Get relevant tokens based on current pair
|
||||
const keyFromPair = currentPair?.key || '';
|
||||
@ -1044,7 +992,7 @@ export function getQueryContextAtCursor(
|
||||
|
||||
// If we're between tokens but not after a space, use previous token to determine context
|
||||
if (previousToken?.channel === 0) {
|
||||
const prevContext = determineTokenContext(previousToken.type);
|
||||
const prevContext = determineTokenContext(previousToken, input);
|
||||
|
||||
// Get relevant tokens based on current pair
|
||||
const keyFromPair = currentPair?.key || '';
|
||||
@ -1221,7 +1169,10 @@ export function extractQueryPairs(query: string): IQueryPair[] {
|
||||
}
|
||||
|
||||
// If token is a KEY, start a new pair
|
||||
if (token.type === FilterQueryLexer.KEY) {
|
||||
if (
|
||||
token.type === FilterQueryLexer.KEY &&
|
||||
!(currentPair && currentPair.key)
|
||||
) {
|
||||
// If we have an existing incomplete pair, add it to the result
|
||||
if (currentPair && currentPair.key) {
|
||||
queryPairs.push({
|
||||
|
||||
70
frontend/src/utils/tokenUtils.ts
Normal file
70
frontend/src/utils/tokenUtils.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import FilterQueryLexer from 'parser/FilterQueryLexer';
|
||||
|
||||
export function isKeyToken(tokenType: number): boolean {
|
||||
return tokenType === FilterQueryLexer.KEY;
|
||||
}
|
||||
|
||||
// Helper function to check if a token is an operator
|
||||
export function isOperatorToken(tokenType: number): boolean {
|
||||
return [
|
||||
FilterQueryLexer.EQUALS,
|
||||
FilterQueryLexer.NOT_EQUALS,
|
||||
FilterQueryLexer.NEQ,
|
||||
FilterQueryLexer.LT,
|
||||
FilterQueryLexer.LE,
|
||||
FilterQueryLexer.GT,
|
||||
FilterQueryLexer.GE,
|
||||
FilterQueryLexer.LIKE,
|
||||
FilterQueryLexer.NOT_LIKE,
|
||||
FilterQueryLexer.ILIKE,
|
||||
FilterQueryLexer.NOT_ILIKE,
|
||||
FilterQueryLexer.BETWEEN,
|
||||
FilterQueryLexer.EXISTS,
|
||||
FilterQueryLexer.REGEXP,
|
||||
FilterQueryLexer.CONTAINS,
|
||||
FilterQueryLexer.IN,
|
||||
FilterQueryLexer.NOT,
|
||||
].includes(tokenType);
|
||||
}
|
||||
|
||||
// Helper function to check if a token is a value
|
||||
export function isValueToken(tokenType: number): boolean {
|
||||
return [
|
||||
FilterQueryLexer.QUOTED_TEXT,
|
||||
FilterQueryLexer.NUMBER,
|
||||
FilterQueryLexer.BOOL,
|
||||
FilterQueryLexer.KEY,
|
||||
].includes(tokenType);
|
||||
}
|
||||
|
||||
// Helper function to check if a token is a conjunction
|
||||
export function isConjunctionToken(tokenType: number): boolean {
|
||||
return [FilterQueryLexer.AND, FilterQueryLexer.OR].includes(tokenType);
|
||||
}
|
||||
|
||||
// Helper function to check if a token is a bracket
|
||||
export function isBracketToken(tokenType: number): boolean {
|
||||
return [
|
||||
FilterQueryLexer.LPAREN,
|
||||
FilterQueryLexer.RPAREN,
|
||||
FilterQueryLexer.LBRACK,
|
||||
FilterQueryLexer.RBRACK,
|
||||
].includes(tokenType);
|
||||
}
|
||||
|
||||
// Helper function to check if an operator typically uses bracket values (multi-value operators)
|
||||
export function isMultiValueOperator(operatorToken?: string): boolean {
|
||||
if (!operatorToken) return false;
|
||||
|
||||
const upperOp = operatorToken.toUpperCase();
|
||||
return upperOp === 'IN' || upperOp === 'NOT IN';
|
||||
}
|
||||
|
||||
export function isFunctionToken(tokenType: number): boolean {
|
||||
return [
|
||||
FilterQueryLexer.HAS,
|
||||
FilterQueryLexer.HASANY,
|
||||
FilterQueryLexer.HASALL,
|
||||
FilterQueryLexer.HASNONE,
|
||||
].includes(tokenType);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user