Skip to content

[SUGGESTION] A cleaner way to parse arguments ? #2565

Open
@denis-migdal

Description

@denis-migdal

Hi,

I recently though of a way to facilitate argument parsing by letting JS doing most of the job.

Frankly, I do not know if this would be faster, but the code should be cleaner.

By declaring the function prototype as:

function foo(_pos_a, _pos_b = 3, locals = {a = _pos_a, b = _pos_b, c, d = 4} = _b_.KW) {
    _b_.KW = {};
    // do stuff
}

We can simply call with :

function call_without_kw(fct, ...args) {
    // verif usage
    // ...
    fct(...args);
}

function call_with_kw(fct, ...args) {
      _b_.KW = args[--args.length];
      // verif usage
      // ....
      fct(...args);
}

call_with_kw( fct, 1, {c=4} );

This would also offer more possibilities when calling Python functions from JS code (e.g. in Brython internals).

To verify the proper usage, we need to ensure:

  1. compare the number of arguments given with the required min/max positional arguments (both precomputed) ;
  2. for each positional parameters without defaults, that wasn't given as a positional argument (so from number of arguments to a precomputed start_of_pos_defaults), verify if it was given as a kw args ;
  3. for each positional parameters that was given as a positional argument, verify it wasn't given as a kw args ;
  4. for each kwonly parameters without defaults (precomputed list), verify if it was given as a kw args ;
  5. if there isn't a **kwargs, verify the kw args keys.

For steps 2-5 there might be a simple algorithm, for example :

const array = new Array(...); // preallocated
const name2id = { // precomputed
    "a" : 0,
    "b" : 1
}

array.fill(false);

for(let key in KW) {
     const idx = name2id[key];
     if( idx === undefined)
            ; // either continue or throw depending if **kwargs parameter
     if( idx === -1 ) // kwonly with default
           ; // continue
     if( idx < nb_pos_given) // duplicate pos/kw argument
           ; // raise error
     array[idx] = true;
}

for(let i = nb_pos_given; i < end; ++i)
    if( array[i] === false ) // missing argument
         ; // raise error.

Another way (I'm a little surprise we don't need to do more) :

const required_args         = ["a", "b"]; // precomputed
const args_with_defaults =["c"];        // precomputed if no **kwargs

for(let i = nb_pos_given; i < end; ++i)
      if( ! (args[i] in KW) )
            ; // throw missing args

if( ! hasKWARGS ) {
    let count = Object.keys(KW).length - required_args + nb_posonly;
    for(let i = 0; i < args_with_defaults.length; ++i)
          if( args_with_defaults[i] in KW )
               --count;
    
    if( count > 0 ) ; // throw unknown args or dupl. args
}
// note: we could also do :
const keys = Object.keys(KW);
keys.includes(key)
// in some cases this might be slightly faster than key in KW (?)

Duplicate keys could be handled at two levels:

  • at the level of a=2, a=3.
  • in call_with_kw(fct, 1, 2, _b_.merge({a:2}, {a:3}) ).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions