feat: add generic and plsql basic parser file
This commit is contained in:
111
src/parser/common/BasicParser.ts
Normal file
111
src/parser/common/BasicParser.ts
Normal 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);
|
||||
}
|
||||
}
|
84
src/parser/common/parserErrorListener.ts
Normal file
84
src/parser/common/parserErrorListener.ts
Normal 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
20
src/parser/generic.ts
Normal 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
20
src/parser/hive.ts
Normal 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
3
src/parser/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './generic';
|
||||
export * from './plsql';
|
||||
export * from './hive';
|
19
src/parser/plsql.ts
Normal file
19
src/parser/plsql.ts
Normal 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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user