From c54628512587e76deac07528af1f2f98047fadd7 Mon Sep 17 00:00:00 2001 From: John Sheehan Date: Wed, 3 Jul 2024 17:17:43 -0700 Subject: [PATCH 1/8] Grouped autotest checks for similar features in builtins module --- .../transcrypt/module_builtin/__init__.py | 103 ++++++++++-------- 1 file changed, 56 insertions(+), 47 deletions(-) diff --git a/transcrypt/development/automated_tests/transcrypt/module_builtin/__init__.py b/transcrypt/development/automated_tests/transcrypt/module_builtin/__init__.py index 3c7a14b3..09dacc12 100644 --- a/transcrypt/development/automated_tests/transcrypt/module_builtin/__init__.py +++ b/transcrypt/development/automated_tests/transcrypt/module_builtin/__init__.py @@ -111,56 +111,65 @@ def run (autoTester): enumerate_list = ['a', 'b', 'c', 'd', 'e'] # JS does not have tuples so coerce to list of lists - autoTester.check([list(item) for item in enumerate(enumerate_list)]) - autoTester.check([list(item) for item in enumerate(enumerate_list, 1)]) - autoTester.check([list(item) for item in enumerate(enumerate_list, start=2)]) + autoTester.check( + [list(item) for item in enumerate(enumerate_list)], + [list(item) for item in enumerate(enumerate_list, 1)], + [list(item) for item in enumerate(enumerate_list, start=2)] + ) replace_test = "abcabcabcabc" - autoTester.check(replace_test.replace("c", "x")) - autoTester.check(replace_test.replace("c", "x", -1)) - autoTester.check(replace_test.replace("c", "x", 0)) - autoTester.check(replace_test.replace("c", "x", 1)) - autoTester.check(replace_test.replace("c", "x", 2)) - autoTester.check(replace_test.replace("c", "x", 10)) + autoTester.check( + replace_test.replace("c", "x"), + replace_test.replace("c", "x", -1), + replace_test.replace("c", "x", 0), + replace_test.replace("c", "x", 1), + replace_test.replace("c", "x", 2), + replace_test.replace("c", "x", 10), + ) - autoTester.check(bin(42)) - autoTester.check(oct(42)) - autoTester.check(hex(42)) - autoTester.check(bin(0)) - autoTester.check(oct(0)) - autoTester.check(hex(0)) - autoTester.check(bin(-42)) - autoTester.check(oct(-42)) - autoTester.check(hex(-42)) + autoTester.check( + bin(42), + oct(42), + hex(42), + bin(0), + oct(0), + hex(0), + bin(-42), + oct(-42), + hex(-42), + ) string_test = "abcdefghijkl" - autoTester.check(string_test.startswith("")) - autoTester.check(string_test.startswith("abcd")) - autoTester.check(string_test.startswith("efgh")) - autoTester.check(string_test.startswith("efgh", 2)) - autoTester.check(string_test.startswith("efgh", 4)) - autoTester.check(string_test.startswith("abcd", 0, 3)) - autoTester.check(string_test.startswith("abcd", 0, 5)) - autoTester.check(string_test.startswith("efgh", 4, -2)) - autoTester.check(string_test.startswith("efgh", 4, -6)) - autoTester.check(string_test.startswith(("abc",))) - autoTester.check(string_test.startswith(("abc", "de", "gh"))) - autoTester.check(string_test.startswith(("abc", "de", "gh"), 2)) - autoTester.check(string_test.startswith(("abc", "de", "gh"), 3)) - autoTester.check(string_test.startswith(("abc", "defgh"), 3, 9)) - autoTester.check(string_test.startswith(("abc", "defgh"), 3, 6)) + autoTester.check(string_test.startswith(""), + string_test.startswith("abcd"), + string_test.startswith("efgh"), + string_test.startswith("efgh", 2), + string_test.startswith("efgh", 4), + string_test.startswith("abcd", 0, 3), + string_test.startswith("abcd", 0, 5), + string_test.startswith("efgh", 4, -2), + string_test.startswith("efgh", 4, -6), + string_test.startswith(("abc",)), + string_test.startswith(("abc", "de", "gh")), + string_test.startswith(("abc", "de", "gh"), 2), + string_test.startswith(("abc", "de", "gh"), 3), + string_test.startswith(("abc", "defgh"), 3, 9), + string_test.startswith(("abc", "defgh"), 3, 6), + ) - autoTester.check(string_test.endswith("")) - autoTester.check(string_test.endswith("ijkl")) - autoTester.check(string_test.endswith("efgh")) - autoTester.check(string_test.endswith("efgh", 2)) - autoTester.check(string_test.endswith("abcd", 0, 3)) - autoTester.check(string_test.endswith("abcd", 0, 4)) - autoTester.check(string_test.endswith("efgh", 4, -2)) - autoTester.check(string_test.endswith("efgh", 4, -4)) - autoTester.check(string_test.endswith(("ijkl",))) - autoTester.check(string_test.endswith(("abc", "de", "gh"))) - autoTester.check(string_test.endswith(("abc", "de", "gh"), 3, -4)) - autoTester.check(string_test.endswith(("abc", "de", "gh"), -6, -4)) - autoTester.check(string_test.endswith(("abc", "defgh"), -3, 8)) - autoTester.check(string_test.endswith(("abc", "defgh"), -9, 8)) + autoTester.check( + string_test.endswith(""), + string_test.endswith("ijkl"), + string_test.endswith("efgh"), + string_test.endswith("efgh", 2), + string_test.endswith("abcd", 0, 3), + string_test.endswith("abcd", 0, 4), + string_test.endswith("efgh", 4, -2), + string_test.endswith("efgh", 4, -4), + string_test.endswith(("ijkl",)), + string_test.endswith(("abc", "de", "gh")), + string_test.endswith(("abc", "de", "gh"), 3, -4), + string_test.endswith(("abc", "de", "gh"), -6, -4), + string_test.endswith(("abc", "defgh"), -3, 8), + string_test.endswith(("abc", "defgh"), -9, 8), + ) From 691a58f85238f9659de9d3aa32c569600b5a29d7 Mon Sep 17 00:00:00 2001 From: John Sheehan Date: Wed, 3 Jul 2024 20:32:01 -0700 Subject: [PATCH 2/8] Added copysign and isclose functions to math module Added autotests for math.copysign() and math.isclose() --- .../transcrypt/module_math/__init__.py | 29 +++++++++++++++++-- transcrypt/modules/math/__init__.py | 8 +++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/transcrypt/development/automated_tests/transcrypt/module_math/__init__.py b/transcrypt/development/automated_tests/transcrypt/module_math/__init__.py index d67de741..f6c53641 100644 --- a/transcrypt/development/automated_tests/transcrypt/module_math/__init__.py +++ b/transcrypt/development/automated_tests/transcrypt/module_math/__init__.py @@ -50,8 +50,31 @@ def run (autoTester): check (floor (3.5)) check (ceil (3.5)) check (trunc (3.5)) - - check (isnan (3)) - check (isnan (nan)) + + # Format float since python adds .0 and JS does not + autoTester.check('{0:g}'.format(copysign(42.0, 99.0))) + autoTester.check('{0:g}'.format(copysign(-42, 99.0))) + autoTester.check('{0:g}'.format(copysign(42, -99.0))) + autoTester.check('{0:g}'.format(copysign(-42.0, -99.0))) + + autoTester.check( + isclose(2.123456, 2.123457), + isclose(2.12, 2.123457), + isclose(2.1234567891, 2.1234567892), + isclose(2.1, 2, rel_tol=0.05), + isclose(2.15, 2, rel_tol=0.05), + isclose(1, 1), + isclose(1, 1.000000002), + isclose(1, 1.0000000002), + isclose(1.0, 1.02, rel_tol=0.02), + isclose(1.0, 1.02, rel_tol=0.0, abs_tol=0.02), + isclose(0.000000001, 0.0, rel_tol=0.000000001), + isclose(0.000000001, 0.0, rel_tol=0.0, abs_tol=0.000000000999), + isclose(0.000000001, 0.0, rel_tol=0.0, abs_tol=0.000000001), + isclose(0.0, 0.000000001, rel_tol=0.0, abs_tol=0.000000001), + ) + + autoTester.check (isnan (3)) + autoTester.check (isnan (nan)) diff --git a/transcrypt/modules/math/__init__.py b/transcrypt/modules/math/__init__.py index 7bbeb002..c3ffa444 100644 --- a/transcrypt/modules/math/__init__.py +++ b/transcrypt/modules/math/__init__.py @@ -50,6 +50,14 @@ def radians (x): ceil = Math.ceil trunc = Math.trunc +def copysign (x, y): + return x if Math.sign(x) == Math.sign(y) else -x + +__pragma__ ('kwargs') +def isclose (a, b, rel_tol=1e-09, abs_tol=0.0): + return Math.abs(a-b) <= Math.max(rel_tol * Math.max(Math.abs(a), Math.abs(b)), abs_tol) +__pragma__ ('nokwargs') + isnan = js_isNaN inf = js_Infinity From 1116b622fcf44063a470e5e16429265fe561aa1a Mon Sep 17 00:00:00 2001 From: John Sheehan Date: Wed, 3 Jul 2024 20:35:10 -0700 Subject: [PATCH 3/8] Bumped version to 3.9.3 --- setup.py | 2 +- transcrypt/modules/org/transcrypt/__envir__.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index e56dd982..5d17819f 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ def read (*paths): setup ( name = 'Transcrypt', - version = '3.9.2', + version = '3.9.3', description = 'Python to JavaScript transpiler, supporting multiple inheritance and generating lean, highly readable code', long_description = ( read ('README.rst') diff --git a/transcrypt/modules/org/transcrypt/__envir__.js b/transcrypt/modules/org/transcrypt/__envir__.js index 1926b086..f8cb4ab8 100644 --- a/transcrypt/modules/org/transcrypt/__envir__.js +++ b/transcrypt/modules/org/transcrypt/__envir__.js @@ -1,4 +1,4 @@ __envir__.interpreter_name = 'python'; __envir__.transpiler_name = 'transcrypt'; __envir__.executor_name = __envir__.transpiler_name; -__envir__.transpiler_version = '3.9.2'; +__envir__.transpiler_version = '3.9.3'; From 4a9e2dbabdb06c82da222116427522790d9bbe47 Mon Sep 17 00:00:00 2001 From: John Sheehan Date: Thu, 4 Jul 2024 01:16:27 -0700 Subject: [PATCH 4/8] Updated map function to accept multiple iterators Created transcrypt/transducers autotest module Moved existing map and filter autotests to transducers autotest module Added new autotests for map --- .../development/automated_tests/transcrypt/autotest.py | 2 ++ .../automated_tests/transcrypt/div_pulls/__init__.py | 4 ---- .../automated_tests/transcrypt/transducers/__init__.py | 7 +++++++ transcrypt/modules/math/__init__.py | 4 ++-- transcrypt/modules/org/transcrypt/__runtime__.py | 6 +++--- 5 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 transcrypt/development/automated_tests/transcrypt/transducers/__init__.py diff --git a/transcrypt/development/automated_tests/transcrypt/autotest.py b/transcrypt/development/automated_tests/transcrypt/autotest.py index 74dd4764..0814f955 100644 --- a/transcrypt/development/automated_tests/transcrypt/autotest.py +++ b/transcrypt/development/automated_tests/transcrypt/autotest.py @@ -56,6 +56,7 @@ import truthyness import tuple_assignment +import transducers autoTester = org.transcrypt.autotester.AutoTester () autoTester.run (arguments, 'arguments') @@ -113,5 +114,6 @@ autoTester.run (truthyness, 'truthyness') autoTester.run (tuple_assignment, 'tuple_assignment') +autoTester.run (transducers, 'transducers') autoTester.done () diff --git a/transcrypt/development/automated_tests/transcrypt/div_pulls/__init__.py b/transcrypt/development/automated_tests/transcrypt/div_pulls/__init__.py index 271246fb..9003239b 100644 --- a/transcrypt/development/automated_tests/transcrypt/div_pulls/__init__.py +++ b/transcrypt/development/automated_tests/transcrypt/div_pulls/__init__.py @@ -40,10 +40,6 @@ def run (autoTester): autoTester.check (s [2:]) autoTester.check (s [::2]) - autoTester.check ('Pull 59') - autoTester.check (list (filter (lambda x: x % 2 == 0, range (10)))) - autoTester.check (list (map (lambda x: x*x, range (0, 31, 3)))) - autoTester.check ('Pull 561') def brackets (word): autoTester.check ('sideeffect') diff --git a/transcrypt/development/automated_tests/transcrypt/transducers/__init__.py b/transcrypt/development/automated_tests/transcrypt/transducers/__init__.py new file mode 100644 index 00000000..f5bb8927 --- /dev/null +++ b/transcrypt/development/automated_tests/transcrypt/transducers/__init__.py @@ -0,0 +1,7 @@ + +def run (autoTester): + autoTester.check (list (filter (lambda x: x % 2 == 0, range (10)))) + + autoTester.check (list (map (lambda x: x* x, range(0, 31, 3)))) + autoTester.check (list (map (lambda x, y: x * y, range(0, 31, 3), [1, 2, 3]))) + autoTester.check (list (map (lambda x, y, z: x * y + z, range(0, 31, 3), [1, 2, 3, 4], (2, 4, 6)))) diff --git a/transcrypt/modules/math/__init__.py b/transcrypt/modules/math/__init__.py index c3ffa444..41f1feeb 100644 --- a/transcrypt/modules/math/__init__.py +++ b/transcrypt/modules/math/__init__.py @@ -53,10 +53,10 @@ def radians (x): def copysign (x, y): return x if Math.sign(x) == Math.sign(y) else -x -__pragma__ ('kwargs') +#__pragma__ ('kwargs') def isclose (a, b, rel_tol=1e-09, abs_tol=0.0): return Math.abs(a-b) <= Math.max(rel_tol * Math.max(Math.abs(a), Math.abs(b)), abs_tol) -__pragma__ ('nokwargs') +#__pragma__ ('nokwargs') isnan = js_isNaN diff --git a/transcrypt/modules/org/transcrypt/__runtime__.py b/transcrypt/modules/org/transcrypt/__runtime__.py index 8b06f7ba..c68f5a84 100644 --- a/transcrypt/modules/org/transcrypt/__runtime__.py +++ b/transcrypt/modules/org/transcrypt/__runtime__.py @@ -122,9 +122,9 @@ def sorted (iterable, key = None, reverse = False): #__pragma__ ('nokwargs') -def map (func, iterable): - return [func (item) for item in iterable] - +def map (func, *iterables): + # return [func (item) for item in iterable] + return [func(*items) for items in zip(*iterables)] def filter (func, iterable): if func == None: From 2c70c6afbbd59e4d5849b2986fed772043c71550 Mon Sep 17 00:00:00 2001 From: John Sheehan Date: Thu, 4 Jul 2024 21:02:53 -0700 Subject: [PATCH 5/8] Updated max function to accept key and default kwargs Updated max function to work with all values instead of just numbers Added additional autotests for max --- .../transcrypt/module_builtin/__init__.py | 32 +++++++++++----- .../modules/org/transcrypt/__builtin__.js | 38 ++++++++++++++++--- .../modules/org/transcrypt/__runtime__.py | 1 - 3 files changed, 55 insertions(+), 16 deletions(-) diff --git a/transcrypt/development/automated_tests/transcrypt/module_builtin/__init__.py b/transcrypt/development/automated_tests/transcrypt/module_builtin/__init__.py index 09dacc12..b6a4f743 100644 --- a/transcrypt/development/automated_tests/transcrypt/module_builtin/__init__.py +++ b/transcrypt/development/automated_tests/transcrypt/module_builtin/__init__.py @@ -14,9 +14,18 @@ def canonizeStringList (stringList): def run (autoTester): autoTester.check ('min', min (-1.1, -1, -3)) autoTester.check ('max', max (-1.1, -1, -3)) + autoTester.check ('max', max ([-1.1, -1, -3])) + autoTester.check ('max', max ((-1.1, -1, -3))) + autoTester.check ('max', max ('abc', 'ABC', 'xyz', 'XYZ')) + autoTester.check ('max', max ('abc', 'ABC', 'xyz', 'XYZ', key=lambda v: v[1])) + autoTester.check ('max', max (['abc', 'ABC', 'xyz', 'XYZ'])) + autoTester.check ('max', autoTester.expectException(lambda: max ([]))) + autoTester.check ('max', max ([], default='zzz')) autoTester.check ('abs', abs (-1), abs (1), abs (0), abs (-0.1), abs (0.1)) + autoTester.check ('ord', ord ('a'), ord ('e´'[0])) # This is the 2 codepoint version - + autoTester.check ('chr', chr (97), chr (122), chr (65), chr (90)) # a z A Z + autoTester.check ('round', round (4.006), round (4.006, 2), @@ -104,21 +113,23 @@ def run (autoTester): '
' ) - autoTester.check("".isalpha()) - autoTester.check("123".isalpha()) - autoTester.check("abc".isalpha()) - autoTester.check("abc123".isalpha()) + autoTester.check("isalpha", + "".isalpha(), + "123".isalpha(), + "abc".isalpha(), + "abc123".isalpha(), + ) enumerate_list = ['a', 'b', 'c', 'd', 'e'] # JS does not have tuples so coerce to list of lists - autoTester.check( + autoTester.check("enumerate", [list(item) for item in enumerate(enumerate_list)], [list(item) for item in enumerate(enumerate_list, 1)], [list(item) for item in enumerate(enumerate_list, start=2)] ) replace_test = "abcabcabcabc" - autoTester.check( + autoTester.check("replace", replace_test.replace("c", "x"), replace_test.replace("c", "x", -1), replace_test.replace("c", "x", 0), @@ -127,7 +138,7 @@ def run (autoTester): replace_test.replace("c", "x", 10), ) - autoTester.check( + autoTester.check("bin-oct-hex", bin(42), oct(42), hex(42), @@ -140,7 +151,8 @@ def run (autoTester): ) string_test = "abcdefghijkl" - autoTester.check(string_test.startswith(""), + autoTester.check("startswith", + string_test.startswith(""), string_test.startswith("abcd"), string_test.startswith("efgh"), string_test.startswith("efgh", 2), @@ -157,7 +169,7 @@ def run (autoTester): string_test.startswith(("abc", "defgh"), 3, 6), ) - autoTester.check( + autoTester.check("endswith", string_test.endswith(""), string_test.endswith("ijkl"), string_test.endswith("efgh"), diff --git a/transcrypt/modules/org/transcrypt/__builtin__.js b/transcrypt/modules/org/transcrypt/__builtin__.js index cb1e3a92..726a6491 100644 --- a/transcrypt/modules/org/transcrypt/__builtin__.js +++ b/transcrypt/modules/org/transcrypt/__builtin__.js @@ -553,7 +553,7 @@ export function py_typeof (anObject) { } } else { - return ( // Odly, the braces are required here + return ( // Oddly, the braces are required here aType == 'boolean' ? bool : aType == 'string' ? str : aType == 'number' ? (anObject % 1 == 0 ? int : float) : @@ -667,10 +667,38 @@ export function ord (aChar) { return aChar.charCodeAt (0); }; -// Maximum of n numbers -export function max (nrOrSeq) { - return arguments.length == 1 ? Math.max (...nrOrSeq) : Math.max (...arguments); -}; +// Maximum of n values +export function max (...args) { + // Assume no kwargs + let dflt = undefined; + function key(x) {return x} + + if (args.length > 0) { + if (args[args.length-1] && args[args.length-1].hasOwnProperty ("__kwargtrans__")) { + const kwargs = args[args.length - 1]; + args = args.slice(0, -1); + if (kwargs.hasOwnProperty('key')) key = kwargs['key']; + if (kwargs.hasOwnProperty('py_default')) dflt = kwargs['py_default']; + } + } + + if (args.length === 0) throw TypeError("expected at least 1 argument, got 0", new Error ()); + if (args.length > 1 && dflt !== undefined) throw TypeError("Cannot specify a default with multiple positional arguments", new Error ()); + if (args.length === 1){ + if (Object.prototype.toString.call(args[0]) === '[object Array]'){ + args = args[0]; + } else { + throw TypeError("object is not iterable", new Error()); + } + } + if (args === null || args.length === 0){ + if (dflt === undefined) throw ValueError ("max() arg is an empty sequence", new Error ()); + return dflt + } + + return args.reduce((max_val, cur_val) => key(cur_val) > key(max_val) ? cur_val : max_val); + // return arguments.length == 1 ? Math.max (...nrOrSeq) : Math.max (...arguments); +} // Minimum of n numbers export function min (nrOrSeq) { diff --git a/transcrypt/modules/org/transcrypt/__runtime__.py b/transcrypt/modules/org/transcrypt/__runtime__.py index c68f5a84..7f777b67 100644 --- a/transcrypt/modules/org/transcrypt/__runtime__.py +++ b/transcrypt/modules/org/transcrypt/__runtime__.py @@ -123,7 +123,6 @@ def sorted (iterable, key = None, reverse = False): #__pragma__ ('nokwargs') def map (func, *iterables): - # return [func (item) for item in iterable] return [func(*items) for items in zip(*iterables)] def filter (func, iterable): From 80f80cf0aeef7fe12f2009fb5260e25b54a296d9 Mon Sep 17 00:00:00 2001 From: John Sheehan Date: Thu, 4 Jul 2024 23:46:40 -0700 Subject: [PATCH 6/8] Updated min function to accept key and default kwargs Added additional autotests for max and min --- .../transcrypt/module_builtin/__init__.py | 39 ++++++++++++++----- .../modules/org/transcrypt/__builtin__.js | 29 +++++++------- 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/transcrypt/development/automated_tests/transcrypt/module_builtin/__init__.py b/transcrypt/development/automated_tests/transcrypt/module_builtin/__init__.py index b6a4f743..e95bd12b 100644 --- a/transcrypt/development/automated_tests/transcrypt/module_builtin/__init__.py +++ b/transcrypt/development/automated_tests/transcrypt/module_builtin/__init__.py @@ -12,15 +12,36 @@ def canonizeStringList (stringList): return [canonizeString (aString) for aString in stringList] def run (autoTester): - autoTester.check ('min', min (-1.1, -1, -3)) - autoTester.check ('max', max (-1.1, -1, -3)) - autoTester.check ('max', max ([-1.1, -1, -3])) - autoTester.check ('max', max ((-1.1, -1, -3))) - autoTester.check ('max', max ('abc', 'ABC', 'xyz', 'XYZ')) - autoTester.check ('max', max ('abc', 'ABC', 'xyz', 'XYZ', key=lambda v: v[1])) - autoTester.check ('max', max (['abc', 'ABC', 'xyz', 'XYZ'])) - autoTester.check ('max', autoTester.expectException(lambda: max ([]))) - autoTester.check ('max', max ([], default='zzz')) + autoTester.check ('min', + min (-1.1, -1, -3), + min ([-1.1, -1, -3]), + min ((-1.1, -1, -3)), + min ('abc', 'ABC', 'xyz', 'XYZ'), + min ('abc', 'ABC', 'xyz', 'XYZ', key=lambda v: v[1]), + min (['abc', 'ABC', 'xyz', 'XYZ']), + min([5,6,7,8,9],[1,2,3,4],key=len), + min([[5,6,7,8,9],[1,2,3,4]],default=[1,1,1],key=len), + min ([], default='zzz'), + ) + + autoTester.check ('max', + max (-1.1, -1, -3), + max ([-1.1, -1, -3]), + max ((-1.1, -1, -3)), + max ('abc', 'ABC', 'xyz', 'XYZ'), + max ('abc', 'ABC', 'xyz', 'XYZ', key=lambda v: v[1]), + max (['abc', 'ABC', 'xyz', 'XYZ']), + max([5,6,7,8,9],[1,2,3,4],key=len), + max([[5,6,7,8,9],[1,2,3,4]],default=[1,1,1],key=len), + max ([], default='zzz'), + ) + # autoTester.check ('max', autoTester.expectException(lambda: max () )) + # autoTester.check ('max', autoTester.expectException(lambda: max (1,2,3,4, default=5) )) + # autoTester.check ('max', autoTester.expectException(lambda: max (default=5))) + # autoTester.check ('max', autoTester.expectException(lambda: max ([]))) + # autoTester.check ('max', autoTester.expectException(lambda: max([5,6,7,8,9],[1,2,3,4],default=[1,1,1],key=len) )) + # autoTester.check ('max', autoTester.expectException(lambda: max ([4, 5, 'xyz', 'XYZ']) )) + autoTester.check ('abs', abs (-1), abs (1), abs (0), abs (-0.1), abs (0.1)) autoTester.check ('ord', ord ('a'), ord ('e´'[0])) # This is the 2 codepoint version diff --git a/transcrypt/modules/org/transcrypt/__builtin__.js b/transcrypt/modules/org/transcrypt/__builtin__.js index 726a6491..166f095f 100644 --- a/transcrypt/modules/org/transcrypt/__builtin__.js +++ b/transcrypt/modules/org/transcrypt/__builtin__.js @@ -667,8 +667,7 @@ export function ord (aChar) { return aChar.charCodeAt (0); }; -// Maximum of n values -export function max (...args) { +function min_max (f_compare, ...args) { // Assume no kwargs let dflt = undefined; function key(x) {return x} @@ -677,33 +676,37 @@ export function max (...args) { if (args[args.length-1] && args[args.length-1].hasOwnProperty ("__kwargtrans__")) { const kwargs = args[args.length - 1]; args = args.slice(0, -1); - if (kwargs.hasOwnProperty('key')) key = kwargs['key']; if (kwargs.hasOwnProperty('py_default')) dflt = kwargs['py_default']; + if (kwargs.hasOwnProperty('key')) key = kwargs['key']; + if (Object.prototype.toString.call(key) !== '[object Function]') throw TypeError("object is not callable", new Error()); } } if (args.length === 0) throw TypeError("expected at least 1 argument, got 0", new Error ()); if (args.length > 1 && dflt !== undefined) throw TypeError("Cannot specify a default with multiple positional arguments", new Error ()); if (args.length === 1){ - if (Object.prototype.toString.call(args[0]) === '[object Array]'){ - args = args[0]; - } else { - throw TypeError("object is not iterable", new Error()); - } + if (Object.prototype.toString.call(args[0]) !== '[object Array]') throw TypeError("object is not iterable", new Error()); + args = args[0]; } if (args === null || args.length === 0){ - if (dflt === undefined) throw ValueError ("max() arg is an empty sequence", new Error ()); + if (dflt === undefined) throw ValueError ("arg is an empty sequence", new Error ()); return dflt } - return args.reduce((max_val, cur_val) => key(cur_val) > key(max_val) ? cur_val : max_val); + return args.reduce((max_val, cur_val) => f_compare(key(cur_val), key(max_val)) ? cur_val : max_val); +} + +// Maximum of n values +export function max (...args) { + return min_max(function (a, b){return a > b}, ...args) // return arguments.length == 1 ? Math.max (...nrOrSeq) : Math.max (...arguments); } // Minimum of n numbers -export function min (nrOrSeq) { - return arguments.length == 1 ? Math.min (...nrOrSeq) : Math.min (...arguments); -}; +export function min (...args) { + return min_max(function (a, b){return a < b}, ...args) + // return arguments.length == 1 ? Math.min (...nrOrSeq) : Math.min (...arguments); +} // Integer to binary export function bin (nbr) { From 70a2d416eb2175e42973782f34868c17df543ee6 Mon Sep 17 00:00:00 2001 From: John Sheehan Date: Sat, 6 Jul 2024 03:54:23 -0700 Subject: [PATCH 7/8] Fixed key-less sort so that numbers automatically sort properly Created copy module and implemented copy and deepcopy Removed broken copy and deepcopy functions from builtins Moved dict related autotest from div_issues to dictionaries Added additional autotests for sort and sorted Added module_copy autotest module Created autotests for copy and deepcopy --- .../automated_tests/transcrypt/autotest.py | 2 + .../transcrypt/dictionaries/__init__.py | 12 +++- .../transcrypt/div_issues/__init__.py | 10 --- .../transcrypt/general_functions/__init__.py | 33 +++++++++- .../transcrypt/module_copy/__init__.py | 23 +++++++ .../transcrypt/transducers/__init__.py | 12 ++-- transcrypt/modules/copy/__init__.js | 41 ++++++++++++ .../modules/org/transcrypt/__builtin__.js | 64 +++++++++---------- .../modules/org/transcrypt/__runtime__.py | 17 ++--- 9 files changed, 154 insertions(+), 60 deletions(-) create mode 100644 transcrypt/development/automated_tests/transcrypt/module_copy/__init__.py create mode 100644 transcrypt/modules/copy/__init__.js diff --git a/transcrypt/development/automated_tests/transcrypt/autotest.py b/transcrypt/development/automated_tests/transcrypt/autotest.py index 0814f955..5b587e09 100644 --- a/transcrypt/development/automated_tests/transcrypt/autotest.py +++ b/transcrypt/development/automated_tests/transcrypt/autotest.py @@ -38,6 +38,7 @@ if __pragma__ ('defined', 'undefined'): import module_collections +import module_copy import module_datetime import module_itertools import module_math @@ -96,6 +97,7 @@ if __pragma__ ('defined', 'undefined'): autoTester.run (module_collections, 'module_collections') +autoTester.run (module_copy, 'module_copy') autoTester.run (module_datetime, 'module_datetime') autoTester.run (module_itertools, 'module_itertools') autoTester.run (module_math, 'module_math') diff --git a/transcrypt/development/automated_tests/transcrypt/dictionaries/__init__.py b/transcrypt/development/automated_tests/transcrypt/dictionaries/__init__.py index 8479f8a5..cd641f37 100644 --- a/transcrypt/development/automated_tests/transcrypt/dictionaries/__init__.py +++ b/transcrypt/development/automated_tests/transcrypt/dictionaries/__init__.py @@ -89,7 +89,6 @@ def run (autoTester): autoTester.check (tel.pop ('foo', None)) # Check compound keys (issue 281) - d = {} d ['a'] = 3777 d [(1, 2)] = 4777 @@ -101,7 +100,16 @@ def run (autoTester): d [(1, 2)] = 4777 autoTester.check (d ['a'], d [(1, 2)]) __pragma__ ('noopov') - + + # Check dict.popitem (issue 306) + dict_306 = {'Abraham': 'Lincoln', 'Barack': 'O\'Bama', 'Thomas': 'Jefferson'} + results = [] + try: + while True: + results.append (list(dict_306.popitem ())) + except Exception as exception: + autoTester.check (sorted (results)) + # Check exceptions knights = {'robin': 'the brave', 'gallahad': 'the pure'} autoTester.check ( diff --git a/transcrypt/development/automated_tests/transcrypt/div_issues/__init__.py b/transcrypt/development/automated_tests/transcrypt/div_issues/__init__.py index 1d115705..ac954b9d 100644 --- a/transcrypt/development/automated_tests/transcrypt/div_issues/__init__.py +++ b/transcrypt/development/automated_tests/transcrypt/div_issues/__init__.py @@ -264,16 +264,6 @@ def filter_word (word0, word1): autoTester.check (filter_word ('_in_', 'kind')) autoTester.check (filter_word ('min_', 'kind')) - autoTester.check ('Issue 306') - dict_306 = {'Abraham': 'Lincoln', 'Barack': 'O\'Bama', 'Thomas': 'Jefferson'} - results = [] - try: - while True: - results.append (dict_306.popitem ()) - except Exception as exception: - autoTester.check (sorted (results)) - autoTester.check ('That\'s it') - autoTester.check ('Issue 314') try: autoTester.check (int (float (123))) diff --git a/transcrypt/development/automated_tests/transcrypt/general_functions/__init__.py b/transcrypt/development/automated_tests/transcrypt/general_functions/__init__.py index 9c40a1ca..4f1c2cf2 100644 --- a/transcrypt/development/automated_tests/transcrypt/general_functions/__init__.py +++ b/transcrypt/development/automated_tests/transcrypt/general_functions/__init__.py @@ -35,12 +35,36 @@ def run (autoTester): autoTester.check (len (collection)) autoTester.check ('sort and sorted
') - a = [1, 5, 3, 2, -1] + a = [11, 7, 23, 4, -1] b = ['sun', 'earth', 'moon'] - + c = [{'name': 'Defg', 'qty': 99}, {'name': 'Abcd', 'qty': 42}, {'name': 'Hijk', 'qty': 11}] + d = [[21, 19, 54], [22, 7, 12, 37]] + + autoTester.check (a) autoTester.check (sorted (a)) + autoTester.check (a) + autoTester.check (sorted (a, key=str)) + autoTester.check (sorted (a, key=int)) autoTester.check (sorted (b)) - + + autoTester.check (sorted(c, key=lambda k: k['qty'])) + autoTester.check (sorted(c, key=lambda k: k['name'])) + autoTester.check (sorted(c, key=lambda k: k['name'], reverse=True)) + + autoTester.check (sorted(d)) + autoTester.check (sorted(d, key=sum)) + + # Make sure sorted is doing a proper shallow copy (issue 866) + e = sorted(d) + autoTester.check(e) + d[1][1] = 9 + autoTester.check(d) + autoTester.check(e) + d[1] = [22,14,36] + autoTester.check(d) + autoTester.check(e) + + a.sort () autoTester.check (a) @@ -53,6 +77,9 @@ def run (autoTester): a.sort (reverse = True) autoTester.check (a) + a.sort (key=int) + autoTester.check (a) + b.sort (reverse = True) autoTester.check (b) diff --git a/transcrypt/development/automated_tests/transcrypt/module_copy/__init__.py b/transcrypt/development/automated_tests/transcrypt/module_copy/__init__.py new file mode 100644 index 00000000..ca8a5860 --- /dev/null +++ b/transcrypt/development/automated_tests/transcrypt/module_copy/__init__.py @@ -0,0 +1,23 @@ +import copy + +def run (autoTester): + a = {'a': 1, 'b': 2, 'c': 3} + b = copy.copy(a) + c = [[1,2,3],[4,5,6]] + d = copy.copy(c) + e = copy.deepcopy(c) + + autoTester.check ('copy') + autoTester.check (b) + autoTester.check (a) + a['b'] = 9 + autoTester.check (a) + autoTester.check (b) + + autoTester.check ('deepcopy') + autoTester.check (d) + autoTester.check (e) + c[1][1] = 9 + autoTester.check (c) + autoTester.check (d) + autoTester.check (e) diff --git a/transcrypt/development/automated_tests/transcrypt/transducers/__init__.py b/transcrypt/development/automated_tests/transcrypt/transducers/__init__.py index f5bb8927..22f2f8d6 100644 --- a/transcrypt/development/automated_tests/transcrypt/transducers/__init__.py +++ b/transcrypt/development/automated_tests/transcrypt/transducers/__init__.py @@ -1,7 +1,11 @@ def run (autoTester): - autoTester.check (list (filter (lambda x: x % 2 == 0, range (10)))) + autoTester.check ('filter', + list (filter (lambda x: x % 2 == 0, range (10))), + ) - autoTester.check (list (map (lambda x: x* x, range(0, 31, 3)))) - autoTester.check (list (map (lambda x, y: x * y, range(0, 31, 3), [1, 2, 3]))) - autoTester.check (list (map (lambda x, y, z: x * y + z, range(0, 31, 3), [1, 2, 3, 4], (2, 4, 6)))) + autoTester.check ('map', + list (map (lambda x: x* x, range(0, 31, 3))), + list (map (lambda x, y: x * y, range(0, 31, 3), [1, 2, 3])), + list (map (lambda x, y, z: x * y + z, range(0, 31, 3), [1, 2, 3, 4], (2, 4, 6))), + ) diff --git a/transcrypt/modules/copy/__init__.js b/transcrypt/modules/copy/__init__.js new file mode 100644 index 00000000..5f7a5578 --- /dev/null +++ b/transcrypt/modules/copy/__init__.js @@ -0,0 +1,41 @@ +export function __copy(isDeep, obj) { + var result; + + // Handle primitives + if (obj === null || typeof obj !== "object") return obj; + + // Handle Date + if (obj instanceof Date) { + result = new Date(); + result.setTime(obj.getTime()); + return result; + } + + // Handle Array + if (obj instanceof Array) { + result = []; + for (var i = 0, len = obj.length; i < len; i++) { + result[i] = isDeep ? __copy(isDeep, obj[i]) : obj[i]; + } + return result; + } + + // Handle Object + if (obj instanceof Object) { + result = {}; + for (var attr in obj) { + if (obj.hasOwnProperty(attr)) result[attr] = isDeep ? __copy(isDeep, obj[attr]) : obj[attr]; + } + return result; + } + + throw new Error("Copy not implemented for type ".concat(Object.prototype.toString.call(obj))); +} + +export function copy(obj) { + return __copy(false, obj); +} + +export function deepcopy(obj) { + return __copy(true, obj); +} \ No newline at end of file diff --git a/transcrypt/modules/org/transcrypt/__builtin__.js b/transcrypt/modules/org/transcrypt/__builtin__.js index 166f095f..76619e8c 100644 --- a/transcrypt/modules/org/transcrypt/__builtin__.js +++ b/transcrypt/modules/org/transcrypt/__builtin__.js @@ -686,9 +686,9 @@ function min_max (f_compare, ...args) { if (args.length > 1 && dflt !== undefined) throw TypeError("Cannot specify a default with multiple positional arguments", new Error ()); if (args.length === 1){ if (Object.prototype.toString.call(args[0]) !== '[object Array]') throw TypeError("object is not iterable", new Error()); - args = args[0]; + args = args[0]; // Passed in arg is itself an iterable } - if (args === null || args.length === 0){ + if (args.length === 0){ if (dflt === undefined) throw ValueError ("arg is an empty sequence", new Error ()); return dflt } @@ -699,13 +699,11 @@ function min_max (f_compare, ...args) { // Maximum of n values export function max (...args) { return min_max(function (a, b){return a > b}, ...args) - // return arguments.length == 1 ? Math.max (...nrOrSeq) : Math.max (...arguments); } // Minimum of n numbers export function min (...args) { return min_max(function (a, b){return a < b}, ...args) - // return arguments.length == 1 ? Math.min (...nrOrSeq) : Math.min (...arguments); } // Integer to binary @@ -978,35 +976,35 @@ export function enumerate(iterable, start = 0) { // Shallow and deepcopy -export function copy (anObject) { - if (anObject == null || typeof anObject == "object") { - return anObject; - } - else { - var result = {}; - for (var attrib in obj) { - if (anObject.hasOwnProperty (attrib)) { - result [attrib] = anObject [attrib]; - } - } - return result; - } -} - -export function deepcopy (anObject) { - if (anObject == null || typeof anObject == "object") { - return anObject; - } - else { - var result = {}; - for (var attrib in obj) { - if (anObject.hasOwnProperty (attrib)) { - result [attrib] = deepcopy (anObject [attrib]); - } - } - return result; - } -} +// export function copy (anObject) { +// if (anObject == null || typeof anObject == "object") { +// return anObject; +// } +// else { +// var result = {}; +// for (var attrib in obj) { +// if (anObject.hasOwnProperty (attrib)) { +// result [attrib] = anObject [attrib]; +// } +// } +// return result; +// } +// } +// +// export function deepcopy (anObject) { +// if (anObject == null || typeof anObject == "object") { +// return anObject; +// } +// else { +// var result = {}; +// for (var attrib in obj) { +// if (anObject.hasOwnProperty (attrib)) { +// result [attrib] = deepcopy (anObject [attrib]); +// } +// } +// return result; +// } +// } // List extensions to Array diff --git a/transcrypt/modules/org/transcrypt/__runtime__.py b/transcrypt/modules/org/transcrypt/__runtime__.py index 7f777b67..1b304a48 100644 --- a/transcrypt/modules/org/transcrypt/__runtime__.py +++ b/transcrypt/modules/org/transcrypt/__runtime__.py @@ -5,8 +5,9 @@ #__pragma__ ('js', '{}', __include__ ('org/transcrypt/__builtin__.js')) #__pragma__ ('skip') -copy = Math = __typeof__ = __repr__ = document = console = window = 0 +Math = __typeof__ = __repr__ = document = console = window = 0 #__pragma__ ('noskip') +from copy import copy as _copy #__pragma__ ('notconv') # !!! tconv gives a problem with __terminal__, needs investigation #__pragma__ ('nokwargs') @@ -102,30 +103,30 @@ class RuntimeWarning (Warning): #__pragma__ ('kwargs') -def __sort__ (iterable, key = None, reverse = False): # Used by py_sort, can deal with kwargs +def __sort__(iterable, key = None, reverse = False): # Used by py_sort, can deal with kwargs if key: iterable.sort (lambda a, b: 1 if key (a) > key (b) else -1) # JavaScript sort, case '==' is irrelevant for sorting else: - iterable.sort () # JavaScript sort + iterable.sort (lambda a, b: 1 if a > b else -1) # JavaScript sort if reverse: iterable.reverse () -def sorted (iterable, key = None, reverse = False): +def sorted(iterable, key = None, reverse = False): if type (iterable) == dict: - result = copy (iterable.keys ()) + result = _copy (iterable.keys ()) else: - result = copy (iterable) + result = _copy (iterable) __sort__ (result, key, reverse) return result #__pragma__ ('nokwargs') -def map (func, *iterables): +def map(func, *iterables): return [func(*items) for items in zip(*iterables)] -def filter (func, iterable): +def filter(func, iterable): if func == None: func = bool return [item for item in iterable if func (item)] From 1f46b5f1131aa5800da2126029c665ff27f8661e Mon Sep 17 00:00:00 2001 From: John Sheehan Date: Sat, 6 Jul 2024 16:14:58 -0700 Subject: [PATCH 8/8] Fixed kwargs getting lost on sort and sorted functions --- transcrypt/modules/copy/__init__.js | 8 +++--- .../modules/org/transcrypt/__runtime__.py | 28 +++++++++++-------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/transcrypt/modules/copy/__init__.js b/transcrypt/modules/copy/__init__.js index 5f7a5578..0b9edeb0 100644 --- a/transcrypt/modules/copy/__init__.js +++ b/transcrypt/modules/copy/__init__.js @@ -1,5 +1,5 @@ -export function __copy(isDeep, obj) { - var result; +function __copy(isDeep, obj) { + let result; // Handle primitives if (obj === null || typeof obj !== "object") return obj; @@ -14,7 +14,7 @@ export function __copy(isDeep, obj) { // Handle Array if (obj instanceof Array) { result = []; - for (var i = 0, len = obj.length; i < len; i++) { + for (let i = 0, len = obj.length; i < len; i++) { result[i] = isDeep ? __copy(isDeep, obj[i]) : obj[i]; } return result; @@ -23,7 +23,7 @@ export function __copy(isDeep, obj) { // Handle Object if (obj instanceof Object) { result = {}; - for (var attr in obj) { + for (let attr in obj) { if (obj.hasOwnProperty(attr)) result[attr] = isDeep ? __copy(isDeep, obj[attr]) : obj[attr]; } return result; diff --git a/transcrypt/modules/org/transcrypt/__runtime__.py b/transcrypt/modules/org/transcrypt/__runtime__.py index 1b304a48..efbee5f6 100644 --- a/transcrypt/modules/org/transcrypt/__runtime__.py +++ b/transcrypt/modules/org/transcrypt/__runtime__.py @@ -103,24 +103,30 @@ class RuntimeWarning (Warning): #__pragma__ ('kwargs') -def __sort__(iterable, key = None, reverse = False): # Used by py_sort, can deal with kwargs +def _sort(iterable, key = None, reverse = False): # Used by py_sort and sorted, can deal with kwargs if key: iterable.sort (lambda a, b: 1 if key (a) > key (b) else -1) # JavaScript sort, case '==' is irrelevant for sorting else: - iterable.sort (lambda a, b: 1 if a > b else -1) # JavaScript sort - + iterable.sort (lambda a, b: 1 if a > b else -1) # JavaScript sort - key needed to properly sort non-string values + if reverse: iterable.reverse () - -def sorted(iterable, key = None, reverse = False): - if type (iterable) == dict: - result = _copy (iterable.keys ()) - else: - result = _copy (iterable) - - __sort__ (result, key, reverse) + + +def sorted(iterable, *, key=None, reverse=False): + if type(iterable) == dict: + result = _copy(iterable.keys()) + else: + result = _copy(iterable) + + _sort(result, key, reverse) return result + +# Used by py_sort +def __sort__(iterable, *, key=None, reverse=False): + _sort(iterable, key, reverse) + #__pragma__ ('nokwargs') def map(func, *iterables):