5 https://github.com/SlexAxton/Jed
7 A gettext compatible i18n library for modern JavaScript Applications
9 by Alex Sexton - AlexSexton [at] gmail - @SlexAxton
11 Dojo CLA for contributions
13 Jed offers the entire applicable GNU gettext spec'd set of
14 functions, but also offers some nicer wrappers around them.
15 The api for gettext was written for a language with no function
16 overloading, so Jed allows a little more of that.
18 Many thanks to Joshua I. Miller - unrtst@cpan.org - who wrote
19 gettext.js back in 2008. I was able to vet a lot of my ideas
20 against his. I also made sure Jed passed against his tests
21 in order to offer easy upgrades -- jsgettext.berlios.de
23 (function (root
, undef
) {
25 // Set up some underscore-style functions, if you already have
26 // underscore, feel free to delete this section, and use it
27 // directly, however, the amount of functions used doesn't
28 // warrant having underscore as a full dependency.
29 // Underscore 1.3.0 was used to port and is licensed
30 // under the MIT License by Jeremy Ashkenas.
31 var ArrayProto
= Array
.prototype,
32 ObjProto
= Object
.prototype,
33 slice
= ArrayProto
.slice
,
34 hasOwnProp
= ObjProto
.hasOwnProperty
,
35 nativeForEach
= ArrayProto
.forEach
,
38 // We're not using the OOP style _ so we don't need the
39 // extra level of indirection. This still means that you
40 // sub out for real `_` though.
42 forEach : function( obj
, iterator
, context
) {
48 if ( nativeForEach
&& obj
.forEach
=== nativeForEach
) {
49 obj
.forEach( iterator
, context
);
51 else if ( obj
.length
=== +obj
.length
) {
52 for ( i
= 0, l
= obj
.length
; i
< l
; i
++ ) {
53 if ( i
in obj
&& iterator
.call( context
, obj
[i
], i
, obj
) === breaker
) {
60 if ( hasOwnProp
.call( obj
, key
) ) {
61 if ( iterator
.call (context
, obj
[key
], key
, obj
) === breaker
) {
68 extend : function( obj
) {
69 this.forEach( slice
.call( arguments
, 1 ), function ( source
) {
70 for ( var prop
in source
) {
71 obj
[prop
] = source
[prop
];
77 // END Miniature underscore impl
79 // Jed is a constructor function
80 var Jed = function ( options
) {
81 // Some minimal defaults
86 "domain" : "messages",
88 "plural_forms" : "nplurals=2; plural=(n != 1);"
90 // There are no default keys, though
93 // The default domain if one is missing
97 // Mix in the sent options with the default options
98 this.options
= _
.extend( {}, this.defaults
, options
);
99 this.textdomain( this.options
.domain
);
101 if ( this.options
.domain
&& ! this.options
.locale_data
[ this.options
.domain
] ) {
102 throw new Error('Text domain set to non-existent domain: `' + this.options
.domain
+ '`');
106 // The gettext spec sets this character as the default
107 // delimiter for context lookups.
108 // e.g.: context\u0004key
109 // If your translation company uses something different,
110 // just change this at any time and it will use that instead.
111 Jed
.context_delimiter
= String
.fromCharCode( 4 );
113 function getPluralFormFunc ( plural_form_string
) {
114 return Jed
.PF
.compile( plural_form_string
|| "nplurals=2; plural=(n != 1);");
117 function Chain( key
, i18n
){
122 // Create a chainable api for adding args prettily
123 _
.extend( Chain
.prototype, {
124 onDomain : function ( domain
) {
125 this._domain
= domain
;
128 withContext : function ( context
) {
129 this._context
= context
;
132 ifPlural : function ( num
, pkey
) {
137 fetch : function ( sArr
) {
138 if ( {}.toString
.call( sArr
) != '[object Array]' ) {
139 sArr
= [].slice
.call(arguments
);
141 return ( sArr
&& sArr
.length
? Jed
.sprintf : function(x
){ return x
; } )(
142 this._i18n
.dcnpgettext(this._domain
, this._context
, this._key
, this._pkey
, this._val
),
148 // Add functions to the Jed prototype.
149 // These will be the functions on the object that's returned
150 // from creating a `new Jed()`
151 // These seem redundant, but they gzip pretty well.
152 _
.extend( Jed
.prototype, {
153 // The sexier api start point
154 translate : function ( key
) {
155 return new Chain( key
, this );
158 textdomain : function ( domain
) {
160 return this._textdomain
;
162 this._textdomain
= domain
;
165 gettext : function ( key
) {
166 return this.dcnpgettext
.call( this, undef
, undef
, key
);
169 dgettext : function ( domain
, key
) {
170 return this.dcnpgettext
.call( this, domain
, undef
, key
);
173 dcgettext : function ( domain
, key
/*, category */ ) {
174 // Ignores the category anyways
175 return this.dcnpgettext
.call( this, domain
, undef
, key
);
178 ngettext : function ( skey
, pkey
, val
) {
179 return this.dcnpgettext
.call( this, undef
, undef
, skey
, pkey
, val
);
182 dngettext : function ( domain
, skey
, pkey
, val
) {
183 return this.dcnpgettext
.call( this, domain
, undef
, skey
, pkey
, val
);
186 dcngettext : function ( domain
, skey
, pkey
, val
/*, category */) {
187 return this.dcnpgettext
.call( this, domain
, undef
, skey
, pkey
, val
);
190 pgettext : function ( context
, key
) {
191 return this.dcnpgettext
.call( this, undef
, context
, key
);
194 dpgettext : function ( domain
, context
, key
) {
195 return this.dcnpgettext
.call( this, domain
, context
, key
);
198 dcpgettext : function ( domain
, context
, key
/*, category */) {
199 return this.dcnpgettext
.call( this, domain
, context
, key
);
202 npgettext : function ( context
, skey
, pkey
, val
) {
203 return this.dcnpgettext
.call( this, undef
, context
, skey
, pkey
, val
);
206 dnpgettext : function ( domain
, context
, skey
, pkey
, val
) {
207 return this.dcnpgettext
.call( this, domain
, context
, skey
, pkey
, val
);
210 // The most fully qualified gettext function. It has every option.
211 // Since it has every option, we can use it from every other method.
212 // This is the bread and butter.
213 // Technically there should be one more argument in this function for 'Category',
214 // but since we never use it, we might as well not waste the bytes to define it.
215 dcnpgettext : function ( domain
, context
, singular_key
, plural_key
, val
) {
218 plural_key
= plural_key
|| singular_key
;
220 // Use the global domain default if one
221 // isn't explicitly passed in
222 domain
= domain
|| this._textdomain
;
224 // Default the value to the singular case
225 val
= typeof val
== 'undefined' ? 1 : val
;
229 // Handle special cases
232 if ( ! this.options
) {
233 // There's likely something wrong, but we'll return the correct key for english
234 // We do this by instantiating a brand new Jed instance with the default set
235 // for everything that could be broken.
236 fallback
= new Jed();
237 return fallback
.dcnpgettext
.call( fallback
, undefined, undefined, singular_key
, plural_key
, val
);
240 // No translation data provided
241 if ( ! this.options
.locale_data
) {
242 throw new Error('No locale data provided.');
245 if ( ! this.options
.locale_data
[ domain
] ) {
246 throw new Error('Domain `' + domain
+ '` was not found.');
249 if ( ! this.options
.locale_data
[ domain
][ "" ] ) {
250 throw new Error('No locale meta information provided.');
253 // Make sure we have a truthy key. Otherwise we might start looking
254 // into the empty string key, which is the options for the locale
256 if ( ! singular_key
) {
257 throw new Error('No translation key found.');
260 // Handle invalid numbers, but try casting strings for good measure
261 if ( typeof val
!= 'number' ) {
262 val
= parseInt( val
, 10 );
264 if ( isNaN( val
) ) {
265 throw new Error('The number that was passed in is not a number.');
269 var key
= context
? context
+ Jed
.context_delimiter
+ singular_key
: singular_key
,
270 locale_data
= this.options
.locale_data
,
271 dict
= locale_data
[ domain
],
272 pluralForms
= dict
[""].plural_forms
|| (locale_data
.messages
|| this.defaults
.locale_data
.messages
)[""].plural_forms
,
273 val_idx
= getPluralFormFunc(pluralForms
)(val
) + 1,
277 // Throw an error if a domain isn't found
279 throw new Error('No domain named `' + domain
+ '` could be found.');
282 val_list
= dict
[ key
];
284 // If there is no match, then revert back to
285 // english style singular/plural with the keys passed in.
286 if ( ! val_list
|| val_idx
>= val_list
.length
) {
287 res
= [ null, singular_key
, plural_key
];
288 return res
[ getPluralFormFunc(pluralForms
)( val
) + 1 ];
291 res
= val_list
[ val_idx
];
293 // This includes empty strings on purpose
295 res
= [ null, singular_key
, plural_key
];
296 return res
[ getPluralFormFunc(pluralForms
)( val
) + 1 ];
303 // We add in sprintf capabilities for post translation value interolation
304 // This is not internally used, so you can remove it if you have this
305 // available somewhere else, or want to use a different system.
307 // We _slightly_ modify the normal sprintf behavior to more gracefully handle
311 sprintf() for JavaScript 0.7-beta1
312 http://www.diveintojavascript.com/projects/javascript-sprintf
314 Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
317 Redistribution and use in source and binary forms, with or without
318 modification, are permitted provided that the following conditions are met:
319 * Redistributions of source code must retain the above copyright
320 notice, this list of conditions and the following disclaimer.
321 * Redistributions in binary form must reproduce the above copyright
322 notice, this list of conditions and the following disclaimer in the
323 documentation and/or other materials provided with the distribution.
324 * Neither the name of sprintf() for JavaScript nor the
325 names of its contributors may be used to endorse or promote products
326 derived from this software without specific prior written permission.
328 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
329 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
330 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
331 DISCLAIMED. IN NO EVENT SHALL Alexandru Marasteanu BE LIABLE FOR ANY
332 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
333 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
334 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
335 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
336 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
337 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
339 var sprintf
= (function() {
340 function get_type(variable
) {
341 return Object
.prototype.toString
.call(variable
).slice(8, -1).toLowerCase();
343 function str_repeat(input
, multiplier
) {
344 for (var output
= []; multiplier
> 0; output
[--multiplier
] = input
) {/* do nothing */}
345 return output
.join('');
348 var str_format = function() {
349 if (!str_format
.cache
.hasOwnProperty(arguments
[0])) {
350 str_format
.cache
[arguments
[0]] = str_format
.parse(arguments
[0]);
352 return str_format
.format
.call(null, str_format
.cache
[arguments
[0]], arguments
);
355 str_format
.format = function(parse_tree
, argv
) {
356 var cursor
= 1, tree_length
= parse_tree
.length
, node_type
= '', arg
, output
= [], i
, k
, match
, pad
, pad_character
, pad_length
;
357 for (i
= 0; i
< tree_length
; i
++) {
358 node_type
= get_type(parse_tree
[i
]);
359 if (node_type
=== 'string') {
360 output
.push(parse_tree
[i
]);
362 else if (node_type
=== 'array') {
363 match
= parse_tree
[i
]; // convenience purposes only
364 if (match
[2]) { // keyword argument
366 for (k
= 0; k
< match
[2].length
; k
++) {
367 if (!arg
.hasOwnProperty(match
[2][k
])) {
368 throw(sprintf('[sprintf] property "%s" does not exist', match
[2][k
]));
370 arg
= arg
[match
[2][k
]];
373 else if (match
[1]) { // positional argument (explicit)
374 arg
= argv
[match
[1]];
376 else { // positional argument (implicit)
377 arg
= argv
[cursor
++];
380 if (/[^s]/.test(match
[8]) && (get_type(arg
) != 'number')) {
381 throw(sprintf('[sprintf] expecting number but found %s', get_type(arg
)));
385 if ( typeof arg
== 'undefined' || arg
=== null ) {
391 case 'b': arg
= arg
.toString(2); break;
392 case 'c': arg
= String
.fromCharCode(arg
); break;
393 case 'd': arg
= parseInt(arg
, 10); break;
394 case 'e': arg
= match
[7] ? arg
.toExponential(match
[7]) : arg
.toExponential(); break;
395 case 'f': arg
= match
[7] ? parseFloat(arg
).toFixed(match
[7]) : parseFloat(arg
); break;
396 case 'o': arg
= arg
.toString(8); break;
397 case 's': arg
= ((arg
= String(arg
)) && match
[7] ? arg
.substring(0, match
[7]) : arg
); break;
398 case 'u': arg
= Math
.abs(arg
); break;
399 case 'x': arg
= arg
.toString(16); break;
400 case 'X': arg
= arg
.toString(16).toUpperCase(); break;
402 arg
= (/[def]/.test(match
[8]) && match
[3] && arg
>= 0 ? '+'+ arg
: arg
);
403 pad_character
= match
[4] ? match
[4] == '0' ? '0' : match
[4].charAt(1) : ' ';
404 pad_length
= match
[6] - String(arg
).length
;
405 pad
= match
[6] ? str_repeat(pad_character
, pad_length
) : '';
406 output
.push(match
[5] ? arg
+ pad
: pad
+ arg
);
409 return output
.join('');
412 str_format
.cache
= {};
414 str_format
.parse = function(fmt
) {
415 var _fmt
= fmt
, match
= [], parse_tree
= [], arg_names
= 0;
417 if ((match
= /^[^\x25]+/.exec(_fmt
)) !== null) {
418 parse_tree
.push(match
[0]);
420 else if ((match
= /^\x25{2}/.exec(_fmt
)) !== null) {
421 parse_tree
.push('%');
423 else if ((match
= /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt
)) !== null) {
426 var field_list
= [], replacement_field
= match
[2], field_match
= [];
427 if ((field_match
= /^([a-z_][a-z_\d]*)/i.exec(replacement_field
)) !== null) {
428 field_list
.push(field_match
[1]);
429 while ((replacement_field
= replacement_field
.substring(field_match
[0].length
)) !== '') {
430 if ((field_match
= /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field
)) !== null) {
431 field_list
.push(field_match
[1]);
433 else if ((field_match
= /^\[(\d+)\]/.exec(replacement_field
)) !== null) {
434 field_list
.push(field_match
[1]);
437 throw('[sprintf] huh?');
442 throw('[sprintf] huh?');
444 match
[2] = field_list
;
449 if (arg_names
=== 3) {
450 throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
452 parse_tree
.push(match
);
455 throw('[sprintf] huh?');
457 _fmt
= _fmt
.substring(match
[0].length
);
465 var vsprintf = function(fmt
, argv
) {
467 return sprintf
.apply(null, argv
);
470 Jed
.parse_plural = function ( plural_forms
, n
) {
471 plural_forms
= plural_forms
.replace(/n
/g
, n
);
472 return Jed
.parse_expression(plural_forms
);
475 Jed
.sprintf = function ( fmt
, args
) {
476 if ( {}.toString
.call( args
) == '[object Array]' ) {
477 return vsprintf( fmt
, [].slice
.call(args
) );
479 return sprintf
.apply(this, [].slice
.call(arguments
) );
482 Jed
.prototype.sprintf = function () {
483 return Jed
.sprintf
.apply(this, arguments
);
485 // END sprintf Implementation
487 // Start the Plural forms section
488 // This is a full plural form expression parser. It is used to avoid
489 // running 'eval' or 'new Function' directly against the plural
492 // This can be important if you get translations done through a 3rd
493 // party vendor. I encourage you to use this instead, however, I
494 // also will provide a 'precompiler' that you can use at build time
495 // to output valid/safe function representations of the plural form
496 // expressions. This means you can build this code out for the most
500 Jed
.PF
.parse = function ( p
) {
501 var plural_str
= Jed
.PF
.extractPluralExpr( p
);
502 return Jed
.PF
.parser
.parse
.call(Jed
.PF
.parser
, plural_str
);
505 Jed
.PF
.compile = function ( p
) {
506 // Handle trues and falses as 0 and 1
507 function imply( val
) {
508 return (val
=== true ? 1 : val
? val
: 0);
511 var ast
= Jed
.PF
.parse( p
);
512 return function ( n
) {
513 return imply( Jed
.PF
.interpreter( ast
)( n
) );
517 Jed
.PF
.interpreter = function ( ast
) {
518 return function ( n
) {
520 switch ( ast
.type
) {
522 return Jed
.PF
.interpreter( ast
.expr
)( n
);
524 if ( Jed
.PF
.interpreter( ast
.expr
)( n
) ) {
525 return Jed
.PF
.interpreter( ast
.truthy
)( n
);
527 return Jed
.PF
.interpreter( ast
.falsey
)( n
);
529 return Jed
.PF
.interpreter( ast
.left
)( n
) || Jed
.PF
.interpreter( ast
.right
)( n
);
531 return Jed
.PF
.interpreter( ast
.left
)( n
) && Jed
.PF
.interpreter( ast
.right
)( n
);
533 return Jed
.PF
.interpreter( ast
.left
)( n
) < Jed
.PF
.interpreter( ast
.right
)( n
);
535 return Jed
.PF
.interpreter( ast
.left
)( n
) > Jed
.PF
.interpreter( ast
.right
)( n
);
537 return Jed
.PF
.interpreter( ast
.left
)( n
) <= Jed
.PF
.interpreter( ast
.right
)( n
);
539 return Jed
.PF
.interpreter( ast
.left
)( n
) >= Jed
.PF
.interpreter( ast
.right
)( n
);
541 return Jed
.PF
.interpreter( ast
.left
)( n
) == Jed
.PF
.interpreter( ast
.right
)( n
);
543 return Jed
.PF
.interpreter( ast
.left
)( n
) != Jed
.PF
.interpreter( ast
.right
)( n
);
545 return Jed
.PF
.interpreter( ast
.left
)( n
) % Jed
.PF
.interpreter( ast
.right
)( n
);
551 throw new Error("Invalid Token found.");
556 Jed
.PF
.extractPluralExpr = function ( p
) {
558 p
= p
.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
560 if (! /;\s*$/.test(p
)) {
564 var nplurals_re
= /nplurals\=(\d+);/,
565 plural_re
= /plural\=(.*);/,
566 nplurals_matches
= p
.match( nplurals_re
),
570 // Find the nplurals number
571 if ( nplurals_matches
.length
> 1 ) {
572 res
.nplurals
= nplurals_matches
[1];
575 throw new Error('nplurals not found in plural_forms string: ' + p
);
578 // remove that data to get to the formula
579 p
= p
.replace( nplurals_re
, "" );
580 plural_matches
= p
.match( plural_re
);
582 if (!( plural_matches
&& plural_matches
.length
> 1 ) ) {
583 throw new Error('`plural` expression not found: ' + p
);
585 return plural_matches
[ 1 ];
588 /* Jison generated parser */
589 Jed
.PF
.parser
= (function(){
591 var parser
= {trace
: function trace() { },
593 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},
594 terminals_
: {2:"error",5:"EOF",6:"?",7:":",8:"||",9:"&&",10:"<",11:"<=",12:">",13:">=",14:"!=",15:"==",16:"%",17:"(",18:")",19:"n",20:"NUMBER"},
595 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]],
596 performAction
: function anonymous(yytext
,yyleng
,yylineno
,yy
,yystate
,$$,_
$) {
598 var $0 = $$.length
- 1;
600 case 1: return { type
: 'GROUP', expr
: $$[$0-1] };
602 case 2:this.$ = { type
: 'TERNARY', expr
: $$[$0-4], truthy
: $$[$0-2], falsey
: $$[$0] };
604 case 3:this.$ = { type
: "OR", left
: $$[$0-2], right
: $$[$0] };
606 case 4:this.$ = { type
: "AND", left
: $$[$0-2], right
: $$[$0] };
608 case 5:this.$ = { type
: 'LT', left
: $$[$0-2], right
: $$[$0] };
610 case 6:this.$ = { type
: 'LTE', left
: $$[$0-2], right
: $$[$0] };
612 case 7:this.$ = { type
: 'GT', left
: $$[$0-2], right
: $$[$0] };
614 case 8:this.$ = { type
: 'GTE', left
: $$[$0-2], right
: $$[$0] };
616 case 9:this.$ = { type
: 'NEQ', left
: $$[$0-2], right
: $$[$0] };
618 case 10:this.$ = { type
: 'EQ', left
: $$[$0-2], right
: $$[$0] };
620 case 11:this.$ = { type
: 'MOD', left
: $$[$0-2], right
: $$[$0] };
622 case 12:this.$ = { type
: 'GROUP', expr
: $$[$0-1] };
624 case 13:this.$ = { type
: 'VAR' };
626 case 14:this.$ = { type
: 'NUM', val
: Number(yytext
) };
630 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]}],
631 defaultActions
: {6:[2,1]},
632 parseError
: function parseError(str
, hash
) {
633 throw new Error(str
);
635 parse
: function parse(input
) {
638 vstack
= [null], // semantic value stack
639 lstack
= [], // location stack
648 //this.reductionCount = this.shiftCount = 0;
650 this.lexer
.setInput(input
);
651 this.lexer
.yy
= this.yy
;
652 this.yy
.lexer
= this.lexer
;
653 if (typeof this.lexer
.yylloc
== 'undefined')
654 this.lexer
.yylloc
= {};
655 var yyloc
= this.lexer
.yylloc
;
658 if (typeof this.yy
.parseError
=== 'function')
659 this.parseError
= this.yy
.parseError
;
661 function popStack (n
) {
662 stack
.length
= stack
.length
- 2*n
;
663 vstack
.length
= vstack
.length
- n
;
664 lstack
.length
= lstack
.length
- n
;
669 token
= self
.lexer
.lex() || 1; // $end = 1
670 // if token isn't its numeric value, convert
671 if (typeof token
!== 'number') {
672 token
= self
.symbols_
[token
] || token
;
677 var symbol
, preErrorSymbol
, state
, action
, a
, r
, yyval
={},p
,len
,newState
, expected
;
679 // retreive state number from top of stack
680 state
= stack
[stack
.length
-1];
682 // use default actions if available
683 if (this.defaultActions
[state
]) {
684 action
= this.defaultActions
[state
];
688 // read action for current state and first input
689 action
= table
[state
] && table
[state
][symbol
];
692 // handle parse error
694 if (typeof action
=== 'undefined' || !action
.length
|| !action
[0]) {
699 for (p
in table
[state
]) if (this.terminals_
[p
] && p
> 2) {
700 expected
.push("'"+this.terminals_
[p
]+"'");
703 if (this.lexer
.showPosition
) {
704 errStr
= 'Parse error on line '+(yylineno
+1)+":\n"+this.lexer
.showPosition()+"\nExpecting "+expected
.join(', ') + ", got '" + this.terminals_
[symbol
]+ "'";
706 errStr
= 'Parse error on line '+(yylineno
+1)+": Unexpected " +
707 (symbol
== 1 /*EOF*/ ? "end of input" :
708 ("'"+(this.terminals_
[symbol
] || symbol
)+"'"));
710 this.parseError(errStr
,
711 {text
: this.lexer
.match
, token
: this.terminals_
[symbol
] || symbol
, line
: this.lexer
.yylineno
, loc
: yyloc
, expected
: expected
});
714 // just recovered from another error
715 if (recovering
== 3) {
717 throw new Error(errStr
|| 'Parsing halted.');
720 // discard current lookahead and grab another
721 yyleng
= this.lexer
.yyleng
;
722 yytext
= this.lexer
.yytext
;
723 yylineno
= this.lexer
.yylineno
;
724 yyloc
= this.lexer
.yylloc
;
728 // try to recover from error
730 // check for error recovery rule in this state
731 if ((TERROR
.toString()) in table
[state
]) {
735 throw new Error(errStr
|| 'Parsing halted.');
738 state
= stack
[stack
.length
-1];
741 preErrorSymbol
= symbol
; // save the lookahead token
742 symbol
= TERROR
; // insert generic error symbol as new lookahead
743 state
= stack
[stack
.length
-1];
744 action
= table
[state
] && table
[state
][TERROR
];
745 recovering
= 3; // allow 3 real symbols to be shifted before reporting a new error
748 // this shouldn't happen, unless resolve defaults are off
749 if (action
[0] instanceof Array
&& action
.length
> 1) {
750 throw new Error('Parse Error: multiple actions possible at state: '+state
+', token: '+symbol
);
759 vstack
.push(this.lexer
.yytext
);
760 lstack
.push(this.lexer
.yylloc
);
761 stack
.push(action
[1]); // push state
763 if (!preErrorSymbol
) { // normal execution/no error
764 yyleng
= this.lexer
.yyleng
;
765 yytext
= this.lexer
.yytext
;
766 yylineno
= this.lexer
.yylineno
;
767 yyloc
= this.lexer
.yylloc
;
770 } else { // error just occurred, resume old lookahead f/ before error
771 symbol
= preErrorSymbol
;
772 preErrorSymbol
= null;
777 //this.reductionCount++;
779 len
= this.productions_
[action
[1]][1];
781 // perform semantic action
782 yyval
.$ = vstack
[vstack
.length
-len
]; // default to $$ = $1
783 // default location, uses first token for firsts, last for lasts
785 first_line
: lstack
[lstack
.length
-(len
||1)].first_line
,
786 last_line
: lstack
[lstack
.length
-1].last_line
,
787 first_column
: lstack
[lstack
.length
-(len
||1)].first_column
,
788 last_column
: lstack
[lstack
.length
-1].last_column
790 r
= this.performAction
.call(yyval
, yytext
, yyleng
, yylineno
, this.yy
, action
[1], vstack
, lstack
);
792 if (typeof r
!== 'undefined') {
798 stack
= stack
.slice(0,-1*len
*2);
799 vstack
= vstack
.slice(0, -1*len
);
800 lstack
= lstack
.slice(0, -1*len
);
803 stack
.push(this.productions_
[action
[1]][0]); // push nonterminal (reduce)
804 vstack
.push(yyval
.$);
805 lstack
.push(yyval
._
$);
806 // goto new state = table[STATE][NONTERMINAL]
807 newState
= table
[stack
[stack
.length
-2]][stack
[stack
.length
-1]];
808 stack
.push(newState
);
818 }};/* Jison generated lexer */
819 var lexer
= (function(){
822 parseError
:function parseError(str
, hash
) {
823 if (this.yy
.parseError
) {
824 this.yy
.parseError(str
, hash
);
826 throw new Error(str
);
829 setInput:function (input
) {
831 this._more
= this._less
= this.done
= false;
832 this.yylineno
= this.yyleng
= 0;
833 this.yytext
= this.matched
= this.match
= '';
834 this.conditionStack
= ['INITIAL'];
835 this.yylloc
= {first_line
:1,first_column
:0,last_line
:1,last_column
:0};
839 var ch
= this._input
[0];
844 var lines
= ch
.match(/\n/);
845 if (lines
) this.yylineno
++;
846 this._input
= this._input
.slice(1);
849 unput:function (ch
) {
850 this._input
= ch
+ this._input
;
857 pastInput:function () {
858 var past
= this.matched
.substr(0, this.matched
.length
- this.match
.length
);
859 return (past
.length
> 20 ? '...':'') + past
.substr(-20).replace(/\n/g, "");
861 upcomingInput:function () {
862 var next
= this.match
;
863 if (next
.length
< 20) {
864 next
+= this._input
.substr(0, 20-next
.length
);
866 return (next
.substr(0,20)+(next
.length
> 20 ? '...':'')).replace(/\n/g, "");
868 showPosition:function () {
869 var pre
= this.pastInput();
870 var c
= new Array(pre
.length
+ 1).join("-");
871 return pre
+ this.upcomingInput() + "\n" + c
+"^";
877 if (!this._input
) this.done
= true;
887 var rules
= this._currentRules();
888 for (var i
=0;i
< rules
.length
; i
++) {
889 match
= this._input
.match(this.rules
[rules
[i
]]);
891 lines
= match
[0].match(/\n.*/g);
892 if (lines
) this.yylineno
+= lines
.length
;
893 this.yylloc
= {first_line
: this.yylloc
.last_line
,
894 last_line
: this.yylineno
+1,
895 first_column
: this.yylloc
.last_column
,
896 last_column
: lines
? lines
[lines
.length
-1].length
-1 : this.yylloc
.last_column
+ match
[0].length
}
897 this.yytext
+= match
[0];
898 this.match
+= match
[0];
899 this.matches
= match
;
900 this.yyleng
= this.yytext
.length
;
902 this._input
= this._input
.slice(match
[0].length
);
903 this.matched
+= match
[0];
904 token
= this.performAction
.call(this, this.yy
, this, rules
[i
],this.conditionStack
[this.conditionStack
.length
-1]);
905 if (token
) return token
;
909 if (this._input
=== "") {
912 this.parseError('Lexical error on line '+(this.yylineno
+1)+'. Unrecognized text.\n'+this.showPosition(),
913 {text
: "", token
: null, line
: this.yylineno
});
918 if (typeof r
!== 'undefined') {
924 begin
:function begin(condition
) {
925 this.conditionStack
.push(condition
);
927 popState
:function popState() {
928 return this.conditionStack
.pop();
930 _currentRules
:function _currentRules() {
931 return this.conditions
[this.conditionStack
[this.conditionStack
.length
-1]].rules
;
933 topState:function () {
934 return this.conditionStack
[this.conditionStack
.length
-2];
936 pushState
:function begin(condition
) {
937 this.begin(condition
);
939 lexer
.performAction
= function anonymous(yy
,yy_
,$avoiding_name_collisions
,YY_START
) {
941 var YYSTATE
=YY_START
;
942 switch($avoiding_name_collisions
) {
943 case 0:/* skip whitespace */
977 case 17:return 'INVALID'
981 lexer
.rules
= [/^\s+/,/^[0-9]+(\.[0-9]+)?\b/,/^n\b/,/^\|\|/,/^&&/,/^\?/,/^:/,/^<=/,/^>=/,/^</,/^>/,/^!=/,/^==/,/^%/,/^\(/,/^\)/,/^$/,/^./];
982 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
;})()
983 parser
.lexer
= lexer
;
988 // Handle node, amd, and global systems
989 if (typeof exports
!== 'undefined') {
990 if (typeof module
!== 'undefined' && module
.exports
) {
991 exports
= module
.exports
= Jed
;
996 if (typeof define
=== 'function' && define
.amd
) {
997 define('jed', function() {
1001 // Leak a global regardless of module system