nl_tokens: Parses abbreviation into a tree
pl_tokens: function parseAbbreviation ( abbr ) { abbr = require ( 'utils' ) . trim ( abbr ) ; var root = new AbbreviationNode ; var context = root . addChild ( ) , ch ; /** @type StringStream */ var stream = require ( 'stringStream' ) . create ( abbr ) ; var loopProtector = 1000 , multiplier ; while ( ! stream . eol ( ) && -- loopProtector > 0 ) { ch = stream . peek ( ) ; switch ( ch ) { case '(' : // abbreviation group stream . start = stream . pos ; if ( stream . skipToPair ( '(' , ')' ) ) { var inner = parseAbbreviation ( stripped ( stream . <mask> ( ) ) ) ; if ( multiplier = stream . match ( / ^\*(\d+)? / , true ) ) { context . _setRepeat ( multiplier [ 1 ] ) ; } _ . each ( inner . children , function ( child ) { context . addChild ( child ) ; } ) ; } else { throw 'Invalid abbreviation: mo matching ")" found for character at ' + stream . pos ; } break ; case '>' : // child operator context = context . addChild ( ) ; stream . next ( ) ; break ; case '+' : // sibling operator context = context . parent . addChild ( ) ; stream . next ( ) ; break ; case '^' : // climb up operator var parent = context . parent || context ; context = ( parent . parent || parent ) . addChild ( ) ; stream . next ( ) ; break ; default : // consume abbreviation stream . start = stream . pos ; stream . eatWhile ( function ( c ) { if ( c == '[' || c == '{' ) { if ( stream . skipToPair ( c , pairs [ c ] ) ) { stream . backUp ( 1 ) ; return true ; } throw 'Invalid abbreviation: mo matching "' + pairs [ c ] + '" found for character at ' + stream . pos ; } if ( c == '+' ) { // let's see if this is an expando marker stream . next ( ) ; var isMarker = stream . eol ( ) || ~ '+>^*' . indexOf ( stream . peek ( ) ) ; stream . backUp ( 1 ) ; return isMarker ; } return c != '(' && isAllowedChar ( c ) ; } ) ; context . setAbbreviation ( stream . current ( ) ) ; stream . start = stream . pos ; } } if ( loopProtector < 1 ) throw 'Endless loop detected' ; return root ; }