# Operators Operators are functions that perform actions on their operands. They provide concise syntax for common operations like arithmetic, comparison, logical operations, and assignment. ## Operator Formats Verse operators come in three formats based on their position relative to their operands: **Prefix Operators** Prefix operators appear before their single operand: - `not Expression` - Logical negation - `-Value` - Numeric negation - `+Value` - Numeric positive (for alignment) **Infix Operators** Infix operators appear between their two operands: - `A + B` - Addition - `A * B` - Multiplication - `A = B` - Equality comparison - `A and B` - Logical AND **Postfix Operators** Postfix operators bind to the expression on their left. While some (like `.`) appear between two elements, they're classified as postfix because they operate on the left-hand expression: - `Value?` - Query operator for logic values - `Object.Member` - Member access (the `.` operates on the object to its left) - `Array[Index]` - Array indexing (the `[]` operates on the array to its left) - `Function()` - Function call (the `()` operates on the function to its left) - `Constructor{}` - Object construction (the `{}` operates on the type to its left) Although `.` appears *between* `Player` and `Respawn` in `Player.Respawn()`, it's considered postfix because it binds to `Player` and selects a member from it. The right side (`Respawn`) is not a separate operand but a member selector ## Precedence When multiple operators appear in the same expression, they are evaluated according to their precedence level. Higher precedence operators are evaluated first. Operators with the same precedence are evaluated left to right (except for assignment and unary operators which are right-associative). The precedence levels from highest to lowest are: | Precedence | Operators | Category | Format | Associativity | Example | |------------|-----------|----------|--------|---------------|--| | 11 | `.`, `[]`, `()`, `{}`, `?` (postfix) | Member access, Indexing, Call, Construction, Query | Postfix | Left | `BossDefeated?`, `Player.Respawn()`| | 10 | `+`, `-` (unary), `not` | Unary operations | Prefix | Right | `+Score`, `-Distance`, `not HasCooldown?` | | 9 | `*`, `/` | Multiplication, Division | Infix | Left | `Score * Multiplier` | | 8 | `+`, `-` (binary) | Addition, Subtraction | Infix | Left | `X + Y`, `Health - Damage` | | 7 | `=` (relational), `<>`, `<`, `<=`, `>`, `>=` | Relational comparison | Infix | Right | `Player <> Target`, `Score > 100` | | 5 | `and` | Logical AND | Infix | Left | `HasPotion? and TryUsePotion[]` | | 4 | `or` | Logical OR | Infix | Left | `IsAlive? or Respawn()` | | 3 | `..` | Range | Infix | Left | `0..100`, `-15..50` | | 2 | ~~Lambda expressions~~ | ~~Function literals~~ (not yet supported) | Special | N/A | N/A | | 1 | `:=`, `set =` | Assignment | Infix | Right | `X := 15`, `set Y = 25` | The `=` symbol serves two distinct purposes in Verse: - **Relational comparison** (precedence 7): When used as an operator in expressions, `A = B` tests equality and returns a logic value - **Assignment** (precedence 1): When used with the `set` keyword, `set X = Value` assigns a new value to an existing variable This is different from `:=`, which always means "define and initialize" for new variables. The context determines which meaning of `=` applies. ## Arithmetic Operators Arithmetic operators perform mathematical operations on numeric values. They work with both `int` and `float` types, with some special behaviors for type conversion and integer division. ### Basic Arithmetic | Operator | Operation | Types | Notes | |----------|-----------|-------|-------| | `+` | Addition | `int`, `float` | Also concatenates strings and arrays | | `-` | Subtraction | `int`, `float` | Can be used as unary negation | | `*` | Multiplication | `int`, `float` | Converts `int` to `float` when mixed | | `/` | Division | `int` (failable), `float` | Integer division returns `rational` | ```verse # Basic arithmetic Sum := 10 + 20 # 30 Diff := 50 - 15 # 35 Prod := 6 * 7 # 42 Quot := 20.0 / 4.0 # 5.0 # Unary operators Negative := -42 # -42 Positive := +42 # 42 (for alignment) # Integer division (failable, returns rational) if (Result := 10 / 3): IntResult := Floor(Result) # 3 # Type conversion through multiplication IntValue:int = 42 FloatValue:float = IntValue * 1.0 # Converts to 42.0 ``` ### Compound Assignments Compound assignment operators combine an arithmetic operation with assignment: | Operator | Equivalent To | Types | |----------|---------------|-------| | `set +=` | `set X = X + Y` | `int`, `float`, `string`, `array` | | `set -=` | `set X = X - Y` | `int`, `float` | | `set *=` | `set X = X * Y` | `int`, `float` | | `set /=` | `set X = X / Y` | `float` only | ```verse var Score:int = 100 set Score += 50 # Score is now 150 set Score -= 25 # Score is now 125 set Score *= 2 # Score is now 250 var Health:float = 100.0 set Health /= 2.0 # Health is now 50.0 # Arrays can use += with both arrays and tuples var Items:[]int = array{1, 2, 3} set Items += array{4, 5} # Items is now array{1, 2, 3, 4, 5} set Items += (6, 7) # Items is now array{1, 2, 3, 4, 5, 6, 7} # Note: set /= doesn't work with integers due to failable division # var IntValue:int = 10 # set IntValue /= 2 # Compile error! ``` ## Comparison Operators Comparison operators test relationships between values and are failable expressions that succeed or fail based on the comparison result. ### Relational Operators | Operator | Meaning | Supported Types | Example | |----------|---------|-----------------|---------| | `<` | Less than | `int`, `float` | `Score < 100` | | `<=` | Less than or equal | `int`, `float` | `Health <= 0.0` | | `>` | Greater than | `int`, `float` | `Level > 5` | | `>=` | Greater than or equal | `int`, `float` | `Time >= MaxTime` | ### Equality Operators | Operator | Meaning | Supported Types | Example | |----------|---------|-----------------|---------| | `=` | Equal to | All comparable types | `Name = "Player1"` | | `<>` | Not equal | All comparable types | `State <> idle` | ```verse # Numeric comparisons if (Score > HighScore): Print("New high score!") if (Health <= 0.0): HandlePlayerDeath() # Example with other comparable types if (PlayerName = "Admin"): EnableAdminMode() if (CurrentState <> game_state.Playing): ShowMenu() # Comparison in complex expressions if (Level >= 10 and Score > 1000): UnlockAchievement() ``` The following types support equality comparison operations (`=` and `<>`): - Numeric types: `int`, `float`, `rational` - Boolean: `logic` - Text: `string`, `char`, `char32` - Enumerations: All `enum` types - Collections: `array`, `map`, `tuple`, `option` (if elements are comparable) - Structs: If all fields are comparable - Unique classes: Classes marked with `` (identity equality only) Comparisons between different types generally fail: ```verse 0 = 0.0 # Fails: int vs float "5" = 5 # Fails: string vs int ``` ## Logical Operators Logical operators work with failable expressions and control the flow of success and failure. ### Query Operator (`?`) The query operator checks if a `logic` value is `true` (see [Failure](08_failure.md#failable-expressions) for how `?` works with other types): ```verse var IsReady:logic = true if (IsReady?): StartGame() # Equivalent to: if (IsReady = true): StartGame() ``` ### Not Operator The `not` operator negates the success or failure of an expression: ```verse if (not IsGameOver?): ContinuePlaying() # Effects are not committed with not var X:int = 0 if (not (set X = 5, IsGameOver?)): # X is still 0 here, even though the assignment "tried" to happen Print("X is {X}") # Prints "X is 0" ``` ### And Operator The `and` operator succeeds only if both operands succeed: ```verse Player:player = player{Level:=10, HasItem:=option{1}} if (HasKey? and DoorUnlocked?): EnterRoom() # Short-circuit evaluation - second operand not evaluated if first fails if (QuickCheck[] and ExpensiveCheck[]): ProcessResult() ``` ### Or Operator The `or` operator succeeds if at least one operand succeeds: ```verse if (HasKeyCard? or HasMasterKey?): OpenDoor() # Short-circuit evaluation - second operand not evaluated if first succeeds if (QuickCheck[] or ExpensiveCheck[]): ProcessResult() ``` ### Truth Table Consider two expressions `P` and `Q` which may either succeed or fail, the following table shows the result of logical operators applied to them: | Expression P | Expression Q | P and Q | P or Q | not P | |--------------|--------------|---------|---------|-------| | Succeeds | Succeeds | Succeeds (Q's value) | Succeeds (P's value) | Fails | | Succeeds | Fails | Fails | Succeeds (P's value) | Fails | | Fails | Succeeds | Fails | Succeeds (Q's value) | Succeeds | | Fails | Fails | Fails | Fails | Succeeds | ## Assignment and Initialization When initializing constants and variables, both `=` and `:=` can be used if an explicit type is provided. For type inference (no type annotation), you must use `:=`. ```verse # Constant initialization with explicit types - both = and := work MaxHealth:int = 100 PlayerName:string := "Hero" # Variable initialization with explicit types - both = and := work var CurrentHealth:int = 100 var Score:int := 0 # Type inference requires := (no type annotation) AutoTyped := 42 # Inferred as int # Note: var requires explicit type - var X := value is not allowed ``` The `set =` operator updates variable values: ```verse var Points:int = 0 set Points = 100 var Position:vector3 = vector3{X := 0.0, Y := 0.0, Z := 0.0} set Position = vector3{X := 10.0, Y := 20.0, Z := 0.0} ``` ## Special Operators ### Indexing The square bracket operator is used for multiple purposes in Verse: 1. **Array/Map indexing** - Access elements in collections 2. **Function calls** - Call functions which may fail ```verse # Array indexing (failable) MyArray := array{10, 20, 30} if (Element := MyArray[1]): Print("Element at index 1: {Element}") # Prints 20 # Map lookup (failable) Scores:[string]int = map{"Alice" => 100, "Bob" => 85} if (AliceScore := Scores["Alice"]): Print("Alice's score: {AliceScore}") # String indexing (failable) Name:string = "Verse" if (FirstChar := Name[0]): Print("First character: {FirstChar}") # Prints 'V' # Function call that can fail Result1 := MyFunction1[Arg1, Arg2] # Can fail Result2 := MyFunction2[?X:=Arg1, ?Y:=Arg2] # Named arguments EmptyCall := MyFunction2[] # and optional values ``` ### Member Access The dot operator accesses fields and methods of objects: ```verse Player.Health Player.GetName() MyVector.X Config.Settings.MaxPlayers ``` ### Range The range operator creates ranges for iteration: ```verse # Inclusive range for (I := 0..4): Print("{I}") # Prints 0, 1, 2, 3, 4 ``` ### Object Construction Verse provides multiple syntaxes for constructing objects. All of the following are equivalent: ```verse # Curly braces with commas Point1 := point{X:= 10, Y:= 20} # Curly braces with semicolons Point2 := point{X:= 10; Y:= 20} # Colon syntax with newlines (no braces) Point3 := point: X:= 10 Y:= 20 # Colon syntax with commas and newlines Point4 := point: X:= 10, Y:= 20 # Fields can be separated by newlines inside braces Player := player_data { Name := "Hero" Level := 5 Health := 100.0 } # Trailing commas are not allowed Config := game_config{ MaxPlayers := 100, EnablePvP := true # , -- comma not allowed here } # Dot syntax for single field (requires defaults for other fields) Point5 := point . X:=10 # Y gets default value 0 Point6 := point . Y:=20 # X gets default value 0 ``` ### Tuple Access Round braces when used with a single argument after a tuple expression, accesses tuple elements: ```verse MyTuple := (10, 20, 30) FirstElement := MyTuple(0) # Access first element SecondElement := MyTuple(1) # Access second element ``` ## Type Conversions Verse has limited implicit type conversion. Most conversions must be explicit: ```verse # No implicit int to float conversion MyInt:int = 42 # MyFloat:float = MyInt # Error! MyFloat:float = MyInt * 1.0 # OK: explicit conversion # No implicit numeric to string conversion Score:int = 100 # Message:string = "Score: " + Score # Error! Message:string = "Score: {Score}" # OK: string interpolation ``` When operators work with mixed types, specific rules apply: ```verse # int * float -> float Result := 5 * 2.0 # Result is 10.0 (float) # Comparisons must be same type if (5 = 5): # OK if (5.0 = 5.0): # OK # if (5 = 5.0): # Fails ```