Skip to content

LYT-515 | Lex unquoted negative numeric literals as signed literals in FilterQL - #58

Draft
onkarj-47 wants to merge 1 commit into
masterfrom
lyt-515-filterql-negative-literals
Draft

LYT-515 | Lex unquoted negative numeric literals as signed literals in FilterQL#58
onkarj-47 wants to merge 1 commit into
masterfrom
lyt-515-filterql-negative-literals

Conversation

@onkarj-47

Copy link
Copy Markdown

🤖 Opened by an AI agent — not a person. This PR was created by the lytics-developer-agent skill (Claude, Anthropic) running unattended. It shows under the assignee's GitHub account because it uses their token, but a human did not hand-write it — and comment replies on this PR from this account are also posted by the agent, not typed by a person. Review, approval, and merge stay human decisions; the agent never marks the PR ready-for-review and never merges.

Merge order

This is the producer in a two-PR change for LYT-515:

  1. this PR (qlbridge) — merge first, then cut/point a release the consumer can pin.
  2. lytics/lio — bumps its qlbridge dependency to this fix and adds the lio-side coercion + regression tests. It currently pins the fix branch commit (e136cbab2); re-pin to this PR's squash-merge commit before merging lio.

What was broken

FilterQL / SegmentQL rejected unquoted negative numeric literals in value positions, while the quoted form was accepted. This blocked the customer-facing remainder of LYT-486 (P&G escalation). Two distinct failure modes, both rooted here in the lexer:

Input Before Why
FILTER visitct IN (-1) FROM user hard parse error (Unrecognized input) LexListOfArgs backs up on -LexExpression emits a standalone TokenMinus, desyncing the array token stream
FILTER visitct IN (-1, 3) FROM user hard parse error same, plus the list-continuation was never re-pushed
FILTER visitct = -1 FROM user parsed as UnaryNode{Minus, Number} → printed as - (1) the - is torn off as TokenMinus before number-lookahead

The number scanner already accepted a leading sign (scanNumericOrDuration), but that path was never reached in comparison-RHS / IN-list positions.

The fix

In lex/lexer.go, when a - appears in a value position — the previous emitted token is a comparator, arithmetic operator, (, ,, logic op, IN/BETWEEN, or start-of-input (TokenNil) — and the next rune is a digit (or .+digit), lex it as a single signed numeric token instead of TokenMinus. Gating is strictly on l.lastToken.T, so binary subtraction (a - b, where the previous token is an identity/number/value/)) is unchanged — important because LexExpression is shared with the SQL dialect.

A matching narrow fix in LexListOfArgs re-pushes the list continuation before delegating a value-position - to LexExpression, so multi-element lists like IN (-1, 3) resolve the trailing ,/) correctly.

Result

= -1, city = -1, = -1.5, IN (-1), IN (-1, 3), IN (-1) on string-typed fields all parse; each negative is a single *expr.NumberNode whose .Text carries the sign; .String() canonicalizes to the bare signed form (FILTER visitct IN (-1) FROM user, never - (1)) and re-parses idempotently — closing the stored-QL round-trip asymmetry from the ticket. Positive, quoted, and binary-minus behavior is unchanged.

Testing

  • New negative-literal coverage in rel/parse_filterql_test.go (= int/float, IN single/multi, int- and string-named fields; asserts node type and stable round-trip) and in lex/lexer_test.go / lex/dialect_filterql_test.go (tokenization + binary-minus-unchanged).
  • Full CI-equivalent suite green: go test -race ./... (26 packages).

Known observations (not changed here)

  • go vet ./... reports a pre-existing unreachable code at testutil/testsuite.go:215 — present on the base commit, untouched by this PR, and not part of this repo's CI (.github/workflows/test.yml runs go test -race ./...). Left alone to keep this PR scoped to the bug fix.

Files

  • lex/lexer.go — value-position signed-literal lexing (valueExpectedTokens, numericAfterSign(), the LexExpression/LexListOfArgs - handling).
  • lex/lexer_test.go, lex/dialect_filterql_test.go, rel/parse_filterql_test.go — tests.

Generated by Claude Code

FilterQL rejected unquoted `-1`/`-1.5` in `=` comparisons and hard-failed
on them in `IN (...)` lists, while the quoted form parsed fine. Gate the
lexer's `-` handling on lastToken so a `-` in a value position (after a
comparator, operator, `(`, `,`, logic, IN/BETWEEN, or start-of-input)
lexes as one signed TokenInteger/TokenFloat instead of TokenMinus, so it
parses to a single *expr.NumberNode and round-trips to a bare `-1`
instead of `- (1)`. Binary subtraction is unaffected.

Co-authored-by: Onkar Jaliminche <onkar.jaliminche@contentstack.com>
Co-authored-by: Vedant Karle <vedant.karle@contentstack.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants