import * as AST from "./ast" ; const digitRegex = /\d+/ ; function gen ( ast , withSlash ? ) { const nodes = Array . isArray ( ast ) ? ast : ast . body ; const r = nodes . map ( ( node ) => { let regex = "" ; switch ( node . type ) { case "choice" : regex = genChoice ( node ) ; break ; case "group" : regex += genGroup ( node ) ; break ; case "character" : regex += genCharacter ( node ) ; break ; case "boundaryAssertion" : regex += genBoundaryAssertionNode ( node ) ; break ; case "lookAroundAssertion" : regex += genLookaroundAssertionNode ( node ) ; break ; case "backReference" : regex += genBackReference ( node ) ; break ; default : break ; } if ( node . type === "character" || node . type === "group" ) { regex += genQuantifier ( node ) ; } return regex ; } ) . join ( "" ) ; if ( ! Array . isArray ( ast ) ) { if ( ( withSlash === undefined && ast . withSlash ) || withSlash ) { const f = ast . flags . map ( ( flag ) => flag ) . join ( "" ) ; return `/${ r }/${ f }` ; } } return r ; } function genChoice ( node ) { const { branches } = node ; return branches . map ( ( branch ) => { return gen ( branch ) ; } ) . join ( "|" ) ; } function genGroup ( node ) { const { children } = node ; const content = gen ( children ) ; switch ( node . kind ) { case "capturing" : return "(" + content + ")" ; case "namedCapturing" : return "(?<" + node . name + ">" + content + ")" ; case "nonCapturing" : return "(?:" + content + ")" ; default : break ; } } function prefix ( value ) { return value . replace ( /[|\\{}()[\]^$+*?./]/g , "\\$&" ) ; } function genCharacter ( node ) { switch ( node . kind ) { case "ranges" : const { negate , ranges } = node ; let str = "" ; ranges . forEach ( ( { from , to } , index ) => { if ( from === "]" || from === "\\" ) { from = "\\" + from ; } if ( to === "]" || to === "\\" ) { to = "\\" + to ; } if ( ! ( index === 0 || index === ranges . length - 1 ) ) { if ( from === "-" ) { from = "\\-" ; } if ( to === "-" ) { to = "\\-" ; } } if ( from !== to ) { str += from + "-" + to ; } else { str += from ; } } ) ; return ( negate ? "[^" : "[" ) + str + "]" ; case "string" : return prefix ( node . value ) ; case "class" : return node . value ; default : return "" ; } } function genQuantifier ( node ) { const { quantifier } = node ; if ( ! quantifier ) { return "" ; } const { kind , min , max , greedy } = quantifier ; let result = "" ; switch ( kind ) { case "*" : result = "*" ; break ; case "+" : result = "+" ; break ; case "?" : result = "?" ; break ; case "custom" : if ( min === max ) { result = `{${ min }}` ; } else if ( max === Infinity ) { result = `{${ min },}` ; } else { result = `{${ min },${ max }}` ; } break ; default : break ; } return result + ( greedy ? "" : "?" ) ; } function genBoundaryAssertionNode ( node ) { switch ( node . kind ) { case "beginning" : return "^" ; case "end" : return "$" ; case "word" : return node . negate ? "\\B" : "\\b" ; default : return "" ; } } const lookAroundMap = { lookahead : [ "(?=" , "(?!" ] , lookbehind : [ "(?<=" , "(?<!" ] , } ; function genLookaroundAssertionNode ( node ) { const { children , kind , negate } = node ; return lookAroundMap [ kind ] [ negate ? 1 : 0 ] + gen ( children ) + ")" ; } function genBackReference ( node ) { const { ref } = node ; if ( digitRegex . test ( ref ) ) { return `\\${ ref }` ; } return `\\k<${ ref }>` ; } export default gen ;