rustleaf 0.1.0

A simple programming language interpreter written in Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
# 3. Types

RustLeaf is a dynamically typed language where types are determined and checked at runtime. This chapter defines the type system, including primitive types, composite types, and the rules governing type conversions and type checking.

### 3.1. Type System Overview

RustLeaf employs dynamic typing with strong type checking at runtime. Variables can hold values of any type, and the type of a value is determined when it is created.

**Core Type System Properties:**
- **Dynamic Typing**: Variables do not have declared types; they acquire the type of their assigned value
- **Strong Typing**: Operations check types at runtime and raise errors for invalid type combinations
- **No Implicit Conversions**: Values are never automatically converted between types (except for string interpolation and display)
- **Runtime Type Information**: Full type information is available at runtime via the `type()` function

**Type Categories:**
1. **Primitive Types**: null, unit, bool, int, float, string
2. **Composite Types**: list, dict, object (class instances)
3. **Callable Types**: function (including closures and methods)
4. **Extension Types**: RustValue (Rust-implemented types)

**Type Identity:**
- Each value has exactly one type
- Types are identified by their type name (a string)
- Type names are compared using string equality

**Example:**
```
var x = 42          // x holds an int
var t = type(x)     // t is "int"
x = "hello"         // x now holds a string
print(type(x))      // "string"

fn void_fn() { print("side effect") }
var result = void_fn()  // result holds unit
print(type(result)) // "unit"

// Type checking
if type(value) == "int" {
    // value is definitely an integer
}
```

### 3.1.1. Functions vs Methods

RustLeaf distinguishes between functions and methods based on how they are called:

**Functions**: Called directly by name, not bound to an instance
- `type(value)` - returns the type name
- `print("hello")` - outputs to console  
- `str(42)` - converts to string

**Methods**: Called on an instance using dot notation
- `"hello".upper()` - converts string to uppercase
- `[1, 2].append(3)` - adds element to list
- `point.distance()` - calls method on custom object

**Bound Methods**: Methods accessed without parentheses, creating callable objects
- `"hello".upper` - creates bound method (callable function object)
- `[1, 2].append` - creates bound method that can be called later
- `point.distance` - creates bound method preserving the `self` reference

Both functions and methods can be either builtin (provided by the language) or user-defined. Bound methods provide a way to create first-class callable objects from methods.

### 3.2. Primitive Types

Primitive types are the fundamental building blocks of the type system. They are immutable (except for their container in variables) and have value semantics.

#### 3.2.1. Null Type

The null type has exactly one value: `null`.

**Properties:**
- Type name: `"null"`
- Represents the absence of a value
- Used as a default return value when no explicit value is provided
- Truthiness: `null` is falsy in boolean contexts

**Operations:**
- Equality: `null == null` is `true`
- Type check: `type(null) == "null"`
- No other operations are valid on null

**Example:**
```
var x = null
print(type(x))      // "null"
if not x {          // null is falsy
    print("x is null")
}
```

#### 3.2.2. Unit Type

The unit type represents the absence of a meaningful return value.

**Properties:**
- Type name: `"unit"`
- Single value that cannot be written as a literal
- Returned by functions that end with a statement rather than an expression
- Returned by `return` statements without an expression
- Returned by expressions without meaningful values (empty blocks, unmatched conditionals, etc.)
- Used as sentinel value in iterator protocol
- Does not participate in boolean contexts (prevents iterator misuse)

**Operations:**
- Equality: unit values are equal to each other
- Type check: `type(unit_value) == "unit"`
- Unit test: `is_unit(value)` built-in function
- Boolean context: Using unit in `if`, `while`, `and`, `or` is a type error (use `is_unit()` to test unit values)

**Examples:**
```
// Function ending with statement returns unit
fn void_function() {
    print("side effect")  // Statement - executed for side effect
    // No final expression, so returns unit
}

// Function ending with expression returns that value
fn value_function() {
    print("side effect")  // Statement - executed for side effect
    42                    // Expression - this is the return value
}

var result1 = void_function()   // result1 is unit
var result2 = value_function()  // result2 is 42

print(type(result1))    // "unit"
print(type(result2))    // "int"
print(is_unit(result1)) // true
print(is_unit(result2)) // false

// Type error: unit cannot be used in boolean contexts
if result1 {            // Error: unit type not allowed in boolean context
    print("never reached")
}

// Correct iterator usage
var next_value = iterator.op_next()
if is_unit(next_value) {  // Correct: explicitly check for unit
    print("Iterator exhausted")
} else {
    process(next_value)
}

// Iterator protocol
fn op_next() {
    if self.index >= self.items.len() {
        return  // Returns unit (iteration complete)
    }
    var value = self.items[self.index];
    self.index += 1;
    value
}
```

#### 3.2.3. Boolean Type  

The boolean type represents logical truth values.

**Properties:**
- Type name: `"bool"`
- Exactly two values: `true` and `false`
- Result type of comparison and logical operations
- Truthiness: `false` is falsy, `true` is truthy

**Operations:**
- Logical: `and`, `or`, `not`
- Equality: `==`, `!=`
- Type check: `type(true) == "bool"`

**Example:**
```
var a = true
var b = false
print(a and b)      // false
print(a or b)       // true
print(not a)        // false
```

#### 3.2.3. Numeric Types

RustLeaf has two numeric types: integers and floating-point numbers.

**Integer Type (int):**
- Type name: `"int"`
- 64-bit signed integers (-2^63 to 2^63-1)
- Overflow/underflow raises a runtime error
- No implicit conversion to float

**Floating-Point Type (float):**
- Type name: `"float"`
- IEEE 754 double-precision (64-bit)
- Supports special values: Infinity, -Infinity, NaN
- NaN propagates through operations
- No implicit conversion to int

**Numeric Operations:**
```
// Arithmetic (int and float)
+   // Addition
-   // Subtraction/negation  
*   // Multiplication
/   // Division (int division truncates, float division is exact)
%   // Modulo
**  // Exponentiation

// Bitwise (int only)
&   // Bitwise AND
|   // Bitwise OR
^   // Bitwise XOR
~   // Bitwise NOT
<<  // Left shift
>>  // Right shift

// Comparison (int and float)
<   // Less than
>   // Greater than
<=  // Less than or equal
>=  // Greater than or equal
==  // Equal
!=  // Not equal
```

**Overflow Behavior:**
```
var max_int = 9223372036854775807
var overflow = max_int + 1  // Error: Integer overflow

var min_int = -9223372036854775808  
var underflow = min_int - 1  // Error: Integer underflow
```

**Example:**
```
// Integers
var x = 42
var y = 13
print(x + y)        // 55
print(x / y)        // 3 (integer division)
print(x % y)        // 3 (remainder)

// Floats
var a = 3.14
var b = 2.0
print(a / b)        // 1.57
print(type(a))      // "float"

// NaN and Infinity
var inf = 1.0 / 0.0   // Infinity
var nan = 0.0 / 0.0   // NaN
print(inf > 1000000)  // true
print(nan == nan)     // false (NaN != NaN)
```

#### 3.2.4. String Type

Strings are immutable sequences of Unicode characters.

**Properties:**
- Type name: `"string"`
- UTF-8 encoded internally
- Immutable (operations return new strings)
- Support for string interpolation
- Implements iterator protocol (Section 12.5)

**String Operations:**
- Concatenation: `+` operator
- Repetition: `*` operator with integer
- Comparison: lexicographic ordering
- Indexing: `str[index]` returns single-character string
- Slicing: `str[start:end]` returns substring
- Length: `str.len()` returns character count

**String Methods:**
- `split(delimiter)` - Split into list of strings
- `trim()` - Remove leading/trailing whitespace
- `upper()` - Convert to uppercase
- `lower()` - Convert to lowercase
- `replace(old, new)` - Replace all occurrences
- `contains(substring)` - Test for substring presence
- `starts_with(prefix)` - Test prefix
- `ends_with(suffix)` - Test suffix
- `is_empty()` - Test if length is 0

**Example:**
```
var s = "Hello"
var t = "World"
var combined = s + ", " + t + "!"  // "Hello, World!"

// String interpolation
var name = "Alice"
var age = 30
var message = "My name is ${name} and I am ${age} years old"

// String methods
var text = "  Hello, World!  "
print(text.trim())          // "Hello, World!"
print(text.upper())         // "  HELLO, WORLD!  "
print(text.contains("World")) // true
```

### 3.3. Composite Types

Composite types are mutable containers that can hold multiple values.

#### 3.3.1. List Type

Lists are ordered, mutable sequences that can contain values of any type.

**Properties:**
- Type name: `"list"`
- Zero-indexed
- Heterogeneous (can mix types)
- Mutable (can be modified in place)
- Dynamic size
- Implements iterator protocol (Section 12.5)

**List Operations:**
- Creation: `[expr1, expr2, ...]`
- Indexing: `list[index]` (negative indices count from end)
- Slicing: `list[start:end]`
- Length: `list.len()`
- Membership: `value in list`
- Concatenation: `list1 + list2` (returns new list)

**List Methods:**
- `append(value)` - Add to end
- `extend(other_list)` - Add all elements from other list
- `insert(index, value)` - Insert at position
- `pop(index?)` - Remove and return element (default: last)
- `remove(value)` - Remove first occurrence
- `clear()` - Remove all elements
- `sort()` - Sort in place
- `reverse()` - Reverse in place
- `map(function)` - Transform elements (returns new list)
- `filter(function)` - Select elements (returns new list)
- `reduce(function, initial?)` - Reduce to single value
- `is_empty()` - Test if length is 0

**Example:**
```
var nums = [1, 2, 3]
nums.append(4)              // [1, 2, 3, 4]
nums[0] = 10               // [10, 2, 3, 4]

var mixed = [1, "hello", true, [2, 3]]
print(mixed.len())         // 4
print(mixed[3][1])         // 3

// Functional methods
var doubled = nums.map(|x| x * 2)
var evens = nums.filter(|x| x % 2 == 0)
var sum = nums.reduce(|a, b| a + b, 0)
```

#### 3.3.2. Dict Type

Dictionaries are mutable mappings from keys to values with preserved insertion order.

**Properties:**
- Type name: `"dict"`
- Keys must be immutable types (string, int, float, bool, null)
- Values can be any type
- Preserves insertion order
- Mutable
- Implements iterator protocol (Section 12.5)

**Dict Operations:**
- Creation: `{key1: value1, key2: value2, ...}`
- Access: `dict[key]` (raises error if key not found)
- Assignment: `dict[key] = value`
- Membership: `key in dict`
- Length: `dict.len()`

**Dict Methods:**
- `get(key, default?)` - Get value or default if not found
- `set(key, value)` - Set key-value pair
- `pop(key, default?)` - Remove and return value
- `clear()` - Remove all entries
- `keys()` - Return list of keys
- `values()` - Return list of values  
- `items()` - Return list of [key, value] pairs
- `update(other_dict)` - Merge other dict into this one
- `has(key)` - Test if key exists
- `is_empty()` - Test if length is 0

**Example:**
```
var person = {
    "name": "Alice",
    "age": 30,
    "hobbies": ["reading", "coding"]
}

person["city"] = "New York"
print(person.get("age"))           // 30
print(person.get("phone", "N/A"))  // "N/A"

for key, value in person.items() {
    print("${key}: ${value}")
}
```

#### 3.3.3. Object Type

Objects are instances of user-defined classes.

**Properties:**
- Type name: The class name
- Contains fields (data) and methods (behavior)
- Mutable
- Each class creates a distinct type

**Object Creation:**
- Call class as constructor: `ClassName()`
- Fields initialize to default values or null
- No automatic constructor parameters

**Field Access:**
- Get: `object.field`
- Set: `object.field = value`
- Dynamic: `object[fieldname]` where fieldname is a string

**Method Calls:**
- `object.method(args...)`
- Methods receive implicit `self` parameter
- Static methods called on class: `ClassName.static_method(args...)`

**Example:**
```
class Point {
    var x = 0;
    var y = 0;
    
    fn distance() {
        (self.x ** 2 + self.y ** 2) ** 0.5
    }
}

var p = Point()
p.x = 3
p.y = 4
print(type(p))         // "Point"
print(p.distance())    // 5.0
```

### 3.4. Function Types

Functions are first-class values that encapsulate executable code.

**Properties:**
- Type name: `"function"`
- Includes regular functions, methods, closures, and lambdas
- Can be assigned to variables, passed as arguments, and returned from functions
- Capture variables from enclosing scope (closures)

**Function Categories:**
1. **Regular Functions**: Defined with `fn` at module level
2. **Closures**: Anonymous functions created with `|params| expression` or `|params| { body }`
3. **Methods**: Functions defined within a class (receive implicit `self`)
4. **Bound Methods**: Methods bound to a specific instance (created by property access)
5. **Builtin Functions**: Global functions provided by the runtime
6. **Builtin Methods**: Methods provided by the runtime on builtin types

**Function Operations:**
- Call: `function(args...)`
- Type check: `type(function) == "function"`
- Equality: Functions are compared by identity

**Example:**
```
fn add(a, b) {
    a + b
}

var f = add
print(type(f))         // "function"
print(f(2, 3))        // 5

// Closure
var square = |x| x * x
print(square(4))      // 16

// Closure
fn make_counter() {
    var count = 0;
    || {
        count = count + 1;
        count
    }
}

var counter = make_counter()
print(counter())      // 1
print(counter())      // 2
```

#### 3.4.1. Bound Methods

Bound methods are function objects that automatically bind the first parameter (`self`) to a specific instance when accessed via property access.

**Properties:**
- Type name: `"function"` (same as regular functions)
- Created when accessing a method without calling it: `obj.method`
- Captures the instance (`self`) at access time
- Can be stored, passed around, and called later
- Always callable with the remaining parameters (excluding `self`)

**Creation:**
Bound methods are created implicitly when accessing a method without parentheses:
```
var obj = SomeClass()
var bound_method = obj.some_method  // Creates bound method
```

**Semantics:**
- The bound method remembers the instance it was bound to
- When called, the bound instance is automatically passed as `self`
- The caller only provides the remaining parameters
- If the original instance is garbage collected, the bound method maintains a reference

**Examples:**
```
class Counter {
    var value = 0;
    
    fn increment() {
        self.value = self.value + 1;
        self.value
    }
    
    fn add(amount) {
        self.value = self.value + amount;
        self.value
    }
}

var counter = Counter()

// Create bound methods
var inc = counter.increment  // Bound method (no parentheses)
var add = counter.add        // Bound method

// Call bound methods
print(inc())                 // 1 (calls counter.increment())
print(add(5))               // 6 (calls counter.add(5))
print(inc())                // 7

// Bound methods can be passed around
fn apply_twice(func) {
    func()
    func()
}

apply_twice(inc)            // Calls counter.increment() twice
print(counter.value)        // 9

// Multiple bound methods from same instance
var counter2 = Counter()
var inc2 = counter2.increment

inc()                       // Affects counter
inc2()                      // Affects counter2
```

**Built-in Type Bound Methods:**
Built-in types also support bound methods:
```
var text = "hello world"
var upper_func = text.upper     // Bound method
print(upper_func())            // "HELLO WORLD"

var numbers = [3, 1, 4, 1, 5]
var append_func = numbers.append
append_func(9)                 // numbers is now [3, 1, 4, 1, 5, 9]
```

**Method vs Function Distinction:**
```
// Method call (immediate execution)
obj.method()                   // Calls method immediately

// Bound method access (creates callable object)  
var bound = obj.method         // Creates bound method object
bound()                       // Calls the bound method

// Function reference (for regular functions)
fn regular_function() { ... }
var func_ref = regular_function // Function reference
func_ref()                     // Calls the function
```

### 3.5. RustValue Type

RustValue is the extension mechanism for implementing custom types in Rust.

**Properties:**
- Type name: Determined by `RustValue::type_name()`
- Enables custom behavior implemented in Rust
- Integrates seamlessly with RustLeaf's type system
- Can have fields and methods like regular objects

**RustValue Interface:**
```rust
trait RustValue {
    fn type_name(&self) -> &str;
    fn get_field(&self, name: &str) -> Option<Value>;
    fn set_field(&mut self, name: &str, value: Value) -> Result<(), String>;
    fn call_method(&self, name: &str, args: Vec<Value>) -> Result<Value, String>;
}
```

**Type Identity:**
- Each RustValue implementation provides its own type name
- Type checking uses the string returned by `type_name()`
- No inheritance or subtyping relationships

**Equality:**
- Default: Reference equality (same Rust object)
- Can be overridden by implementing custom equality in Rust

**Example (from RustLeaf perspective):**
```
// Assuming a Point type implemented in Rust
var p = Point(3.0, 4.0)
print(type(p))           // "Point"
print(p.x)               // 3.0
print(p.distance())      // 5.0

// Type checking
if type(p) == "Point" {
    print("p is a Point")
}
```

### 3.6. Type Conversions

RustLeaf performs no implicit type conversions. All conversions must be explicit.

**No Implicit Conversions:**
- Numeric types are never automatically converted
- No automatic string coercion (except in string interpolation)
- Boolean contexts require actual boolean values

**Explicit Conversion Functions:**
- `int(value)` - Convert to integer
  - From float: truncates toward zero
  - From string: parses decimal integer
  - From bool: true→1, false→0
- `float(value)` - Convert to float
  - From int: exact conversion
  - From string: parses floating-point number
- `str(value)` - Convert to string
  - All types have string representations
  - Used for display and debugging
- `bool(value)` - Convert to boolean
  - Only valid for types with truthiness

**String Interpolation:**
The only implicit conversion occurs in string interpolation, where values are automatically converted to strings:
```
var n = 42
var s = "The answer is ${n}"  // n implicitly converted to "42"
```

**Conversion Errors:**
Invalid conversions raise runtime errors:
```
int("hello")    // Error: Invalid integer format
float("abc")    // Error: Invalid float format
bool([1, 2, 3]) // Error: List has no truthiness
```

### 3.7. Type Checking

Type checking in RustLeaf occurs at runtime when operations are performed.

**Type Checking Mechanisms:**

1. **type() Function**:
   ```
   var t = type(value)  // Returns type name as string
   ```

2. **Direct Comparison**:
   ```
   if type(x) == "int" {
       // x is an integer
   }
   ```

3. **Pattern Matching**:
   ```
   match value {
       case n if type(n) == "int" {
           // Handle integer
       }
       case s if type(s) == "string" {
           // Handle string  
       }
   }
   ```

**Type Errors:**
Operations that receive invalid types raise descriptive errors:
```
"hello" + 42        // Error: Cannot add string and int
[1, 2] * "abc"      // Error: Cannot multiply list and string
null.foo()          // Error: null has no method 'foo'
```

**Error Message Format:**
Type errors include:
- The operation attempted
- The actual type(s) received
- The expected type(s)
- Source location (line, column)

**Duck Typing:**
RustLeaf supports duck typing—if an object has the required methods or fields, operations succeed regardless of its type:
```
class Duck {
    fn quack() { "Quack!" }
}

class Person {
    fn quack() { "I'm quacking!" }
}

fn make_it_quack(thing) {
    print(thing.quack())
}

make_it_quack(Duck())     // "Quack!"
make_it_quack(Person())   // "I'm quacking!"
```

**Truthiness:**
Only `null` and boolean values have truthiness. All other types raise an error in boolean contexts:
```
// Valid
if true { }
if false { }
if null { }          // null is falsy
if not null { }      // true

// Invalid - these raise errors
if 0 { }             // Error: int has no truthiness
if "" { }            // Error: string has no truthiness  
if [] { }            // Error: list has no truthiness

// Must use explicit tests
if x == 0 { }
if s.is_empty() { }
if list.is_empty() { }
```

---