-
Notifications
You must be signed in to change notification settings - Fork 154
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #156 from eemeli/lax-number
Throw fewer runtime errors for `#`
- Loading branch information
Showing
5 changed files
with
137 additions
and
92 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
{ | ||
"source": { "include": [ "lib/index.js", "lib/compiler.js" ] }, | ||
"source": { "include": [ "lib/" ] }, | ||
"plugins": [ "plugins/markdown" ], | ||
"opts": { "destination": "./doc/" } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
var Compiler = require('./compiler'); | ||
|
||
|
||
/** A set of utility functions that are called by the compiled Javascript | ||
* functions, these are included locally in the output of {@link | ||
* MessageFormat#compile compile()}. | ||
* | ||
* @class | ||
* @param {MessageFormat} mf - A MessageFormat instance | ||
*/ | ||
function Runtime(mf) { | ||
this.mf = mf; | ||
this.setStrictNumber(mf.strictNumberSign); | ||
} | ||
|
||
module.exports = Runtime; | ||
|
||
|
||
/** Utility function for `#` in plural rules | ||
* | ||
* Will throw an Error if `value` has a non-numeric value and `offset` is | ||
* non-zero or {@link MessageFormat#setStrictNumberSign} is set. | ||
* | ||
* @function Runtime#number | ||
* @param {number} value - The value to operate on | ||
* @param {string} name - The name of the argument, used for error reporting | ||
* @param {number} [offset=0] - An optional offset, set by the surrounding context | ||
* @returns {number|string} The result of applying the offset to the input value | ||
*/ | ||
function defaultNumber(value, name, offset) { | ||
if (!offset) return value; | ||
if (isNaN(value)) throw new Error('Can\'t apply offset:' + offset + ' to argument `' + name + | ||
'` with non-numerical value ' + JSON.stringify(value) + '.'); | ||
return value - offset; | ||
} | ||
|
||
|
||
/** @private */ | ||
function strictNumber(value, name, offset) { | ||
if (isNaN(value)) throw new Error('Argument `' + name + '` has non-numerical value ' + JSON.stringify(value) + '.'); | ||
return value - (offset || 0); | ||
} | ||
|
||
|
||
/** Set how strictly the {@link number} method parses its input. | ||
* | ||
* According to the ICU MessageFormat spec, `#` can only be used to replace a | ||
* number input of a `plural` statement. By default, messageformat.js does not | ||
* throw a runtime error if you use non-numeric argument with a `plural` rule, | ||
* unless rule also includes a non-zero `offset`. | ||
* | ||
* This is called by {@link MessageFormat#setStrictNumberSign} to follow the | ||
* stricter ICU MessageFormat spec. | ||
* | ||
* @param {boolean} [enable=false] | ||
*/ | ||
Runtime.prototype.setStrictNumber = function(enable) { | ||
this.number = enable ? strictNumber : defaultNumber; | ||
} | ||
|
||
|
||
/** Utility function for `{N, plural|selectordinal, ...}` | ||
* | ||
* @param {number} value - The key to use to find a pluralization rule | ||
* @param {number} offset - An offset to apply to `value` | ||
* @param {function} lcfunc - A locale function from `pluralFuncs` | ||
* @param {Object.<string,string>} data - The object from which results are looked up | ||
* @param {?boolean} isOrdinal - If true, use ordinal rather than cardinal rules | ||
* @returns {string} The result of the pluralization | ||
*/ | ||
Runtime.prototype.plural = function(value, offset, lcfunc, data, isOrdinal) { | ||
if ({}.hasOwnProperty.call(data, value)) return data[value]; | ||
if (offset) value -= offset; | ||
var key = lcfunc(value, isOrdinal); | ||
if (key in data) return data[key]; | ||
return data.other; | ||
} | ||
|
||
|
||
/** Utility function for `{N, select, ...}` | ||
* | ||
* @param {number} value - The key to use to find a selection | ||
* @param {Object.<string,string>} data - The object from which results are looked up | ||
* @returns {string} The result of the select statement | ||
*/ | ||
Runtime.prototype.select = function(value, data) { | ||
if ({}.hasOwnProperty.call(data, value)) return data[value]; | ||
return data.other; | ||
} | ||
|
||
|
||
/** @private */ | ||
Runtime.prototype.toString = function(pluralFuncs, compiler) { | ||
function _stringify(o, level) { | ||
if (typeof o != 'object') { | ||
var funcStr = o.toString().replace(/^(function )\w*/, '$1'); | ||
var indent = /([ \t]*)\S.*$/.exec(funcStr); | ||
return indent ? funcStr.replace(new RegExp('^' + indent[1], 'mg'), '') : funcStr; | ||
} | ||
var s = []; | ||
for (var i in o) { | ||
if (level == 0) s.push('var ' + i + ' = ' + _stringify(o[i], level + 1) + ';\n'); | ||
else s.push(Compiler.propname(i) + ': ' + _stringify(o[i], level + 1)); | ||
} | ||
if (level == 0) return s.join(''); | ||
if (s.length == 0) return '{}'; | ||
var indent = ' '; while (--level) indent += ' '; | ||
return '{\n' + s.join(',\n').replace(/^/gm, indent) + '\n}'; | ||
} | ||
|
||
var obj = {}; | ||
Object.keys(compiler.locales).forEach(function(lc) { obj[Compiler.funcname(lc)] = pluralFuncs[lc]; }); | ||
Object.keys(compiler.runtime).forEach(function(fn) { obj[fn] = this[fn]; }, this); | ||
var fmtKeys = Object.keys(compiler.formatters); | ||
var fmt = this.mf.fmt; | ||
if (fmtKeys.length) obj.fmt = fmtKeys.reduce(function(o, key) { o[key] = fmt[key]; return o; }, {}); | ||
return _stringify(obj, 0); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters