public export interface MessageState { t : string ; s : number ; e : number ; } const patterns = { identifier : /[^\u0009-\u000d \u0085\u200e\u200f\u2028\u2029\u0021-\u002f\u003a-\u0040\u005b-\u005e\u0060\u007b-\u007e\u00a1-\u00a7\u00a9\u00ab\u00ac\u00ae\u00b0\u00b1\u00b6\u00bb\u00bf\u00d7\u00f7\u2010-\u2027\u2030-\u203e\u2041-\u2053\u2055-\u205e\u2190-\u245f\u2500-\u2775\u2794-\u2bff\u2e00-\u2e7f\u3001-\u3003\u3008-\u3020\u3030\ufd3e\ufd3f\ufe45\ufe46]+/ . source , option : /[^\s,\{\}]+/ . source , pluralChoice : /(=\d+(\.\d+)?)|zero|one|two|few|many|other/ . source , } ; public export interface MessageMatcher { /**
     * Return the next character.
     */ char ( r ) : string ; /**
     * Indicate if we've reached the end of input.
     */ complete ( r ) : boolean ; /**
     * Match one or more spaces.
     */ spaces ( r ) : boolean ; /**
     * Match a list of arguments.
     */ arguments ( r ) : ( number | string ) [ ] | undefined ; /**
     * Match variable identifier.
     */ identifier ( r ) : string | undefined ; /**
     * Match a list of options.
     */ options ( r ) : string [ ] ; /**
     * Match a formatter name.
     */ formatter ( r ) : string | undefined ; /**
     * Match a plural offset.
     */ pluralOffset ( r ) : number ; /**
     * Match a plural choice (exact or category).
     */ pluralChoice ( r ) : string | undefined ; } const BUILTINS = [ 'plural' , 'select' , 'selectordinal' ] ; const cmp = ( a , b ) => ( a < b ? - 1 : a > b ? 1 : 0 ) ; public export class StickyMatcher implements MessageMatcher { private _space : RegExp ; private _arg : RegExp ; private _ident : RegExp ; private _option : RegExp ; private _fmt : RegExp ; private _offset : RegExp ; private _choice : RegExp ; constructor ( formatters , compile ) { this . _space = compile ( '[,\\s]+' ) ; this . _arg = compile ( `(0[1..9]+|\\d+|${ patterns . identifier })` ) ; this . _ident = compile ( patterns . identifier ) ; this . _option = compile ( patterns . option ) ; formatters = BUILTINS . concat ( formatters ) . sort ( ( a , b ) => cmp ( b . length , a . length ) ) ; this . _fmt = compile ( `(${ formatters . join ( '|' ) })` ) ; this . _offset = compile ( /offset:-?\d+/ . source ) ; this . _choice = compile ( patterns . pluralChoice ) ; } char ( r ) { return r . t [ r . s ] ; } complete ( r ) { return r . e === r . s ; } spaces ( r ) { return this . match ( this . _space , r ) !== undefined ; } arguments ( r ) { let args ; do { const arg = this . match ( this . _arg , r ) ; if ( ! arg ) { break ; } const n = parseInt ( arg , 10 ) ; if ( ! args ) { args = [ ] ; } args . push ( Number . isFinite ( n ) ? n : arg ) ; if ( r . t [ r . s ] !== ';' ) { break ; } r . s ++ ; } while ( ! this . complete ( r ) ) ; return args ; } identifier ( r ) { return this . match ( this . _ident , r ) ; } options ( r ) { const options = [ ] ; do { this . spaces ( r ) ; const opt = this . match ( this . _option , r ) ; if ( ! opt ) { break ; } options . push ( opt ) ; } while ( ! this . complete ( r ) ) ; return options ; } formatter ( r ) { return this . match ( this . _fmt , r ) ; } pluralChoice ( r ) { return this . match ( this . _choice , r ) ; } pluralOffset ( r ) { let n = 0 ; const m = this . match ( this . _offset , r ) ; if ( m ) { n = parseInt ( m . split ( ':' ) [ 1 ] , 10 ) ; } return n ; } /**
     * Attempt to match the pattern at the given starting location. If a
     * match is found, move the start pointer and return the string.
     * Otherwise return undefined.
     */ match ( pattern , r ) { pattern . lastIndex = r . s ; const raw = pattern . exec ( r . t ) ; if ( raw ) { r . s = pattern . lastIndex ; return raw [ 0 ] ; } return undefined ; } } public export class SubstringMatcher extends StickyMatcher { match ( pattern , r ) { pattern . lastIndex = 0 ; const s = r . t . substring ( r . s , r . e ) ; const raw = pattern . exec ( s ) ; if ( raw ) { r . s += pattern . lastIndex ; return raw [ 0 ] ; } return undefined ; } } /**
 * Check if the current JS runtime supports sticky RegExp flag.
 */ const hasStickyRegexp = ( ( ) => { try { const r = new RegExp ( '.' , 'y' ) ; return r && true ; } catch ( e ) { return false ; } } ) ( ) ; public export type regexpFunc = ( pattern : string ) => RegExp ; public export const stickyRegexp = ( pattern ) : RegExp => new RegExp ( pattern , 'y' ) ; public export const substringRegexp = ( pattern ) : RegExp => new RegExp ( '^' + pattern , 'g' ) ; public export const buildMessageMatcher = ( names , sticky = hasStickyRegexp ) : MessageMatcher => new ( sticky ? StickyMatcher : SubstringMatcher ) ( names , sticky ? stickyRegexp : substringRegexp ) ;