rapydscript-ns 0.8.3 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +1351 -141
  3. package/TODO.md +12 -6
  4. package/language-service/index.js +184 -26
  5. package/package.json +1 -1
  6. package/release/baselib-plain-pretty.js +5895 -1928
  7. package/release/baselib-plain-ugly.js +140 -3
  8. package/release/compiler.js +16282 -5408
  9. package/release/signatures.json +25 -22
  10. package/src/ast.pyj +94 -1
  11. package/src/baselib-builtins.pyj +362 -3
  12. package/src/baselib-bytes.pyj +664 -0
  13. package/src/baselib-containers.pyj +99 -0
  14. package/src/baselib-errors.pyj +45 -1
  15. package/src/baselib-internal.pyj +346 -49
  16. package/src/baselib-itertools.pyj +17 -4
  17. package/src/baselib-str.pyj +46 -4
  18. package/src/lib/abc.pyj +317 -0
  19. package/src/lib/copy.pyj +120 -0
  20. package/src/lib/dataclasses.pyj +532 -0
  21. package/src/lib/enum.pyj +125 -0
  22. package/src/lib/pythonize.pyj +1 -1
  23. package/src/lib/re.pyj +35 -1
  24. package/src/lib/react.pyj +74 -0
  25. package/src/lib/typing.pyj +577 -0
  26. package/src/monaco-language-service/builtins.js +19 -4
  27. package/src/monaco-language-service/diagnostics.js +40 -19
  28. package/src/output/classes.pyj +161 -25
  29. package/src/output/codegen.pyj +16 -2
  30. package/src/output/exceptions.pyj +97 -1
  31. package/src/output/functions.pyj +87 -5
  32. package/src/output/jsx.pyj +164 -0
  33. package/src/output/literals.pyj +28 -2
  34. package/src/output/loops.pyj +5 -2
  35. package/src/output/modules.pyj +1 -1
  36. package/src/output/operators.pyj +108 -36
  37. package/src/output/statements.pyj +2 -2
  38. package/src/output/stream.pyj +1 -0
  39. package/src/parse.pyj +496 -128
  40. package/src/tokenizer.pyj +38 -4
  41. package/test/abc.pyj +291 -0
  42. package/test/arithmetic_nostrict.pyj +88 -0
  43. package/test/arithmetic_types.pyj +169 -0
  44. package/test/baselib.pyj +91 -0
  45. package/test/bytes.pyj +467 -0
  46. package/test/classes.pyj +1 -0
  47. package/test/comparison_ops.pyj +173 -0
  48. package/test/dataclasses.pyj +253 -0
  49. package/test/enum.pyj +134 -0
  50. package/test/eval_exec.pyj +56 -0
  51. package/test/format.pyj +148 -0
  52. package/test/object.pyj +64 -0
  53. package/test/python_compat.pyj +17 -15
  54. package/test/python_features.pyj +89 -21
  55. package/test/regexp.pyj +29 -1
  56. package/test/tuples.pyj +96 -0
  57. package/test/typing.pyj +469 -0
  58. package/test/unit/index.js +2292 -70
  59. package/test/unit/language-service.js +674 -4
  60. package/test/unit/web-repl.js +1106 -0
  61. package/test/vars_locals_globals.pyj +94 -0
  62. package/tools/cli.js +11 -0
  63. package/tools/compile.js +5 -0
  64. package/tools/embedded_compiler.js +15 -4
  65. package/tools/lint.js +16 -19
  66. package/tools/repl.js +1 -1
  67. package/web-repl/env.js +122 -0
  68. package/web-repl/main.js +1 -3
  69. package/web-repl/rapydscript.js +125 -3
  70. package/PYTHON_DIFFERENCES_REPORT.md +0 -291
  71. package/PYTHON_FEATURE_COVERAGE.md +0 -200
  72. package/hack_demo.pyj +0 -112
@@ -0,0 +1,173 @@
1
+ from __python__ import overload_operators
2
+
3
+ # ── helpers ──────────────────────────────────────────────────────────────────
4
+
5
+ def _raises(fn, exc_type):
6
+ """Return True if fn() raises exc_type, False otherwise."""
7
+ try:
8
+ fn()
9
+ except exc_type:
10
+ return True
11
+ return False
12
+
13
+ # ── 1. Numeric comparisons (should still work correctly) ─────────────────────
14
+
15
+ assert 1 < 2
16
+ assert not (2 < 1)
17
+ assert 2 > 1
18
+ assert not (1 > 2)
19
+ assert 1 <= 1
20
+ assert 2 <= 3
21
+ assert not (3 <= 2)
22
+ assert 3 >= 3
23
+ assert 3 >= 2
24
+ assert not (2 >= 3)
25
+
26
+ # ── 2. String comparisons ────────────────────────────────────────────────────
27
+
28
+ assert 'abc' < 'abd'
29
+ assert 'abc' <= 'abc'
30
+ assert 'z' > 'a'
31
+ assert 'abc' >= 'abc'
32
+ assert not ('b' < 'a')
33
+
34
+ # ── 3. List lexicographic ordering ───────────────────────────────────────────
35
+
36
+ # First differing element determines order
37
+ assert [1, 2] < [1, 3]
38
+ assert not ([1, 3] < [1, 2])
39
+
40
+ # Prefix rule: shorter list with equal prefix is smaller
41
+ assert [1, 2] < [1, 2, 0]
42
+ assert not ([1, 2, 0] < [1, 2])
43
+
44
+ # First element dominates
45
+ assert [2] > [1, 99]
46
+ assert not ([1, 99] > [2])
47
+
48
+ # Equal lists
49
+ assert [1, 2] <= [1, 2]
50
+ assert [1, 2] >= [1, 2]
51
+ assert not ([1, 2] < [1, 2])
52
+ assert not ([1, 2] > [1, 2])
53
+
54
+ # Empty lists
55
+ assert [] < [1]
56
+ assert [] <= []
57
+ assert [1] > []
58
+ assert not ([] > [])
59
+
60
+ # ── 4. Nested list lexicographic ordering ────────────────────────────────────
61
+
62
+ assert [[1, 2], [3]] < [[1, 2], [4]]
63
+ assert [[1], [2]] < [[1], [2], []]
64
+
65
+ # ── 5. Bool operands (bool is int in Python) ─────────────────────────────────
66
+
67
+ assert not (True < True)
68
+ assert False < True
69
+ assert True > False
70
+ assert True >= True
71
+
72
+ # ── 6. Custom __lt__ / __le__ / __gt__ / __ge__ dispatch ────────────────────
73
+
74
+ class Vec:
75
+ def __init__(self, x):
76
+ self.x = x
77
+ def __lt__(self, other):
78
+ return self.x < other.x
79
+ def __le__(self, other):
80
+ return self.x <= other.x
81
+ def __gt__(self, other):
82
+ return self.x > other.x
83
+ def __ge__(self, other):
84
+ return self.x >= other.x
85
+
86
+ v1 = Vec(1)
87
+ v2 = Vec(2)
88
+ v3 = Vec(2)
89
+
90
+ assert v1 < v2
91
+ assert not (v2 < v1)
92
+ assert v2 > v1
93
+ assert not (v1 > v2)
94
+ assert v1 <= v2
95
+ assert v2 <= v3
96
+ assert v2 >= v3
97
+ assert v2 >= v1
98
+ assert not (v1 >= v2)
99
+
100
+ # ── 7. Reflected dunder dispatch ─────────────────────────────────────────────
101
+ # When left has no __gt__, Python calls right.__lt__
102
+
103
+ class LtOnly:
104
+ """Only defines __lt__; reflected ops depend on the right-hand operand."""
105
+ def __init__(self, x):
106
+ self.x = x
107
+ def __lt__(self, other):
108
+ return self.x < other.x
109
+
110
+ a = LtOnly(1)
111
+ b = LtOnly(2)
112
+
113
+ assert a < b # a.__lt__(b)
114
+ assert b > a # b.__gt__ absent → ρσ_op_gt falls back → ρσ_op_lt(a, b) → a.__lt__(b)
115
+
116
+ # ── 8. TypeError for incompatible types ──────────────────────────────────────
117
+
118
+ assert _raises(def(): return 1 < 'x';, TypeError)
119
+ assert _raises(def(): return 'x' < 1;, TypeError)
120
+ assert _raises(def(): return [1] < 1;, TypeError)
121
+ assert _raises(def(): return 1 < [1];, TypeError)
122
+ assert _raises(def(): return 1 > 'x';, TypeError)
123
+ assert _raises(def(): return 1 <= 'x';, TypeError)
124
+ assert _raises(def(): return 1 >= 'x';, TypeError)
125
+
126
+ # ── 9. TypeError message contains operator symbol ────────────────────────────
127
+
128
+ def _err_msg(fn):
129
+ try:
130
+ fn()
131
+ except TypeError as e:
132
+ return str(e)
133
+ return ''
134
+
135
+ msg = _err_msg(def(): return 1 < 'x';)
136
+ assert "'<'" in msg, msg
137
+ assert 'int' in msg, msg
138
+ assert 'str' in msg, msg
139
+
140
+ msg = _err_msg(def(): return 1 > [1];)
141
+ assert "'>'" in msg, msg
142
+
143
+ msg = _err_msg(def(): return 1 <= {};)
144
+ assert "'<='" in msg, msg
145
+
146
+ msg = _err_msg(def(): return 1 >= {};)
147
+ assert "'>='" in msg, msg
148
+
149
+ # ── 10. Chained comparisons with overloaded ops ───────────────────────────────
150
+
151
+ assert 1 < 2 < 3
152
+ assert not (1 < 2 < 2)
153
+ assert 3 > 2 > 1
154
+ assert 1 <= 1 <= 2
155
+ assert [1] < [2] < [3]
156
+ assert [3] > [2] > [1]
157
+
158
+ # Mixed direction chain
159
+ assert 1 < 2 > 0
160
+ assert not (1 < 2 > 3)
161
+
162
+ # ── 11. Chained comparison: middle expression evaluated once ─────────────────
163
+
164
+ counter = [0]
165
+ def inc_and_return(val):
166
+ counter[0] += 1
167
+ return val
168
+
169
+ # a < inc_and_return(b) < c — middle must be evaluated exactly once
170
+ assert 1 < inc_and_return(2) < 3
171
+ assert counter[0] == 1, counter[0]
172
+
173
+ print('All comparison_ops tests passed.')
@@ -0,0 +1,253 @@
1
+ # globals: assrt
2
+ # vim:fileencoding=utf-8
3
+ #
4
+ # dataclasses.pyj
5
+ # Tests for the dataclasses standard library.
6
+
7
+ ae = assrt.equal
8
+ ade = assrt.deepEqual
9
+ ok = assrt.ok
10
+
11
+ from dataclasses import dataclass, field, fields, asdict, astuple, replace, is_dataclass, MISSING
12
+
13
+ # ── 1. Basic @dataclass: __init__, __repr__, __eq__ ───────────────────────────
14
+
15
+ @dataclass
16
+ class _Point:
17
+ x: float
18
+ y: float
19
+
20
+ p1 = _Point(1, 2)
21
+ ae(p1.x, 1)
22
+ ae(p1.y, 2)
23
+ ae(repr(p1), '_Point(x=1, y=2)')
24
+ ok(p1 == _Point(1, 2))
25
+ ok(p1 is not _Point(1, 2)) # different instances
26
+ ok(not (p1 == _Point(1, 9)))
27
+
28
+ # ── 2. Default values ─────────────────────────────────────────────────────────
29
+
30
+ @dataclass
31
+ class _Config:
32
+ name: str
33
+ debug: bool = False
34
+ count: int = 0
35
+
36
+ c1 = _Config('myapp')
37
+ ae(c1.name, 'myapp')
38
+ ok(c1.debug is False)
39
+ ae(c1.count, 0)
40
+
41
+ c2 = _Config('other', True, 5)
42
+ ae(c2.name, 'other')
43
+ ok(c2.debug is True)
44
+ ae(c2.count, 5)
45
+
46
+ # ── 3. Keyword arguments ──────────────────────────────────────────────────────
47
+
48
+ c3 = _Config(name='test', debug=True)
49
+ ae(c3.name, 'test')
50
+ ok(c3.debug is True)
51
+ ae(c3.count, 0)
52
+
53
+ # ── 4. field() with default_factory ──────────────────────────────────────────
54
+
55
+ @dataclass
56
+ class _Container:
57
+ items: list = field(default_factory=list)
58
+ label: str = 'default'
59
+
60
+ a = _Container()
61
+ b = _Container()
62
+ # Each instance gets its own list
63
+ a.items.push(1)
64
+ ae(a.items.length, 1)
65
+ ae(b.items.length, 0)
66
+ ae(a.label, 'default')
67
+
68
+ d = _Container(label='custom')
69
+ ae(d.label, 'custom')
70
+
71
+ # ── 5. field() repr=False ─────────────────────────────────────────────────────
72
+
73
+ @dataclass
74
+ class _Hidden:
75
+ public: str
76
+ secret: str = field('s3cr3t', repr=False)
77
+
78
+ h = _Hidden("visible")
79
+ ae(repr(h), '_Hidden(public="visible")')
80
+ ae(h.secret, 's3cr3t')
81
+
82
+ # ── 6. field() init=False ─────────────────────────────────────────────────────
83
+
84
+ @dataclass
85
+ class _WithPostInit:
86
+ x: int
87
+ y: int
88
+ dist: int = field(init=False, repr=True)
89
+
90
+ def __post_init__(self):
91
+ this.dist = this.x + this.y
92
+
93
+ w = _WithPostInit(3, 4)
94
+ ae(w.x, 3)
95
+ ae(w.y, 4)
96
+ ae(w.dist, 7)
97
+
98
+ # ── 7. fields() returns Field objects ────────────────────────────────────────
99
+
100
+ fs = fields(_Point)
101
+ ae(fs.length, 2)
102
+ ae(fs[0].name, 'x')
103
+ ae(fs[1].name, 'y')
104
+
105
+ # Also works on an instance
106
+ fs2 = fields(p1)
107
+ ae(fs2.length, 2)
108
+ ae(fs2[0].name, 'x')
109
+
110
+ # ── 8. asdict() ───────────────────────────────────────────────────────────────
111
+
112
+ @dataclass
113
+ class _Inner:
114
+ value: int
115
+
116
+ @dataclass
117
+ class _Outer:
118
+ inner: object
119
+ tag: str
120
+
121
+ d_obj = _Outer(_Inner(42), 'hello')
122
+ d_dict = asdict(d_obj)
123
+ ae(d_dict['tag'], 'hello')
124
+ ae(d_dict['inner']['value'], 42)
125
+
126
+ # ── 9. astuple() ──────────────────────────────────────────────────────────────
127
+
128
+ pt = _Point(3, 4)
129
+ t = astuple(pt)
130
+ ae(t[0], 3)
131
+ ae(t[1], 4)
132
+
133
+ # ── 10. replace() ────────────────────────────────────────────────────────────
134
+
135
+ p2 = _Point(1, 2)
136
+ p3 = replace(p2, y=99)
137
+ ae(p3.x, 1)
138
+ ae(p3.y, 99)
139
+ ae(p2.y, 2) # original unchanged
140
+
141
+ # ── 11. is_dataclass() ───────────────────────────────────────────────────────
142
+
143
+ ok(is_dataclass(_Point))
144
+ ok(is_dataclass(p1))
145
+ ok(not is_dataclass(42))
146
+ ok(not is_dataclass('hello'))
147
+ ok(not is_dataclass({}))
148
+ ok(not is_dataclass(None))
149
+
150
+ # ── 12. @dataclass(order=True) ───────────────────────────────────────────────
151
+ # Note: RapydScript's < / > operators do not dispatch to __lt__/__gt__, so
152
+ # order=True is tested by calling the generated comparison methods directly.
153
+
154
+ @dataclass(order=True)
155
+ class _Version:
156
+ major: int
157
+ minor: int
158
+
159
+ v1 = _Version(1, 0)
160
+ v2 = _Version(1, 5)
161
+ v3 = _Version(2, 0)
162
+
163
+ ok(v1.__lt__(v2))
164
+ ok(v2.__lt__(v3))
165
+ ok(v3.__gt__(v1))
166
+ ok(v1.__le__(v1))
167
+ ok(v1.__ge__(v1))
168
+ ok(v2.__ge__(v1))
169
+ ok(not v1.__lt__(v1))
170
+
171
+ # ── 13. @dataclass(frozen=True) ──────────────────────────────────────────────
172
+
173
+ @dataclass(frozen=True)
174
+ class _ImmutablePoint:
175
+ x: int
176
+ y: int
177
+
178
+ ip = _ImmutablePoint(1, 2)
179
+ ae(ip.x, 1)
180
+
181
+ # Assigning to a frozen dataclass field raises an error (TypeError in JS,
182
+ # since writable:false is enforced via Object.defineProperty).
183
+ _frozen_error = None
184
+ try:
185
+ ip.x = 99
186
+ except Exception as e:
187
+ _frozen_error = str(e)
188
+ ok(_frozen_error is not None)
189
+
190
+ # ── 14. Inheritance ───────────────────────────────────────────────────────────
191
+
192
+ @dataclass
193
+ class _Base:
194
+ x: int
195
+ y: int = 0
196
+
197
+ @dataclass
198
+ class _Child(_Base):
199
+ z: str = 'default'
200
+
201
+ ch = _Child(x=1, y=2, z='hi')
202
+ ae(ch.x, 1)
203
+ ae(ch.y, 2)
204
+ ae(ch.z, 'hi')
205
+
206
+ ch2 = _Child(10)
207
+ ae(ch2.x, 10)
208
+ ae(ch2.y, 0)
209
+ ae(ch2.z, 'default')
210
+
211
+ # repr shows all fields including inherited
212
+ # (RapydScript repr() uses double quotes for strings, like JavaScript)
213
+ r = repr(ch2)
214
+ ok('x=10' in r)
215
+ ok('y=0' in r)
216
+ ok('z="default"' in r)
217
+
218
+ # ── 15. MISSING sentinel ──────────────────────────────────────────────────────
219
+
220
+ ae(repr(MISSING), 'MISSING')
221
+
222
+ # ── 16. Multiple independent dataclasses ─────────────────────────────────────
223
+
224
+ @dataclass
225
+ class _A:
226
+ val: int = 1
227
+
228
+ @dataclass
229
+ class _B:
230
+ val: int = 2
231
+
232
+ ok(_A().val is 1)
233
+ ok(_B().val is 2)
234
+
235
+ # ── 17. @dataclass as decorator factory (explicit parens, no args) ────────────
236
+
237
+ @dataclass()
238
+ class _Explicit:
239
+ name: str = 'x'
240
+
241
+ ex = _Explicit()
242
+ ae(ex.name, 'x')
243
+ ae(repr(ex), '_Explicit(name="x")')
244
+
245
+ # ── 18. Nested list field with asdict ────────────────────────────────────────
246
+
247
+ @dataclass
248
+ class _WithList:
249
+ numbers: list = field(default_factory=list)
250
+
251
+ wl = _WithList([1, 2, 3])
252
+ d2 = asdict(wl)
253
+ ade(d2['numbers'], [1, 2, 3])
package/test/enum.pyj ADDED
@@ -0,0 +1,134 @@
1
+ # globals: assrt
2
+ # vim:fileencoding=utf-8
3
+ #
4
+ # enum.pyj
5
+ # Tests for the enum standard library (from enum import Enum).
6
+
7
+ ae = assrt.equal
8
+ ade = assrt.deepEqual
9
+ ok = assrt.ok
10
+
11
+ from enum import Enum
12
+
13
+ # ── 1. Basic member access: .name and .value ─────────────────────────────────
14
+ # STATUS: ✓ WORKS
15
+
16
+ class _Color(Enum):
17
+ RED = 1
18
+ GREEN = 2
19
+ BLUE = 3
20
+
21
+ ae(_Color.RED.name, 'RED')
22
+ ae(_Color.RED.value, 1)
23
+ ae(_Color.GREEN.name, 'GREEN')
24
+ ae(_Color.GREEN.value, 2)
25
+ ae(_Color.BLUE.name, 'BLUE')
26
+ ae(_Color.BLUE.value, 3)
27
+
28
+ # ── 2. Member singletons (identity) ──────────────────────────────────────────
29
+ # STATUS: ✓ WORKS
30
+
31
+ ok(_Color.RED is _Color.RED)
32
+ ok(_Color.RED is not _Color.GREEN)
33
+ ok(_Color.GREEN is not _Color.BLUE)
34
+
35
+ # ── 3. repr and str ──────────────────────────────────────────────────────────
36
+ # STATUS: ✓ WORKS
37
+ # Note: repr() for integer values uses the numeric representation.
38
+ # The class name in repr/str reflects the actual JS constructor name.
39
+
40
+ ae(repr(_Color.RED), '<_Color.RED: 1>')
41
+ ae(repr(_Color.GREEN), '<_Color.GREEN: 2>')
42
+ ae(str(_Color.RED), '_Color.RED')
43
+ ae(str(_Color.BLUE), '_Color.BLUE')
44
+
45
+ # ── 4. isinstance checks ─────────────────────────────────────────────────────
46
+ # STATUS: ✓ WORKS
47
+
48
+ ok(isinstance(_Color.RED, _Color))
49
+ ok(isinstance(_Color.RED, Enum))
50
+ ok(isinstance(_Color.GREEN, _Color))
51
+ ok(not isinstance(1, _Color))
52
+ ok(not isinstance(1, Enum))
53
+
54
+ # ── 5. Iteration via list() ───────────────────────────────────────────────────
55
+ # STATUS: ✓ WORKS
56
+
57
+ members = list(_Color)
58
+ ae(len(members), 3)
59
+ ae(members[0].name, 'RED')
60
+ ae(members[1].name, 'GREEN')
61
+ ae(members[2].name, 'BLUE')
62
+ ae(members[0].value, 1)
63
+ ae(members[1].value, 2)
64
+ ae(members[2].value, 3)
65
+
66
+ # ── 6. for loop iteration ─────────────────────────────────────────────────────
67
+ # STATUS: ✓ WORKS
68
+
69
+ _found_names = []
70
+ for _m in _Color:
71
+ _found_names.push(_m.name)
72
+ ade(_found_names, ['RED', 'GREEN', 'BLUE'])
73
+
74
+ _found_values = []
75
+ for _m in _Color:
76
+ _found_values.push(_m.value)
77
+ ade(_found_values, [1, 2, 3])
78
+
79
+ # ── 7. _member_names_ registry ───────────────────────────────────────────────
80
+ # STATUS: ✓ WORKS
81
+
82
+ ade(_Color._member_names_, ['RED', 'GREEN', 'BLUE'])
83
+
84
+ # ── 8. String-valued enum ─────────────────────────────────────────────────────
85
+ # STATUS: ✓ WORKS
86
+
87
+ class _Direction(Enum):
88
+ NORTH = 'north'
89
+ SOUTH = 'south'
90
+ EAST = 'east'
91
+ WEST = 'west'
92
+
93
+ ae(_Direction.NORTH.name, 'NORTH')
94
+ ae(_Direction.NORTH.value, 'north')
95
+ ae(_Direction.SOUTH.value, 'south')
96
+ # RapydScript repr() wraps strings in double quotes (JS convention)
97
+ ae(repr(_Direction.NORTH), '<_Direction.NORTH: "north">')
98
+ ae(str(_Direction.EAST), '_Direction.EAST')
99
+ ae(len(list(_Direction)), 4)
100
+
101
+ # ── 9. Enum subclass with extra methods ───────────────────────────────────────
102
+ # STATUS: ✓ WORKS
103
+
104
+ class _Status(Enum):
105
+ ACTIVE = 1
106
+ INACTIVE = 0
107
+
108
+ def is_active(self):
109
+ return this is _Status.ACTIVE
110
+
111
+ ok(_Status.ACTIVE.is_active())
112
+ ok(not _Status.INACTIVE.is_active())
113
+
114
+ # ── 10. Multiple Enum subclasses are independent ──────────────────────────────
115
+ # STATUS: ✓ WORKS
116
+
117
+ ae(len(list(_Color)), 3)
118
+ ae(len(list(_Direction)), 4)
119
+ ae(len(list(_Status)), 2)
120
+ ok(_Color._member_names_ is not _Direction._member_names_)
121
+ ok(_Color.RED is not _Status.ACTIVE) # different classes, different members
122
+
123
+ # ── 11. Members are not confused with plain class instances ───────────────────
124
+ # STATUS: ✓ WORKS
125
+
126
+ ok(isinstance(_Color.RED, _Color))
127
+ ok(_Color.RED is _Color.RED) # same object every time
128
+
129
+ # ── 12. Values from _members_by_value_ lookup ────────────────────────────────
130
+ # STATUS: ✓ WORKS
131
+
132
+ ae(_Color._members_by_value_[1].name, 'RED')
133
+ ae(_Color._members_by_value_[2].name, 'GREEN')
134
+ ae(_Color._members_by_value_[3].name, 'BLUE')
@@ -0,0 +1,56 @@
1
+ # vim:fileencoding=utf-8
2
+ # globals: assrt
3
+
4
+ ae = assrt.equal
5
+ ade = assrt.deepEqual
6
+ ok = assrt.ok
7
+
8
+ # ── eval — basic expression evaluation ────────────────────────────────────────
9
+
10
+ ae(eval("1 + 2"), 3)
11
+ ae(eval("10 * 5"), 50)
12
+ ae(eval("100 - 37"), 63)
13
+ ae(eval('"hello" + " world"'), "hello world")
14
+ ae(eval("Math.pow(2, 8)"), 256)
15
+ ae(eval("True"), True)
16
+ ae(eval("None"), None)
17
+
18
+ # ── eval — with explicit globals dict ─────────────────────────────────────────
19
+
20
+ ae(eval("x + y", {"x": 10, "y": 5}), 15)
21
+ ae(eval("a * b", {"a": 3, "b": 4}), 12)
22
+ ae(eval("n * n", {"n": 7}), 49)
23
+
24
+ # ── eval — locals override globals ────────────────────────────────────────────
25
+
26
+ ae(eval("x", {"x": 1}, {"x": 99}), 99)
27
+ ae(eval("x + y", {"x": 10, "y": 20}, {"y": 1}), 11)
28
+
29
+ # ── exec — always returns None ────────────────────────────────────────────────
30
+
31
+ ae(exec("1 + 2"), None)
32
+ ae(exec("_ignored = 42"), None)
33
+
34
+ # ── exec — side effects via mutable objects in globals ────────────────────────
35
+
36
+ log = []
37
+ exec("log.push('hello')", {"log": log})
38
+ ae(log[0], "hello")
39
+
40
+ exec("log.push(1 + 2)", {"log": log})
41
+ ae(log[1], 3)
42
+
43
+ # exec with a function in globals
44
+ out = []
45
+ def _adder(a, b): out.push(a + b);
46
+ exec("fn(10, 7)", {"fn": _adder, "out": out})
47
+ ae(out[0], 17)
48
+
49
+ # exec returns None even when globals/locals provided
50
+ ae(exec("1 + 1", {"x": 5}), None)
51
+
52
+ # ── exec — JS loop, mutation visible in caller ────────────────────────────────
53
+
54
+ nums = []
55
+ exec("for _i in range(4):\n nums.append(_i * _i)", {"nums": nums})
56
+ ade(nums, [0, 1, 4, 9])