Background
This is the CCL-side work required for cinchapi/concourse#533 (strict navigation in multi-valued link queries).
Concourse needs a strict(...) wrapper keyword in CCL that groups conditions requiring same-destination evaluation for navigation keys. For example:
find where strict(A.foo = "A" AND A.bar = "B")
This tells the engine that all conditions inside strict() must be satisfied by the same linked record, rather than evaluating each condition independently (which can produce false positives when multiple linked records each satisfy different conditions).
Changes Required
1. Grammar (grammar/grammar.jjt)
Add token (in the TOKEN section, near other keywords):
Add production (new grammar rule):
void StrictExpression() #Strict : {}
{
(<STRICT>) (<OPEN_PARENTHESES>) DisjunctionExpression() (<CLOSE_PARENTHESES>)
}
Modify UnaryExpression() to include the new production as an alternative (with LOOKAHEAD):
void UnaryExpression() : {}
{
LOOKAHEAD(2)
StrictExpression()
|
(<OPEN_PARENTHESES>) DisjunctionExpression() (<CLOSE_PARENTHESES>)
|
RelationalExpression()
}
Same change for UnaryExpressionNoTimestamp().
Regenerate the parser using the existing javacc-parser-generator.sh script.
2. New AST node: StrictConditionTree
File: src/main/java/com/cinchapi/ccl/syntax/StrictConditionTree.java
A ConditionTree that wraps inner conditions requiring same-destination evaluation:
- Extends
BaseAbstractSyntaxTree, implements ConditionTree
- Holds a single
ConditionTree condition (the wrapped inner conditions)
condition() accessor returns the inner tree
accept() calls visitor.visit(this, data)
3. Update Visitor interface
File: src/main/java/com/cinchapi/ccl/syntax/Visitor.java
- Add
T visit(StrictConditionTree tree, Object... data) method
- Update default
visit(ConditionTree) to handle StrictConditionTree instances (dispatch to the new method)
4. Update ConditionTreeVisitor
File: src/main/java/com/cinchapi/ccl/ConditionTreeVisitor.java
Allow StrictConditionTree through (it IS a condition tree type). Add the visit(StrictConditionTree) method -- can be abstract or have a default implementation.
5. Update CompilerJavaCC
File: src/main/java/com/cinchapi/ccl/CompilerJavaCC.java
Add visitor handler for ASTStrict (the JJTree-generated node):
public Object visit(ASTStrict node, Object data) {
ConditionTree inner = (ConditionTree) node.jjtGetChild(0)
.jjtAccept(this, data);
return new StrictConditionTree(inner);
}
6. Tests
Add parser tests verifying:
strict(A.foo = "A" AND A.bar = "B") parses into a StrictConditionTree wrapping an AndTree
strict(A.foo = "A") parses into a StrictConditionTree wrapping an ExpressionTree
strict(A.foo = "A" AND A.bar = "B") OR name = "test" -- strict inside a larger expression
- Existing expressions without
strict continue to parse identically
Edge Cases the Grammar Must Accept
The grammar itself is straightforward — strict() wraps any valid expression. However, the following inputs are syntactically valid and the parser must accept them without error, even though some are semantically degenerate (the engine handles the semantics, not the parser):
Non‑navigation keys inside strict()
strict(name = "Jeff" AND age > 30)
The parser treats this the same as any other strict(...) — it wraps the inner conditions in a StrictConditionTree. The engine is responsible for detecting that there are no navigation keys and evaluating as normal AND.
No shared navigation prefix
strict(A.foo = "X" AND B.bar = "Y")
Again, syntactically valid. The parser produces a StrictConditionTree wrapping an AndTree. The engine computes an empty common prefix and degrades to normal AND.
Pure OR inside strict()
strict(A.foo = "X" OR A.bar = "Y")
Valid and parsed as StrictConditionTree wrapping an OrTree. Semantically equivalent to the non‑strict version (strict only changes behavior when AND creates cross‑link ambiguity), but not the parser's concern.
Nested strict() inside larger expressions
name = "Jeff" OR strict(A.foo = "X" AND A.bar = "Y") AND age > 20
The strict(...) block participates in the normal operator precedence. The parser should handle this via the existing UnaryExpression integration without special precedence rules.
Background
This is the CCL-side work required for cinchapi/concourse#533 (strict navigation in multi-valued link queries).
Concourse needs a
strict(...)wrapper keyword in CCL that groups conditions requiring same-destination evaluation for navigation keys. For example:This tells the engine that all conditions inside
strict()must be satisfied by the same linked record, rather than evaluating each condition independently (which can produce false positives when multiple linked records each satisfy different conditions).Changes Required
1. Grammar (
grammar/grammar.jjt)Add token (in the TOKEN section, near other keywords):
Add production (new grammar rule):
Modify
UnaryExpression()to include the new production as an alternative (with LOOKAHEAD):Same change for
UnaryExpressionNoTimestamp().Regenerate the parser using the existing
javacc-parser-generator.shscript.2. New AST node:
StrictConditionTreeFile:
src/main/java/com/cinchapi/ccl/syntax/StrictConditionTree.javaA
ConditionTreethat wraps inner conditions requiring same-destination evaluation:BaseAbstractSyntaxTree, implementsConditionTreeConditionTree condition(the wrapped inner conditions)condition()accessor returns the inner treeaccept()callsvisitor.visit(this, data)3. Update
VisitorinterfaceFile:
src/main/java/com/cinchapi/ccl/syntax/Visitor.javaT visit(StrictConditionTree tree, Object... data)methodvisit(ConditionTree)to handleStrictConditionTreeinstances (dispatch to the new method)4. Update
ConditionTreeVisitorFile:
src/main/java/com/cinchapi/ccl/ConditionTreeVisitor.javaAllow
StrictConditionTreethrough (it IS a condition tree type). Add thevisit(StrictConditionTree)method -- can be abstract or have a default implementation.5. Update
CompilerJavaCCFile:
src/main/java/com/cinchapi/ccl/CompilerJavaCC.javaAdd visitor handler for
ASTStrict(the JJTree-generated node):6. Tests
Add parser tests verifying:
strict(A.foo = "A" AND A.bar = "B")parses into aStrictConditionTreewrapping anAndTreestrict(A.foo = "A")parses into aStrictConditionTreewrapping anExpressionTreestrict(A.foo = "A" AND A.bar = "B") OR name = "test"--strictinside a larger expressionstrictcontinue to parse identicallyEdge Cases the Grammar Must Accept
The grammar itself is straightforward —
strict()wraps any valid expression. However, the following inputs are syntactically valid and the parser must accept them without error, even though some are semantically degenerate (the engine handles the semantics, not the parser):Non‑navigation keys inside
strict()The parser treats this the same as any other
strict(...)— it wraps the inner conditions in aStrictConditionTree. The engine is responsible for detecting that there are no navigation keys and evaluating as normal AND.No shared navigation prefix
Again, syntactically valid. The parser produces a
StrictConditionTreewrapping anAndTree. The engine computes an empty common prefix and degrades to normal AND.Pure OR inside
strict()Valid and parsed as
StrictConditionTreewrapping anOrTree. Semantically equivalent to the non‑strict version (strict only changes behavior when AND creates cross‑link ambiguity), but not the parser's concern.Nested
strict()inside larger expressionsThe
strict(...)block participates in the normal operator precedence. The parser should handle this via the existingUnaryExpressionintegration without special precedence rules.