Cypher Parser Modes¶
Environment Variable:
NORNICDB_PARSER
Options:nornic(default) |antlr
NornicDB supports two Cypher parser implementations that can be switched at runtime.
Architecture¶
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1f6feb', 'primaryTextColor': '#c9d1d9', 'primaryBorderColor': '#30363d', 'lineColor': '#8b949e', 'secondaryColor': '#238636', 'tertiaryColor': '#21262d', 'background': '#0d1117', 'mainBkg': '#161b22'}}}%%
flowchart TB
subgraph ENV["π§ Configuration"]
direction LR
E1["NORNICDB_PARSER"]
E2["nornic | antlr"]
end
Q[/"Cypher Query"/]
Q --> VALIDATE["validateSyntax()"]
VALIDATE --> |"NORNICDB_PARSER=nornic"| NORNIC
VALIDATE --> |"NORNICDB_PARSER=antlr"| ANTLR
subgraph NORNIC["β‘ Nornic Parser (Default)"]
direction TB
N1["String-based validation"]
N2["Regex + indexOf"]
N3["Direct execution"]
N1 --> N2 --> N3
end
subgraph ANTLR["π³ ANTLR Parser"]
direction TB
A1["ANTLR Lexer"]
A2["ANTLR Parser"]
A3["Full Parse Tree"]
A4["Syntax Validation"]
A1 --> A2 --> A3 --> A4
end
NORNIC --> EXEC["Execute Query"]
ANTLR --> EXEC
EXEC --> RESULT[("Result")]
style ENV fill:#21262d,stroke:#30363d
style NORNIC fill:#161b22,stroke:#238636
style ANTLR fill:#161b22,stroke:#a371f7
style RESULT fill:#238636,stroke:#3fb950 Real-World Benchmarks (Northwind Database)¶
| Query | β‘ Nornic | π³ ANTLR | Slowdown |
|---|---|---|---|
| Count all nodes | 3,272 hz | 45 hz | 73x |
| Count all relationships | 3,693 hz | 50 hz | 74x |
| Find customer by ID | 4,213 hz | 2,153 hz | 2x |
| Products supplied by supplier | 4,023 hz | 53 hz | 76x |
| SupplierβCategory traversal | 3,225 hz | 22 hz | 147x |
| Products with/without orders | 3,881 hz | 0.82 hz | 4,753x |
| Create/delete relationship | 3,974 hz | 62 hz | 64x |
Total test suite time: | Mode | Time | |------|------| | β‘ Nornic | 17.5s | | π³ ANTLR | 35.3s (2x slower) |
Mode Comparison¶
| Feature | β‘ Nornic (Default) | π³ ANTLR |
|---|---|---|
| Throughput | 3,000-4,200 ops/sec | 0.8-2,100 ops/sec |
| Worst Case | - | 4,753x slower |
| Error Messages | Classified with restored line/column positions | Detailed (line/column) |
| Syntax Validation | Strict, TCK-vetted (see below) | Strict OpenCypher |
| Memory Usage | Lowest (recursive key scanner, no parse tree) | Higher |
| Best For | Production | Development/Debugging |
Note: both modes share a single converged execution pipeline. There are no legacy alternate executors or text re-dispatch fallbacks: every statement runs through the same typed router (
Handled/NotApplicable/ParseRejected/Failed), and unhandled statements fail with Neo4j'sNeo.ClientError.Statement.SyntaxErrorclassification rather than being re-routed.
Nornic validation is TCK-vetted¶
The Nornic scanner uses a recursive key scanner with lightweight lexing only where needed (pkg/cypher/keyword_scan.go), with quote/comment/bracket-aware fragment consumption shared by all callers. The official OpenCypher TCK is pinned at revision 370fe27f and the ratchet records 7794/7794 scenario/mode outcomes passing with zero expected gaps, setup blocks or harness errors (make cypher-tck-ratchet, make cypher-tck-vetted). Rejections are classified with Neo4j Bolt status codes and error positions are restored from the original query text, including CR/CRLF/LF line counting.
Configuration¶
# Production (default) - fastest
export NORNICDB_PARSER=nornic
# Development/Debugging - strict validation, better errors
export NORNICDB_PARSER=antlr
Programmatic Switching¶
import "github.com/orneryd/nornicdb/pkg/config"
// Check current parser
if config.IsNornicParser() {
// Using fast Nornic parser
}
// Switch to ANTLR temporarily
cleanup := config.WithANTLRParser()
defer cleanup()
// ... queries use ANTLR parser here
// Direct set
config.SetParserType(config.ParserTypeANTLR)
config.SetParserType(config.ParserTypeNornic)
When to Use Each Parser¶
β‘ Nornic Parser (NORNICDB_PARSER=nornic) β Default¶
Use when: - Production deployments - Maximum performance is critical - Simple, well-tested query patterns - High-throughput workloads
Pros: - Fastest execution β 3,000-4,200 ops/sec - πΎ Lowest memory β No parse tree allocation - π§ Battle-tested β Original implementation, converged single pipeline - β‘ Zero parsing overhead β recursive key scanner, lightweight lexing only as needed
Cons: - π No structured parse tree β debugging uses restored positions, not AST inspection - π Semantic scope checks are clause-targeted rather than grammar-derived; the TCK ratchet gates the supported surface
π³ ANTLR Parser (NORNICDB_PARSER=antlr)¶
Use when: - Development and debugging - Need detailed syntax error messages - Strict OpenCypher compliance required - Building query analysis tools
Pros: - β Strict validation β Full OpenCypher grammar - π Detailed errors β Line and column numbers - π³ Full parse tree β For analysis/tooling - π οΈ Extensible β Easy to add new features
Cons: - π’ Much slower β 50-5000x slower than Nornic - πΎ Higher memory β Full parse tree allocation - β±οΈ Not for production β Too slow for high-throughput
Error Message Comparison¶
Invalid query: MATCH (n RETURN n (missing closing paren)
| Parser | Error Message |
|---|---|
| Nornic | syntax error: unbalanced parentheses classified as Neo.ClientError.Statement.SyntaxError (offset-tracked errors additionally restore line/column positions from the original query text) |
| ANTLR | syntax error: line 1:9 no viable alternative at input 'MATCH (n RETURN' |
Make Targets¶
# Run entire test suite with ANTLR parser
make antlr-test
# Run cypher tests with both parsers
make test-parsers
# Regenerate ANTLR parser from grammar
make antlr-generate
Files¶
| File | Description |
|---|---|
pkg/config/feature_flags.go | Parser type configuration |
pkg/cypher/executor.go | validateSyntax() dispatcher |
pkg/cypher/antlr/ | ANTLR parser implementation |
pkg/cypher/antlr/*.g4 | ANTLR grammar files |
TL;DR: Use nornic (default) for production. Use antlr only for development/debugging when you need detailed error messages.