@neo4j-cypher/language-support
Version:
Cypher language support
76 lines (65 loc) • 2.73 kB
text/typescript
import { CharStreams, CommonTokenStream } from 'antlr4';
import CypherLexer from '../generated-parser/CypherCmdLexer';
import CypherParser from '../generated-parser/CypherCmdParser';
import { getTokens } from '../helpers';
import { CypherTokenType, lexerSymbols, tokenNames } from '../lexerSymbols';
describe('Lexer tokens', () => {
test('List of tokens should match generated parser symbolic names', () => {
// Read in auto-generated symbolic names from the parser and remove special characters
const symbolicNames = CypherLexer.symbolicNames;
// Lexer keyword list which we want to keep up-to-date
// Note that it is numeric so we need to map to it's corresponding symbolicName
const lexerKeywords = Object.keys(lexerSymbols).map((k) => {
const symbolNumber = parseInt(k, 10);
// There is no "-1" position in the symbolic name list so we handle EOF separately
if (symbolNumber === CypherParser.EOF) {
return 'EOF';
}
return symbolicNames[symbolNumber];
});
// Check for keywords that exist in the parser but not the lexer
const addedKeywords = symbolicNames.filter(
(x) => x !== null && !lexerKeywords.includes(x),
);
expect(addedKeywords).toHaveLength(0);
// Check for keywords that exist in the lexer but not the parser
// With the current setup this should not happen
const deletedKeywords = lexerKeywords.filter(
(x) => x !== 'EOF' && !symbolicNames.includes(x),
);
expect(deletedKeywords).toHaveLength(0);
});
test('Names of lexer keywords can be lexed back into the token', () => {
const keywordTokens = Object.entries(lexerSymbols)
.filter(([, v]) => v === CypherTokenType.keyword)
.map(([k]) => parseInt(k, 10));
keywordTokens.forEach((token) => {
const tokenName = tokenNames[token];
const inputStream = CharStreams.fromString(tokenName);
const lexer = new CypherLexer(inputStream);
const tokenStream = new CommonTokenStream(lexer);
tokenStream.fill();
const tokens = getTokens(tokenStream);
expect(tokens.length).toBe(2);
// If the test fails, it is useful to see the token that was parsed
if (tokens[0].type !== token) {
console.error(
'unexpected token',
tokenName,
token,
'symbolic name',
CypherLexer.symbolicNames[token],
);
console.error(
'expected token',
tokenNames[tokens[0].type],
tokens[0].type,
'symbolic name',
CypherLexer.symbolicNames[tokens[0].type],
);
}
expect(tokens[0].type).toBe(token);
expect(tokens[1].type).toBe(CypherLexer.EOF);
});
});
});