Skip to content
This repository has been archived by the owner on May 31, 2020. It is now read-only.

Commit

Permalink
Merge pull request #749 from nicklambourne/master
Browse files Browse the repository at this point in the history
Implemented sum() builtin functionality for other types.
  • Loading branch information
freakboy3742 authored May 17, 2018
2 parents cb70313 + 615b4d4 commit d40c47b
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 19 deletions.
57 changes: 46 additions & 11 deletions batavia/builtins/sum.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,62 @@ function sum(args, kwargs) {
throw new exceptions.TypeError.$pyclass("sum() doesn't accept keyword arguments")
}
if (!args || args.length === 0) {
throw new exceptions.TypeError.$pyclass('sum() expected at least 1 argument, got ' + args.length)
throw new exceptions.TypeError.$pyclass('sum expected at least 1 arguments, got ' + args.length)
}
if (args.length > 2) {
throw new exceptions.TypeError.$pyclass('sum() expected at most 2 argument, got ' + args.length)
throw new exceptions.TypeError.$pyclass('sum expected at most 2 arguments, got ' + args.length)
}
if (!args[0].__iter__) {
throw new exceptions.TypeError.$pyclass("'" + type_name(args[0]) + "' object is not iterable")
}

try {
return args[0].reduce(function(a, b) {
let value = args[0]
// This is unique behaviour that is handled before types are resolved and added.
// Empty strings, sets resolve to zero when provided as a single argument to sum().
let resolve_to_zero_types = [types.Set, types.Str]
if (types.isinstance(value, resolve_to_zero_types)) {
if (value === '' || (types.isinstance(value, types.Set) && value.toString() === (new types.Set()).toString())) {
return new types.Int(0)
} else if (types.isinstance(value, types.Str)) {
// Reject all non-empty strings.
return new types.Int(0).__add__(value) // This will throw the correct TypeError.
}
}
// Sets need to be handled differently to other iterable types due to use of dict for data.
if (types.isinstance(value, [types.Set, types.FrozenSet])) {
return value.data.keys().reduce(function(a, b) {
return a.__add__(b)
}, new types.Int(0))
} catch (err) {
// a and b could fail to add due to many possible type incompatibilities,
// all of which would need to be reflected in this error message -
// but we don't have to check for them here, because we've already
// tested for them in __add__.
throw new exceptions.TypeError.$pyclass(err.msg)
} else if (types.isinstance(value, types.Range)) {
// This doesn't work yet due to upstream error.
let retval = types.Int(0)
for (let i = value.start; i < value.end; i += value.step) {
retval = retval.__add__(new types.Int(value.val[i]))
}
return retval
} else if (types.isinstance(value, types.Dict)) {
// Sum of dict is sum of keys.
return value.keys().reduce(function(a, b) {
return a.__add__(b)
}, new types.Int(0))
} else if (types.isinstance(value, [types.Bytes, types.Bytearray])) {
// Sum of bytearray is sum of internal value.
if (types.isinstance(value, types.Bytearray)) {
value = value.valueOf()
}
let retval = new types.Int(0)
// Sum of non-empty byte-strings is the sum of ASCII values of each byte.
if (value.__len__() > 0) {
for (let i = 0; i < value.__len__(); i++) {
retval = retval.__add__(new types.Int(value.val[i]))
}
}
return retval
}
return value.reduce(function(a, b) {
return a.__add__(b)
}, new types.Int(0))
}

sum.__doc__ = "sum(iterable[, start]) -> value\n\nReturn the sum of an iterable of numbers (NOT strings) plus the value\nof parameter 'start' (which defaults to 0). When the iterable is\nempty, return start."

module.exports = sum
19 changes: 11 additions & 8 deletions tests/builtins/test_sum.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,16 @@ def test_sum_mix_floats_and_ints(self):
class BuiltinSumFunctionTests(BuiltinFunctionTestCase, TranspileTestCase):
function = "sum"

# these are implemented, but the exact exception thrown depends on the order
# that they are iterated on being the exact same in both CPython and batavia,
# which is not guaranteed. (if an unsupported string follows an int, the error
# will be different than if it followed a float)

is_flakey = [
'test_frozenset', # This works, but python dict.keys() returns non-deterministically
'test_set', # This works, but python dict.keys() returns non-deterministically.
]

not_implemented = [
'test_noargs',
'test_bytearray',
'test_bytes',
'test_dict',
'test_frozenset',
'test_range',
'test_set',
'test_str',
'test_range', # This has been implemented, but fails upstream on isinstance.
]

0 comments on commit d40c47b

Please sign in to comment.