Comparing version 0.2.1 to 0.3.0
@@ -5,6 +5,16 @@ # overload2 | ||
## [0.2.1] - 2017-09 | ||
## [0.3.0] - Feb 23th, 2018, RISKY | ||
### New | ||
* Optional parameter avaiable. | ||
### Fixed | ||
* Bugs hiding in mutable parameters. | ||
## [0.2.1] - Nov, 2017 | ||
### Fixed | ||
* In the previous version, if argument is valued with null / undefined and param is not defined with NULL / UNDEFINED, overload2 will conclude that the argument and the param are not matching, even if the param's datatype accepts null / undefined. It is really ambiguous and has been corrected in this version. | ||
@@ -11,0 +21,0 @@ |
/** | ||
* 函数多态的中量级实现。 | ||
* @author youngoat@163.com | ||
* | ||
* 注释中的术语表: | ||
* * 参数 = 定义中的形式参数 | ||
* * 值组 = 多态函数调用时的实际参数表 | ||
*/ | ||
/* eslint-disable no-shadow-restricted-names */ | ||
(function(global, undefined) { | ||
var MODULE_REQUIRE | ||
@@ -37,4 +41,6 @@ /* built-in */ | ||
/* eslint-disable new-cap */ | ||
var err = new parent; | ||
this.stack = [ this.name + ': ' + this.message ].concat( err.stack.split('\n').slice(2) ).join('\n'); | ||
/* eslint-enable new-cap */ | ||
this.stack = [ this.name + ': ' + this.message ].concat(err.stack.split('\n').slice(2)).join('\n'); | ||
}; | ||
@@ -56,2 +62,3 @@ | ||
// 同时,我们在自定义错误的设计上也尽量遵循了准确和具有建设性的原则。 | ||
/* eslint-disable key-spacing, comma-style */ | ||
, ERR = { | ||
@@ -65,3 +72,3 @@ Generic: declareException('Error'), | ||
Type: declareException('TypeError', TypeError, function(/*string*/ desc, /*string|Array*/ types, /*string*/ actual) { | ||
this.message = desc + ' must be ' + ( typeof types == 'string' ? types : types.join(' | ') ) + ': ' + actual; | ||
this.message = desc + ' must be ' + (typeof types == 'string' ? types : types.join(' | ')) + ': ' + actual; | ||
}), | ||
@@ -79,2 +86,3 @@ | ||
} | ||
/* eslint-enable key-spacing, comma-style */ | ||
@@ -97,9 +105,34 @@ /** | ||
// 数据是否包含指定元素。 | ||
// 数组是否包含指定元素。 | ||
, has = function(arr, item) { | ||
return arr.indexOf(item) >= 0; | ||
} | ||
// 数组中的每个元素是否均能通过指定函数的测试。 | ||
, eachMatch = function(arr, fn) { | ||
var matched = true; | ||
for (var i = 0; matched && i < arr.length; i++) { | ||
matched = fn(arr[i], i); | ||
} | ||
return matched; | ||
} | ||
// 数组中任意元素通过指定函数的测试。 | ||
, onceMatch = function(arr, fn) { | ||
var matched = false; | ||
for (var i = 0; !matched && i < arr.length; i++) { | ||
matched = fn(arr[i], i); | ||
} | ||
return matched; | ||
} | ||
; | ||
/** | ||
* 自定义缺省值类。 | ||
*/ | ||
function Absent(value) { | ||
this.value = value; | ||
} | ||
/** | ||
* 自定义数据类型类。 | ||
@@ -121,36 +154,88 @@ * @param {function} matcher | ||
if (matcher instanceof RegExp) | ||
this.match = function(value) { return matcher.test(value); } | ||
else | ||
if (matcher instanceof RegExp) { | ||
this.match = function(value) { | ||
return matcher.test(value); | ||
}; | ||
} | ||
else { | ||
this.match = matcher; | ||
} | ||
} | ||
// --------------------------- | ||
// 预定义数据类型。 | ||
Type.ANY = new Type(function() { | ||
return true; | ||
}); | ||
Type.BOOLEAN = new Type(function(value) { | ||
return typeof value === 'boolean'; | ||
}); | ||
Type.CHAR = new Type(function(value) { | ||
return typeof value === 'string' && value.length == 1; | ||
}); | ||
Type.NUMBER = new Type(function(value) { | ||
return typeof value === 'number'; | ||
}); | ||
Type.PLAIN_OBJECT = new Type(function(value) { | ||
return typeof value === 'object' && value.constructor === Object; | ||
}); | ||
Type.SCALAR = new Type(function(value) { | ||
return [ 'boolean', 'number', 'string' ].indexOf(typeof value) >= 0; | ||
}); | ||
Type.STRING = new Type(function(value) { | ||
return typeof value === 'string'; | ||
}); | ||
// --------------------------- | ||
// 自定义数据类型生成器。 | ||
/** | ||
* 参数类。 | ||
* 构造函数支持重载,可能的参数形式包括: | ||
* "alias ...decorator" | ||
* type, ...decorator | ||
* type, decorators | ||
* 其中 type 可以是 Type 对象、普通的构造函数或者类型别名,decorators 则代表由多个修饰符组成的字符串,以空格分隔。 | ||
* 创建枚举类型。 | ||
*/ | ||
function Param() { | ||
if (arguments.length == 0) { | ||
throw new ERR.Arguments('1+', 0); | ||
} | ||
Type.enum = function() { | ||
var args = Array.from(arguments); | ||
return new Type(function(value) { | ||
return args.indexOf(value) >= 0; | ||
}); | ||
}; | ||
if (arguments.length == 1 && arguments[0] instanceof Param) { | ||
return arguments[0]; | ||
} | ||
// --------------------------- | ||
// 有关类型的逻辑工具。 | ||
var type = arguments[0], decos = []; | ||
// 根据现有类型,创建新的复合类型。 | ||
Type.and = function() { | ||
var types = Array.from(arguments).map(Type.parse); | ||
return new Type(function(value) { | ||
return eachMatch(types, function(type) { | ||
return type.match(value); | ||
}); | ||
}); | ||
}; | ||
// --------------------------- | ||
// 处理数据类型。 | ||
// 根据现有类型,创建新的复合类型。 | ||
Type.or = function() { | ||
var types = Array.from(arguments).map(Type.parse); | ||
return new Type(function(value) { | ||
return onceMatch(types, function(type) { | ||
return type.match(value); | ||
}); | ||
}); | ||
}; | ||
// 取当前类型的补集。 | ||
Type.not = function(type) { | ||
return new Type(function(value) { | ||
return !type.match(value); | ||
}); | ||
}; | ||
Type.parse = function(type) { | ||
if (typeof type == 'string') { | ||
// 如果首参数是字符串,则它可能包括数据类型别名及修饰符,我们需要首先将其拆分。 | ||
// 拆分后第一个字符串作为数据类型(别名,后面还会继续处理),其余作为修饰符。 | ||
decos = type.trim().split(/\s+/); | ||
type = decos.shift(); | ||
var formalType = TYPE_ALIAS[type]; | ||
@@ -180,16 +265,145 @@ if (!formalType) { | ||
} | ||
this.type = type; | ||
return type; | ||
}; | ||
/** | ||
* 参数类。 | ||
* 构造函数支持重载,可能的参数形式包括: | ||
* "<alias> ...<decorator>" | ||
* type, ...decorator | ||
* type, decorators | ||
* 其中 type 可以是 Type 对象、普通的构造函数或者类型别名,decorators 则代表由多个修饰符组成的字符串,以空格分隔。 | ||
*/ | ||
function Param() { | ||
if (arguments.length == 0) { | ||
throw new ERR.Arguments('1+', 0); | ||
} | ||
if (arguments.length == 1 && arguments[0] instanceof Param) { | ||
return arguments[0]; | ||
} | ||
this.type = null; | ||
this.minSize = 1; | ||
this.maxSize = 1; | ||
this.nil = false; | ||
this.undef = false; | ||
this.absent = false; | ||
this.absentValue = undefined; | ||
this.arrayed = false; | ||
var setSize = (function(size) { | ||
if (typeof size == 'string') { | ||
// 清除所有空白字符。 | ||
size = size.replace(/\s/g, ''); | ||
if (size == '*' || size == '...') { | ||
this.minSize = 0; | ||
this.maxSize = Infinity; | ||
this.arrayed = true; | ||
return true; | ||
} | ||
if (size == '+') { | ||
this.minSize = 1; | ||
this.maxSize = Infinity; | ||
this.arrayed = true; | ||
return true; | ||
} | ||
// 区间形式。 | ||
if (/^\{.+\}$/.test(size)) { | ||
size = size.slice(1, -1); | ||
} | ||
if (/^\d+$/.test(size)) { | ||
this.minSize = this.maxSize = parseInt(size); | ||
this.arrayed = true; | ||
return true; | ||
} | ||
if (/^,(\d+)$/.test(size)) { | ||
this.minSize = 0; | ||
this.maxSize = parseInt(RegExp.$1); | ||
this.arrayed = true; | ||
return true; | ||
} | ||
if (/^(\d+),$/.test(size)) { | ||
this.minSize = parseInt(RegExp.$1); | ||
this.maxSize = Infinity; | ||
this.arrayed = true; | ||
return true; | ||
} | ||
if (/^(\d+),(\d+)$/.test(size)) { | ||
this.minSize = parseInt(RegExp.$1); | ||
this.maxSize = parseInt(RegExp.$2); | ||
this.arrayed = true; | ||
return true; | ||
} | ||
return false; | ||
} | ||
if (Number.isInteger(size) && size > 0) { | ||
this.minSize = this.maxSize = size; | ||
this.arrayed = true; | ||
return true; | ||
} | ||
return false; | ||
}).bind(this); | ||
var type = arguments[0], decos = []; | ||
// --------------------------- | ||
// 处理特殊的参数占位符。 | ||
if (setSize(type)) { | ||
this.type = Type.ANY; | ||
return this; | ||
} | ||
// --------------------------- | ||
// 处理数据类型。 | ||
if (typeof type == 'string') { | ||
// 如果首参数是字符串,则它可能包括数据类型别名及修饰符,我们需要首先将其拆分。 | ||
// 拆分后第一个字符串作为数据类型(别名,后面还会继续处理),其余作为修饰符。 | ||
decos = type.trim().split(/\s+/); | ||
type = decos.shift(); | ||
} | ||
this.type = Type.parse(type); | ||
// --------------------------- | ||
// 处理修饰符。 | ||
var DECOS = ['NULL', 'UNDEFINED']; | ||
var DECOS = ['NULL', 'UNDEFINED', 'ABSENT'], i; | ||
for (var i = 1; i < arguments.length; i++) { | ||
decos = decos.concat(arguments[i].trim().split(/\s+/)); | ||
for (i = 1; i < arguments.length; i++) { | ||
if (typeof arguments[i] == 'string') { | ||
decos = decos.concat(arguments[i].trim().split(/\s+/)); | ||
} | ||
else if (Number.isInteger(arguments[i])) { | ||
decos.push(arguments[i]); | ||
} | ||
// @tag 20180222 | ||
else if (arguments[i] instanceof Absent) { | ||
this.absent = true; | ||
this.absentValue = arguments[i].value; | ||
} | ||
} | ||
for (var i = 0; i < decos.length; i++) { | ||
for (i = 0; i < decos.length; i++) { | ||
var rawDeco = decos[i]; | ||
// 修饰符不区分大小写。 | ||
// 尝试作为量化修饰符应用。 | ||
if (setSize(rawDeco)) continue; | ||
// 设置缺省值。 | ||
// 注意:为避免歧义,缺省值字面量中不得出现空格。 | ||
// @tag 20180222 | ||
if (/^=(.+)$/.test(rawDeco)) { | ||
this.absent = true; | ||
this.absentValue = eval(RegExp.$1); | ||
continue; | ||
} | ||
// 普通修饰符不区分大小写。 | ||
decos[i] = decos[i].toUpperCase(); | ||
@@ -206,6 +420,11 @@ | ||
// 参数是否可以为 null | ||
this.nil = has(decos, 'null'); | ||
this.nil = has(decos, 'NULL'); | ||
// 参数是否可以为 undefined | ||
this.undef = has(decos, 'undefined'); | ||
this.undef = has(decos, 'UNDEFINED'); | ||
// 参数是否可以缺席。 | ||
if (!this.absent) { | ||
this.absent = has(decos, 'ABSENT'); | ||
} | ||
} | ||
@@ -215,6 +434,11 @@ | ||
// 判断值是否合乎参数限定。 | ||
/** | ||
* 判断值是否合乎参数限定。 | ||
*/ | ||
Param.prototype.satisfy = function(value) { | ||
if (value === null && this.nil) return true; | ||
if (value === undefined && this.undef) return true; | ||
// if (value === null && this.nil) return true; | ||
// if (value === undefined && this.undef) return true; | ||
if (value === null) return this.nil; | ||
if (value === undefined) return this.undef; | ||
return this.type.match(value); | ||
@@ -229,11 +453,23 @@ }; | ||
var params = []; | ||
var minSize = 0; | ||
var maxSize = 0; | ||
var easy = true; | ||
for (var i = 0, args; i < arguments.length; i++) { | ||
args = (arguments[i] instanceof Array) ? arguments[i] : [ arguments[i] ]; | ||
params[i] = Param.parse.apply(null, args); | ||
maxSize += params[i].maxSize; | ||
// @tag 20180222 | ||
// 如果参数允许缺省,则在整个实参列表中占据的最小长度为 0。 | ||
minSize += params[i].absent ? 0 : params[i].minSize; | ||
// 如果形参可缺省,或者为多占位或可变长度,则不适用简易模式。 | ||
easy = easy && !( params[i].absent || params[i].arrayed ); | ||
} | ||
this.length = params.length; | ||
this.item = function(i) { | ||
return params[i]; | ||
}; | ||
this.easy = easy; | ||
this.minSize = minSize; | ||
this.maxSize = maxSize; | ||
this.params = params; | ||
} | ||
@@ -244,16 +480,188 @@ | ||
/** | ||
* 判断值组是否合乎参数值限定。 | ||
* 判断值组是否合乎参数定义。 | ||
* @deprecated 为了支持可变长度参数,用更复杂的 .parse() 方法取代。 | ||
*/ | ||
ParamList.prototype.satisfy = function(args) { | ||
// 参数表长度检查。 | ||
if (args.length != this.length) { | ||
if (args.length != this.params.length) { | ||
return false; | ||
} | ||
// 参数类型核对 | ||
var matched = true; | ||
for (var i = 0; matched && i < this.length; i++) { | ||
matched = this.item(i).satisfy(args[i]); | ||
// 参数类型核对。 | ||
return eachMatch(this.params, function(param, index) { | ||
return param.satisfy(args[index]); | ||
}); | ||
} | ||
/** | ||
* 尝试按参数定义解析值组。 | ||
* @return false 匹配失败 | ||
* @return Array 匹配成功,返回与定义一一对应的新值组(因为参数可能是可变长度的) | ||
*/ | ||
ParamList.prototype.parse = function(args) { | ||
// --------------------------- | ||
// 检查值组的长度是否在参数列表定义的区间内。 | ||
if (args.length < this.minSize || this.maxSize < args.length) { | ||
return null; | ||
} | ||
return matched; | ||
// --------------------------- | ||
// 适用简易匹配逻辑。 | ||
if (this.easy) { | ||
var matched = eachMatch(this.params, function(param, index) { | ||
return param.satisfy(args[index]); | ||
}); | ||
return matched ? args : null; | ||
} | ||
// --------------------------- | ||
// 否则,适用复杂匹配逻辑。 | ||
// 注意:此函数将被递归调用。 | ||
var matching = function(args, params) { | ||
var newArgs = []; | ||
var argCursor = 0, paramCursor = 0; | ||
for (; argCursor < args.length; argCursor++) { | ||
// 如果所有形式参数匹配成功之后,仍有多余的实参未参加匹配,则认为整个参数表匹配失败。 | ||
if (paramCursor >= params.length) { | ||
return null; | ||
} | ||
// 当前参数。 | ||
var param = params[paramCursor]; | ||
// 当前实参。 | ||
var arg = args[argCursor]; | ||
// --------------------------- | ||
// 如果形式参数对应单个实参且不可缺省,则按简易逻辑处理。 | ||
if (!param.absent && param.minSize == 1 && param.maxSize == 1) { | ||
if (!param.satisfy(arg)) { | ||
// 如果实参与形式参数不匹配,则终止后续匹配,整个参数表匹配失败。 | ||
return null; | ||
} | ||
else { | ||
newArgs.push(arg); | ||
paramCursor++; | ||
} | ||
continue; | ||
} | ||
// --------------------------- | ||
// 否则,适用复杂逻辑。 | ||
// 如果剩余实参数量不足以匹配当前形式参数: | ||
if (args.length - argCursor < param.minSize) { | ||
// 若参数可缺省: | ||
if (param.absent) { | ||
// 使用缺省值替补。 | ||
newArgs.push(param.absentValue); | ||
// 不消耗实参。 | ||
argCursor--; | ||
// 当前形参匹配完毕。 | ||
continue; | ||
} | ||
// 否则当前形式参数匹配失败,整个参数表匹配失败。 | ||
return null; | ||
} | ||
// 依次储存当前形式参数匹配的实参。 | ||
var paramArgs = []; | ||
var pushParamArg = function() { | ||
if (param.arrayed) newArgs.push(paramArgs); | ||
else newArgs.push(paramArgs[0]); | ||
}; | ||
for (; argCursor < args.length && param.satisfy(args[argCursor]); argCursor++) { | ||
paramArgs.push(args[argCursor]); | ||
} | ||
argCursor--; | ||
paramCursor++; | ||
// 如当前形式参数匹配实参个数未达到最小值: | ||
if (paramArgs.length < param.minSize) { | ||
// 若参数可缺省: | ||
if (param.absent) { | ||
// 使用缺省值替补。 | ||
newArgs.push(param.absentValue); | ||
// 回吐所有已消耗的实参。 | ||
argCursor -= paramArgs.length; | ||
// 当前形参匹配完毕。 | ||
continue; | ||
} | ||
// 否则当前形式参数匹配失败,整个参数表匹配失败。 | ||
return null; | ||
} | ||
var restParams = params.slice(paramCursor); | ||
// 抵达匹配边界时,若 | ||
// 仅匹配了最小数量的实参且该参数不可缺省,或者已是最后一个形式参数, | ||
// 则直接固定参数值。 | ||
if (!param.absent && paramArgs.length == param.minSize || restParams.length == 0) { | ||
pushParamArg(); | ||
continue; | ||
} | ||
// 否则,须尝试让贤。 | ||
var restArgs = args.slice(argCursor + 1); | ||
// 步步让贤,直到让无可让。 | ||
do { | ||
var restNewArgs = matching(restArgs, restParams); | ||
// 剩余参数匹配成功,则拼接匹配结果,整个参数表匹配成功。 | ||
if (restNewArgs != null) { | ||
pushParamArg(); | ||
newArgs = newArgs.concat(restNewArgs); | ||
break; | ||
} | ||
// 如果已让无可让: | ||
if (paramArgs.length == param.minSize) { | ||
// 如果参数可以缺省,统统不要了,全给后续形参去。 | ||
if (param.absent) { | ||
restArgs = paramArgs.concat(restArgs); | ||
paramArgs = param.absentValue; | ||
continue; | ||
} | ||
// 否则,就此罢了(liao)。 | ||
newArgs = null; | ||
break; | ||
} | ||
else { | ||
restArgs.unshift(paramArgs.pop()); | ||
} | ||
} while (true); | ||
// 行进至此,不成功,则成仁。 | ||
return newArgs; | ||
} | ||
// 此时,所有实参均已消耗完毕。 | ||
for (; newArgs.length < params.length; paramCursor++) { | ||
if (params[paramCursor].absent) { | ||
newArgs.push(params[paramCursor].absentValue); | ||
} | ||
else if (params[paramCursor].minSize == 0) { | ||
newArgs.push([]); | ||
} | ||
else { | ||
return null; | ||
} | ||
} | ||
return newArgs; | ||
}; | ||
return matching(Array.from(args), this.params); | ||
}; | ||
@@ -282,7 +690,9 @@ | ||
if (typeof args[0] == 'number') { | ||
if (args.length > 1) { | ||
throw new ERR.Arguments(2, args.length + 1); | ||
} | ||
this.paramCount = args[0]; | ||
// 支持可变长度参数后,用于代表值组长度的数字,也可以用 Param 实例来表征。 | ||
// 在此我们仍保留 argLength,是出于提高性能的考虑。 | ||
if (typeof args[0] == 'number' && args.length == 1) { | ||
// if (args.length > 1) { | ||
// throw new ERR.Arguments(2, args.length + 1); | ||
// } | ||
this.argLength = args[0]; | ||
} | ||
@@ -307,9 +717,15 @@ else if (args[0] instanceof ParamList) { | ||
Overload.prototype.exec = function(scope, args) { | ||
// 若指定了形参数量,而实参数量不符,则直接跳过。 | ||
if (typeof this.paramCount == 'number' && (args.length != this.paramCount)) { | ||
return false; | ||
// 对于每一个重载实现,值组长度和形参表是互斥的。 | ||
// 或者指定值组长度,或者指定形参表,而不是同时指定。 | ||
// 如指定值组长度,则仅判断长度。 | ||
if (typeof this.argLength == 'number') { | ||
if (args.length != this.argLength) return false; | ||
} | ||
if (this.params && !this.params.satisfy(args)) { | ||
return false; | ||
// 否则,须尝试匹配值组与参数列表。 | ||
else { | ||
// 尝试获取按形式参数列表重整后的值组。 | ||
args = this.params.parse(args); | ||
if (!args) return false; | ||
} | ||
@@ -360,7 +776,13 @@ | ||
if (this._defaultMethod) { | ||
return this.defaultMethod.apply(scope, args); | ||
return this._defaultMethod.apply(scope, args); | ||
} | ||
// 表示没有任何重载形参与实参匹配。 | ||
throw new ERR.Unmatching('Unmatching arguments: ' + args); | ||
var types = []; | ||
for (var i = 0, type; i < args.length; i++) { | ||
type = (args[i] === null) ? 'null' : typeof args[i]; | ||
if (type === 'object') type = args[i].constructor.name; | ||
types.push(type); | ||
} | ||
throw new ERR.Unmatching('Unmatching arguments: ' + types.join(', ')); | ||
}; | ||
@@ -392,3 +814,2 @@ | ||
if (arguments.length > 2) { | ||
console.warn('redundant arguments found'); | ||
} | ||
@@ -408,3 +829,2 @@ } | ||
if (arguments.length > 1) { | ||
console.warn('redundant arguments found'); | ||
} | ||
@@ -418,3 +838,2 @@ this._defaultMethod = method; | ||
function Overloader() { | ||
var overloaded = new OverloadedFunction(); | ||
@@ -425,19 +844,2 @@ var append = function(args) { | ||
// 保存多态实现。 | ||
// 注意:如果多态实现不合规,应当在定义时抛出异常,调用时不再进行合规校验。 | ||
var saveDEF = function(args, isDefault) { | ||
var overloadInstance; | ||
if (args[0] instanceof Overload) { | ||
overloadInstance = args[0]; | ||
} | ||
else { | ||
overloadInstance = Overload.parse.apply(null, args); | ||
} | ||
overloaded.overload(overloadInstance); | ||
if (isDefault) { | ||
overloaded.defaultMethod = overloadInstance.method; | ||
} | ||
}; | ||
if (arguments.length) { | ||
@@ -469,65 +871,40 @@ // append(arguments); | ||
// 预定义数据类型。 | ||
// 为了向前兼容,保留 0.1.0 版本之前的预定义数据类型,但以后新增类型仅挂靠 Type 函数。 | ||
Overloader.ANY = new Type(function(value) { | ||
return true; | ||
}); | ||
Overloader.ANY = Type.ANY; | ||
Overloader.BOOLEAN = Type.BOOLEAN; | ||
Overloader.CHAR = Type.CHAR; | ||
Overloader.NUMBER = Type.NUMBER; | ||
Overloader.SCALAR = Type.SCALAR; | ||
Overloader.STRING = Type.STRING; | ||
Overloader.BOOLEAN = new Type(function(value) { | ||
return typeof value == 'boolean'; | ||
}); | ||
Overloader.CHAR = new Type(function(value) { | ||
return typeof value == 'string' && value.length == 1; | ||
}); | ||
Overloader.NUMBER = new Type(function(value) { | ||
return typeof value == 'number'; | ||
}); | ||
Overloader.SCALAR = new Type(function(value) { | ||
return [ 'boolean', 'number', 'string' ].indexOf(typeof value) >= 0; | ||
}); | ||
Overloader.STRING = new Type(function(value) { | ||
return typeof value == 'string'; | ||
}); | ||
// --------------------------- | ||
// 自定义数据类型生成器。 | ||
// 为了向前兼容,保留 0.1.0 版本之前的数据类型生成工具,但以后新增工具仅挂靠 Type 函数。 | ||
/** | ||
* 创建枚举类型。 | ||
*/ | ||
Overloader.enum = function() { | ||
var args = []; | ||
for (var i = 0; i < arguments.length; i++) { | ||
args.push(arguments[i]); | ||
} | ||
Overloader.enum = Type.enum; | ||
return new Type(function(value) { | ||
return args.indexOf(value) >= 0; | ||
}); | ||
}; | ||
// --------------------------- | ||
Overloader.Type = Type; | ||
Overloader.type = Type; | ||
Overloader.parseType = Type; | ||
Overloader.Type = Type; | ||
Overloader.type = Type; | ||
// Overloader.parseType = Type; | ||
Overloader.Param = Param; | ||
Overloader.param = Param.parse; | ||
Overloader.parseParam = Param.parse; | ||
Overloader.Param = Param; | ||
Overloader.param = Param.parse; | ||
Overloader.parseParam = Param.parse; | ||
Overloader.ParamList = ParamList; | ||
Overloader.paramList = ParamList.parse; | ||
Overloader.ParamList = ParamList; | ||
Overloader.paramList = ParamList.parse; | ||
Overloader.parseParamList = ParamList.parse; | ||
Overloader.Overload = Overload; | ||
Overloader.overload = Overload.parse; | ||
Overloader.parseOverload = Overload.parse; | ||
Overloader.Overload = Overload; | ||
Overloader.overload = Overload.parse; | ||
Overloader.parseOverload = Overload.parse; | ||
Overloader.Function = OverloadedFunction; | ||
Overloader.Function = OverloadedFunction; | ||
Overloader.createFunction = OverloadedFunction; | ||
Overloader.absent = function(value) { return new Absent(value); }; | ||
// 输出所有自定义异常。 | ||
@@ -542,13 +919,13 @@ for (var name in ERR) { | ||
var TYPE_ALIAS = { | ||
'*' : Overloader.ANY | ||
, 'any' : Overloader.ANY | ||
, 'boolean' : Overloader.BOOLEAN | ||
, 'char' : Overloader.CHAR | ||
, 'number' : Overloader.NUMBER | ||
, 'scalar' : Overloader.SCALAR | ||
, 'string' : Overloader.STRING | ||
}; | ||
var TYPE_ALIAS = | ||
{ '?' : Type.ANY | ||
, 'any' : Type.ANY | ||
, 'boolean' : Type.BOOLEAN | ||
, 'char' : Type.CHAR | ||
, 'number' : Type.NUMBER | ||
, 'object' : Type.PLAIN_OBJECT | ||
, 'scalar' : Type.SCALAR | ||
, 'string' : Type.STRING | ||
}; | ||
// --------------------------- | ||
@@ -563,4 +940,4 @@ // 模块输出 | ||
// RequireJS | ||
else if (typeof define == 'function') { | ||
define(function() { | ||
else if (typeof global.define == 'function') { | ||
global.define(function() { | ||
return Overloader; | ||
@@ -567,0 +944,0 @@ }); |
@@ -1,2 +0,1 @@ | ||
(function(t,e){var n,r=function y(t,e,n){if(!e)e=Error;var r=function(r){this.name="OVERLOAD2."+t;if(n){n.apply(this,arguments)}else{this.message=r}var a=new e;this.stack=[this.name+": "+this.message].concat(a.stack.split("\n").slice(2)).join("\n")};r.prototype=Object.create(e.prototype);r.prototype.consturctor=r;r.prototype.toString=r.prototype.valueOf=function(){return"["+this.name+": "+this.message+"]"};Object.defineProperty(r,"name",{value:t});return r},a={Generic:r("Error"),Arguments:r("ArgumentsError",TypeError,function(t,e){this.message=t+" argument(s) expected but actual "+e}),Type:r("TypeError",TypeError,function(t,e,n){this.message=t+" must be "+(typeof e=="string"?e:e.join(" | "))+": "+n}),Range:r("RangeError",RangeError,function(t,e,n){if(e instanceof Array){e=e.join(" | ")}this.message=t+" must be "+e+": "+n}),NotImplemented:r("EmptyException"),Unmatching:r("UnmatchingException",TypeError)},o=function(t){function e(e){return t.apply(this,e)}e.prototype=t.prototype;return function(){return new e(arguments)}},i=function(t,e){return t.indexOf(e)>=0};function s(t){if(arguments.length!=1){throw new a.Arguments(1,arguments.length)}if(!(typeof t=="function"||t instanceof RegExp)){throw new a.Type("Type matcher",["Function","RegExp"],t)}if(!(this instanceof s))return new s(t);if(t instanceof RegExp)this.match=function(e){return t.test(e)};else this.match=t}function u(){if(arguments.length==0){throw new a.Arguments("1+",0)}if(arguments.length==1&&arguments[0]instanceof u){return arguments[0]}var t=arguments[0],e=[];if(typeof t=="string"){e=t.trim().split(/\s+/);t=e.shift();var n=g[t];if(!n){throw new a.Range("type alias",Object.keys(g),t)}t=n}else if(t instanceof s){}else if(typeof t=="function"){t=function(t){return new s(function(e){return e instanceof t})}(t)}else{throw new a.Type("Param type",["overload2.Type","string","Function"],t)}this.type=t;var r=["NULL","UNDEFINED"];for(var o=1;o<arguments.length;o++){e=e.concat(arguments[o].trim().split(/\s+/))}for(var o=0;o<e.length;o++){var f=e[o];e[o]=e[o].toUpperCase();if(!i(r,e[o])){throw new a.Range("param decorator",r,f)}}this.nil=i(e,"null");this.undef=i(e,"undefined")}u.parse=o(u);u.prototype.satisfy=function(t){if(t===null&&this.nil)return true;if(t===e&&this.undef)return true;return this.type.match(t)};function f(){var t=[];for(var e=0,n;e<arguments.length;e++){n=arguments[e]instanceof Array?arguments[e]:[arguments[e]];t[e]=u.parse.apply(null,n)}this.length=t.length;this.item=function(e){return t[e]}}f.parse=o(f);f.prototype.satisfy=function(t){if(t.length!=this.length){return false}var e=true;for(var n=0;e&&n<this.length;n++){e=this.item(n).satisfy(t[n])}return e};function p(){if(arguments.length==0){throw new a.Arguments("1+",0)}var t=Array.from(arguments);var e=t.pop();if(typeof e=="function"){this.method=e}else{throw new a.Generic("overloading implementation function missed")}if(typeof t[0]=="number"){if(t.length>1){throw new a.Arguments(2,t.length+1)}this.paramCount=t[0]}else if(t[0]instanceof f){if(t.length>1){throw new a.Arguments(2,t.length+1)}this.params=t[0]}else{this.params=f.parse.apply(null,t)}}p.parse=o(p);p.prototype.exec=function(t,e){if(typeof this.paramCount=="number"&&e.length!=this.paramCount){return false}if(this.params&&!this.params.satisfy(e)){return false}return[this.method.apply(t,e)]};function l(){if(!(this instanceof l))return new l;this._defaultMethod=null;this._overloads=[]}l.prototype.exec=function(){return this.apply(this,arguments)};l.prototype.apply=function(t,e){if(this._overloads.length==0){throw new a.NotImplemented}for(var n=0,r;n<this._overloads.length;n++){r=this._overloads[n].exec(t,e);if(r){return r[0]}}if(this._defaultMethod){return this.defaultMethod.apply(t,e)}throw new a.Unmatching("Unmatching arguments: "+e)};l.prototype.call=function(t){var e=Array.from(arguments);return this.apply(t,e.slice(1))};l.prototype.overload=function(){var t;if(arguments[0]instanceof p){t=arguments[0];if(arguments[1]){this._defaultMethod=t.method}if(arguments.length>2){console.warn("redundant arguments found")}}else{t=p.parse.apply(null,arguments)}this._overloads.push(t);return this};l.prototype.default=function(t){if(typeof t!="function"){throw new a.Generic("invalid default method")}if(arguments.length>1){console.warn("redundant arguments found")}this._defaultMethod=t};function h(){var t=new l;var e=function(e){t.overload.apply(t,e)};var n=function(e,n){var r;if(e[0]instanceof p){r=e[0]}else{r=p.parse.apply(null,e)}t.overload(r);if(n){t.defaultMethod=r.method}};if(arguments.length){}var r=function(){return t.apply(this,arguments)};r.overload=function(){e(arguments);return r};r.default=function(e){t.default(e);return r};return r}h.ANY=new s(function(t){return true});h.BOOLEAN=new s(function(t){return typeof t=="boolean"});h.CHAR=new s(function(t){return typeof t=="string"&&t.length==1});h.NUMBER=new s(function(t){return typeof t=="number"});h.SCALAR=new s(function(t){return["boolean","number","string"].indexOf(typeof t)>=0});h.STRING=new s(function(t){return typeof t=="string"});h.enum=function(){var t=[];for(var e=0;e<arguments.length;e++){t.push(arguments[e])}return new s(function(e){return t.indexOf(e)>=0})};h.Type=s;h.type=s;h.parseType=s;h.Param=u;h.param=u.parse;h.parseParam=u.parse;h.ParamList=f;h.paramList=f.parse;h.parseParamList=f.parse;h.Overload=p;h.overload=p.parse;h.parseOverload=p.parse;h.Function=l;h.createFunction=l;for(var c in a){if(a.hasOwnProperty(c)){var m=a[c];h[m.name]=m}}var g={"*":h.ANY,any:h.ANY,"boolean":h.BOOLEAN,"char":h.CHAR,number:h.NUMBER,scalar:h.SCALAR,string:h.STRING};if(typeof module=="object"&&typeof module.exports=="object"){module.exports=h}else if(typeof define=="function"){define(function(){return h})}else{t.overload2=h}})(this); | ||
//# sourceMappingURL=overload2.map | ||
(function(global,undefined){var MODULE_REQUIRE,declareException=function e(t,r,n){if(!r)r=Error;var a=function(e){this.name="OVERLOAD2."+t;if(n){n.apply(this,arguments)}else{this.message=e}var a=new r;this.stack=[this.name+": "+this.message].concat(a.stack.split("\n").slice(2)).join("\n")};a.prototype=Object.create(r.prototype);a.prototype.consturctor=a;a.prototype.toString=a.prototype.valueOf=function(){return"["+this.name+": "+this.message+"]"};Object.defineProperty(a,"name",{value:t});return a},ERR={Generic:declareException("Error"),Arguments:declareException("ArgumentsError",TypeError,function(e,t){this.message=e+" argument(s) expected but actual "+t}),Type:declareException("TypeError",TypeError,function(e,t,r){this.message=e+" must be "+(typeof t=="string"?t:t.join(" | "))+": "+r}),Range:declareException("RangeError",RangeError,function(e,t,r){if(t instanceof Array){t=t.join(" | ")}this.message=e+" must be "+t+": "+r}),NotImplemented:declareException("EmptyException"),Unmatching:declareException("UnmatchingException",TypeError)},generateCreator=function(e){function t(t){return e.apply(this,t)}t.prototype=e.prototype;return function(){return new t(arguments)}},has=function(e,t){return e.indexOf(t)>=0},eachMatch=function(e,t){var r=true;for(var n=0;r&&n<e.length;n++){r=t(e[n],n)}return r},onceMatch=function(e,t){var r=false;for(var n=0;!r&&n<e.length;n++){r=t(e[n],n)}return r};function Absent(e){this.value=e}function Type(e){if(arguments.length!=1){throw new ERR.Arguments(1,arguments.length)}if(!(typeof e=="function"||e instanceof RegExp)){throw new ERR.Type("Type matcher",["Function","RegExp"],e)}if(!(this instanceof Type))return new Type(e);if(e instanceof RegExp){this.match=function(t){return e.test(t)}}else{this.match=e}}Type.ANY=new Type(function(){return true});Type.BOOLEAN=new Type(function(e){return typeof e==="boolean"});Type.CHAR=new Type(function(e){return typeof e==="string"&&e.length==1});Type.NUMBER=new Type(function(e){return typeof e==="number"});Type.PLAIN_OBJECT=new Type(function(e){return typeof e==="object"&&e.constructor===Object});Type.SCALAR=new Type(function(e){return["boolean","number","string"].indexOf(typeof e)>=0});Type.STRING=new Type(function(e){return typeof e==="string"});Type.enum=function(){var e=Array.from(arguments);return new Type(function(t){return e.indexOf(t)>=0})};Type.and=function(){var e=Array.from(arguments).map(Type.parse);return new Type(function(t){return eachMatch(e,function(e){return e.match(t)})})};Type.or=function(){var e=Array.from(arguments).map(Type.parse);return new Type(function(t){return onceMatch(e,function(e){return e.match(t)})})};Type.not=function(e){return new Type(function(t){return!e.match(t)})};Type.parse=function(e){if(typeof e=="string"){var t=TYPE_ALIAS[e];if(!t){throw new ERR.Range("type alias",Object.keys(TYPE_ALIAS),e)}e=t}else if(e instanceof Type){}else if(typeof e=="function"){e=function(e){return new Type(function(t){return t instanceof e})}(e)}else{throw new ERR.Type("Param type",["overload2.Type","string","Function"],e)}return e};function Param(){if(arguments.length==0){throw new ERR.Arguments("1+",0)}if(arguments.length==1&&arguments[0]instanceof Param){return arguments[0]}this.type=null;this.minSize=1;this.maxSize=1;this.nil=false;this.undef=false;this.absent=false;this.absentValue=undefined;this.arrayed=false;var setSize=function(e){if(typeof e=="string"){e=e.replace(/\s/g,"");if(e=="*"||e=="..."){this.minSize=0;this.maxSize=Infinity;this.arrayed=true;return true}if(e=="+"){this.minSize=1;this.maxSize=Infinity;this.arrayed=true;return true}if(/^\{.+\}$/.test(e)){e=e.slice(1,-1)}if(/^\d+$/.test(e)){this.minSize=this.maxSize=parseInt(e);this.arrayed=true;return true}if(/^,(\d+)$/.test(e)){this.minSize=0;this.maxSize=parseInt(RegExp.$1);this.arrayed=true;return true}if(/^(\d+),$/.test(e)){this.minSize=parseInt(RegExp.$1);this.maxSize=Infinity;this.arrayed=true;return true}if(/^(\d+),(\d+)$/.test(e)){this.minSize=parseInt(RegExp.$1);this.maxSize=parseInt(RegExp.$2);this.arrayed=true;return true}return false}if(Number.isInteger(e)&&e>0){this.minSize=this.maxSize=e;this.arrayed=true;return true}return false}.bind(this);var type=arguments[0],decos=[];if(setSize(type)){this.type=Type.ANY;return this}if(typeof type=="string"){decos=type.trim().split(/\s+/);type=decos.shift()}this.type=Type.parse(type);var DECOS=["NULL","UNDEFINED","ABSENT"],i;for(i=1;i<arguments.length;i++){if(typeof arguments[i]=="string"){decos=decos.concat(arguments[i].trim().split(/\s+/))}else if(Number.isInteger(arguments[i])){decos.push(arguments[i])}else if(arguments[i]instanceof Absent){this.absent=true;this.absentValue=arguments[i].value}}for(i=0;i<decos.length;i++){var rawDeco=decos[i];if(setSize(rawDeco))continue;if(/^=(.+)$/.test(rawDeco)){this.absent=true;this.absentValue=eval(RegExp.$1);continue}decos[i]=decos[i].toUpperCase();if(!has(DECOS,decos[i])){throw new ERR.Range("param decorator",DECOS,rawDeco)}}this.nil=has(decos,"NULL");this.undef=has(decos,"UNDEFINED");if(!this.absent){this.absent=has(decos,"ABSENT")}}Param.parse=generateCreator(Param);Param.prototype.satisfy=function(e){if(e===null)return this.nil;if(e===undefined)return this.undef;return this.type.match(e)};function ParamList(){var e=[];var t=0;var r=0;var n=true;for(var a=0,i;a<arguments.length;a++){i=arguments[a]instanceof Array?arguments[a]:[arguments[a]];e[a]=Param.parse.apply(null,i);r+=e[a].maxSize;t+=e[a].absent?0:e[a].minSize;n=n&&!(e[a].absent||e[a].arrayed)}this.easy=n;this.minSize=t;this.maxSize=r;this.params=e}ParamList.parse=generateCreator(ParamList);ParamList.prototype.satisfy=function(e){if(e.length!=this.params.length){return false}return eachMatch(this.params,function(t,r){return t.satisfy(e[r])})};ParamList.prototype.parse=function(e){if(e.length<this.minSize||this.maxSize<e.length){return null}if(this.easy){var t=eachMatch(this.params,function(t,r){return t.satisfy(e[r])});return t?e:null}var r=function(e,t){var n=[];var a=0,i=0;for(;a<e.length;a++){if(i>=t.length){return null}var o=t[i];var s=e[a];if(!o.absent&&o.minSize==1&&o.maxSize==1){if(!o.satisfy(s)){return null}else{n.push(s);i++}continue}if(e.length-a<o.minSize){if(o.absent){n.push(o.absentValue);a--;continue}return null}var u=[];var l=function(){if(o.arrayed)n.push(u);else n.push(u[0])};for(;a<e.length&&o.satisfy(e[a]);a++){u.push(e[a])}a--;i++;if(u.length<o.minSize){if(o.absent){n.push(o.absentValue);a-=u.length;continue}return null}var p=t.slice(i);if(!o.absent&&u.length==o.minSize||p.length==0){l();continue}var f=e.slice(a+1);do{var c=r(f,p);if(c!=null){l();n=n.concat(c);break}if(u.length==o.minSize){if(o.absent){f=u.concat(f);u=o.absentValue;continue}n=null;break}else{f.unshift(u.pop())}}while(true);return n}for(;n.length<t.length;i++){if(t[i].absent){n.push(t[i].absentValue)}else if(t[i].minSize==0){n.push([])}else{return null}}return n};return r(Array.from(e),this.params)};function Overload(){if(arguments.length==0){throw new ERR.Arguments("1+",0)}var e=Array.from(arguments);var t=e.pop();if(typeof t=="function"){this.method=t}else{throw new ERR.Generic("overloading implementation function missed")}if(typeof e[0]=="number"&&e.length==1){this.argLength=e[0]}else if(e[0]instanceof ParamList){if(e.length>1){throw new ERR.Arguments(2,e.length+1)}this.params=e[0]}else{this.params=ParamList.parse.apply(null,e)}}Overload.parse=generateCreator(Overload);Overload.prototype.exec=function(e,t){if(typeof this.argLength=="number"){if(t.length!=this.argLength)return false}else{t=this.params.parse(t);if(!t)return false}return[this.method.apply(e,t)]};function OverloadedFunction(){if(!(this instanceof OverloadedFunction))return new OverloadedFunction;this._defaultMethod=null;this._overloads=[]}OverloadedFunction.prototype.exec=function(){return this.apply(this,arguments)};OverloadedFunction.prototype.apply=function(e,t){if(this._overloads.length==0){throw new ERR.NotImplemented}for(var r=0,n;r<this._overloads.length;r++){n=this._overloads[r].exec(e,t);if(n){return n[0]}}if(this._defaultMethod){return this._defaultMethod.apply(e,t)}var a=[];for(var r=0,i;r<t.length;r++){i=t[r]===null?"null":typeof t[r];if(i==="object")i=t[r].constructor.name;a.push(i)}throw new ERR.Unmatching("Unmatching arguments: "+a.join(", "))};OverloadedFunction.prototype.call=function(e){var t=Array.from(arguments);return this.apply(e,t.slice(1))};OverloadedFunction.prototype.overload=function(){var e;if(arguments[0]instanceof Overload){e=arguments[0];if(arguments[1]){this._defaultMethod=e.method}if(arguments.length>2){}}else{e=Overload.parse.apply(null,arguments)}this._overloads.push(e);return this};OverloadedFunction.prototype.default=function(e){if(typeof e!="function"){throw new ERR.Generic("invalid default method")}if(arguments.length>1){}this._defaultMethod=e};function Overloader(){var e=new OverloadedFunction;var t=function(t){e.overload.apply(e,t)};if(arguments.length){}var r=function(){return e.apply(this,arguments)};r.overload=function(){t(arguments);return r};r.default=function(t){e.default(t);return r};return r}Overloader.ANY=Type.ANY;Overloader.BOOLEAN=Type.BOOLEAN;Overloader.CHAR=Type.CHAR;Overloader.NUMBER=Type.NUMBER;Overloader.SCALAR=Type.SCALAR;Overloader.STRING=Type.STRING;Overloader.enum=Type.enum;Overloader.Type=Type;Overloader.type=Type;Overloader.Param=Param;Overloader.param=Param.parse;Overloader.parseParam=Param.parse;Overloader.ParamList=ParamList;Overloader.paramList=ParamList.parse;Overloader.parseParamList=ParamList.parse;Overloader.Overload=Overload;Overloader.overload=Overload.parse;Overloader.parseOverload=Overload.parse;Overloader.Function=OverloadedFunction;Overloader.createFunction=OverloadedFunction;Overloader.absent=function(e){return new Absent(e)};for(var name in ERR){if(ERR.hasOwnProperty(name)){var Err=ERR[name];Overloader[Err.name]=Err}}var TYPE_ALIAS={"?":Type.ANY,any:Type.ANY,boolean:Type.BOOLEAN,char:Type.CHAR,number:Type.NUMBER,object:Type.PLAIN_OBJECT,scalar:Type.SCALAR,string:Type.STRING};if(typeof module=="object"&&typeof module.exports=="object"){module.exports=Overloader}else if(typeof global.define=="function"){global.define(function(){return Overloader})}else{global.overload2=Overloader}})(this); |
@@ -12,5 +12,8 @@ /** | ||
// Decorators are appended to the param after datatype. | ||
[ Date, 'null', 'undefined' ], | ||
// Decorator "null" means actual value `null` is accepted. | ||
// Decorator "undefined" means actual value `undefined` is accepted. | ||
// ATTENTION: Decorators are case insensitive. | ||
[ Date, 'null', 'unDefined' ], | ||
function date_instance(d) { | ||
function f_date(d) { | ||
if (d === null || d === undefined) { | ||
@@ -22,6 +25,6 @@ d = new Date(0); | ||
) | ||
.default( | ||
function date_others() { | ||
var d = new Date(); | ||
return 1900 + d.getYear(); | ||
function f_now() { | ||
return 1900 + (new Date).getYear(); | ||
} | ||
@@ -31,10 +34,12 @@ ) | ||
// date_instance invoked. | ||
// f_date() invoked. | ||
var year = getYear(null); | ||
assert.equal(1970, year); | ||
// date_instance invoked. | ||
// f_date() invoked. | ||
var year = getYear(undefined); | ||
assert.equal(1970, year); | ||
var year = getYear(); | ||
console.log('THIS LINE REACHED MEANS EVERYTHING IS OK.'); |
169
overload2.js
@@ -126,2 +126,9 @@ /** | ||
/** | ||
* 自定义缺省值类。 | ||
*/ | ||
function Absent(value) { | ||
this.value = value; | ||
} | ||
/** | ||
* 自定义数据类型类。 | ||
@@ -260,3 +267,3 @@ * @param {function} matcher | ||
* 构造函数支持重载,可能的参数形式包括: | ||
* "alias ...decorator" | ||
* "<alias> ...<decorator>" | ||
* type, ...decorator | ||
@@ -280,2 +287,5 @@ * type, decorators | ||
this.undef = false; | ||
this.absent = false; | ||
this.absentValue = undefined; | ||
this.arrayed = false; | ||
@@ -289,2 +299,3 @@ var setSize = (function(size) { | ||
this.maxSize = Infinity; | ||
this.arrayed = true; | ||
return true; | ||
@@ -295,2 +306,3 @@ } | ||
this.maxSize = Infinity; | ||
this.arrayed = true; | ||
return true; | ||
@@ -305,2 +317,3 @@ } | ||
this.minSize = this.maxSize = parseInt(size); | ||
this.arrayed = true; | ||
return true; | ||
@@ -311,2 +324,3 @@ } | ||
this.maxSize = parseInt(RegExp.$1); | ||
this.arrayed = true; | ||
return true; | ||
@@ -317,2 +331,3 @@ } | ||
this.maxSize = Infinity; | ||
this.arrayed = true; | ||
return true; | ||
@@ -323,2 +338,3 @@ } | ||
this.maxSize = parseInt(RegExp.$2); | ||
this.arrayed = true; | ||
return true; | ||
@@ -331,2 +347,3 @@ } | ||
this.minSize = this.maxSize = size; | ||
this.arrayed = true; | ||
return true; | ||
@@ -362,3 +379,3 @@ } | ||
var DECOS = ['NULL', 'UNDEFINED'], i; | ||
var DECOS = ['NULL', 'UNDEFINED', 'ABSENT'], i; | ||
@@ -372,3 +389,10 @@ for (i = 1; i < arguments.length; i++) { | ||
} | ||
// @tag 20180222 | ||
else if (arguments[i] instanceof Absent) { | ||
this.absent = true; | ||
this.absentValue = arguments[i].value; | ||
} | ||
} | ||
for (i = 0; i < decos.length; i++) { | ||
@@ -380,2 +404,11 @@ var rawDeco = decos[i]; | ||
// 设置缺省值。 | ||
// 注意:为避免歧义,缺省值字面量中不得出现空格。 | ||
// @tag 20180222 | ||
if (/^=(.+)$/.test(rawDeco)) { | ||
this.absent = true; | ||
this.absentValue = eval(RegExp.$1); | ||
continue; | ||
} | ||
// 普通修饰符不区分大小写。 | ||
@@ -397,2 +430,7 @@ decos[i] = decos[i].toUpperCase(); | ||
this.undef = has(decos, 'UNDEFINED'); | ||
// 参数是否可以缺席。 | ||
if (!this.absent) { | ||
this.absent = has(decos, 'ABSENT'); | ||
} | ||
} | ||
@@ -422,10 +460,17 @@ | ||
var maxSize = 0; | ||
var easy = true; | ||
for (var i = 0, args; i < arguments.length; i++) { | ||
args = (arguments[i] instanceof Array) ? arguments[i] : [ arguments[i] ]; | ||
params[i] = Param.parse.apply(null, args); | ||
minSize += params[i].minSize; | ||
maxSize += params[i].maxSize; | ||
// @tag 20180222 | ||
// 如果参数允许缺省,则在整个实参列表中占据的最小长度为 0。 | ||
minSize += params[i].absent ? 0 : params[i].minSize; | ||
// 如果形参可缺省,或者为多占位或可变长度,则不适用简易模式。 | ||
easy = easy && !( params[i].absent || params[i].arrayed ); | ||
} | ||
// 指定匹配值组的最小和最大长度。 | ||
this.easy = easy; | ||
this.minSize = minSize; | ||
@@ -441,3 +486,3 @@ this.maxSize = maxSize; | ||
* 判断值组是否合乎参数定义。 | ||
* @deprecated | ||
* @deprecated 为了支持可变长度参数,用更复杂的 .parse() 方法取代。 | ||
*/ | ||
@@ -469,4 +514,4 @@ ParamList.prototype.satisfy = function(args) { | ||
// --------------------------- | ||
// 若整个参数组非可变长,则适用简易匹配逻辑。 | ||
if (this.minSize == this.maxSize) { | ||
// 适用简易匹配逻辑。 | ||
if (this.easy) { | ||
var matched = eachMatch(this.params, function(param, index) { | ||
@@ -499,4 +544,4 @@ return param.satisfy(args[index]); | ||
// --------------------------- | ||
// 如果形式参数对应单个实参(不包括多于 1 的定长),则按简易逻辑处理。 | ||
if (param.minSize == param.maxSize == 1) { | ||
// 如果形式参数对应单个实参且不可缺省,则按简易逻辑处理。 | ||
if (!param.absent && param.minSize == 1 && param.maxSize == 1) { | ||
if (!param.satisfy(arg)) { | ||
@@ -516,4 +561,17 @@ // 如果实参与形式参数不匹配,则终止后续匹配,整个参数表匹配失败。 | ||
// 如果剩余实参数量不足以匹配当前形式参数,则匹配失败。 | ||
// 如果剩余实参数量不足以匹配当前形式参数: | ||
if (args.length - argCursor < param.minSize) { | ||
// 若参数可缺省: | ||
if (param.absent) { | ||
// 使用缺省值替补。 | ||
newArgs.push(param.absentValue); | ||
// 不消耗实参。 | ||
argCursor--; | ||
// 当前形参匹配完毕。 | ||
continue; | ||
} | ||
// 否则当前形式参数匹配失败,整个参数表匹配失败。 | ||
return null; | ||
@@ -524,2 +582,6 @@ } | ||
var paramArgs = []; | ||
var pushParamArg = function() { | ||
if (param.arrayed) newArgs.push(paramArgs); | ||
else newArgs.push(paramArgs[0]); | ||
}; | ||
@@ -529,43 +591,76 @@ for (; argCursor < args.length && param.satisfy(args[argCursor]); argCursor++) { | ||
} | ||
argCursor--; | ||
paramCursor++; | ||
// 如当前形式参数匹配实参个数未达到最小值,则认为当前形式参数匹配失败,整个参数表匹配失败。 | ||
// 如当前形式参数匹配实参个数未达到最小值: | ||
if (paramArgs.length < param.minSize) { | ||
// 若参数可缺省: | ||
if (param.absent) { | ||
// 使用缺省值替补。 | ||
newArgs.push(param.absentValue); | ||
// 回吐所有已消耗的实参。 | ||
argCursor -= paramArgs.length; | ||
// 当前形参匹配完毕。 | ||
continue; | ||
} | ||
// 否则当前形式参数匹配失败,整个参数表匹配失败。 | ||
return null; | ||
} | ||
var restParams = params.slice(++paramCursor); | ||
var restArgs = args.slice(argCursor); | ||
var restParams = params.slice(paramCursor); | ||
// 抵达匹配边界时,若仅匹配了最小数量的实参,或者已是最后一个形式参数,则直接固定。 | ||
if (paramArgs.length == param.minSize || restParams.length == 0) { | ||
newArgs.push(paramArgs); | ||
argCursor--; | ||
// 抵达匹配边界时,若 | ||
// 仅匹配了最小数量的实参且该参数不可缺省,或者已是最后一个形式参数, | ||
// 则直接固定参数值。 | ||
if (!param.absent && paramArgs.length == param.minSize || restParams.length == 0) { | ||
pushParamArg(); | ||
continue; | ||
} | ||
// 否则,须尝试让贤。 | ||
else { | ||
var restArgs = args.slice(argCursor + 1); | ||
// 步步让贤,直到让无可让。 | ||
do { | ||
var restNewArgs = matching(restArgs, restParams); | ||
// 步步让贤,直到让无可让。 | ||
do { | ||
var restNewArgs = matching(restArgs, restParams); | ||
// 剩余参数匹配成功,则拼接匹配结果,整个参数表匹配成功。 | ||
if (restNewArgs != null) { | ||
newArgs.push(paramArgs); | ||
return newArgs.concat(restNewArgs); | ||
// 剩余参数匹配成功,则拼接匹配结果,整个参数表匹配成功。 | ||
if (restNewArgs != null) { | ||
pushParamArg(); | ||
newArgs = newArgs.concat(restNewArgs); | ||
break; | ||
} | ||
// 如果已让无可让: | ||
if (paramArgs.length == param.minSize) { | ||
// 如果参数可以缺省,统统不要了,全给后续形参去。 | ||
if (param.absent) { | ||
restArgs = paramArgs.concat(restArgs); | ||
paramArgs = param.absentValue; | ||
continue; | ||
} | ||
// 已无余量。 | ||
else if (paramArgs.length == 0) { | ||
break; | ||
} | ||
else { | ||
restArgs.unshift(paramArgs.pop()); | ||
} | ||
} while (paramArgs.length >= param.minSize); | ||
return null; | ||
} | ||
// 否则,就此罢了(liao)。 | ||
newArgs = null; | ||
break; | ||
} | ||
else { | ||
restArgs.unshift(paramArgs.pop()); | ||
} | ||
} while (true); | ||
// 行进至此,不成功,则成仁。 | ||
return newArgs; | ||
} | ||
// 此时,所有实参均已消耗完毕。 | ||
for (; newArgs.length < params.length; paramCursor++) { | ||
if (params[paramCursor].minSize == 0) { | ||
if (params[paramCursor].absent) { | ||
newArgs.push(params[paramCursor].absentValue); | ||
} | ||
else if (params[paramCursor].minSize == 0) { | ||
newArgs.push([]); | ||
@@ -815,2 +910,4 @@ } | ||
Overloader.absent = function(value) { return new Absent(value); }; | ||
// 输出所有自定义异常。 | ||
@@ -817,0 +914,0 @@ for (var name in ERR) { |
{ | ||
"name": "overload2", | ||
"version": "0.2.1", | ||
"version": "0.3.0", | ||
"description": "Elegant solution for function overloading in JavaScript", | ||
@@ -10,3 +10,3 @@ "main": "overload2.js", | ||
"scripts": { | ||
"prepublishOnly": "mkdir -p dist && cp overload2.js dist && cd dist && uglifyjs --mangle --output overload2.min.js --source-map overload2.map overload2.js", | ||
"prepublishOnly": "mkdir -p dist && cp overload2.js dist && cd dist && uglifyjs --mangle --output overload2.min.js --source-map filename=overload2.map overload2.js", | ||
"test": "./node_modules/.bin/mocha" | ||
@@ -13,0 +13,0 @@ }, |
@@ -301,2 +301,3 @@ # overload2 | ||
| undefined | If argument equals undefined (the place should be occupied), it matches the parameter. | | ||
| absent | The argument may be absent (optional). See [example code](./example/optional.js) for more details. | | ||
@@ -392,2 +393,11 @@ * new __overload2.Param__( string "\<alias\> \<decorator\> ..." ) | ||
* [Mutable Parameters](./example/mutable.js) | ||
Explain how to define mutable parameters. | ||
* [Optional Parameters](./example/optional.js) | ||
Explain show how to indicate a param which may be absent (that means it is optional), and how to set the default value. | ||
* [Parameter Decorators](./example/param-deco.js) | ||
Introduce available decorators used to define a more complex parameter. | ||
* [Advanced Usage](./example/advanced.js) | ||
@@ -394,0 +404,0 @@ Use *overload2* in complex situations. |
@@ -39,4 +39,4 @@ var MODULE_REQUIRE | ||
DE([ 2, [ number, [] ] ], fn(number)); | ||
DE([ 2, [ number, [number] ] ], fn(number, number)); | ||
DE([ 2, [ number, [string,number] ] ], fn(number, string, number)); | ||
// DE([ 2, [ number, [number] ] ], fn(number, number)); | ||
// DE([ 2, [ number, [string,number] ] ], fn(number, string, number)); | ||
}); | ||
@@ -225,1 +225,20 @@ | ||
}); | ||
describe('Regurgitation', function() { | ||
it('regurgitation', function() { | ||
var fn = overload2() | ||
.overload('number +', 'number 2', function(a, b) { | ||
return a.length; | ||
}) | ||
.overload('number +', function(a) { | ||
return a.length; | ||
}) | ||
; | ||
assert.equal(1, fn(1)); | ||
assert.equal(2, fn(1, 2)); | ||
assert.equal(1, fn(1, 2, 3)); | ||
assert.equal(2, fn(1, 2, 3, 4)); | ||
assert.equal(3, fn(1, 2, 3, 4, 5)); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
149209
23
2498
433