differentmatt / filbert

JavaScript parser of Python
Other
133 stars 27 forks source link

Advanced function arguments #8

Closed differentmatt closed 10 years ago

differentmatt commented 10 years ago

Default arguments Keyword arguments Arbitrary argument lists Unpacking argument lists

https://docs.python.org/3.4/tutorial/controlflow.html#more-on-defining-functions

This may need to be broken up into separate pieces. Please feel free to take on a subset of this work instead of the whole enchilada.

differentmatt commented 10 years ago

Tentative plan:

  1. Modify function calls - For calls to user functions and runtime library functions, convert all parameters to a single parameters object that JavaScript can handle, such as {formals:[], keywords:{}}.
  2. Modify function definitions - For all user function and library function definitions, move parameter variables to function body, and function body to internal iife.

What does this get us?

The example below doesn't include unpacking argument lists, but we can imagine passing a similar object containing the correct parameters.

Python input:

def foo(voltage, amps='a', charge='high', *args, **keywords):
  print('voltage', voltage)
  print('amps', amps)
  print('charge', charge)
  print('args', [a for a in args])
  print('keywords', [(k, keywords[k]) for k in keywords])
  print('')

foo(3)
foo(amps=4, voltage=7)
foo(1, 2, 3, 4, 5, 6, p="hi", q='boo')

JavaScript output:

// print() is in runtime library, included here for completeness
function print() {
  var s = '';
  for (var i = 0; i < arguments.length; i++)
    s += arguments[i] + ' ';
  console.log(s);
}

function foo() {
  var _p = arguments[0];
  var _fi = 0;
  var voltage;
  if (_fi < _p.formals.length) {
    voltage = _p.formals[_fi];
    _fi++;
  } else if ('voltage' in _p.keywords) {
    voltage = _p.keywords['voltage'];
    delete _p.keywords['voltage'];
  }
  var amps = 'a';
  if (_fi < _p.formals.length) {
    amps = _p.formals[_fi];
    _fi++;
  } else if ('amps' in _p.keywords) {
    amps = _p.keywords['amps'];
    delete _p.keywords['amps'];
  }
  var charge = 'high';
  if (_fi < _p.formals.length) {
    charge = _p.formals[_fi];
    _fi++;
  } else if ('charge' in _p.keywords) {
    charge = _p.keywords['charge'];
    delete _p.keywords['charge'];
  }
  var args = [];
  while (_fi < _p.formals.length) {
    args.push(_p.formals[_fi]);
    _fi++;
  }
  var keywords = _p.keywords;

  return (function() {
    print('voltage', voltage)
    print('amps', amps)
    print('charge', charge)
    print('args', args)
    var kws = '{'
    for (var k in keywords) {
      kws += k + ":" + keywords[k] + " ";
    }
    kws += '}';
    print('keywords', kws)
    print('')
  })();
}

foo({formals:[3], keywords:{}});
foo({formals:[], keywords:{amps:4, voltage:7}})
foo({formals:[1, 2, 3, 4, 5, 6], keywords:{p:"hi", q:'boo'}})
differentmatt commented 10 years ago

Closing this uber-issue, and will open separate ones for remaining work.

Example for user-defined functions:

def foo(voltage, amps='a', charge='high', *args, **keywords):
  print('voltage', voltage)
  print('amps', amps)
  print('charge', charge)
  print('args length', len(args))
  print('keywords length', keywords.length)
  print('')

foo(3)
foo(amps=4, voltage=7)
foo(1, 2, 3, 4, 5, 6, p="hi", q='boo')
function foo() {
    var __params0 = arguments.length === 1 && arguments[0].formals && arguments[0].keywords ? arguments[0] : null;
    var __formalsIndex0 = 0;
    var __args0 = arguments;
    function __getParam0(v, d) {
        var r = d;
        if (__params0) {
            if (__formalsIndex0 < __params0.formals.length) {
                r = __params0.formals[__formalsIndex0++];
            } else if (v in __params0.keywords) {
                r = __params0.keywords[v];
                delete __params0.keywords[v];
            }
        } else if (__formalsIndex0 < __args0.length) {
            r = __args0[__formalsIndex0++];
        }
        return r;
    }
    var voltage = __getParam0('voltage');
    var amps = __getParam0('amps', 'a');
    var charge = __getParam0('charge', 'high');
    var args = [];
    var keywords = {};
    if (__params0) {
        while (__formalsIndex0 < __params0.formals.length) {
            args.push(__params0.formals[__formalsIndex0++]);
        }
        keywords = __params0.keywords;
    } else {
        while (__formalsIndex0 < __args0.length) {
            args.push(__args0[__formalsIndex0++]);
        }
    }
    return function () {
        __pythonRuntime.functions.print('voltage', voltage);
        __pythonRuntime.functions.print('amps', amps);
        __pythonRuntime.functions.print('charge', charge);
        __pythonRuntime.functions.print('args length', __pythonRuntime.functions.len(args));
        __pythonRuntime.functions.print('keywords length', keywords.length);
        __pythonRuntime.functions.print('');
    }.call(this);
}
foo(__pythonRuntime.utils.createParamsObj(3));
foo(__pythonRuntime.utils.createParamsObj({
    amps: 4,
    __kwp: true
}, {
    voltage: 7,
    __kwp: true
}));
foo(__pythonRuntime.utils.createParamsObj(1, 2, 3, 4, 5, 6, {
    p: 'hi',
    __kwp: true
}, {
    q: 'boo',
    __kwp: true
}));