import { Parser as NearleyParser , Grammar as NearleyGrammar } from 'nearley' ; import { JSONSchema7 } from 'json-schema' ; import { isString } from './shared/utils' ; import { Grammar as MySQLGrammarRules } from './mysql/language' ; import { format as MySQLCompactFormatter } from './mysql/formatter/compact' ; import { format as MySQLJSONSchemaFormatter } from './mysql/formatter/json-schema' ; import { JSONSchemaFormatOptions , JSONSchemaFormatter , CompactJSONFormat , CompactFormatter , P_MAIN , P_DDS , } from './typings' ; /**
 * Main Parser class, wraps nearley parser main methods.
 */ export class Parser { private compiledGrammar ! : NearleyGrammar ; private parser ! : NearleyParser ; private jsonSchemaFormatter ! : JSONSchemaFormatter ; private compactFormatter ! : CompactFormatter ; /**
     * Parsed statements.
     */ private statements : string [ ] = [ ] ; /**
     * Remains of string feed, after last parsed statement.
     */ private remains = '' ; /**
     * Whether preparser is currently escaped.
     */ private escaped = false ; /**
     * Current quote char of preparser.
     */ private quoted = '' ; param dialect constructor ( dialect = 'mysql' ) { if ( ! dialect || dialect === 'mysql' || dialect === 'mariadb' ) { this . compiledGrammar = NearleyGrammar . fromCompiled ( MySQLGrammarRules ) ; this . compactFormatter = MySQLCompactFormatter ; this . jsonSchemaFormatter = MySQLJSONSchemaFormatter ; } else { throw new TypeError ( `Unsupported SQL dialect given to parser: '${ dialect }. ` + "Please provide 'mysql', 'mariadb' or none to use default." ) ; } this . resetParser ( ) ; } param chunk feed ( chunk ) { let i ; let char ; let parsed = '' ; let lastStatementIndex = 0 ; for ( i = 0 ; i < chunk . length ; i += 1 ) { char = chunk [ i ] ; parsed += char ; if ( char === '\\' ) { this . escaped = ! this . escaped ; } else { if ( ! this . escaped && Parser . isQuoteChar ( char ) ) { if ( this . quoted ) { if ( this . quoted === char ) { this . quoted = '' ; } } else { this . quoted = char ; } } else if ( char === ';' && ! this . quoted ) { const statement = this . remains + parsed . substr ( lastStatementIndex , i + 1 ) ; this . statements . push ( statement ) ; this . remains = '' ; lastStatementIndex = i + 1 ; } this . escaped = false ; } } this . remains += parsed . substr ( lastStatementIndex ) ; return this ; } /**
     * Recreates NearleyParser using grammar given in constructor.
     */ resetParser ( ) { this . parser = new NearleyParser ( this . compiledGrammar ) ; } param char private static isQuoteChar ( char ) { return char === '"' || char === "'" || char === '`' ; } param results private static tidy ( results ) { return results [ 0 ] ; } /**
     * Parser results getter. Will run nearley parser on string fed to this parser.
     */ get results ( ) : P_MAIN { let lineCount = 1 ; let statement = this . statements . shift ( ) ; const results = [ ] ; /**
         * Since we separate the statements, if there is a parse error in a block among
         * several statements in a stream, the parser will throw the error with line
         * 0 counting from the beginning of the statement, not the stream. So we
         * need to catch and correct line count incrementally along the stream.
         *
         * https://github.com/duartealexf/sql-ddl-to-json-schema/issues/20
         */ try { while ( statement ) { this . parser . feed ( statement ) ; lineCount += ( statement . match ( /\r\n|\r|\n/g ) ?? [ ] ) . length ; results . push ( Parser . tidy ( this . parser . results ) ) ; statement = this . statements . shift ( ) ; this . resetParser ( ) ; } } catch ( e ) { /**
             * Apply line count correction.
             */ if ( e . message && isString ( e . message ) ) { const matches = e . message . match ( /at line (\d+)/ ) ; if ( matches && Array . isArray ( matches ) && matches . length > 1 ) { const errorLine = Number ( matches [ 1 ] ) ; const newCount = lineCount + errorLine - 1 ; e . message = e . message . replace ( /\d+/ , newCount ) ; } } /**
             * Reset everything to not affect next feed.
             */ this . resetParser ( ) ; this . statements = [ ] ; this . remains = '' ; this . escaped = false ; this . quoted = '' ; throw e ; } /**
         * Reset remains to not affect next feed.
         */ this . remains = '' ; this . escaped = false ; this . quoted = '' ; return { id : 'MAIN' , def : results , } ; } param json toCompactJson ( json ? ) { return this . compactFormatter ( json ?? this . results ) ; } param tables param options toJsonSchemaArray ( options = { useRef : true , } , tables ? ) { return this . jsonSchemaFormatter ( tables ?? this . toCompactJson ( ) , options ) ; } }