diff --git a/batavia/builtins/iter.js b/batavia/builtins/iter.js index 271d775a6..5a5167757 100755 --- a/batavia/builtins/iter.js +++ b/batavia/builtins/iter.js @@ -1,6 +1,7 @@ var exceptions = require('../core').exceptions var callables = require('../core').callables var type_name = require('../core').type_name +var types = require('../types') function iter(args, kwargs) { if (arguments.length !== 2) { @@ -13,7 +14,7 @@ function iter(args, kwargs) { throw new exceptions.TypeError.$pyclass('iter() expected at least 1 arguments, got 0') } if (args.length === 2) { - throw new exceptions.NotImplementedError.$pyclass("Builtin Batavia function 'iter' with callable/sentinel not implemented") + return new types.CallableIterator(args[0], args[1]) } if (args.length > 2) { throw new exceptions.TypeError.$pyclass('iter() expected at most 2 arguments, got 3') diff --git a/batavia/core/types/PYCFile.js b/batavia/core/types/PYCFile.js index a308dec51..d6bed47da 100644 --- a/batavia/core/types/PYCFile.js +++ b/batavia/core/types/PYCFile.js @@ -5,6 +5,11 @@ var constants = require('../constants') *************************************************************************/ var PYCFile = function(data) { + if (!Uint8Array.prototype.slice) { + Object.defineProperty(Uint8Array.prototype, 'slice', { // eslint-disable-line no-extend-native + value: Array.prototype.slice + }) + } Object.call(this) this.magic = data.slice(0, 4) this.modtime = data.slice(4, 8) diff --git a/batavia/types.js b/batavia/types.js index 6aa40d3c1..c67da9891 100644 --- a/batavia/types.js +++ b/batavia/types.js @@ -45,6 +45,8 @@ types['Generator'] = require('./types/Generator') types['Range'] = require('./types/Range') types['Slice'] = require('./types/Slice') +types['CallableIterator'] = require('./types/CallableIterator') + /************************************************************************* * Type comparison defintions that match Python-like behavior. *************************************************************************/ diff --git a/batavia/types/Bool.js b/batavia/types/Bool.js index f87d8d682..08bb8c93f 100644 --- a/batavia/types/Bool.js +++ b/batavia/types/Bool.js @@ -519,6 +519,12 @@ Bool.prototype.__lshift__ = function(other) { return new types.Int(0) } } else if (types.isinstance(other, types.Int)) { + if (other.valueOf() < 0) { + throw new exceptions.ValueError.$pyclass('negative shift count') + } + if (Number.MAX_SAFE_INTEGER < other.valueOf()) { + throw new exceptions.OverflowError.$pyclass('Python int too large to convert to C ssize_t') + } if (this.valueOf()) { this_bool = 1 } else { @@ -540,6 +546,12 @@ Bool.prototype.__rshift__ = function(other) { return new types.Int(0) } } else if (types.isinstance(other, types.Int)) { + if (other.valueOf() < 0) { + throw new exceptions.ValueError.$pyclass('negative shift count') + } + if (Number.MAX_SAFE_INTEGER < Math.abs(other.valueOf())) { + throw new exceptions.OverflowError.$pyclass('Python int too large to convert to C ssize_t') + } if (this.valueOf()) { this_bool = 1 } else { diff --git a/batavia/types/CallableIterator.js b/batavia/types/CallableIterator.js new file mode 100644 index 000000000..2e67b9121 --- /dev/null +++ b/batavia/types/CallableIterator.js @@ -0,0 +1,43 @@ +var PyObject = require('../core').Object +var exceptions = require('../core').exceptions +var create_pyclass = require('../core').create_pyclass + +/************************************************** + * Callable Iterator + **************************************************/ + +function CallableIterator(callable, sentinel) { + PyObject.call(this) + this.callable = callable + this.sentinel = sentinel + this.exhausted = false +} + +create_pyclass(CallableIterator, 'callable_iterator') + +CallableIterator.prototype.__next__ = function() { + if (this.exhausted) { + throw new exceptions.StopIteration.$pyclass() + } + + var item = this.callable.__call__([]) + if (item.__eq__(this.sentinel)) { + this.exhausted = true + throw new exceptions.StopIteration.$pyclass() + } + return item +} + +CallableIterator.prototype.__iter__ = function() { + return this +} + +CallableIterator.prototype.__str__ = function() { + return '' +} + +/************************************************** + * Module exports + **************************************************/ + +module.exports = CallableIterator diff --git a/tests/builtins/test_iter.py b/tests/builtins/test_iter.py index 5f712d9e7..2798c82af 100644 --- a/tests/builtins/test_iter.py +++ b/tests/builtins/test_iter.py @@ -8,6 +8,44 @@ def test_iter_bytes(self): print(list(iter(b"abcdefgh"))) """) + def test_iter_sentinel_range(self): + self.assertCodeExecution(""" + seq = iter(range(10)) + callable = lambda: next(seq) + result = iter(callable, 3) + print(list(result)) + """) + + def test_iter_sentinel_repeated(self): + self.assertCodeExecution(""" + seq = iter(range(10)) + callable = lambda: next(seq) + iterator = iter(callable, 3) + print(next(iterator)) + print(next(iterator)) + print(next(iterator)) + try: + print(next(iterator)) + except StopIteration: + pass + try: + print(next(iterator)) + except StopIteration: + pass + """) + + def test_iter_sentinel_gen(self): + self.assertCodeExecution(""" + def gen(): + abc = 'abcdefghij' + for letter in abc: + yield letter + + g = gen() + callable = lambda: next(g) + result = iter(callable, 'd') + print(list(result)) + """) class BuiltinIterFunctionTests(BuiltinFunctionTestCase, TranspileTestCase): diff --git a/tests/datatypes/test_bool.py b/tests/datatypes/test_bool.py index 73a4b5137..a25134ef5 100644 --- a/tests/datatypes/test_bool.py +++ b/tests/datatypes/test_bool.py @@ -27,11 +27,7 @@ class BinaryBoolOperationTests(BinaryOperationTestCase, TranspileTestCase): data_type = 'bool' not_implemented = [ - 'test_lshift_int', - - 'test_rshift_int', - - 'test_true_divide_complex', + 'test_true_divide_complex' ] @@ -39,10 +35,5 @@ class InplaceBoolOperationTests(InplaceOperationTestCase, TranspileTestCase): data_type = 'bool' not_implemented = [ - - 'test_lshift_int', - - 'test_rshift_int', - 'test_true_divide_complex', ] diff --git a/tests/datatypes/test_int.py b/tests/datatypes/test_int.py index e6c2dd726..fda3832e9 100644 --- a/tests/datatypes/test_int.py +++ b/tests/datatypes/test_int.py @@ -93,9 +93,6 @@ class InplaceIntOperationTests(InplaceOperationTestCase, TranspileTestCase): 'test_power_complex', 'test_power_float', - 'test_rshift_int', # this works, but some of the cases are too large - # until we replace bignumber.js - 'test_subtract_complex', 'test_true_divide_complex', diff --git a/tests/utils.py b/tests/utils.py index 283a5888c..88c23803b 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1170,7 +1170,7 @@ def assertInplaceOperation(self, x_values, y_values, operation, format, substitu 'test_lshift_%s' % datatype, 'x <<= y', examples, small_ints=True ) vars()['test_rshift_%s' % datatype] = _inplace_test( - 'test_rshift_%s' % datatype, 'x >>= y', examples + 'test_rshift_%s' % datatype, 'x >>= y', examples, small_ints=True ) vars()['test_and_%s' % datatype] = _inplace_test( 'test_and_%s' % datatype, 'x &= y', examples