Dialect rules (generated)¶
This page lists every typing rule the compiler will apply, dumped from the rule table itself (situ.compiler.lang.rules). Generated page. Run situ rules to regenerate it; make lint fails if it is stale.
Each row is one rule: at these operand types, the construct has this result type and lowers to this JavaScript. The last column is why that JavaScript means what Python means. A combination that appears nowhere below has no rule, so no lowering; the compiler rejects it with a CompileError. That is the dialect's enforcement-by-omission: a missing entry is the rejection.
$a / $b are the operand placeholders. For the prose version (what to write, and why the dialect is shaped this way) see The dialect.
Binary operators¶
| Construct | Left | Right | Result | Lowers to | Why it is faithful |
|---|---|---|---|---|---|
a + b |
bool |
bool |
int |
($a + $b) |
JS + on two numbers is numeric addition |
a + b |
bool |
int |
int |
($a + $b) |
JS + on two numbers is numeric addition |
a + b |
bool |
float |
float |
($a + $b) |
JS + on two numbers is numeric addition |
a + b |
int |
bool |
int |
($a + $b) |
JS + on two numbers is numeric addition |
a + b |
int |
int |
int |
($a + $b) |
JS + on two numbers is numeric addition |
a + b |
int |
float |
float |
($a + $b) |
JS + on two numbers is numeric addition |
a + b |
float |
bool |
float |
($a + $b) |
JS + on two numbers is numeric addition |
a + b |
float |
int |
float |
($a + $b) |
JS + on two numbers is numeric addition |
a + b |
float |
float |
float |
($a + $b) |
JS + on two numbers is numeric addition |
a + b |
str |
str |
str |
($a + $b) |
JS + on two strings is concatenation |
a + b |
list[int] |
list[int] |
list[int] |
[...$a, ...$b] |
JS + on arrays coerces to string; a spread concatenates |
a + b |
list[str] |
list[str] |
list[str] |
[...$a, ...$b] |
JS + on arrays coerces to string; a spread concatenates |
a - b |
bool |
bool |
int |
($a - $b) |
identical |
a - b |
bool |
int |
int |
($a - $b) |
identical |
a - b |
bool |
float |
float |
($a - $b) |
identical |
a - b |
int |
bool |
int |
($a - $b) |
identical |
a - b |
int |
int |
int |
($a - $b) |
identical |
a - b |
int |
float |
float |
($a - $b) |
identical |
a - b |
float |
bool |
float |
($a - $b) |
identical |
a - b |
float |
int |
float |
($a - $b) |
identical |
a - b |
float |
float |
float |
($a - $b) |
identical |
a - b |
set[int] |
set[int] |
set[int] |
S.sdiff($a, $b) |
Python set difference/union/intersection/symmetric-difference. JS Set has no operators at all, and its .has() uses SameValueZero (so it would miss that Python holds True and 1 as one member) -- hence a helper over S.eq. |
a * b |
bool |
bool |
int |
($a * $b) |
identical |
a * b |
bool |
int |
int |
($a * $b) |
identical |
a * b |
bool |
float |
float |
($a * $b) |
identical |
a * b |
bool |
str |
str |
($b).repeat(Math.max(0, $a)) |
Python 'ab' * 3 repeats (JS * on a string is NaN). The count must be an INT -- 'ab' * 2.5 is a TypeError in Python, and String.repeat would happily truncate it. A negative count gives '' in Python where repeat() throws, hence the clamp. Commutative: 3 * 'ab'. |
a * b |
int |
bool |
int |
($a * $b) |
identical |
a * b |
int |
int |
int |
($a * $b) |
identical |
a * b |
int |
float |
float |
($a * $b) |
identical |
a * b |
int |
str |
str |
($b).repeat(Math.max(0, $a)) |
Python 'ab' * 3 repeats (JS * on a string is NaN). The count must be an INT -- 'ab' * 2.5 is a TypeError in Python, and String.repeat would happily truncate it. A negative count gives '' in Python where repeat() throws, hence the clamp. Commutative: 3 * 'ab'. |
a * b |
float |
bool |
float |
($a * $b) |
identical |
a * b |
float |
int |
float |
($a * $b) |
identical |
a * b |
float |
float |
float |
($a * $b) |
identical |
a * b |
str |
bool |
str |
($a).repeat(Math.max(0, $b)) |
Python 'ab' * 3 repeats (JS * on a string is NaN). The count must be an INT -- 'ab' * 2.5 is a TypeError in Python, and String.repeat would happily truncate it. A negative count gives '' in Python where repeat() throws, hence the clamp. |
a * b |
str |
int |
str |
($a).repeat(Math.max(0, $b)) |
Python 'ab' * 3 repeats (JS * on a string is NaN). The count must be an INT -- 'ab' * 2.5 is a TypeError in Python, and String.repeat would happily truncate it. A negative count gives '' in Python where repeat() throws, hence the clamp. |
a / b |
bool |
bool |
float |
S.div($a, $b) |
Python / is true division: it ALWAYS yields a float, even for 4 / 2 (hence the FLOAT result type: str(4/2) is '2.0'). It also RAISES on a zero divisor where JS yields Infinity with no error, so it cannot be a bare /. S.div throws, as Python does. |
a / b |
bool |
int |
float |
S.div($a, $b) |
Python / is true division: it ALWAYS yields a float, even for 4 / 2 (hence the FLOAT result type: str(4/2) is '2.0'). It also RAISES on a zero divisor where JS yields Infinity with no error, so it cannot be a bare /. S.div throws, as Python does. |
a / b |
bool |
float |
float |
S.div($a, $b) |
Python / is true division: it ALWAYS yields a float, even for 4 / 2 (hence the FLOAT result type: str(4/2) is '2.0'). It also RAISES on a zero divisor where JS yields Infinity with no error, so it cannot be a bare /. S.div throws, as Python does. |
a / b |
int |
bool |
float |
S.div($a, $b) |
Python / is true division: it ALWAYS yields a float, even for 4 / 2 (hence the FLOAT result type: str(4/2) is '2.0'). It also RAISES on a zero divisor where JS yields Infinity with no error, so it cannot be a bare /. S.div throws, as Python does. |
a / b |
int |
int |
float |
S.div($a, $b) |
Python / is true division: it ALWAYS yields a float, even for 4 / 2 (hence the FLOAT result type: str(4/2) is '2.0'). It also RAISES on a zero divisor where JS yields Infinity with no error, so it cannot be a bare /. S.div throws, as Python does. |
a / b |
int |
float |
float |
S.div($a, $b) |
Python / is true division: it ALWAYS yields a float, even for 4 / 2 (hence the FLOAT result type: str(4/2) is '2.0'). It also RAISES on a zero divisor where JS yields Infinity with no error, so it cannot be a bare /. S.div throws, as Python does. |
a / b |
float |
bool |
float |
S.div($a, $b) |
Python / is true division: it ALWAYS yields a float, even for 4 / 2 (hence the FLOAT result type: str(4/2) is '2.0'). It also RAISES on a zero divisor where JS yields Infinity with no error, so it cannot be a bare /. S.div throws, as Python does. |
a / b |
float |
int |
float |
S.div($a, $b) |
Python / is true division: it ALWAYS yields a float, even for 4 / 2 (hence the FLOAT result type: str(4/2) is '2.0'). It also RAISES on a zero divisor where JS yields Infinity with no error, so it cannot be a bare /. S.div throws, as Python does. |
a / b |
float |
float |
float |
S.div($a, $b) |
Python / is true division: it ALWAYS yields a float, even for 4 / 2 (hence the FLOAT result type: str(4/2) is '2.0'). It also RAISES on a zero divisor where JS yields Infinity with no error, so it cannot be a bare /. S.div throws, as Python does. |
a // b |
bool |
bool |
int |
S.floordiv($a, $b) |
Python // floors toward -inf (-7 // 2 == -4, where JS trunc gives -3), and raises on a zero divisor. It keeps the operands' kind: int // int is int, 7.0 // 2 is a float. |
a // b |
bool |
int |
int |
S.floordiv($a, $b) |
Python // floors toward -inf (-7 // 2 == -4, where JS trunc gives -3), and raises on a zero divisor. It keeps the operands' kind: int // int is int, 7.0 // 2 is a float. |
a // b |
bool |
float |
float |
S.floordiv($a, $b) |
Python // floors toward -inf (-7 // 2 == -4, where JS trunc gives -3), and raises on a zero divisor. It keeps the operands' kind: int // int is int, 7.0 // 2 is a float. |
a // b |
int |
bool |
int |
S.floordiv($a, $b) |
Python // floors toward -inf (-7 // 2 == -4, where JS trunc gives -3), and raises on a zero divisor. It keeps the operands' kind: int // int is int, 7.0 // 2 is a float. |
a // b |
int |
int |
int |
S.floordiv($a, $b) |
Python // floors toward -inf (-7 // 2 == -4, where JS trunc gives -3), and raises on a zero divisor. It keeps the operands' kind: int // int is int, 7.0 // 2 is a float. |
a // b |
int |
float |
float |
S.floordiv($a, $b) |
Python // floors toward -inf (-7 // 2 == -4, where JS trunc gives -3), and raises on a zero divisor. It keeps the operands' kind: int // int is int, 7.0 // 2 is a float. |
a // b |
float |
bool |
float |
S.floordiv($a, $b) |
Python // floors toward -inf (-7 // 2 == -4, where JS trunc gives -3), and raises on a zero divisor. It keeps the operands' kind: int // int is int, 7.0 // 2 is a float. |
a // b |
float |
int |
float |
S.floordiv($a, $b) |
Python // floors toward -inf (-7 // 2 == -4, where JS trunc gives -3), and raises on a zero divisor. It keeps the operands' kind: int // int is int, 7.0 // 2 is a float. |
a // b |
float |
float |
float |
S.floordiv($a, $b) |
Python // floors toward -inf (-7 // 2 == -4, where JS trunc gives -3), and raises on a zero divisor. It keeps the operands' kind: int // int is int, 7.0 // 2 is a float. |
a % b |
bool |
bool |
int |
S.mod($a, $b) |
Python % takes the sign of the DIVISOR (floor-mod): -1 % 3 is 2 in Python and -1 in JS. It also raises on a zero divisor. This is why % was removed from the untyped emitter -- with types it comes back, correct. |
a % b |
bool |
int |
int |
S.mod($a, $b) |
Python % takes the sign of the DIVISOR (floor-mod): -1 % 3 is 2 in Python and -1 in JS. It also raises on a zero divisor. This is why % was removed from the untyped emitter -- with types it comes back, correct. |
a % b |
bool |
float |
float |
S.mod($a, $b) |
Python % takes the sign of the DIVISOR (floor-mod): -1 % 3 is 2 in Python and -1 in JS. It also raises on a zero divisor. This is why % was removed from the untyped emitter -- with types it comes back, correct. |
a % b |
int |
bool |
int |
S.mod($a, $b) |
Python % takes the sign of the DIVISOR (floor-mod): -1 % 3 is 2 in Python and -1 in JS. It also raises on a zero divisor. This is why % was removed from the untyped emitter -- with types it comes back, correct. |
a % b |
int |
int |
int |
S.mod($a, $b) |
Python % takes the sign of the DIVISOR (floor-mod): -1 % 3 is 2 in Python and -1 in JS. It also raises on a zero divisor. This is why % was removed from the untyped emitter -- with types it comes back, correct. |
a % b |
int |
float |
float |
S.mod($a, $b) |
Python % takes the sign of the DIVISOR (floor-mod): -1 % 3 is 2 in Python and -1 in JS. It also raises on a zero divisor. This is why % was removed from the untyped emitter -- with types it comes back, correct. |
a % b |
float |
bool |
float |
S.mod($a, $b) |
Python % takes the sign of the DIVISOR (floor-mod): -1 % 3 is 2 in Python and -1 in JS. It also raises on a zero divisor. This is why % was removed from the untyped emitter -- with types it comes back, correct. |
a % b |
float |
int |
float |
S.mod($a, $b) |
Python % takes the sign of the DIVISOR (floor-mod): -1 % 3 is 2 in Python and -1 in JS. It also raises on a zero divisor. This is why % was removed from the untyped emitter -- with types it comes back, correct. |
a % b |
float |
float |
float |
S.mod($a, $b) |
Python % takes the sign of the DIVISOR (floor-mod): -1 % 3 is 2 in Python and -1 in JS. It also raises on a zero divisor. This is why % was removed from the untyped emitter -- with types it comes back, correct. |
a | b |
set[int] |
set[int] |
set[int] |
S.sunion($a, $b) |
Python set difference/union/intersection/symmetric-difference. JS Set has no operators at all, and its .has() uses SameValueZero (so it would miss that Python holds True and 1 as one member) -- hence a helper over S.eq. |
a & b |
set[int] |
set[int] |
set[int] |
S.sinter($a, $b) |
Python set difference/union/intersection/symmetric-difference. JS Set has no operators at all, and its .has() uses SameValueZero (so it would miss that Python holds True and 1 as one member) -- hence a helper over S.eq. |
a ^ b |
set[int] |
set[int] |
set[int] |
S.sxor($a, $b) |
Python set difference/union/intersection/symmetric-difference. JS Set has no operators at all, and its .has() uses SameValueZero (so it would miss that Python holds True and 1 as one member) -- hence a helper over S.eq. |
Comparisons¶
| Construct | Left | Right | Result | Lowers to | Why it is faithful |
|---|---|---|---|---|---|
a == b |
bool |
bool |
bool |
(Number($a) === Number($b)) |
a Python bool IS an int (True == 1), but JS true === 1 is false. Number() on both sides restores Python's answer. |
a == b |
bool |
int |
bool |
(Number($a) === Number($b)) |
a Python bool IS an int (True == 1), but JS true === 1 is false. Number() on both sides restores Python's answer. |
a == b |
bool |
float |
bool |
(Number($a) === Number($b)) |
a Python bool IS an int (True == 1), but JS true === 1 is false. Number() on both sides restores Python's answer. |
a == b |
int |
bool |
bool |
(Number($a) === Number($b)) |
a Python bool IS an int (True == 1), but JS true === 1 is false. Number() on both sides restores Python's answer. |
a == b |
int |
int |
bool |
(Number($a) === Number($b)) |
a Python bool IS an int (True == 1), but JS true === 1 is false. Number() on both sides restores Python's answer. |
a == b |
int |
float |
bool |
(Number($a) === Number($b)) |
a Python bool IS an int (True == 1), but JS true === 1 is false. Number() on both sides restores Python's answer. |
a == b |
float |
bool |
bool |
(Number($a) === Number($b)) |
a Python bool IS an int (True == 1), but JS true === 1 is false. Number() on both sides restores Python's answer. |
a == b |
float |
int |
bool |
(Number($a) === Number($b)) |
a Python bool IS an int (True == 1), but JS true === 1 is false. Number() on both sides restores Python's answer. |
a == b |
float |
float |
bool |
(Number($a) === Number($b)) |
a Python bool IS an int (True == 1), but JS true === 1 is false. Number() on both sides restores Python's answer. |
a == b |
str |
str |
bool |
($a === $b) |
=== on two strings is value equality in both languages |
a == b |
none |
none |
bool |
($a === $b) |
null === null |
a == b |
list[int] |
list[int] |
bool |
S.eq($a, $b) |
Python compares lists/dicts/sets STRUCTURALLY; JS === is reference identity, so xs == [1, 2] was false in the browser and true in Python. S.eq is a deep compare. |
a == b |
list[str] |
list[str] |
bool |
S.eq($a, $b) |
Python compares lists/dicts/sets STRUCTURALLY; JS === is reference identity, so xs == [1, 2] was false in the browser and true in Python. S.eq is a deep compare. |
a == b |
set[int] |
set[int] |
bool |
S.eq($a, $b) |
Python compares lists/dicts/sets STRUCTURALLY; JS === is reference identity, so xs == [1, 2] was false in the browser and true in Python. S.eq is a deep compare. |
a == b |
dict[str, int] |
dict[str, int] |
bool |
S.eq($a, $b) |
Python compares lists/dicts/sets STRUCTURALLY; JS === is reference identity, so xs == [1, 2] was false in the browser and true in Python. S.eq is a deep compare. |
a != b |
bool |
bool |
bool |
(!(Number($a) === Number($b))) |
a Python bool IS an int (True == 1), but JS true === 1 is false. Number() on both sides restores Python's answer. |
a != b |
bool |
int |
bool |
(!(Number($a) === Number($b))) |
a Python bool IS an int (True == 1), but JS true === 1 is false. Number() on both sides restores Python's answer. |
a != b |
bool |
float |
bool |
(!(Number($a) === Number($b))) |
a Python bool IS an int (True == 1), but JS true === 1 is false. Number() on both sides restores Python's answer. |
a != b |
int |
bool |
bool |
(!(Number($a) === Number($b))) |
a Python bool IS an int (True == 1), but JS true === 1 is false. Number() on both sides restores Python's answer. |
a != b |
int |
int |
bool |
(!(Number($a) === Number($b))) |
a Python bool IS an int (True == 1), but JS true === 1 is false. Number() on both sides restores Python's answer. |
a != b |
int |
float |
bool |
(!(Number($a) === Number($b))) |
a Python bool IS an int (True == 1), but JS true === 1 is false. Number() on both sides restores Python's answer. |
a != b |
float |
bool |
bool |
(!(Number($a) === Number($b))) |
a Python bool IS an int (True == 1), but JS true === 1 is false. Number() on both sides restores Python's answer. |
a != b |
float |
int |
bool |
(!(Number($a) === Number($b))) |
a Python bool IS an int (True == 1), but JS true === 1 is false. Number() on both sides restores Python's answer. |
a != b |
float |
float |
bool |
(!(Number($a) === Number($b))) |
a Python bool IS an int (True == 1), but JS true === 1 is false. Number() on both sides restores Python's answer. |
a != b |
str |
str |
bool |
(!($a === $b)) |
=== on two strings is value equality in both languages |
a != b |
none |
none |
bool |
(!($a === $b)) |
null === null |
a != b |
list[int] |
list[int] |
bool |
(!S.eq($a, $b)) |
Python compares lists/dicts/sets STRUCTURALLY; JS === is reference identity, so xs == [1, 2] was false in the browser and true in Python. S.eq is a deep compare. |
a != b |
list[str] |
list[str] |
bool |
(!S.eq($a, $b)) |
Python compares lists/dicts/sets STRUCTURALLY; JS === is reference identity, so xs == [1, 2] was false in the browser and true in Python. S.eq is a deep compare. |
a != b |
set[int] |
set[int] |
bool |
(!S.eq($a, $b)) |
Python compares lists/dicts/sets STRUCTURALLY; JS === is reference identity, so xs == [1, 2] was false in the browser and true in Python. S.eq is a deep compare. |
a != b |
dict[str, int] |
dict[str, int] |
bool |
(!S.eq($a, $b)) |
Python compares lists/dicts/sets STRUCTURALLY; JS === is reference identity, so xs == [1, 2] was false in the browser and true in Python. S.eq is a deep compare. |
a < b |
bool |
bool |
bool |
(Number($a) < Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a < b |
bool |
int |
bool |
(Number($a) < Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a < b |
bool |
float |
bool |
(Number($a) < Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a < b |
int |
bool |
bool |
(Number($a) < Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a < b |
int |
int |
bool |
(Number($a) < Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a < b |
int |
float |
bool |
(Number($a) < Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a < b |
float |
bool |
bool |
(Number($a) < Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a < b |
float |
int |
bool |
(Number($a) < Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a < b |
float |
float |
bool |
(Number($a) < Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a < b |
str |
str |
bool |
($a < $b) |
JS compares strings lexicographically, as Python does |
a <= b |
bool |
bool |
bool |
(Number($a) <= Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a <= b |
bool |
int |
bool |
(Number($a) <= Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a <= b |
bool |
float |
bool |
(Number($a) <= Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a <= b |
int |
bool |
bool |
(Number($a) <= Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a <= b |
int |
int |
bool |
(Number($a) <= Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a <= b |
int |
float |
bool |
(Number($a) <= Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a <= b |
float |
bool |
bool |
(Number($a) <= Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a <= b |
float |
int |
bool |
(Number($a) <= Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a <= b |
float |
float |
bool |
(Number($a) <= Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a <= b |
str |
str |
bool |
($a <= $b) |
JS compares strings lexicographically, as Python does |
a > b |
bool |
bool |
bool |
(Number($a) > Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a > b |
bool |
int |
bool |
(Number($a) > Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a > b |
bool |
float |
bool |
(Number($a) > Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a > b |
int |
bool |
bool |
(Number($a) > Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a > b |
int |
int |
bool |
(Number($a) > Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a > b |
int |
float |
bool |
(Number($a) > Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a > b |
float |
bool |
bool |
(Number($a) > Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a > b |
float |
int |
bool |
(Number($a) > Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a > b |
float |
float |
bool |
(Number($a) > Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a > b |
str |
str |
bool |
($a > $b) |
JS compares strings lexicographically, as Python does |
a >= b |
bool |
bool |
bool |
(Number($a) >= Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a >= b |
bool |
int |
bool |
(Number($a) >= Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a >= b |
bool |
float |
bool |
(Number($a) >= Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a >= b |
int |
bool |
bool |
(Number($a) >= Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a >= b |
int |
int |
bool |
(Number($a) >= Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a >= b |
int |
float |
bool |
(Number($a) >= Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a >= b |
float |
bool |
bool |
(Number($a) >= Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a >= b |
float |
int |
bool |
(Number($a) >= Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a >= b |
float |
float |
bool |
(Number($a) >= Number($b)) |
both numeric; Number() normalizes a bool (Python's True > 0 is True) |
a >= b |
str |
str |
bool |
($a >= $b) |
JS compares strings lexicographically, as Python does |
Membership¶
| Construct | Left | Right | Result | Lowers to | Why it is faithful |
|---|---|---|---|---|---|
a in b |
bool |
list[int] |
bool |
($b).some((v) => Number(v) === Number($a)) |
.includes / Set.has use ===, so True in [1, 2] was false in JS and true in Python. Comparing through Number() restores Python's bool-is-an-int rule. |
a in b |
bool |
set[int] |
bool |
[...$b].some((v) => Number(v) === Number($a)) |
.includes / Set.has use ===, so True in [1, 2] was false in JS and true in Python. Comparing through Number() restores Python's bool-is-an-int rule. |
a in b |
int |
list[int] |
bool |
($b).some((v) => Number(v) === Number($a)) |
.includes / Set.has use ===, so True in [1, 2] was false in JS and true in Python. Comparing through Number() restores Python's bool-is-an-int rule. |
a in b |
int |
set[int] |
bool |
[...$b].some((v) => Number(v) === Number($a)) |
.includes / Set.has use ===, so True in [1, 2] was false in JS and true in Python. Comparing through Number() restores Python's bool-is-an-int rule. |
a in b |
float |
list[int] |
bool |
($b).some((v) => Number(v) === Number($a)) |
.includes / Set.has use ===, so True in [1, 2] was false in JS and true in Python. Comparing through Number() restores Python's bool-is-an-int rule. |
a in b |
float |
set[int] |
bool |
[...$b].some((v) => Number(v) === Number($a)) |
.includes / Set.has use ===, so True in [1, 2] was false in JS and true in Python. Comparing through Number() restores Python's bool-is-an-int rule. |
a in b |
str |
str |
bool |
($b).includes($a) |
substring containment, as in Python |
a in b |
str |
list[str] |
bool |
($b).includes($a) |
same-typed non-numeric elements: === is value equality |
a in b |
str |
dict[str, int] |
bool |
Object.hasOwn($b, $a) |
Python's in on a dict tests KEYS. .includes searches values, which is why the untyped emitter had to ban this outright; with types it is one call. |
Unary operators¶
| Construct | Left | Right | Result | Lowers to | Why it is faithful |
|---|---|---|---|---|---|
-x |
bool |
int |
(-$a) |
identical, but a bool operand yields an int as in Python | |
-x |
int |
int |
(-$a) |
identical, but a bool operand yields an int as in Python | |
-x |
float |
float |
(-$a) |
identical, but a bool operand yields an int as in Python | |
+x |
bool |
int |
(+$a) |
identical, but a bool operand yields an int as in Python | |
+x |
int |
int |
(+$a) |
identical, but a bool operand yields an int as in Python | |
+x |
float |
float |
(+$a) |
identical, but a bool operand yields an int as in Python | |
not x |
bool |
bool |
(!S.truthy($a)) |
Python and JS disagree on truthiness: [] and {} are FALSY in Python and TRUTHY in JS, so not xs (xs == []) was True in Python and false in the browser. S.truthy implements Python's rule (0, '', [], {}, set(), None are falsy). Every not / and / or / ternary / :show condition goes through it. |
|
not x |
int |
bool |
(!S.truthy($a)) |
Python and JS disagree on truthiness: [] and {} are FALSY in Python and TRUTHY in JS, so not xs (xs == []) was True in Python and false in the browser. S.truthy implements Python's rule (0, '', [], {}, set(), None are falsy). Every not / and / or / ternary / :show condition goes through it. |
|
not x |
float |
bool |
(!S.truthy($a)) |
Python and JS disagree on truthiness: [] and {} are FALSY in Python and TRUTHY in JS, so not xs (xs == []) was True in Python and false in the browser. S.truthy implements Python's rule (0, '', [], {}, set(), None are falsy). Every not / and / or / ternary / :show condition goes through it. |
|
not x |
str |
bool |
(!S.truthy($a)) |
Python and JS disagree on truthiness: [] and {} are FALSY in Python and TRUTHY in JS, so not xs (xs == []) was True in Python and false in the browser. S.truthy implements Python's rule (0, '', [], {}, set(), None are falsy). Every not / and / or / ternary / :show condition goes through it. |
|
not x |
none |
bool |
(!S.truthy($a)) |
Python and JS disagree on truthiness: [] and {} are FALSY in Python and TRUTHY in JS, so not xs (xs == []) was True in Python and false in the browser. S.truthy implements Python's rule (0, '', [], {}, set(), None are falsy). Every not / and / or / ternary / :show condition goes through it. |
|
not x |
list[int] |
bool |
(!S.truthy($a)) |
Python and JS disagree on truthiness: [] and {} are FALSY in Python and TRUTHY in JS, so not xs (xs == []) was True in Python and false in the browser. S.truthy implements Python's rule (0, '', [], {}, set(), None are falsy). Every not / and / or / ternary / :show condition goes through it. |
|
not x |
list[str] |
bool |
(!S.truthy($a)) |
Python and JS disagree on truthiness: [] and {} are FALSY in Python and TRUTHY in JS, so not xs (xs == []) was True in Python and false in the browser. S.truthy implements Python's rule (0, '', [], {}, set(), None are falsy). Every not / and / or / ternary / :show condition goes through it. |
|
not x |
set[int] |
bool |
(!S.truthy($a)) |
Python and JS disagree on truthiness: [] and {} are FALSY in Python and TRUTHY in JS, so not xs (xs == []) was True in Python and false in the browser. S.truthy implements Python's rule (0, '', [], {}, set(), None are falsy). Every not / and / or / ternary / :show condition goes through it. |
|
not x |
dict[str, int] |
bool |
(!S.truthy($a)) |
Python and JS disagree on truthiness: [] and {} are FALSY in Python and TRUTHY in JS, so not xs (xs == []) was True in Python and false in the browser. S.truthy implements Python's rule (0, '', [], {}, set(), None are falsy). Every not / and / or / ternary / :show condition goes through it. |
Subscript¶
| Construct | Left | Right | Result | Lowers to | Why it is faithful |
|---|---|---|---|---|---|
a[b] |
str |
int |
str |
S.idx($a, $b) |
Python indexes from the end for a negative index (xs[-1]) -- JS [-1] is undefined, which is why the untyped emitter had to ban it. S.idx uses .at() AND raises out of range, because Python raises IndexError where .at() would return undefined with no error. A lowering that swallows a Python exception is not a faithful lowering. |
a[b] |
list[int] |
int |
int |
S.idx($a, $b) |
Python indexes from the end for a negative index (xs[-1]) -- JS [-1] is undefined, which is why the untyped emitter had to ban it. S.idx uses .at() AND raises out of range, because Python raises IndexError where .at() would return undefined with no error. A lowering that swallows a Python exception is not a faithful lowering. |
a[b] |
list[str] |
int |
str |
S.idx($a, $b) |
Python indexes from the end for a negative index (xs[-1]) -- JS [-1] is undefined, which is why the untyped emitter had to ban it. S.idx uses .at() AND raises out of range, because Python raises IndexError where .at() would return undefined with no error. A lowering that swallows a Python exception is not a faithful lowering. |
a[b] |
dict[str, int] |
str |
int |
S.key($a, $b) |
Python raises KeyError on a missing key; a bare JS property read returns undefined. |
Whitelisted calls¶
| Construct | Left | Right | Result | Lowers to | Why it is faithful |
|---|---|---|---|---|---|
len(str) |
int |
($a).length |
same count; len() is always an int | ||
len(list[int]) |
int |
($a).length |
same count; len() is always an int | ||
len(set[int]) |
int |
($a).size |
a JS Set exposes .size, not .length | ||
len(dict[str, int]) |
int |
Object.keys($a).length |
Python's len(dict) counts keys | ||
abs(int) |
int |
Math.abs($a) |
identical | ||
abs(float) |
float |
Math.abs($a) |
identical | ||
round(float) |
int |
S.round($a) |
Python rounds HALF TO EVEN (round(2.5) == 2, round(3.5) == 4); JS Math.round rounds half up (2.5 -> 3). S.round implements banker's rounding. | ||
round(float, int) |
num |
S.round($a, $b) |
as above; the precision must be an int | ||
min(int, int) |
int |
(($a) <= ($b) ? ($a) : ($b)) |
Python's min/max return the OPERAND, keeping its type (min(True, 2) is True); Math.min would coerce it to a number. A ternary preserves the value itself. | ||
max(float, float) |
float |
(($a) >= ($b) ? ($a) : ($b)) |
Python's min/max return the OPERAND, keeping its type (min(True, 2) is True); Math.min would coerce it to a number. A ternary preserves the value itself. | ||
sum(list[int]) |
int |
($a).reduce((x, y) => x + y, 0) |
Python's sum() starts at the int 0, as does this | ||
str(int) |
str |
S.str($a) |
Python's str() of a list/dict/bool is not JS String() |
String methods¶
| Construct | Left | Right | Result | Lowers to | Why it is faithful |
|---|---|---|---|---|---|
s.endswith(…) |
str |
bool |
($a).endsWith($b) |
identical (no position argument) | |
s.lower() |
str |
str |
($a).toLowerCase() |
identical for ASCII; both use Unicode simple case | |
s.startswith(…) |
str |
bool |
($a).startsWith($b) |
identical (no position argument) | |
s.strip() |
str |
str |
($a).trim() |
Python's argument-less strip trims whitespace | |
s.upper() |
str |
str |
($a).toUpperCase() |
as above |
180 rules.