Commits

masklinn committed 2644569 Merge

Keyword arguments support, fixes #3

  • Participants
  • Parent commits 7be96c3, 331bb6b
  • Tags 0.5

Comments (0)

Files changed (4)

 bead3db1137463bd51c322be85df441d457c39f7 0.2
 7239793417b5e0fb682660d22b2a4961b4f535c2 0.3
 a2f78eecb94af8e38839e47b3a5b2ca21f1ed6c3 0.4
+b8d11436d86d453fdf42e3c5223bcdc8ae67c3bf 0.5
+b8d11436d86d453fdf42e3c5223bcdc8ae67c3bf 0.5
+0000000000000000000000000000000000000000 0.5
     symbol(':'); symbol(')'); symbol(']'); symbol('}'); symbol(',');
     symbol('else');
 
+    infix('=', 10, function (left) {
+        if (left.id !== '(name)') {
+            throw new Error("Expected keyword argument name, got " + token.id);
+        }
+        this.first = left;
+        this.second = expression();
+        return this;
+    });
+
     symbol('lambda', 20).nud = function () {
         this.first = [];
         if (token.id !== ':') {
 
         if (val instanceof py.object
             || val === py.object
-            || py.issubclass.__call__(val, py.object) === py.True) {
+            || py.issubclass.__call__([val, py.object]) === py.True) {
             return val;
         }
 
                 proto[k] = dict[k];
             }
         }
-        constructor.__call__ = function () {
+        constructor.__call__ = function (args) {
             // create equivalent type with same prototype
             var instance = create(proto);
             // call actual constructor
-            var res = constructor.apply(instance, arguments);
+            var res = constructor.apply(instance, args);
             // return result of constructor if any, otherwise instance
             return res || instance;
         };
     py.NotImplemented = new NotImplementedType();
     var booleans_initialized = false;
     py.bool = py.type(function bool(value) {
+        value = (value instanceof Array) ? value[0] : value;
         // The only actual instance of py.bool should be py.True
         // and py.False. Return the new instance of py.bool if we
         // are initializing py.True and py.False, otherwise always
     py.False = new py.bool();
     booleans_initialized = true;
     py.float = py.type(function float(value) {
+        value = (value instanceof Array) ? value[0] : value;
         this._value = value;
     }, py.object, {
         __eq__: function (other) {
         }
     });
     py.str = py.type(function str(s) {
+        s = (s instanceof Array) ? s[0] : s;
         this._value = s;
     }, py.object, {
         __eq__: function (other) {
         this._inst = null;
         this._func = nativefunc;
     }, py.object, {
-        __call__: function () {
+        __call__: function (args, kwargs) {
             // don't want to rewrite __call__ for instancemethod
-            return this._func.apply(this._inst, arguments);
+            return this._func.call(this._inst, args, kwargs);
         },
         toJSON: function () {
             return this._func;
         this._func = nativefunc;
     }, py.def, {});
 
-    py.issubclass = new py.def(function issubclass(derived, parent) {
+    py.issubclass = new py.def(function issubclass(args) {
+        var derived = args[0], parent = args[1];
         // still hurts my brain that this can work
         return derived.prototype instanceof py.object
             ? py.True
             return py.evaluate(expr.second, context);
         case '(':
             if (expr.second) {
-                var callable = py.evaluate(expr.first, context), args=[];
+                var callable = py.evaluate(expr.first, context);
+                var args = [], kwargs = {};
                 for (var jj=0; jj<expr.second.length; ++jj) {
-                    args.push(py.evaluate(
-                        expr.second[jj], context));
+                    var arg = expr.second[jj];
+                    if (arg.id !== '=') {
+                        // arg
+                        args.push(py.evaluate(arg, context));
+                    } else {
+                        // kwarg
+                        kwargs[arg.first.value] =
+                            py.evaluate(arg.second, context);
+                    }
                 }
-                return callable.__call__.apply(callable, args);
+                return callable.__call__(args, kwargs);
             }
             var tuple_exprs = expr.first,
                 tuple_values = [];

File test/parser.js

                 'expected ' + this.obj + ' to not have an end token');
 };
 
+expect.Assertion.prototype.named = function (value) {
+    this.assert(this.obj.id === '(name)',
+                'expected ' + this.obj + ' to be a name token',
+                'expected ' + this.obj + ' not to be a name token');
+    this.assert(this.obj.value === value,
+                'expected ' + this.obj + ' to have tokenized ' + value,
+                'expected ' + this.obj + ' not to have tokenized ' + value);
+};
 expect.Assertion.prototype.constant = function (value) {
     this.assert(this.obj.id === '(constant)',
                 'expected ' + this.obj + ' to be a constant token',
             expect(toks[1].id).to.be(')');
         });
     });
+    describe('functions', function () {
+        it('tokenizes kwargs', function () {
+            var toks = py.tokenize('foo(bar=3, qux=4)');
+            expect(toks).to.have.tokens(10);
+        });
+    });
+});
+
+describe('Parser', function () {
+    describe('functions', function () {
+        var ast = py.parse(py.tokenize('foo(bar=3, qux=4)'));
+        expect(ast.id).to.be('(');
+        expect(ast.first).to.be.named('foo');
+
+        args = ast.second;
+        expect(args[0].id).to.be('=');
+        expect(args[0].first).to.be.named('bar');
+        expect(args[0].second).to.be.number(3);
+
+        expect(args[1].id).to.be('=');
+        expect(args[1].first).to.be.named('qux');
+        expect(args[1].second).to.be.number(4);
+    });
 });

File test/test.js

         });
         expect(py.eval('MyType()', {MyType: typ})).to.be(true);
     });
+    it('should accept kwargs', function () {
+        expect(py.eval('foo(ok=True)', {
+            foo: function foo() { return py.True; }
+        })).to.be(true);
+    });
+    it('should be able to get its kwargs', function () {
+        expect(py.eval('foo(ok=True)', {
+            foo: function foo(args, kwargs) { return kwargs.ok; }
+        })).to.be(true);
+    });
+    it('should be able to have both args and kwargs', function () {
+        expect(py.eval('foo(1, 2, 3, ok=True, nok=False)', {
+            foo: function (args, kwargs) {
+                expect(args).to.have.length(3);
+                expect(args[0].toJSON()).to.be(1);
+                expect(kwargs).to.only.have.keys('ok', 'nok')
+                expect(kwargs.nok.toJSON()).to.be(false);
+                return kwargs.ok;
+            }
+        })).to.be(true);
+    });
 });
 describe('issubclass', function () {
     it('should say a type is its own subclass', function () {
-        expect(py.issubclass.__call__(py.dict, py.dict).toJSON())
+        expect(py.issubclass.__call__([py.dict, py.dict]).toJSON())
             .to.be(true);
         expect(py.eval('issubclass(dict, dict)'))
             .to.be(true);
     });
     it('should work with subtypes', function () {
-        expect(py.issubclass.__call__(py.bool, py.object).toJSON())
+        expect(py.issubclass.__call__([py.bool, py.object]).toJSON())
             .to.be(true);
     });
 });