Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extended parsing of preparser options #301

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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/four-tools-leave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@neo4j-cypher/language-support': patch
---

Added parsing of CYPHER <version> and CYPHER <optionName> = <value>
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@ EXPLAIN:
E X P L A I N;

PROFILE:
P R O F I L E;
P R O F I L E;

CYPHER:
C Y P H E R;
11 changes: 10 additions & 1 deletion packages/language-support/src/antlr-grammar/CypherPreParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,13 @@ preparsedStatement:
preparserOption? statement;

preparserOption:
EXPLAIN | PROFILE;
EXPLAIN | PROFILE | cypherOptions;

cypherOptions:
CYPHER (cypherVersion | cypherOption)*;

cypherOption:
IDENTIFIER EQ (IDENTIFIER | numberLiteral);

cypherVersion:
UNSIGNED_DECIMAL_INTEGER;
1 change: 1 addition & 0 deletions packages/language-support/src/lexerSymbols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ export const lexerKeywords = [
// Preparser tokens
CypherLexer.EXPLAIN,
CypherLexer.PROFILE,
CypherLexer.CYPHER,
];

export const lexerConsoleCmds = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,27 @@ describe('MATCH auto-completion', () => {
});
});

test('Correctly completes even with preparser options', () => {
const query = 'CYPHER 25 runtime=pipelined MATCH (n:P';

testCompletions({
query,
dbSchema: { labels: ['Cat', 'Person', 'Dog'] },
expected: [{ label: 'Person', kind: CompletionItemKind.TypeParameter }],
});

const query2 = 'CYPHER 25 runtime=pipelined MATCH (n:Person)-[:W';

testCompletions({
query: query2,
dbSchema: {
labels: ['Cat', 'Person', 'Dog'],
relationshipTypes: ['WALKS', 'OWNS'],
},
expected: [{ label: 'WALKS', kind: CompletionItemKind.TypeParameter }],
});
});

test("Doesn't complete label before : is entered", () => {
const query = 'MATCH (n';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,186 @@ describe('Semantic validation spec', () => {
]);
});

test('Semantic errors work using empty preparser options. Like CYPHER <rest of query', () => {
const query = `CYPHER MATCH (n);
CYPHER MATCH (m) RETURN n`;

expect(getDiagnosticsForQuery({ query })).toEqual([
{
message:
'Query cannot conclude with MATCH (must be a RETURN clause, a FINISH clause, an update clause, a unit subquery call, or a procedure call with no YIELD).',
offsets: {
end: 16,
start: 7,
},
range: {
end: {
character: 16,
line: 0,
},
start: {
character: 7,
line: 0,
},
},
severity: 1,
},
{
message: 'Variable `n` not defined',
offsets: {
end: 62,
start: 61,
},
range: {
end: {
character: 44,
line: 1,
},
start: {
character: 43,
line: 1,
},
},
severity: 1,
},
]);
});

test('Semantic errors work using both version syntax and key-value syntax for preparser options. Like CYPHER <version> <option> = <value>', () => {
const query = `CYPHER 25 runtime = pipelined timeout = 1000 MATCH (n);
CYPHER 5 timeout= 555 MATCH (m) RETURN n`;

expect(getDiagnosticsForQuery({ query })).toEqual([
{
message:
'Query cannot conclude with MATCH (must be a RETURN clause, a FINISH clause, an update clause, a unit subquery call, or a procedure call with no YIELD).',
offsets: {
end: 54,
start: 45,
},
range: {
end: {
character: 54,
line: 0,
},
start: {
character: 45,
line: 0,
},
},
severity: 1,
},
{
message: 'Variable `n` not defined',
offsets: {
end: 115,
start: 114,
},
range: {
end: {
character: 59,
line: 1,
},
start: {
character: 58,
line: 1,
},
},
severity: 1,
},
]);
});

test('Semantic errors work with preparser option CYPHER <option> = <value>', () => {
const query = `CYPHER runtime = pipelined timeout = 1000 MATCH (n);
CYPHER planner=cost MATCH (m) RETURN n`;

expect(getDiagnosticsForQuery({ query })).toEqual([
{
message:
'Query cannot conclude with MATCH (must be a RETURN clause, a FINISH clause, an update clause, a unit subquery call, or a procedure call with no YIELD).',
offsets: {
end: 53,
start: 44,
},
range: {
end: {
character: 53,
line: 0,
},
start: {
character: 44,
line: 0,
},
},
severity: 1,
},
{
message: 'Variable `n` not defined',
offsets: {
end: 112,
start: 111,
},
range: {
end: {
character: 57,
line: 1,
},
start: {
character: 56,
line: 1,
},
},
severity: 1,
},
]);
});

test('Semantic errors work with preparser option CYPHER <version>', () => {
const query = `CYPHER 25 MATCH (n);
CYPHER 5 MATCH (m) RETURN n`;

expect(getDiagnosticsForQuery({ query })).toEqual([
{
message:
'Query cannot conclude with MATCH (must be a RETURN clause, a FINISH clause, an update clause, a unit subquery call, or a procedure call with no YIELD).',
offsets: {
end: 19,
start: 10,
},
range: {
end: {
character: 19,
line: 0,
},
start: {
character: 10,
line: 0,
},
},
severity: 1,
},
{
message: 'Variable `n` not defined',
offsets: {
end: 67,
start: 66,
},
range: {
end: {
character: 46,
line: 1,
},
start: {
character: 45,
line: 1,
},
},
severity: 1,
},
]);
});

test('Semantic errors work when we also have preparser options', () => {
const query = `EXPLAIN MATCH (n);
PROFILE MATCH (m) RETURN n`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('Syntactic validation spec', () => {
start: 0,
},
message:
'Expected any of ALTER, CALL, CREATE, DEALLOCATE, DELETE, DENY, DETACH, DROP, DRYRUN, ENABLE, EXPLAIN, FINISH, FOREACH, GRANT, INSERT, LIMIT, LOAD, MATCH, MERGE, NODETACH, OFFSET, OPTIONAL, ORDER, PROFILE, REALLOCATE, REMOVE, RENAME, RETURN, REVOKE, SET, SHOW, SKIP, START, STOP, TERMINATE, UNWIND, USE, USING or WITH',
'Expected any of ALTER, CALL, CREATE, CYPHER, DEALLOCATE, DELETE, DENY, DETACH, DROP, DRYRUN, ENABLE, EXPLAIN, FINISH, FOREACH, GRANT, INSERT, LIMIT, LOAD, MATCH, MERGE, NODETACH, OFFSET, OPTIONAL, ORDER, PROFILE, REALLOCATE, REMOVE, RENAME, RETURN, REVOKE, SET, SHOW, SKIP, START, STOP, TERMINATE, UNWIND, USE, USING or WITH',
range: {
end: {
character: 3,
Expand Down Expand Up @@ -1046,6 +1046,7 @@ describe('Syntactic validation spec', () => {

test.each([
`MATCH (n:Test1) RETURN n.profile`,
`MATCH (n:MyLabel) RETURN n.CYPHER`,
`CREATE (n:Test1 {explain: 'Explain'});`,
`RETURN { clear: 'Clear', params: 'params', history: 'history'}`,
])(
Expand Down
2 changes: 1 addition & 1 deletion packages/vscode-extension/syntaxes/cypher.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"name": "keyword.operator"
},
{
"match": "(?i)\\b(ACCESS|ACTIVE|ADMIN|ADMINISTRATOR|ALIAS|ALIASES|ALL|allShortestPaths|ALTER|AND|ANY|ARRAY|AS|ASC|ASCENDING|ASSERT|ASSIGN|AT|AUTH|BINDINGS|BOOL|BOOLEAN|BOOSTED|BOTH|BREAK|BRIEF|BTREE|BUILT|BY|CALL|CASCADE|CASE|CIDR|CHANGE|COLLECT|COMMAND|COMMANDS|COMMIT|COMPOSITE|CONSTRAINT|CONSTRAINTS|CONTAINS|CONTINUE|COPY|COUNT|CREATE|CSV|CONCURRENT|CURRENT|DATA|DATABASE|DATABASES|DATE|DATETIME|DBMS|DEALLOCATE|DEFAULT|DEFINED|DELETE|DENY|DESC|DESCENDING|DESTROY|DETACH|DIFFERENT|DISTINCT|DRIVER|DROP|DRYRUN|DUMP|DURATION|EACH|EDGE|ELEMENT|ELEMENTS|ELSE|ENABLE|ENCRYPTED|END|ENDS|ERROR|EXECUTABLE|EXECUTE|EXIST|EXISTENCE|EXISTS|FAIL|FALSE|FIELDTERMINATOR|FINISH|FLOAT|FOR|FOREACH|FROM|FULLTEXT|FUNCTION|FUNCTIONS|GRANT|GRAPH|GRAPHS|GROUP|GROUPS|HEADERS|HOME|ID|IF|IMMUTABLE|IMPERSONATE|IN|INDEX|INDEXES|INF|INFINITY|INSERT|INT|INTEGER|IS|JOIN|KEY|LABEL|LABELS|LEADING|LIMIT|LIST|LOAD|LOCAL|LOOKUP|MANAGEMENT|MAP|MATCH|MERGE|NAME|NAMES|NAN|NEW|NFC|NFD|NFKC|NFKD|NODE|NODETACH|NODES|NONE|NORMALIZE|NORMALIZED|NOT|NOTHING|NOWAIT|NULL|OF|OFFSET|ON|ONLY|OPTION|OPTIONAL|OPTIONS|OR|ORDER|OUTPUT|PASSWORD|PASSWORDS|PATH|PATHS|PERIODIC|PLAINTEXT|POINT|POPULATED|PRIMARY|PRIMARIES|PRIVILEGE|PRIVILEGES|PROCEDURE|PROCEDURES|PROPERTIES|PROPERTY|PROVIDER|PROVIDERS|RANGE|READ|REALLOCATE|REDUCE|REL|RELATIONSHIP|RELATIONSHIPS|REMOVE|RENAME|REPEATABLE|REPLACE|REPORT|REQUIRE|REQUIRED|RESTRICT|RETURN|REVOKE|ROLE|ROLES|ROW|ROWS|SCAN|SECONDARY|SECONDARIES|SEC|SECOND|SECONDS|SEEK|SERVER|SERVERS|SET|SETTING|SETTINGS|SHORTEST|shortestPath|SHOW|SIGNED|SINGLE|SKIP|START|STARTS|STATUS|STOP|VARCHAR|STRING|SUPPORTED|SUSPENDED|TARGET|TERMINATE|TEXT|THEN|TIME|TIMESTAMP|TIMEZONE|TO|TOPOLOGY|TRAILING|TRANSACTION|TRANSACTIONS|TRAVERSE|TRIM|TRUE|TYPE|TYPED|TYPES|UNION|UNIQUE|UNIQUENESS|UNWIND|URL|USE|USER|USERS|USING|VALUE|VECTOR|VERBOSE|VERTEX|WAIT|WHEN|WHERE|WITH|WITHOUT|WRITE|XOR|YIELD|ZONE|ZONED|EXPLAIN|PROFILE)\\b",
"match": "(?i)\\b(ACCESS|ACTIVE|ADMIN|ADMINISTRATOR|ALIAS|ALIASES|ALL|allShortestPaths|ALTER|AND|ANY|ARRAY|AS|ASC|ASCENDING|ASSERT|ASSIGN|AT|AUTH|BINDINGS|BOOL|BOOLEAN|BOOSTED|BOTH|BREAK|BRIEF|BTREE|BUILT|BY|CALL|CASCADE|CASE|CIDR|CHANGE|COLLECT|COMMAND|COMMANDS|COMMIT|COMPOSITE|CONSTRAINT|CONSTRAINTS|CONTAINS|CONTINUE|COPY|COUNT|CREATE|CSV|CONCURRENT|CURRENT|DATA|DATABASE|DATABASES|DATE|DATETIME|DBMS|DEALLOCATE|DEFAULT|DEFINED|DELETE|DENY|DESC|DESCENDING|DESTROY|DETACH|DIFFERENT|DISTINCT|DRIVER|DROP|DRYRUN|DUMP|DURATION|EACH|EDGE|ELEMENT|ELEMENTS|ELSE|ENABLE|ENCRYPTED|END|ENDS|ERROR|EXECUTABLE|EXECUTE|EXIST|EXISTENCE|EXISTS|FAIL|FALSE|FIELDTERMINATOR|FINISH|FLOAT|FOR|FOREACH|FROM|FULLTEXT|FUNCTION|FUNCTIONS|GRANT|GRAPH|GRAPHS|GROUP|GROUPS|HEADERS|HOME|ID|IF|IMMUTABLE|IMPERSONATE|IN|INDEX|INDEXES|INF|INFINITY|INSERT|INT|INTEGER|IS|JOIN|KEY|LABEL|LABELS|LEADING|LIMIT|LIST|LOAD|LOCAL|LOOKUP|MANAGEMENT|MAP|MATCH|MERGE|NAME|NAMES|NAN|NEW|NFC|NFD|NFKC|NFKD|NODE|NODETACH|NODES|NONE|NORMALIZE|NORMALIZED|NOT|NOTHING|NOWAIT|NULL|OF|OFFSET|ON|ONLY|OPTION|OPTIONAL|OPTIONS|OR|ORDER|OUTPUT|PASSWORD|PASSWORDS|PATH|PATHS|PERIODIC|PLAINTEXT|POINT|POPULATED|PRIMARY|PRIMARIES|PRIVILEGE|PRIVILEGES|PROCEDURE|PROCEDURES|PROPERTIES|PROPERTY|PROVIDER|PROVIDERS|RANGE|READ|REALLOCATE|REDUCE|REL|RELATIONSHIP|RELATIONSHIPS|REMOVE|RENAME|REPEATABLE|REPLACE|REPORT|REQUIRE|REQUIRED|RESTRICT|RETURN|REVOKE|ROLE|ROLES|ROW|ROWS|SCAN|SECONDARY|SECONDARIES|SEC|SECOND|SECONDS|SEEK|SERVER|SERVERS|SET|SETTING|SETTINGS|SHORTEST|shortestPath|SHOW|SIGNED|SINGLE|SKIP|START|STARTS|STATUS|STOP|VARCHAR|STRING|SUPPORTED|SUSPENDED|TARGET|TERMINATE|TEXT|THEN|TIME|TIMESTAMP|TIMEZONE|TO|TOPOLOGY|TRAILING|TRANSACTION|TRANSACTIONS|TRAVERSE|TRIM|TRUE|TYPE|TYPED|TYPES|UNION|UNIQUE|UNIQUENESS|UNWIND|URL|USE|USER|USERS|USING|VALUE|VECTOR|VERBOSE|VERTEX|WAIT|WHEN|WHERE|WITH|WITHOUT|WRITE|XOR|YIELD|ZONE|ZONED|EXPLAIN|PROFILE|CYPHER)\\b",
"name": "keyword"
}
]
Expand Down
Loading