mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-11-03 19:17:02 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			570 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			570 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
# vim:fileencoding=utf-8
 | 
						|
# License: BSD Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
 | 
						|
 | 
						|
# noqa: eol-semicolon
 | 
						|
 | 
						|
# The Plural-Forms parser {{{
 | 
						|
# From: https://github.com/SlexAxton/Jed/blob/master/jed.js licensed under the WTFPL
 | 
						|
 | 
						|
Jed = {}
 | 
						|
 | 
						|
vr'''
 | 
						|
  Jed.PF = {};
 | 
						|
 | 
						|
  Jed.PF.parse = function ( p ) {
 | 
						|
    var plural_str = Jed.PF.extractPluralExpr( p );
 | 
						|
    return Jed.PF.parser.parse.call(Jed.PF.parser, plural_str);
 | 
						|
  };
 | 
						|
 | 
						|
  Jed.PF.compile = function ( p ) {
 | 
						|
    // Handle trues and falses as 0 and 1
 | 
						|
    function imply( val ) {
 | 
						|
      return (val === true ? 1 : val ? val : 0);
 | 
						|
    }
 | 
						|
 | 
						|
    var ast = Jed.PF.parse( p );
 | 
						|
    return function ( n ) {
 | 
						|
      return imply( Jed.PF.interpreter( ast )( n ) );
 | 
						|
    };
 | 
						|
  };
 | 
						|
 | 
						|
  Jed.PF.interpreter = function ( ast ) {
 | 
						|
    return function ( n ) {
 | 
						|
      var res;
 | 
						|
      switch ( ast.type ) {
 | 
						|
        case 'GROUP':
 | 
						|
          return Jed.PF.interpreter( ast.expr )( n );
 | 
						|
        case 'TERNARY':
 | 
						|
          if ( Jed.PF.interpreter( ast.expr )( n ) ) {
 | 
						|
            return Jed.PF.interpreter( ast.truthy )( n );
 | 
						|
          }
 | 
						|
          return Jed.PF.interpreter( ast.falsey )( n );
 | 
						|
        case 'OR':
 | 
						|
          return Jed.PF.interpreter( ast.left )( n ) || Jed.PF.interpreter( ast.right )( n );
 | 
						|
        case 'AND':
 | 
						|
          return Jed.PF.interpreter( ast.left )( n ) && Jed.PF.interpreter( ast.right )( n );
 | 
						|
        case 'LT':
 | 
						|
          return Jed.PF.interpreter( ast.left )( n ) < Jed.PF.interpreter( ast.right )( n );
 | 
						|
        case 'GT':
 | 
						|
          return Jed.PF.interpreter( ast.left )( n ) > Jed.PF.interpreter( ast.right )( n );
 | 
						|
        case 'LTE':
 | 
						|
          return Jed.PF.interpreter( ast.left )( n ) <= Jed.PF.interpreter( ast.right )( n );
 | 
						|
        case 'GTE':
 | 
						|
          return Jed.PF.interpreter( ast.left )( n ) >= Jed.PF.interpreter( ast.right )( n );
 | 
						|
        case 'EQ':
 | 
						|
          return Jed.PF.interpreter( ast.left )( n ) == Jed.PF.interpreter( ast.right )( n );
 | 
						|
        case 'NEQ':
 | 
						|
          return Jed.PF.interpreter( ast.left )( n ) != Jed.PF.interpreter( ast.right )( n );
 | 
						|
        case 'MOD':
 | 
						|
          return Jed.PF.interpreter( ast.left )( n ) % Jed.PF.interpreter( ast.right )( n );
 | 
						|
        case 'VAR':
 | 
						|
          return n;
 | 
						|
        case 'NUM':
 | 
						|
          return ast.val;
 | 
						|
        default:
 | 
						|
          throw new Error("Invalid Token found.");
 | 
						|
      }
 | 
						|
    };
 | 
						|
  };
 | 
						|
 | 
						|
  Jed.PF.extractPluralExpr = function ( p ) {
 | 
						|
    // trim first
 | 
						|
    p = p.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
 | 
						|
 | 
						|
    if (! /;\s*$/.test(p)) {
 | 
						|
      p = p.concat(';');
 | 
						|
    }
 | 
						|
 | 
						|
    var nplurals_re = /nplurals\=(\d+);/,
 | 
						|
        plural_re = /plural\=(.*);/,
 | 
						|
        nplurals_matches = p.match( nplurals_re ),
 | 
						|
        res = {},
 | 
						|
        plural_matches;
 | 
						|
 | 
						|
    // Find the nplurals number
 | 
						|
    if ( nplurals_matches.length > 1 ) {
 | 
						|
      res.nplurals = nplurals_matches[1];
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      throw new Error('nplurals not found in plural_forms string: ' + p );
 | 
						|
    }
 | 
						|
 | 
						|
    // remove that data to get to the formula
 | 
						|
    p = p.replace( nplurals_re, "" );
 | 
						|
    plural_matches = p.match( plural_re );
 | 
						|
 | 
						|
    if (!( plural_matches && plural_matches.length > 1 ) ) {
 | 
						|
      throw new Error('`plural` expression not found: ' + p);
 | 
						|
    }
 | 
						|
    return plural_matches[ 1 ];
 | 
						|
  };
 | 
						|
 | 
						|
  /* Jison generated parser */
 | 
						|
  Jed.PF.parser = (function(){
 | 
						|
 | 
						|
var parser = {trace: function trace() { },
 | 
						|
yy: {},
 | 
						|
symbols_: {"error":2,"expressions":3,"e":4,"EOF":5,"?":6,":":7,"||":8,"&&":9,"<":10,"<=":11,">":12,">=":13,"!=":14,"==":15,"%":16,"(":17,")":18,"n":19,"NUMBER":20,"$accept":0,"$end":1},
 | 
						|
terminals_: {2:"error",5:"EOF",6:"?",7:":",8:"||",9:"&&",10:"<",11:"<=",12:">",13:">=",14:"!=",15:"==",16:"%",17:"(",18:")",19:"n",20:"NUMBER"},
 | 
						|
productions_: [0,[3,2],[4,5],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,1],[4,1]],
 | 
						|
performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
 | 
						|
 | 
						|
var $0 = $$.length - 1;
 | 
						|
switch (yystate) {
 | 
						|
case 1: return { type : 'GROUP', expr: $$[$0-1] };
 | 
						|
case 2:this.$ = { type: 'TERNARY', expr: $$[$0-4], truthy : $$[$0-2], falsey: $$[$0] };
 | 
						|
break;
 | 
						|
case 3:this.$ = { type: "OR", left: $$[$0-2], right: $$[$0] };
 | 
						|
break;
 | 
						|
case 4:this.$ = { type: "AND", left: $$[$0-2], right: $$[$0] };
 | 
						|
break;
 | 
						|
case 5:this.$ = { type: 'LT', left: $$[$0-2], right: $$[$0] };
 | 
						|
break;
 | 
						|
case 6:this.$ = { type: 'LTE', left: $$[$0-2], right: $$[$0] };
 | 
						|
break;
 | 
						|
case 7:this.$ = { type: 'GT', left: $$[$0-2], right: $$[$0] };
 | 
						|
break;
 | 
						|
case 8:this.$ = { type: 'GTE', left: $$[$0-2], right: $$[$0] };
 | 
						|
break;
 | 
						|
case 9:this.$ = { type: 'NEQ', left: $$[$0-2], right: $$[$0] };
 | 
						|
break;
 | 
						|
case 10:this.$ = { type: 'EQ', left: $$[$0-2], right: $$[$0] };
 | 
						|
break;
 | 
						|
case 11:this.$ = { type: 'MOD', left: $$[$0-2], right: $$[$0] };
 | 
						|
break;
 | 
						|
case 12:this.$ = { type: 'GROUP', expr: $$[$0-1] };
 | 
						|
break;
 | 
						|
case 13:this.$ = { type: 'VAR' };
 | 
						|
break;
 | 
						|
case 14:this.$ = { type: 'NUM', val: Number(yytext) };
 | 
						|
break;
 | 
						|
}
 | 
						|
},
 | 
						|
table: [{3:1,4:2,17:[1,3],19:[1,4],20:[1,5]},{1:[3]},{5:[1,6],6:[1,7],8:[1,8],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16]},{4:17,17:[1,3],19:[1,4],20:[1,5]},{5:[2,13],6:[2,13],7:[2,13],8:[2,13],9:[2,13],10:[2,13],11:[2,13],12:[2,13],13:[2,13],14:[2,13],15:[2,13],16:[2,13],18:[2,13]},{5:[2,14],6:[2,14],7:[2,14],8:[2,14],9:[2,14],10:[2,14],11:[2,14],12:[2,14],13:[2,14],14:[2,14],15:[2,14],16:[2,14],18:[2,14]},{1:[2,1]},{4:18,17:[1,3],19:[1,4],20:[1,5]},{4:19,17:[1,3],19:[1,4],20:[1,5]},{4:20,17:[1,3],19:[1,4],20:[1,5]},{4:21,17:[1,3],19:[1,4],20:[1,5]},{4:22,17:[1,3],19:[1,4],20:[1,5]},{4:23,17:[1,3],19:[1,4],20:[1,5]},{4:24,17:[1,3],19:[1,4],20:[1,5]},{4:25,17:[1,3],19:[1,4],20:[1,5]},{4:26,17:[1,3],19:[1,4],20:[1,5]},{4:27,17:[1,3],19:[1,4],20:[1,5]},{6:[1,7],8:[1,8],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16],18:[1,28]},{6:[1,7],7:[1,29],8:[1,8],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16]},{5:[2,3],6:[2,3],7:[2,3],8:[2,3],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16],18:[2,3]},{5:[2,4],6:[2,4],7:[2,4],8:[2,4],9:[2,4],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16],18:[2,4]},{5:[2,5],6:[2,5],7:[2,5],8:[2,5],9:[2,5],10:[2,5],11:[2,5],12:[2,5],13:[2,5],14:[2,5],15:[2,5],16:[1,16],18:[2,5]},{5:[2,6],6:[2,6],7:[2,6],8:[2,6],9:[2,6],10:[2,6],11:[2,6],12:[2,6],13:[2,6],14:[2,6],15:[2,6],16:[1,16],18:[2,6]},{5:[2,7],6:[2,7],7:[2,7],8:[2,7],9:[2,7],10:[2,7],11:[2,7],12:[2,7],13:[2,7],14:[2,7],15:[2,7],16:[1,16],18:[2,7]},{5:[2,8],6:[2,8],7:[2,8],8:[2,8],9:[2,8],10:[2,8],11:[2,8],12:[2,8],13:[2,8],14:[2,8],15:[2,8],16:[1,16],18:[2,8]},{5:[2,9],6:[2,9],7:[2,9],8:[2,9],9:[2,9],10:[2,9],11:[2,9],12:[2,9],13:[2,9],14:[2,9],15:[2,9],16:[1,16],18:[2,9]},{5:[2,10],6:[2,10],7:[2,10],8:[2,10],9:[2,10],10:[2,10],11:[2,10],12:[2,10],13:[2,10],14:[2,10],15:[2,10],16:[1,16],18:[2,10]},{5:[2,11],6:[2,11],7:[2,11],8:[2,11],9:[2,11],10:[2,11],11:[2,11],12:[2,11],13:[2,11],14:[2,11],15:[2,11],16:[2,11],18:[2,11]},{5:[2,12],6:[2,12],7:[2,12],8:[2,12],9:[2,12],10:[2,12],11:[2,12],12:[2,12],13:[2,12],14:[2,12],15:[2,12],16:[2,12],18:[2,12]},{4:30,17:[1,3],19:[1,4],20:[1,5]},{5:[2,2],6:[1,7],7:[2,2],8:[1,8],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16],18:[2,2]}],
 | 
						|
defaultActions: {6:[2,1]},
 | 
						|
parseError: function parseError(str, hash) {
 | 
						|
    throw new Error(str);
 | 
						|
},
 | 
						|
parse: function parse(input) {
 | 
						|
    var self = this,
 | 
						|
        stack = [0],
 | 
						|
        vstack = [null], // semantic value stack
 | 
						|
        lstack = [], // location stack
 | 
						|
        table = this.table,
 | 
						|
        yytext = '',
 | 
						|
        yylineno = 0,
 | 
						|
        yyleng = 0,
 | 
						|
        recovering = 0,
 | 
						|
        TERROR = 2,
 | 
						|
        EOF = 1;
 | 
						|
 | 
						|
    //this.reductionCount = this.shiftCount = 0;
 | 
						|
 | 
						|
    this.lexer.setInput(input);
 | 
						|
    this.lexer.yy = this.yy;
 | 
						|
    this.yy.lexer = this.lexer;
 | 
						|
    if (typeof this.lexer.yylloc == 'undefined')
 | 
						|
        this.lexer.yylloc = {};
 | 
						|
    var yyloc = this.lexer.yylloc;
 | 
						|
    lstack.push(yyloc);
 | 
						|
 | 
						|
    if (typeof this.yy.parseError === 'function')
 | 
						|
        this.parseError = this.yy.parseError;
 | 
						|
 | 
						|
    function popStack (n) {
 | 
						|
        stack.length = stack.length - 2*n;
 | 
						|
        vstack.length = vstack.length - n;
 | 
						|
        lstack.length = lstack.length - n;
 | 
						|
    }
 | 
						|
 | 
						|
    function lex() {
 | 
						|
        var token;
 | 
						|
        token = self.lexer.lex() || 1; // $end = 1
 | 
						|
        // if token isn't its numeric value, convert
 | 
						|
        if (typeof token !== 'number') {
 | 
						|
            token = self.symbols_[token] || token;
 | 
						|
        }
 | 
						|
        return token;
 | 
						|
    }
 | 
						|
 | 
						|
    var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected, errStr;
 | 
						|
    while (true) {
 | 
						|
        // retreive state number from top of stack
 | 
						|
        state = stack[stack.length-1];
 | 
						|
 | 
						|
        // use default actions if available
 | 
						|
        if (this.defaultActions[state]) {
 | 
						|
            action = this.defaultActions[state];
 | 
						|
        } else {
 | 
						|
            if (symbol === null || symbol === undefined)
 | 
						|
                symbol = lex();
 | 
						|
            // read action for current state and first input
 | 
						|
            action = table[state] && table[state][symbol];
 | 
						|
        }
 | 
						|
 | 
						|
        // handle parse error
 | 
						|
        _handle_error:
 | 
						|
        if (typeof action === 'undefined' || !action.length || !action[0]) {
 | 
						|
 | 
						|
            if (!recovering) {
 | 
						|
                // Report error
 | 
						|
                expected = [];
 | 
						|
                for (p in table[state]) if (this.terminals_[p] && p > 2) {
 | 
						|
                    expected.push("'"+this.terminals_[p]+"'");
 | 
						|
                }
 | 
						|
                errStr = '';
 | 
						|
                if (this.lexer.showPosition) {
 | 
						|
                    errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + this.terminals_[symbol]+ "'";
 | 
						|
                } else {
 | 
						|
                    errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " +
 | 
						|
                                  (symbol == 1 /*EOF*/ ? "end of input" :
 | 
						|
                                              ("'"+(this.terminals_[symbol] || symbol)+"'"));
 | 
						|
                }
 | 
						|
                this.parseError(errStr,
 | 
						|
                    {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
 | 
						|
            }
 | 
						|
 | 
						|
            // just recovered from another error
 | 
						|
            if (recovering == 3) {
 | 
						|
                if (symbol == EOF) {
 | 
						|
                    throw new Error(errStr || 'Parsing halted.');
 | 
						|
                }
 | 
						|
 | 
						|
                // discard current lookahead and grab another
 | 
						|
                yyleng = this.lexer.yyleng;
 | 
						|
                yytext = this.lexer.yytext;
 | 
						|
                yylineno = this.lexer.yylineno;
 | 
						|
                yyloc = this.lexer.yylloc;
 | 
						|
                symbol = lex();
 | 
						|
            }
 | 
						|
 | 
						|
            // try to recover from error
 | 
						|
            while (1) {
 | 
						|
                // check for error recovery rule in this state
 | 
						|
                if ((TERROR.toString()) in table[state]) {
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                if (state === 0) {
 | 
						|
                    throw new Error(errStr || 'Parsing halted.');
 | 
						|
                }
 | 
						|
                popStack(1);
 | 
						|
                state = stack[stack.length-1];
 | 
						|
            }
 | 
						|
 | 
						|
            preErrorSymbol = symbol; // save the lookahead token
 | 
						|
            symbol = TERROR;         // insert generic error symbol as new lookahead
 | 
						|
            state = stack[stack.length-1];
 | 
						|
            action = table[state] && table[state][TERROR];
 | 
						|
            recovering = 3; // allow 3 real symbols to be shifted before reporting a new error
 | 
						|
        }
 | 
						|
 | 
						|
        // this shouldn't happen, unless resolve defaults are off
 | 
						|
        if (action[0] instanceof Array && action.length > 1) {
 | 
						|
            throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol);
 | 
						|
        }
 | 
						|
 | 
						|
        switch (action[0]) {
 | 
						|
 | 
						|
            case 1: // shift
 | 
						|
                //this.shiftCount++;
 | 
						|
 | 
						|
                stack.push(symbol);
 | 
						|
                vstack.push(this.lexer.yytext);
 | 
						|
                lstack.push(this.lexer.yylloc);
 | 
						|
                stack.push(action[1]); // push state
 | 
						|
                symbol = null;
 | 
						|
                if (!preErrorSymbol) { // normal execution/no error
 | 
						|
                    yyleng = this.lexer.yyleng;
 | 
						|
                    yytext = this.lexer.yytext;
 | 
						|
                    yylineno = this.lexer.yylineno;
 | 
						|
                    yyloc = this.lexer.yylloc;
 | 
						|
                    if (recovering > 0)
 | 
						|
                        recovering--;
 | 
						|
                } else { // error just occurred, resume old lookahead f/ before error
 | 
						|
                    symbol = preErrorSymbol;
 | 
						|
                    preErrorSymbol = null;
 | 
						|
                }
 | 
						|
                break;
 | 
						|
 | 
						|
            case 2: // reduce
 | 
						|
                //this.reductionCount++;
 | 
						|
 | 
						|
                len = this.productions_[action[1]][1];
 | 
						|
 | 
						|
                // perform semantic action
 | 
						|
                yyval.$ = vstack[vstack.length-len]; // default to $$ = $1
 | 
						|
                // default location, uses first token for firsts, last for lasts
 | 
						|
                yyval._$ = {
 | 
						|
                    first_line: lstack[lstack.length-(len||1)].first_line,
 | 
						|
                    last_line: lstack[lstack.length-1].last_line,
 | 
						|
                    first_column: lstack[lstack.length-(len||1)].first_column,
 | 
						|
                    last_column: lstack[lstack.length-1].last_column
 | 
						|
                };
 | 
						|
                r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
 | 
						|
 | 
						|
                if (typeof r !== 'undefined') {
 | 
						|
                    return r;
 | 
						|
                }
 | 
						|
 | 
						|
                // pop off stack
 | 
						|
                if (len) {
 | 
						|
                    stack = stack.slice(0,-1*len*2);
 | 
						|
                    vstack = vstack.slice(0, -1*len);
 | 
						|
                    lstack = lstack.slice(0, -1*len);
 | 
						|
                }
 | 
						|
 | 
						|
                stack.push(this.productions_[action[1]][0]);    // push nonterminal (reduce)
 | 
						|
                vstack.push(yyval.$);
 | 
						|
                lstack.push(yyval._$);
 | 
						|
                // goto new state = table[STATE][NONTERMINAL]
 | 
						|
                newState = table[stack[stack.length-2]][stack[stack.length-1]];
 | 
						|
                stack.push(newState);
 | 
						|
                break;
 | 
						|
 | 
						|
            case 3: // accept
 | 
						|
                return true;
 | 
						|
        }
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
}};/* Jison generated lexer */
 | 
						|
var lexer = (function(){
 | 
						|
 | 
						|
var lexer = ({EOF:1,
 | 
						|
parseError:function parseError(str, hash) {
 | 
						|
        if (this.yy.parseError) {
 | 
						|
            this.yy.parseError(str, hash);
 | 
						|
        } else {
 | 
						|
            throw new Error(str);
 | 
						|
        }
 | 
						|
    },
 | 
						|
setInput:function (input) {
 | 
						|
        this._input = input;
 | 
						|
        this._more = this._less = this.done = false;
 | 
						|
        this.yylineno = this.yyleng = 0;
 | 
						|
        this.yytext = this.matched = this.match = '';
 | 
						|
        this.conditionStack = ['INITIAL'];
 | 
						|
        this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
 | 
						|
        return this;
 | 
						|
    },
 | 
						|
input:function () {
 | 
						|
        var ch = this._input[0];
 | 
						|
        this.yytext+=ch;
 | 
						|
        this.yyleng++;
 | 
						|
        this.match+=ch;
 | 
						|
        this.matched+=ch;
 | 
						|
        var lines = ch.match(/\n/);
 | 
						|
        if (lines) this.yylineno++;
 | 
						|
        this._input = this._input.slice(1);
 | 
						|
        return ch;
 | 
						|
    },
 | 
						|
unput:function (ch) {
 | 
						|
        this._input = ch + this._input;
 | 
						|
        return this;
 | 
						|
    },
 | 
						|
more:function () {
 | 
						|
        this._more = true;
 | 
						|
        return this;
 | 
						|
    },
 | 
						|
pastInput:function () {
 | 
						|
        var past = this.matched.substr(0, this.matched.length - this.match.length);
 | 
						|
        return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
 | 
						|
    },
 | 
						|
upcomingInput:function () {
 | 
						|
        var next = this.match;
 | 
						|
        if (next.length < 20) {
 | 
						|
            next += this._input.substr(0, 20-next.length);
 | 
						|
        }
 | 
						|
        return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
 | 
						|
    },
 | 
						|
showPosition:function () {
 | 
						|
        var pre = this.pastInput();
 | 
						|
        var c = new Array(pre.length + 1).join("-");
 | 
						|
        return pre + this.upcomingInput() + "\n" + c+"^";
 | 
						|
    },
 | 
						|
next:function () {
 | 
						|
        if (this.done) {
 | 
						|
            return this.EOF;
 | 
						|
        }
 | 
						|
        if (!this._input) this.done = true;
 | 
						|
 | 
						|
        var token,
 | 
						|
            match,
 | 
						|
            col,
 | 
						|
            lines;
 | 
						|
        if (!this._more) {
 | 
						|
            this.yytext = '';
 | 
						|
            this.match = '';
 | 
						|
        }
 | 
						|
        var rules = this._currentRules();
 | 
						|
        for (var i=0;i < rules.length; i++) {
 | 
						|
            match = this._input.match(this.rules[rules[i]]);
 | 
						|
            if (match) {
 | 
						|
                lines = match[0].match(/\n.*/g);
 | 
						|
                if (lines) this.yylineno += lines.length;
 | 
						|
                this.yylloc = {first_line: this.yylloc.last_line,
 | 
						|
                               last_line: this.yylineno+1,
 | 
						|
                               first_column: this.yylloc.last_column,
 | 
						|
                               last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length};
 | 
						|
                this.yytext += match[0];
 | 
						|
                this.match += match[0];
 | 
						|
                this.matches = match;
 | 
						|
                this.yyleng = this.yytext.length;
 | 
						|
                this._more = false;
 | 
						|
                this._input = this._input.slice(match[0].length);
 | 
						|
                this.matched += match[0];
 | 
						|
                token = this.performAction.call(this, this.yy, this, rules[i],this.conditionStack[this.conditionStack.length-1]);
 | 
						|
                if (token) return token;
 | 
						|
                else return;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (this._input === "") {
 | 
						|
            return this.EOF;
 | 
						|
        } else {
 | 
						|
            this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
 | 
						|
                    {text: "", token: null, line: this.yylineno});
 | 
						|
        }
 | 
						|
    },
 | 
						|
lex:function lex() {
 | 
						|
        var r = this.next();
 | 
						|
        if (typeof r !== 'undefined') {
 | 
						|
            return r;
 | 
						|
        } else {
 | 
						|
            return this.lex();
 | 
						|
        }
 | 
						|
    },
 | 
						|
begin:function begin(condition) {
 | 
						|
        this.conditionStack.push(condition);
 | 
						|
    },
 | 
						|
popState:function popState() {
 | 
						|
        return this.conditionStack.pop();
 | 
						|
    },
 | 
						|
_currentRules:function _currentRules() {
 | 
						|
        return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
 | 
						|
    },
 | 
						|
topState:function () {
 | 
						|
        return this.conditionStack[this.conditionStack.length-2];
 | 
						|
    },
 | 
						|
pushState:function begin(condition) {
 | 
						|
        this.begin(condition);
 | 
						|
    }});
 | 
						|
lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
 | 
						|
 | 
						|
var YYSTATE=YY_START;
 | 
						|
switch($avoiding_name_collisions) {
 | 
						|
case 0:/* skip whitespace */
 | 
						|
break;
 | 
						|
case 1:return 20
 | 
						|
break;
 | 
						|
case 2:return 19
 | 
						|
break;
 | 
						|
case 3:return 8
 | 
						|
break;
 | 
						|
case 4:return 9
 | 
						|
break;
 | 
						|
case 5:return 6
 | 
						|
break;
 | 
						|
case 6:return 7
 | 
						|
break;
 | 
						|
case 7:return 11
 | 
						|
break;
 | 
						|
case 8:return 13
 | 
						|
break;
 | 
						|
case 9:return 10
 | 
						|
break;
 | 
						|
case 10:return 12
 | 
						|
break;
 | 
						|
case 11:return 14
 | 
						|
break;
 | 
						|
case 12:return 15
 | 
						|
break;
 | 
						|
case 13:return 16
 | 
						|
break;
 | 
						|
case 14:return 17
 | 
						|
break;
 | 
						|
case 15:return 18
 | 
						|
break;
 | 
						|
case 16:return 5
 | 
						|
break;
 | 
						|
case 17:return 'INVALID'
 | 
						|
break;
 | 
						|
}
 | 
						|
};
 | 
						|
lexer.rules = [/^\s+/,/^[0-9]+(\.[0-9]+)?\b/,/^n\b/,/^\|\|/,/^&&/,/^\?/,/^:/,/^<=/,/^>=/,/^</,/^>/,/^!=/,/^==/,/^%/,/^\(/,/^\)/,/^$/,/^./];
 | 
						|
lexer.conditions = {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17],"inclusive":true}};return lexer;})()
 | 
						|
parser.lexer = lexer;
 | 
						|
return parser;
 | 
						|
})();
 | 
						|
'''
 | 
						|
plural_forms_parser = Jed.PF
 | 
						|
# }}}
 | 
						|
 | 
						|
def _get_plural_forms_function(plural_forms_string):
 | 
						|
    return plural_forms_parser.compile(plural_forms_string or "nplurals=2; plural=(n != 1);")
 | 
						|
 | 
						|
_gettext = def(text): return text
 | 
						|
 | 
						|
_ngettext = def(text, plural, n): return text if n is 1 else plural
 | 
						|
 | 
						|
def gettext(text):
 | 
						|
    return _gettext(text)
 | 
						|
 | 
						|
def ngettext(text, plural, n):
 | 
						|
    return _ngettext(text, plural, n)
 | 
						|
 | 
						|
def install(translation_data):
 | 
						|
    t = new Translations(translation_data)
 | 
						|
    t.install()
 | 
						|
    for func in register_callback.install_callbacks:
 | 
						|
        try:
 | 
						|
            func(t)
 | 
						|
        except:
 | 
						|
            pass
 | 
						|
    return t
 | 
						|
 | 
						|
has_prop = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty)
 | 
						|
 | 
						|
def register_callback(func):
 | 
						|
    # Register callbacks that will be called when new translation data is
 | 
						|
    # installed
 | 
						|
    register_callback.install_callbacks.push(func)
 | 
						|
register_callback.install_callbacks = v'[]'
 | 
						|
 | 
						|
empty_translation_data = {'entries': {}}
 | 
						|
 | 
						|
class Translations:
 | 
						|
 | 
						|
    def __init__(self, translation_data):
 | 
						|
        translation_data = translation_data or empty_translation_data
 | 
						|
        func = _get_plural_forms_function(translation_data.plural_forms)
 | 
						|
        self.translations = [[translation_data, func]]
 | 
						|
        self.language = translation_data['language']
 | 
						|
 | 
						|
    def add_fallback(self, fallback):
 | 
						|
        fallback = fallback or empty_translation_data
 | 
						|
        func = _get_plural_forms_function(fallback.plural_forms)
 | 
						|
        self.translations.push([fallback, func])
 | 
						|
 | 
						|
    def gettext(self, text):
 | 
						|
        for t in self.translations:
 | 
						|
            m = t[0].entries
 | 
						|
            if has_prop(m, text):
 | 
						|
                return m[text][0]
 | 
						|
        return text
 | 
						|
 | 
						|
    def ngettext(self, text, plural, n):
 | 
						|
        for t in self.translations:
 | 
						|
            m = t[0].entries
 | 
						|
            if has_prop(m, text):
 | 
						|
                idx = t[1](n)
 | 
						|
                return m[text][idx] or (text if n is 1 else plural)
 | 
						|
        return text if n is 1 else plural
 | 
						|
 | 
						|
    def install(self):
 | 
						|
        nonlocal _gettext, _ngettext
 | 
						|
        _gettext = def ():
 | 
						|
            return self.gettext.apply(self, arguments)
 | 
						|
        _ngettext = def ():
 | 
						|
            return self.ngettext.apply(self, arguments)
 |