Dragon

Operators and Symbols

This appendix is the authoritative reference for every operator and symbol Dragon recognizes. Each group below is a GFM table of the form Operator | Meaning | Example, and a single precedence table near the end fixes how they bind. Where Dragon's behavior differs from the Python you might expect - chiefly list + and the set algebra operators

  • that is called out honestly with the working alternative.

Operators are grouped by category. The categories follow the source order in which Dragon's parser climbs from loosest- to tightest-binding, so reading top to bottom is also a tour of precedence from low to high.

Arithmetic

Standard arithmetic on int and float. / is true division and always yields a float; // is floor division and % is the remainder. ** is exponentiation and binds tighter than unary minus on its left but looser on its right (-2 ** 2 is -(2 ** 2)).

OperatorMeaningExample
+addition7 + 3 is 10
-subtraction / unary negation7 - 3 is 4; -x
*multiplication7 * 3 is 21
/true division (always float)7 / 2 is 3.5
//floor division7 // 3 is 2
%remainder (modulo)7 % 3 is 1
**exponentiation7 ** 3 is 343

+ and * are also defined on str: "foo" + "bar" is "foobar" and "ab" * 3 is "ababab". List repetition with * works too - [0] * 3 is [0, 0, 0]. See Strings and Collections.

Comparison

All six comparisons return a bool. Dragon supports chained comparisons exactly as Python does: 1 < a < 10 is evaluated as 1 < a and a < 10, with a read once.

OperatorMeaningExample
==equal5 == 8 is False
!=not equal5 != 8 is True
<less than5 < 8 is True
>greater than5 > 8 is False
<=less than or equal5 <= 5 is True
>=greater than or equal8 >= 5 is True

Logical

Short-circuiting boolean operators. not is a prefix unary operator and binds looser than comparison, so not a == b parses as not (a == b).

OperatorMeaningExample
andlogical and (short-circuit)True and False is False
orlogical or (short-circuit)True or False is True
notlogical negationnot True is False

Bitwise

Bitwise operators act on int operands. ~ is the prefix one's complement; the rest are infix.

OperatorMeaningExample
&bitwise and12 & 10 is 8
`\`bitwise or`12 \10 is 14`
^bitwise xor12 ^ 10 is 6
~bitwise not (one's complement)~12 is -13
<<left shift12 << 2 is 48
>>right shift12 >> 1 is 6

Membership and identity

Membership (in, not in) tests by value against any container. Identity (is, is not) is primarily used to test against None.

OperatorMeaningExample
inmember of a container2 in [1, 2, 3] is True
not innot a member5 not in [1, 2, 3] is True
isidentity testn is None
is notnegated identityn is not None

Assignment and augmented assignment

= is reassignment - the name on the left must already be declared in scope (x: T = ... introduces it; bare x = v updates it). Each augmented form is x op= y shorthand for x = x op y, including the float case y /= 4.0. See Variables for the declare-with-: / assign-with-= rule.

OperatorMeaningExample
=assign (to an already-declared name)x = 5
+=add and assignx += 5
-=subtract and assignx -= 2
*=multiply and assignx *= 3
/=true-divide and assigny /= 4.0
//=floor-divide and assignx //= 4
%=modulo and assignx %= 5
**=exponentiate and assignx **= 2
&=bitwise-and and assignx &= 6
`\=`bitwise-or and assign`x \= 1`
^=bitwise-xor and assignx ^= 3
<<=left-shift and assignx <<= 1
>>=right-shift and assignx >>= 1

Structural operators and symbols

These are the operators that build expressions and bindings rather than compute values.

OperatorMeaningExample
:=walrus - assign within an expressionif (n := len(xs)) > 3 { ... }
->return-type arrow in a defdef add(x: int, y: int) -> int { ... }
x if c else yternary conditionalm: int = a if a > b else b
.attribute / method accessuser.name, xs.append(4)
[i]subscript (index)xs[1], s[0]
[a:b]slice (stop exclusive)s[0:5], xs[1:3]
[a:b:c]slice with steps[::2]

The walrus := is one of Dragon's implicit binding forms: it declares n with an inferred type, so it needs no annotation. Slices accept any of the three components independently - xs[1:], xs[:3], and xs[::2] are all valid.

Template sigils

Inside a template { ... } block the braces are literal text; two sigils punch into Dragon code and back. !{ } breaks out of content into a Dragon expression, and :{ } breaks back into content from inside that code. They are not general-purpose operators - they exist only within a template block. See Templates.

SigilMeaningExample
!{ expr }interpolate a Dragon expression into contenttemplate {Hello !{name}}
:{ content }return to content mode from inside !{ } code!{ for x in xs :{ <li>!{x}</li> } }

Operator precedence

From loosest binding (evaluated last) to tightest binding (evaluated first). Operators on the same row share a precedence level. Except for **, all binary operators are left-associative; ** is right-associative, and the unary prefixes (-, +, ~, not) are right-associative.

PrecedenceOperatorsAssociativity
1 (loosest)x if c else y (ternary)right
2orleft
3andleft
4not x (prefix)right
5== != < > <= >= in not in is is notchained
6`\` (bitwise or)left
7^ (bitwise xor)left
8& (bitwise and)left
9<< >>left
10+ - (binary)left
11* / // %left
12- + ~ (unary prefix)right
13 (tightest)**right
14call () , subscript [], attribute .left

The walrus := and the assignment operators (=, +=, ...) are statement-level, not part of the expression precedence ladder; a walrus must be parenthesized to appear inside a larger expression.

Known gaps

Two operators that Python defines are not yet wired in Dragon. They fail at compile time with a DRAGON SCALE ERROR, and each has a working method-based alternative.

WantedStatusUse instead
list + list (concatenation)not wired - unsupported operand types for +: 'list[int]' and 'list[int]'a.extend(b)
`set \set` (union)not wireda.union(b)
set & set (intersection)not wireda.intersection(b)
set - set (difference)not wireda.difference(b)

The set operators report their operands as list[int] in the error message because Dragon's type system currently represents a set as a list internally; the method forms (.union, .intersection, .difference) are fully supported and return a proper set:

a: list[int] = [1, 2]
a.extend([3, 4])           # [1, 2, 3, 4]  - not  a + [3, 4]

s1: set[int] = {1, 2, 3}
s2: set[int] = {3, 4, 5}
print(s1.union(s2))        # {1, 2, 3, 4, 5}
print(s1.intersection(s2)) # {3}
print(s1.difference(s2))   # {1, 2}

See Collections for the full set of list, set, and dict methods, and Appendix A: Keywords for the word-operators (and, or, not, in, is) as reserved keywords.