Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/chilly-tires-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@shipfox/shipql-parser": patch
---

Use shorthand `-` for negating expressions in ShipQL
8 changes: 4 additions & 4 deletions libs/common/shipql-parser/src/stringify.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,13 @@ describe('stringify', () => {
it('should stringify NOT', () => {
const result = stringify(parse('NOT status:error'));

expect(result).toBe('NOT status:error');
expect(result).toBe('-status:error');
});

it('should stringify dash negation as NOT', () => {
it('should stringify dash negation as dash', () => {
const result = stringify(parse('-status:error'));

expect(result).toBe('NOT status:error');
expect(result).toBe('-status:error');
});

it('should wrap compound expressions inside NOT', () => {
Expand All @@ -157,7 +157,7 @@ describe('stringify', () => {
it('should stringify NOT combined with AND', () => {
const result = stringify(parse('env:prod NOT status:error'));

expect(result).toBe('env:prod NOT status:error');
expect(result).toBe('env:prod -status:error');
});

// ── free text ─────────────────────────────────────────────────────
Expand Down
9 changes: 7 additions & 2 deletions libs/common/shipql-parser/src/stringify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {AstNode} from './index';
*
* The output is a canonical form:
* - AND is always implicit (no "AND" keyword)
* - NOT is always the keyword form (no "-" shorthand)
* - NOT uses the shorthand "-" prefix form when possible, falls back to "NOT" for compound expressions
* - Values are quoted only when necessary (spaces, empty, or special characters)
* - Parentheses are added only when needed to preserve precedence
*/
Expand All @@ -31,7 +31,12 @@ export function stringify(ast: AstNode | null): string {
return `${stringifyChild(ast.left, 'or')} OR ${stringifyChild(ast.right, 'or')}`;

case 'not':
return `NOT ${stringifyChild(ast.expr, 'not')}`;
// The "-" shorthand only works directly attached to a term;
// compound expressions (AND/OR) need parens which requires the "NOT" keyword form.
if (ast.expr.type === 'and' || ast.expr.type === 'or') {
return `NOT (${stringify(ast.expr)})`;
}
return `-${stringify(ast.expr)}`;
}
}

Expand Down
Loading