feat: add generic and plsql basic parser file

This commit is contained in:
xiaowei
2020-09-11 17:39:10 +08:00
parent 68800312d3
commit f85163892a
83 changed files with 326433 additions and 155590 deletions

View File

@ -0,0 +1,111 @@
import { Token, Lexer } from 'antlr4';
import { ParseTreeWalker } from 'antlr4/tree';
import ParserErrorListener, {
ParserError,
ErrorHandler,
ParserErrorCollector,
} from './parserErrorListener';
/**
* Custom Parser class, subclass needs extends it.
*/
export default abstract class BasicParser<C = any> {
private _parser;
public parse(
input: string,
errorListener?: ErrorHandler,
) {
const parser = this.createParser(input);
this._parser = parser;
parser.removeErrorListeners();
parser.addErrorListener(new ParserErrorListener(errorListener));
const parserTree = parser.program();
return parserTree;
}
public validate(input: string): ParserError[] {
const lexerError = []; const syntaxErrors = [];
const parser = this.createParser(input);
this._parser = parser;
parser.removeErrorListeners();
parser.addErrorListener(new ParserErrorCollector(syntaxErrors));
parser.program();
return lexerError.concat(syntaxErrors);
}
/**
* Create antrl4 Lexer object
* @param input source string
*/
public abstract createLexer(input: string): Lexer;
/**
* Create Parser by lexer
* @param lexer Lexer
*/
public abstract createParserFromLexer(lexer: Lexer);
/**
* Visit parser tree
* @param parserTree
*/
// public abstract visit(visitor: any, parserTree: any);
/**
* The source string
* @param input string
*/
public getAllTokens(input: string): Token[] {
return this.createLexer(input).getAllTokens();
};
/**
* Get Parser instance by input string
* @param input
*/
public createParser(input: string) {
const lexer = this.createLexer(input);
const parser: any = this.createParserFromLexer(lexer);
parser.buildParseTrees = true;
this._parser = parser;
return parser;
}
/**
* It convert tree to string, it's convenient to use in unit test.
* @param string input
*/
public parserTreeToString(input: string): string {
const parser = this.createParser(input);
this._parser = parser;
const tree = parser.statement();
return tree.toStringTree(parser.ruleNames);
}
/**
* Get List-like style tree string
* @param parserTree
*/
public toString(parserTree: any): string {
return parserTree.toStringTree(this._parser.ruleNames);
}
/**
* @param listener Listener instance extends ParserListener
* @param parserTree parser Tree
*/
public listen(listener: any, parserTree: any) {
ParseTreeWalker.DEFAULT.walk(listener, parserTree);
}
}

View File

@ -0,0 +1,84 @@
import { Token, Recognizer } from 'antlr4';
import { ErrorListener } from 'antlr4/error';
export interface ParserError {
startLine: number;
endLine: number;
startCol: number;
endCol: number;
message: string;
}
export interface SyntaxError {
recognizer: Recognizer;
offendingSymbol: Token;
line: number;
charPositionInLine: number;
msg: string;
e: any;
}
export type ErrorHandler = (err: ParserError, errOption: SyntaxError) => void;
export class ParserErrorCollector extends ErrorListener {
private _errors: ParserError[];
constructor(error: ParserError[]) {
super();
this._errors = error;
}
syntaxError(
recognizer: Recognizer, offendingSymbol: Token, line: number,
charPositionInLine: number, msg: string, e: any,
) {
let endCol = charPositionInLine + 1;
if (offendingSymbol &&offendingSymbol.text !== null) {
endCol = charPositionInLine + offendingSymbol.text.length;
}
this._errors.push({
startLine: line,
endLine: line,
startCol: charPositionInLine,
endCol: endCol,
message: msg,
});
}
}
export default class ParserErrorListener extends ErrorListener {
private _errorHandler;
constructor(errorListener: ErrorHandler) {
super();
this._errorHandler = errorListener;
}
syntaxError(
recognizer: Recognizer, offendingSymbol: Token, line: number,
charPositionInLine: number, msg: string, e: any,
) {
let endCol = charPositionInLine + 1;
if (offendingSymbol &&offendingSymbol.text !== null) {
endCol = charPositionInLine + offendingSymbol.text.length;
}
if (this._errorHandler) {
this._errorHandler({
startLine: line,
endLine: line,
startCol: charPositionInLine,
endCol: endCol,
message: msg,
}, {
e,
line,
msg,
recognizer,
offendingSymbol,
charPositionInLine,
});
}
}
}

20
src/parser/generic.ts Normal file
View File

@ -0,0 +1,20 @@
import { InputStream, CommonTokenStream, Lexer } from 'antlr4';
import { SqlLexer } from '../lib/generic/SqlLexer';
import { SqlParser } from '../lib/generic/SqlParser';
export * from '../lib/generic/SqlParserVisitor';
export * from '../lib/generic/SqlParserListener';
import BasicParser from './common/BasicParser';
export default class GenericSQL extends BasicParser {
public createLexer(input: string): Lexer {
const chars = new InputStream(input.toUpperCase()); // Some Lexer only support uppercase token, So you need transform
const lexer = <unknown> new SqlLexer(chars) as Lexer;
return lexer;
}
public createParserFromLexer(lexer: Lexer) {
const tokenStream = new CommonTokenStream(lexer);
return new SqlParser(tokenStream);
}
}

20
src/parser/hive.ts Normal file
View File

@ -0,0 +1,20 @@
import { InputStream, CommonTokenStream, Lexer } from 'antlr4';
import { HiveSqlLexer } from '../lib/hive/HiveSqlLexer';
import { HiveSqlParser } from '../lib/hive/HiveSqlParser';
export * from '../lib/hive/HiveSqlListener';
export * from '../lib/hive/HiveSqlVisitor';
import BasicParser from './common/BasicParser';
export default class HiveSQL extends BasicParser {
public createLexer(input: string): Lexer {
const chars = new InputStream(input);
const lexer = <unknown> new HiveSqlLexer(chars) as Lexer;
return lexer;
}
public createParserFromLexer(lexer: Lexer) {
const tokenStream = new CommonTokenStream(lexer);
return new HiveSqlParser(tokenStream);
}
}

3
src/parser/index.ts Normal file
View File

@ -0,0 +1,3 @@
export * from './generic';
export * from './plsql';
export * from './hive';

19
src/parser/plsql.ts Normal file
View File

@ -0,0 +1,19 @@
import { InputStream, CommonTokenStream, Lexer } from 'antlr4';
import { PlSqlLexer } from '../lib/plsql/PlSqlLexer';
import { PlSqlParser } from '../lib/plsql/PlSqlParser';
export * from '../lib/plsql/PlSqlParserListener';
export * from '../lib/plsql/PlSqlParserVisitor';
import BasicParser from './common/BasicParser';
export default class PLSQLParser extends BasicParser {
public createLexer(input: string): Lexer {
const chars = new InputStream(input.toUpperCase());
const lexer = <unknown> new PlSqlLexer(chars) as Lexer;
return lexer;
}
public createParserFromLexer(lexer: Lexer) {
const tokenStream = new CommonTokenStream(lexer);
return new PlSqlParser(tokenStream);
}
}