UNPKG

zpt

Version:

Zenon Page Templates - JS (ZPT-JS)

141 lines (113 loc) 4.44 kB
/* Class ExpressionTokenizer */ module.exports = function( exp, delimiter, escape ) { "use strict"; var expressionBuilder = require( './expressionBuilder.js' ); var removeParenthesisIfAny = expressionBuilder.removeParenthesisIfAny; var expression = exp.trim(); var iterator; var currIndex = 0; var delimiterCount = 0; var delimiters = []; var makeIterator = function( array ){ var nextIndex = 0; return { next: function(){ return nextIndex < array.length ? array [ nextIndex++ ] : undefined; }, hasNext: function(){ return nextIndex < array.length; } }; }; var analyze = function(){ var avoidRepeatedSeparators = delimiter === ' '; // Go ahead and find delimiters, if any, at construction time var parentLevel = 0; var inQuote = false; var previousCh = ''; // Scan for delimiters var length = expression.length; for ( var i = 0; i < length; i++ ) { var ch = expression.charAt( i ); if ( ch === delimiter ) { // If delimiter is not buried in parentheses or a quote if ( parentLevel === 0 && ! inQuote ) { if ( avoidRepeatedSeparators && ( previousCh === delimiter || previousCh === '\n' ) ) { continue; } var nextCh = ( i + 1 < length ) ? expression.charAt( i + 1 ) : ''; // And if delimiter is not escaped if ( ! ( escape && nextCh === delimiter ) ) { delimiterCount++; delimiters.push( i ); } else { // Somewhat inefficient way to pare the // escaped delimiter down to a single // character without breaking our stride expression = expression.substring( 0, i + 1 ) + expression.substring( i + 2 ); length--; } } // Increment parenthesis level } else if ( ch === '(' || ch === '[' ) { parentLevel++; // Decrement parenthesis level } else if ( ch === ')' || ch === ']' ) { parentLevel--; // If unmatched right parenthesis if ( parentLevel < 0 ) { throw 'Syntax error. Unmatched right parenthesis: ' + expression; } // Start or end quote } else if ( ch === '\'' ) { inQuote = ! inQuote; } previousCh = ch; } // If unmatched left parenthesis if ( parentLevel > 0 ) { throw 'Syntax error: unmatched left parenthesis: ' + expression; } // If runaway quote if ( inQuote ) { throw 'Syntax error: runaway quotation: ' + expression; } iterator = makeIterator( delimiters ); }(); var hasMoreTokens = function( ) { return currIndex < expression.length; }; var nextToken = function( ) { var token; if ( iterator.hasNext() ) { var next = iterator.next(); var delim = parseInt( next ); token = expression.substring( currIndex, delim ).trim(); currIndex = delim + 1; delimiterCount--; return removeParenthesisIfAny( token ); } token = expression.substring( currIndex ).trim(); currIndex = expression.length; return removeParenthesisIfAny( token ); }; var countTokens = function( ) { if ( hasMoreTokens() ) { return delimiterCount + 1; } return 0; }; var nextTokenIfAny = function( defaultValue ) { return hasMoreTokens()? nextToken(): defaultValue; }; return { hasMoreTokens: hasMoreTokens, nextToken: nextToken, countTokens: countTokens, nextTokenIfAny: nextTokenIfAny }; };