diff --git a/www/src/py_list.js b/www/src/py_list.js index d7503a5b8..6cb5b083e 100644 --- a/www/src/py_list.js +++ b/www/src/py_list.js @@ -277,11 +277,415 @@ list.__imul__ = function() { return $.self } -list.__init__ = function(self, arg){ - var $ = $B.args('__init__', 1, {self: null}, ['self'], arguments, {}, - 'args', null), - self = $.self, - args = $.args + +function argsB(fct, args) { + + //const args = _args; // should remove this line... + const HAS_KW = args[args.length-1]?.$kw !== undefined + let ARGS_POS_COUNT = args.length, + ARGS_NAMED = null; + + if(HAS_KW){ + --ARGS_POS_COUNT + ARGS_NAMED = args[ARGS_POS_COUNT].$kw + } + + + // using const should enable the browser to perform some optimisation. + const $INFOS = fct.$infos, + $CODE = $INFOS.__code__, + + PARAMS_NAMES = $INFOS.arg_names, + PARAMS_POS_COUNT = $CODE.co_argcount, + PARAMS_NAMED_COUNT = $CODE.co_kwonlyargcount, + + PARAMS_VARARGS_NAME = $INFOS.vararg, + PARAMS_KWARGS_NAME = $INFOS.kwarg, + + PARAMS_POS_DEFAULTS = $INFOS.__defaults__, + PARAMS_POS_DEFAULTS_COUNT = PARAMS_POS_DEFAULTS.length, + + PARAMS_POS_DEFAULTS_OFFSET= PARAMS_POS_COUNT - PARAMS_POS_DEFAULTS_COUNT + + let varargs_offset = 0; + if( PARAMS_VARARGS_NAME !== null ) + varargs_offset = 1; + const result = new Array( PARAMS_NAMES.length + varargs_offset + PARAMS_KWARGS_NAME !== null ); + + // process positional arguments => positional parameters... + const min = Math.min(ARGS_POS_COUNT, PARAMS_POS_COUNT) + + let offset = 0 + for(; offset < min; ++offset){ + result[offset] = args[offset] + } + // process positional arguments => vargargs parameters... + if( PARAMS_VARARGS_NAME !== null ){ + // can be speed up if arguments is an array in the first place + // TODO: opti, better way to construct tuple from subarray ? + result[PARAMS_POS_COUNT] = Array.prototype.slice.call(args, PARAMS_POS_COUNT, ARGS_POS_COUNT ); + // maybe there is a faster way to build a tuple from a subset of an array. + }else if( ARGS_POS_COUNT > PARAMS_POS_COUNT){ + args0(fct, args) + throw new Error('Too much positional arguments given (args0 should have raised an error) !') + } + + // if no named arguments has been given... + if(ARGS_NAMED === null){ + + // Handle default positional parameters... + if(offset < PARAMS_POS_DEFAULTS_OFFSET){ + args0(fct, args) + throw new Error('Not enough positional arguments given (args0 should have raised an error) !') + } + + for(let i = offset - PARAMS_POS_DEFAULTS_OFFSET; + i < PARAMS_POS_DEFAULTS_COUNT; + ++i){ + result[offset++] = PARAMS_POS_DEFAULTS[i] + } + // Handle kwargs parameters... + if(PARAMS_KWARGS_NAME !== null){ + result[result.length-1] = {} + } + // Shortcut : no named parameters. + if( PARAMS_NAMED_COUNT === 0 ){ + return result + } + + // Handle defaults value for named parameters. + // Optimize: precompute the number of named parameters with a default value, or just a boolean ? + + let kwargs_defaults = $INFOS.__kwdefaults__.$jsobj + if(kwargs_defaults === undefined || kwargs_defaults === null){ + + kwargs_defaults = $INFOS.__kwdefaults__.$strings + if(kwargs_defaults === undefined || kwargs_defaults === null){ + args0(fct, args) + throw new Error('Named argument expected (args0 should have raised an error) !') + } + } + + const named_default_values = Object.values(kwargs_defaults), // TODO: precompute this plz. + nb_named_defaults = named_default_values.length + + if(nb_named_defaults < PARAMS_NAMED_COUNT){ + args0(fct, args) + throw new Error('Named argument expected (args0 should have raised an error) !') + } + + for(let i = 0; i < nb_named_defaults; ++i){ + result[offset++ + varargs_offset] = named_default_values[i] + } + return result + } + + let kwargs_defaults = $INFOS.__kwdefaults__.$jsobj; + if(kwargs_defaults === undefined || kwargs_defaults == null){ + kwargs_defaults = $INFOS.__kwdefaults__.$strings + if( kwargs_defaults === undefined || kwargs_defaults == null ){ + kwargs_defaults = {} + } + } + + // Construct the list of default values... + // Optimize : I'd need an object containing ALL default values instead of + // having to build one... + // If not done, I can work on it to remove the construction of this object + // (which cost a little). + // Note: I should exclude posonly default parameters... (we don't need + // them as remaining positional only parameters'd be consumed next) + + // Consume remaining positional only parameters (no positional arguments + // given, so expect default value). + const PARAMS_POSONLY_COUNT = $CODE.co_posonlyargcount, + PARAMS_POS_DEFAULTS_MAXID = PARAMS_POS_DEFAULTS_COUNT + + PARAMS_POS_DEFAULTS_OFFSET + + if(offset < PARAMS_POSONLY_COUNT){ + if(offset < PARAMS_POS_DEFAULTS_OFFSET){ + args0(fct, args) + throw new Error('Not enough positional parameters given (args0 should have raised an error) !') + } + + const max = PARAMS_POS_DEFAULTS_COUNT - + (PARAMS_POS_COUNT - PARAMS_POSONLY_COUNT) + + // default parameters + for(let i = offset - PARAMS_POS_DEFAULTS_OFFSET; + i < max; + ++i){ + result[offset++] = PARAMS_POS_DEFAULTS[i] + } + } + + // No **kwargs parameter (i.e. unknown name = error). + if(PARAMS_KWARGS_NAME === null){ + let nb_named_args = 0 + for(let id = 0, len = ARGS_NAMED.length; id < len; ++id){ + const _kargs = ARGS_NAMED[id] + let kargs = _kargs.$jsobj + if(kargs === undefined || kargs === null){ + kargs = _kargs.$strings + if(kargs === undefined || kargs === null){ + kargs= _kargs + } + } + for(let argname in kargs) { + + const idx = PARAMS_NAMES.indexOf(argnames); + if(idx === -1) { + throw new Error(); + } + result[ idx ] = kargs[argname] + ++nb_named_args + } + } + + let found = 0 + let ioffset = offset + for( ; ioffset < PARAMS_POS_DEFAULTS_OFFSET; ++ioffset) { + if( ioffset in result ){ // maybe could be speed up using "!(key in result)" + continue + } + args0(fct, args) + throw new Error('Missing a named arguments (args0 should have raised an error) !') + } + for( ; ioffset < PARAMS_POS_DEFAULTS_MAXID; ++ioffset){ + if(ioffset in result){ + continue + } + result[ioffset] = PARAMS_POS_DEFAULTS[ioffset - PARAMS_POS_DEFAULTS_OFFSET] + ++found + } + for( ; ioffset < PARAMS_NAMES.length; ++ioffset){ + + let i = ioffset + varargs_offset; + if( i in result ){ + continue + } + if(! (key in kwargs_defaults)){ + args0(fct, args) + throw new Error('Missing a named arguments (args0 should have raised an error) !'); + } + + result[i] = kwargs_defaults[key] + ++found + } + + // PARAMS_NAMES.length - offset = the number of expected named arguments. + // found + nb_named_args = the number of given named arguments + the + // number of named arguments we found a default value for. + // If they aren't equal, we either gave several times the same + // argument or gave an inexisting name. + if(found + nb_named_args !== PARAMS_NAMES.length - offset){ + args0(fct, args) + throw new Error('Duplicate named arguments (args0 should have raised an error) !') + } + return result + } + + // With **kwargs parameter (i.e. unknown name = put in extra). + const extra = {} + + // we count the number of arguments given to normal named parameters and the number given to **kwargs. + let nb_named_args = 0 + let nb_extra_args = 0 + + for(let id = 0; id < ARGS_NAMED.length; ++id){ + const _kargs = ARGS_NAMED[id] + let kargs = _kargs.$jsobj + if(kargs === undefined || kargs === null){ + kargs = _kargs.$strings + if(kargs === undefined || kargs === null){ + kargs= _kargs + } + } + for(let argname in kargs){ + const i = PARAMS_NAMES.indexOf(argname, PARAMS_POSONLY_COUNT); + if( i !== -1){ + result[ i ] = kargs[argname] + ++nb_named_args + }else{ + extra[ argname ] = kargs[argname] + ++nb_extra_args + } + } + } + + // Same as "No **kwargs parameter". + // Checks default values... + // What is quicker ? An object or 1 array of name (with indexOf) and 1 array of values ? + let found = 0 + let ioffset = offset + for( ; ioffset < PARAMS_POS_DEFAULTS_OFFSET; ++ioffset){ + if(ioffset in result){ // maybe could be speed up using "!(key in result)" + continue + } + args0(fct, args) + throw new Error('Missing a named arguments (args0 should have raised an error) !') + } + for( ; ioffset < PARAMS_POS_DEFAULTS_MAXID; ++ioffset){ + if(ioffset in result){ + continue + } + result[ioffset] = PARAMS_POS_DEFAULTS[ioffset - PARAMS_POS_DEFAULTS_OFFSET] + ++found + } + for( ; ioffset < PARAMS_NAMES.length; ++ioffset){ + + let i = ioffset + varargs_offset; + const key = PARAMS_NAMES[ioffset] + if( i in result ){ + continue + } + if(! (key in kwargs_defaults)){ + args0(fct, args) + throw new Error('Missing a named arguments (args0 should have raised an error) !') + } + result[i] = kwargs_defaults[key] + ++found + } + + // Same as "No **kwargs parameter". + // PARAMS_NAMES.length - offset = the number of expected named arguments. + // found + nb_named_args = the number of given named arguments + the + // number of named arguments we found a default value for. + // If they aren't equal, we either gave several times the same argument or + // gave an inexisting name. + + if(found + nb_named_args !== PARAMS_NAMES.length - offset){ + args0(fct, args) + throw new Error('Inexistant or duplicate named arguments (args0 should have raised an error) !') + } + + // verify if the number of extra arguments (**kargs) found matches the + // numbers of elements in extra. + // if not, it means that the same name has been given several times. + if(Object.keys(extra).length !== nb_extra_args){ + args0(fct, args) + throw new Error('Duplicate name given to **kargs parameter (args0 should have raised an error) !') + } + + result[result.length-1] = extra + + return result + +} + +//const __args__init__ = generateArgs({}, "self, *args"); +function addMethod(klass, fct, args_str, defaults = {}) { + + const __args__ = generateArgs(fct, args_str, defaults); + + const method = function(...args) { + return fct( ...argsB(__args__, args) ); + //return fct( $B.args0(__args__, args) ); + }; + + klass[fct.name] = method; +} + +function generateArgs(fct, args_str, defaults = {}) { + + const $INFOS = fct.$infos = {}; + const $CODE = $INFOS.__code__ = {}; + + let args = args_str.split(','); + args = args.map( e => e.trim() ); + + $INFOS.arg_names = new Array(args.length); + $INFOS.__defaults__ = new Array(args.length); + $INFOS.__kwdefaults__ = {}; + const KWDEFAULTS = $INFOS.__kwdefaults__.$jsobj = {}; + + let offset_arg = 0; + let offset_argnames = 0; + let offset_posdef = 0; + + let posonly_end = args.indexOf('/'); + if( posonly_end === -1 ) + $CODE.co_posonlyargcount = 0; + else { + $CODE.co_posonlyargcount = posonly_end; + offset_arg = posonly_end+1; + } + + for(let i = 0 ; i < $CODE.co_posonlyargcount ; ++i) { + + const argname = args[i]; + $INFOS.arg_names[offset_argnames++] = argname; + + if( argname in defaults) + $INFOS.__defaults__[offset_posdef++] = defaults[name]; + } + + + let pos_end = args.findIndex( function(e) { return e[0] === "*" } ); + + if(pos_end === -1) { + $INFOS.vararg = null; + $CODE.co_argcount = args.length; + } else { + + $CODE.co_argcount = pos_end; + + if(args[pos_end].length > 1 && args[pos_end] !== '*') + $INFOS.vararg = args[pos_end].slice(1); + + } + + + for(let i = offset_arg ; i < $CODE.co_argcount ; ++i) { + + const argname = args[i]; + $INFOS.arg_names[offset_argnames++] = argname; + + if( argname in defaults) + $INFOS.__defaults__[offset_posdef++] = defaults[name]; + } + + if( offset_arg !== 0) + --$CODE.co_argcount; + + offset_arg = pos_end + 1; + $CODE.co_kwonlyargcount = pos_end === -1 ? 0 : args.length - offset_arg; + + const last = args[args.length - 1]; + if( last[0] === "*" && last[1] === "*") { + + --$CODE.co_kwonlyargcount; + $INFOS.kwarg = last; + } else { + $INFOS.kwarg = null; + } + + for(let i = 0 ; i < $CODE.co_kwonlyargcount ; ++i) { + + const argname = args[offset_arg + i]; + $INFOS.arg_names[offset_argnames++] = argname; + + if( argname in defaults) + KWDEFAULTS[argname] = defaults[name]; + } + + $INFOS.arg_names.length = offset_argnames; + $INFOS.__defaults__.length = offset_posdef; + + return fct +} + + + +//function __init__({self, args}){ +function __init__(self, args){ + + //const {self, args} = $B.args0(__args__init__, arguments); + + //var $ = $B.args('__init__', 1, {self: null}, ['self'], arguments, {}, + // 'args', null), + // self = $.self, + // args = $.args if(args.length > 1){ throw _b_.TypeError.$factory('expected at most 1 argument, got ' + args.length) @@ -313,6 +717,9 @@ list.__init__ = function(self, arg){ return _b_.None } + +addMethod(list, __init__, "self, *args"); + var list_iterator = $B.make_iterator_class("list_iterator") list_iterator.__reduce__ = list_iterator.__reduce_ex__ = function(self){ return $B.fast_tuple([_b_.iter, $B.fast_tuple([list.$factory(self)]), 0])