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

[DON'T MERGE] Moves syntax errors to the semantic analysis for supporting both Cypher25 and Cypher5 #300

Closed
wants to merge 5 commits into from
Closed
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
6 changes: 3 additions & 3 deletions modify-semantic-analysis.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ fs.readFile(file, 'utf8', function (err, data) {
` cnsa_Main_$callClinit();
var$1 = var$1.data;
cnsa_Main_updateSignatureResolver(null);
cnsa_Main_analyzeQuery(var$1[0]);
cnsa_Main_analyzeQuery(var$1[0], $rt_s(1));
cnsa_Main_updateSignatureResolver(null);
cnsa_Main_analyzeQuery(var$1[0]);`,
cnsa_Main_analyzeQuery(var$1[0], $rt_s(2));`,
` cnsa_Main_$callClinit();`,
);

Expand All @@ -51,7 +51,7 @@ $rt_exports.main([]);
$rt_exports.updateSignatureResolver = cnsa_Main_updateSignatureResolver;

// Export the analyze function as well
$rt_exports.semanticAnalysis = $rt_mainStarter(($args) => cnsa_Main_analyzeQuery($args.data[0]));`,
$rt_exports.semanticAnalysis = $rt_mainStarter(($args) => cnsa_Main_analyzeQuery($args.data[0], $args.data[1]));`,
);
fs.writeFile(file, result, 'utf8', function (err) {
if (err) return console.log(err);
Expand Down
12 changes: 5 additions & 7 deletions packages/language-server/src/lintWorker.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { validateSemantics } from '@neo4j-cypher/language-support';
import { lintCypherQuery } from '@neo4j-cypher/language-support';
import workerpool from 'workerpool';

workerpool.worker({ validateSemantics });
workerpool.worker({ lintCypherQuery });

type LinterArgs = Parameters<typeof validateSemantics>;
type LinterArgs = Parameters<typeof lintCypherQuery>;

export type LinterTask = workerpool.Promise<
ReturnType<typeof validateSemantics>
>;
export type LinterTask = workerpool.Promise<ReturnType<typeof lintCypherQuery>>;

export type LintWorker = {
validateSemantics: (...args: LinterArgs) => LinterTask;
lintCypherQuery: (...args: LinterArgs) => LinterTask;
};
29 changes: 11 additions & 18 deletions packages/language-server/src/linting.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { validateSyntax } from '@neo4j-cypher/language-support';
import { Neo4jSchemaPoller } from '@neo4j-cypher/schema-poller';
import debounce from 'lodash.debounce';
import { join } from 'path';
Expand Down Expand Up @@ -28,25 +27,19 @@ async function rawLintDocument(
}

const dbSchema = neo4j.metadata?.dbSchema ?? {};
const syntaxErrors = validateSyntax(query, dbSchema);

sendDiagnostics(syntaxErrors);

if (syntaxErrors.length === 0) {
try {
if (lastSemanticJob !== undefined && !lastSemanticJob.resolved) {
void lastSemanticJob.cancel();
}
try {
if (lastSemanticJob !== undefined && !lastSemanticJob.resolved) {
void lastSemanticJob.cancel();
}

const proxyWorker = (await pool.proxy()) as unknown as LintWorker;
lastSemanticJob = proxyWorker.validateSemantics(query, dbSchema);
const result = await lastSemanticJob;
const proxyWorker = (await pool.proxy()) as unknown as LintWorker;
lastSemanticJob = proxyWorker.lintCypherQuery(query, dbSchema);
const result = await lastSemanticJob;

sendDiagnostics(result);
} catch (err) {
if (!(err instanceof workerpool.Promise.CancellationError)) {
console.error(err);
}
sendDiagnostics(result);
} catch (err) {
if (!(err instanceof workerpool.Promise.CancellationError)) {
console.error(err);
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion packages/language-support/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@
"vscode-languageserver-types": "^3.17.3"
},
"scripts": {
"gen-preparser": "antlr4 -Dlanguage=TypeScript src/PreParser/antlr-grammar/PreParser.g4 -o src/PreParser/generated-preparser/ -Xexact-output-dir",
"gen-parser": "antlr4 -Dlanguage=TypeScript -visitor src/antlr-grammar/CypherCmdLexer.g4 src/antlr-grammar/CypherCmdParser.g4 -o src/generated-parser/ -Xexact-output-dir",
"build": "npm run gen-parser && concurrently 'npm:build-types' 'npm:build-esm' 'npm:build-commonjs'",
"build": "npm run gen-preparser && npm run gen-parser && concurrently 'npm:build-types' 'npm:build-esm' 'npm:build-commonjs'",
"build-types": "tsc --emitDeclarationOnly --outDir dist/types",
"build-esm": "esbuild ./src/index.ts --bundle --format=esm --sourcemap --outfile=dist/esm/index.mjs",
"build-commonjs": "esbuild ./src/index.ts --bundle --format=cjs --sourcemap --outfile=dist/cjs/index.cjs",
Expand Down
58 changes: 58 additions & 0 deletions packages/language-support/src/PreParser/antlr-grammar/PreParser.g4
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
grammar PreParser;
options { caseInsensitive = true; }

query:
cypherVersion? CHAR* EOF;

cypherVersion:
cypherFive | cypherTwentyFive;

cypherFive:
CYPHER FIVE;

FIVE: '5';

TWENTYFIVE: '25';

cypherTwentyFive:
CYPHER TWENTYFIVE;

CYPHER:
'CYPHER';

SPACE
: ( '\u0009'
| '\n' //can't parse this in unicode
| '\u000B'
| '\u000C'
| '\r' //can't parse this in unicode
| '\u001C'
| '\u001D'
| '\u001E'
| '\u001F'
| '\u0020'
| '\u0085'
| '\u00A0'
| '\u1680'
| '\u2000'
| '\u2001'
| '\u2002'
| '\u2003'
| '\u2004'
| '\u2005'
| '\u2006'
| '\u2007'
| '\u2008'
| '\u2009'
| '\u200A'
| '\u2028'
| '\u2029'
| '\u202F'
| '\u205F'
| '\u3000'
) -> channel (HIDDEN)
;


CHAR:
[\u0000-\uFFFE];
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Generated from src/PreParser/antlr-grammar/PreParser.g4 by ANTLR 4.13.2
// noinspection ES6UnusedImports,JSUnusedGlobalSymbols,JSUnusedLocalSymbols
import {
ATN,
ATNDeserializer,
CharStream,
DecisionState, DFA,
Lexer,
LexerATNSimulator,
RuleContext,
PredictionContextCache,
Token
} from "antlr4";
export default class PreParserLexer extends Lexer {
public static readonly FIVE = 1;
public static readonly TWENTYFIVE = 2;
public static readonly CYPHER = 3;
public static readonly SPACE = 4;
public static readonly CHAR = 5;
public static readonly EOF = Token.EOF;

public static readonly channelNames: string[] = [ "DEFAULT_TOKEN_CHANNEL", "HIDDEN" ];
public static readonly literalNames: (string | null)[] = [ null, "'5'",
"'25'", "'CYPHER'" ];
public static readonly symbolicNames: (string | null)[] = [ null, "FIVE",
"TWENTYFIVE",
"CYPHER", "SPACE",
"CHAR" ];
public static readonly modeNames: string[] = [ "DEFAULT_MODE", ];

public static readonly ruleNames: string[] = [
"FIVE", "TWENTYFIVE", "CYPHER", "SPACE", "CHAR",
];


constructor(input: CharStream) {
super(input);
this._interp = new LexerATNSimulator(this, PreParserLexer._ATN, PreParserLexer.DecisionsToDFA, new PredictionContextCache());
}

public get grammarFileName(): string { return "PreParser.g4"; }

public get literalNames(): (string | null)[] { return PreParserLexer.literalNames; }
public get symbolicNames(): (string | null)[] { return PreParserLexer.symbolicNames; }
public get ruleNames(): string[] { return PreParserLexer.ruleNames; }

public get serializedATN(): number[] { return PreParserLexer._serializedATN; }

public get channelNames(): string[] { return PreParserLexer.channelNames; }

public get modeNames(): string[] { return PreParserLexer.modeNames; }

public static readonly _serializedATN: number[] = [4,0,5,29,6,-1,2,0,7,
0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,1,0,1,0,1,1,1,1,1,1,1,2,1,2,1,2,1,2,1,
2,1,2,1,2,1,3,1,3,1,3,1,3,1,4,1,4,0,0,5,1,1,3,2,5,3,7,4,9,5,1,0,8,2,0,67,
67,99,99,2,0,89,89,121,121,2,0,80,80,112,112,2,0,72,72,104,104,2,0,69,69,
101,101,2,0,82,82,114,114,10,0,9,13,28,32,133,133,160,160,5760,5760,8192,
8202,8232,8233,8239,8239,8287,8287,12288,12288,1,0,0,65534,28,0,1,1,0,0,
0,0,3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,0,0,9,1,0,0,0,1,11,1,0,0,0,3,13,1,0,
0,0,5,16,1,0,0,0,7,23,1,0,0,0,9,27,1,0,0,0,11,12,5,53,0,0,12,2,1,0,0,0,
13,14,5,50,0,0,14,15,5,53,0,0,15,4,1,0,0,0,16,17,7,0,0,0,17,18,7,1,0,0,
18,19,7,2,0,0,19,20,7,3,0,0,20,21,7,4,0,0,21,22,7,5,0,0,22,6,1,0,0,0,23,
24,7,6,0,0,24,25,1,0,0,0,25,26,6,3,0,0,26,8,1,0,0,0,27,28,7,7,0,0,28,10,
1,0,0,0,1,0,1,0,1,0];

private static __ATN: ATN;
public static get _ATN(): ATN {
if (!PreParserLexer.__ATN) {
PreParserLexer.__ATN = new ATNDeserializer().deserialize(PreParserLexer._serializedATN);
}

return PreParserLexer.__ATN;
}


static DecisionsToDFA = PreParserLexer._ATN.decisionToState.map( (ds: DecisionState, index: number) => new DFA(ds, index) );
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Generated from src/PreParser/antlr-grammar/PreParser.g4 by ANTLR 4.13.2

import {ParseTreeListener} from "antlr4";


import { QueryContext } from "./PreParserParser.js";
import { CypherVersionContext } from "./PreParserParser.js";
import { CypherFiveContext } from "./PreParserParser.js";
import { CypherTwentyFiveContext } from "./PreParserParser.js";


/**
* This interface defines a complete listener for a parse tree produced by
* `PreParserParser`.
*/
export default class PreParserListener extends ParseTreeListener {
/**
* Enter a parse tree produced by `PreParserParser.query`.
* @param ctx the parse tree
*/
enterQuery?: (ctx: QueryContext) => void;
/**
* Exit a parse tree produced by `PreParserParser.query`.
* @param ctx the parse tree
*/
exitQuery?: (ctx: QueryContext) => void;
/**
* Enter a parse tree produced by `PreParserParser.cypherVersion`.
* @param ctx the parse tree
*/
enterCypherVersion?: (ctx: CypherVersionContext) => void;
/**
* Exit a parse tree produced by `PreParserParser.cypherVersion`.
* @param ctx the parse tree
*/
exitCypherVersion?: (ctx: CypherVersionContext) => void;
/**
* Enter a parse tree produced by `PreParserParser.cypherFive`.
* @param ctx the parse tree
*/
enterCypherFive?: (ctx: CypherFiveContext) => void;
/**
* Exit a parse tree produced by `PreParserParser.cypherFive`.
* @param ctx the parse tree
*/
exitCypherFive?: (ctx: CypherFiveContext) => void;
/**
* Enter a parse tree produced by `PreParserParser.cypherTwentyFive`.
* @param ctx the parse tree
*/
enterCypherTwentyFive?: (ctx: CypherTwentyFiveContext) => void;
/**
* Exit a parse tree produced by `PreParserParser.cypherTwentyFive`.
* @param ctx the parse tree
*/
exitCypherTwentyFive?: (ctx: CypherTwentyFiveContext) => void;
}

Loading
Loading