import { ATOM , Compiler , EOF , ERROR_Eof_Unmatch , ERROR_Not_IncorrectMatch , ERROR_Or_UnmatchAll , ERROR_Pair_Unmatch , ERROR_Regex_Unmatch , ERROR_Repeat_RangeError , ERROR_Seq_Stop , ERROR_Token_Unmatch , InternalParser , NOT , OR , PAIR_CLOSE , PAIR_OPEN , ParseContext , ParseError , ParseErrorData , ParseResult , ParseSuccess , Range , REF , REGEX , REPEAT , Repeat , Reshape , Rule , SEQ , TOKEN , } from "./types" ; import { buildRangesToString , createRegexMatcher , createStringMatcher , } from "./utils" ; const USE_RANGE = Symbol ( ) ; export const createParseSuccess = ( ctx , result , pos , len , ranges = [ [ pos , pos + len ] ] , reshape ? ) => { let newRanges = [ ] ; if ( ranges . length > 0 ) { newRanges = ranges . reduce ( ( acc , range , index ) => { if ( index === 0 ) return [ range ] ; if ( typeof range === "string" ) { return [ ... acc , range ] as ( Range | string ) [ ] ; } const [ nextStart , nextEnd ] = range ; if ( nextStart === nextEnd ) return acc ; const last = acc . slice ( - 1 ) [ 0 ] ; if ( typeof last === "string" ) { return [ ... acc , range ] ; } if ( last [ 1 ] === nextStart ) { return [ ... acc . slice ( 0 , - 1 ) , [ last [ 0 ] , nextEnd ] ] ; } return [ ... acc , [ nextStart , nextEnd ] ] ; } , [ ] as ( Range | string ) [ ] ) ; } if ( result === USE_RANGE ) { result = buildRangesToString ( ctx . raw , newRanges ) ; } return { error : false , result : reshape ? reshape ( result ) : result , len , pos , ranges : newRanges , reshaped : ! ! reshape , } as ParseSuccess ; } ; export function createParseError < ErrorData extends ParseErrorData > ( rule , pos , rootId , errorData ) { return { error : true , rootId , pos , ... errorData , } ; } export function compileFragment ( rule , compiler , rootId ) { const internalParser = compileFragmentInternal ( rule , compiler , rootId ) ; const parser = ( ctx , pos ) => { const beforeStack = ctx . openStack . slice ( ) ; const ret = ctx . cache . getOrCreate ( rule . id , pos , ( ) => { return internalParser ( ctx , pos ) ; } ) ; if ( ret . error ) { ctx . openStack = beforeStack ; } return ret ; } ; return parser ; } function compileFragmentInternal ( rule , compiler , rootId ) { switch ( rule . kind ) { case NOT : { const childParsers = rule . patterns . map ( ( pat ) => compileFragment ( pat , compiler , rootId ) ) ; return ( ctx , pos ) => { for ( const childParser of childParsers ) { const result = childParser ( ctx , pos ) ; if ( result . error === true ) { return createParseSuccess ( ctx , result , pos , 0 , undefined , rule . reshape ) ; } } return createParseError ( rule , pos , rootId , { code : ERROR_Not_IncorrectMatch , } ) ; } ; } case REF : { return ( ctx , pos ) => { const resolved = compiler . parsers . get ( rule . ref ) ; return resolved ! ( ctx , pos ) ; } ; } case ATOM : { return ( ctx , pos ) => { return rule . parse ( ctx , pos ) ; } ; } case EOF : { return ( ctx , pos ) => { const ended = Array . from ( ctx . raw ) . length === pos ; if ( ended ) { return createParseSuccess ( ctx , "" , pos , 0 , undefined , rule . reshape ) ; } return createParseError ( rule , pos , rootId , { code : ERROR_Eof_Unmatch , } ) ; } ; } case REGEX : { let expr = rule . expr ; const matcher = createRegexMatcher ( expr ) ; return ( ctx , pos ) => { const matched = matcher ( ctx . raw , pos ) ; if ( matched == null ) { if ( rule . optional ) { return createParseSuccess ( ctx , null , pos , 0 , undefined , rule . reshape ) ; } else { return createParseError ( rule , pos , rootId , { code : ERROR_Regex_Unmatch , expr : expr , } ) ; } } return createParseSuccess ( ctx , matched , pos , Array . from ( matched ) . length , undefined , rule . reshape ) ; } ; } case TOKEN : { let expr = rule . expr ; const matcher = createStringMatcher ( expr ) ; return ( ctx , pos ) => { const matched = matcher ( ctx . raw , pos ) ; if ( matched == null ) { if ( rule . optional ) { return createParseSuccess ( ctx , null , pos , 0 , undefined , rule . reshape ) ; } else { return createParseError ( rule , pos , rootId , { code : ERROR_Token_Unmatch , } ) ; } } return createParseSuccess ( ctx , matched , pos , Array . from ( matched ) . length , undefined , rule . reshape ) ; } ; } case OR : { const compiledPatterns = rule . patterns . map ( ( p ) => { return { parse : compileFragment ( p , compiler , rootId ) , node : p , } ; } ) ; return ( ctx , pos ) => { const errors = [ ] ; for ( const next of compiledPatterns ) { const parsed = next . parse ( ctx , pos ) ; if ( parsed . error === true ) { if ( rule . optional ) { return createParseSuccess ( ctx , undefined , pos , 0 , undefined , rule . reshape ) ; } errors . push ( parsed ) ; continue ; } return parsed as ParseResult ; } return createParseError ( rule , pos , rootId , { code : ERROR_Or_UnmatchAll , errors , } ) ; } ; } case REPEAT : { const parser = compileFragment ( rule . pattern , compiler , rootId ) ; return ( ctx , pos ) => { const repeat = rule as Repeat ; const xs = [ ] ; let ranges = [ ] ; let cursor = pos ; while ( cursor < Array . from ( ctx . raw ) . length ) { const parseResult = parser ( ctx , cursor ) ; if ( parseResult . error === true ) break ; if ( parseResult . len === 0 ) { throw new Error ( `ZeroRepeat` ) ; } xs . push ( parseResult . result ) ; ranges . push ( ... parseResult . ranges ) ; cursor += parseResult . len ; } if ( xs . length < repeat . min || ( repeat . max && xs . length > repeat . max ) ) { return createParseError ( rule , pos , rootId , { code : ERROR_Repeat_RangeError , } ) ; } return createParseSuccess ( ctx , rule . reshape ? xs . map ( rule . reshape as any ) : xs , pos , cursor - pos , ranges ) ; } ; } case SEQ : { let isObjectMode = false ; const parsers = rule . children . map ( ( c ) => { const parse = compileFragment ( c , compiler , rootId ) ; if ( c . key ) isObjectMode = true ; return { parse , node : c } ; } ) ; return ( ctx , pos ) => { let cursor = pos ; if ( isObjectMode ) { const result = { } ; for ( const parser of parsers ) { const parseResult = parser . parse ( ctx , cursor ) ; if ( parseResult . error ) { if ( parser . node . optional ) continue ; return createParseError ( rule , cursor , rootId , { code : ERROR_Seq_Stop , childError : parseResult , index : parsers . indexOf ( parser ) , } ) ; } if ( parser . node . key && ! parser . node . skip ) { const reshaped = parseResult . result ; result [ parser . node . key ] = reshaped ; } cursor += parseResult . len ; } return createParseSuccess ( ctx , result , pos , cursor - pos , undefined , rule . reshape ) ; } else { let ranges = [ ] ; for ( const parser of parsers ) { const parseResult = parser . parse ( ctx , cursor ) ; if ( parseResult . error ) { if ( parser . node . optional ) continue ; return createParseError ( rule , cursor , rootId , { code : ERROR_Seq_Stop , childError : parseResult , index : parsers . indexOf ( parser ) , } ) ; } if ( ! parser . node . skip ) { if ( parseResult . reshaped ) { ranges . push ( parseResult . result ) ; } else { ranges . push ( ... parseResult . ranges ) ; } } cursor += parseResult . len ; } return createParseSuccess ( ctx , USE_RANGE , pos , cursor - pos , ranges , rule . reshape ) ; } } ; } case PAIR_OPEN : { const parser = compileFragment ( rule . pattern , compiler , rootId ) ; return ( ctx , pos ) => { const parsed = parser ( ctx , pos ) ; if ( ! parsed . error ) { ctx . openStack . push ( parsed . result as string ) ; } return parsed ; } ; } case PAIR_CLOSE : { const parser = compileFragment ( rule . pattern , compiler , rootId ) ; return ( ctx , pos ) => { const parsed = parser ( ctx , pos ) ; if ( ! parsed . error ) { const lastItem = ctx . openStack . slice ( - 1 ) [ 0 ] ; if ( lastItem === parsed . result ) { ctx . openStack . pop ( ) ; return parsed ; } else { return createParseError ( rule , pos , rootId , { code : ERROR_Pair_Unmatch , } ) ; } } return parsed ; } ; } default : { throw new Error ( ) ; } } }