Bob/Reference/BOB_LANGUAGE_REFERENCE.md
Bobby Lucero 3138f6fb92 Various changes, again. Updated extension. Added classes, super, this, polymorphism.
Runtime: add method dispatch for array/string/dict/number (.len, .push, .pop, .keys, .values, .has, .toInt)
Stdlib: delete global len/push/pop/keys/values/has
Tests/docs/examples: migrate to method style; add tests/test_builtin_methods_style.bob
All tests pass
Breaking: global len/push/pop/keys/values/has removed; use methods instead
Parser/AST: add class/extends/extension/super, field initializers
Runtime: shared methods with this injection; classParents/classTemplates; super resolution; ownerClass/currentClass; extension lookup order
Builtins: method dispatch for array/string/dict/number (.len/.push/.pop/.keys/.values/.has/.toInt); remove global forms
Tests/docs/examples: add/refresh for classes, inheritance, super, polymorphism; migrate to method style; all tests pass
VS Code extension: update grammar/readme/snippets for new features
2025-08-10 22:44:46 -04:00

10 KiB
Raw Blame History

Bob Language Reference

Quick Start

# Build Bob
cmake -G Ninja -B build -DCMAKE_BUILD_TYPE=Release
ninja -C build

# Run a file
./build/bin/bob script.bob

# Interactive mode
./build/bin/bob

Data Types

Numbers

var integer = 42;
var float = 3.14;
var negative = -10;

Strings

var text = "Hello, World!";
var empty = "";
var escaped = "Line 1\nLine 2\t\"quoted\"";
var concat = "Hello" + " " + "World";
var repeat = "hi" * 3;  // "hihihi"

Booleans

var yes = true;
var no = false;

None

var nothing = none;

Arrays

var numbers = [1, 2, 3];
var mixed = [42, "hello", true];
var nested = [[1, 2], [3, 4]];

// Access and modify
print(numbers[0]);  // 1
numbers[1] = 99;

// Array properties (read-only)
print(numbers.length);  // 3
print(numbers.first);   // 1
print(numbers.last);    // 3
print(numbers.empty);   // false

var empty = [];
print(empty.length);    // 0
print(empty.first);     // none
print(empty.empty);     // true

Dictionaries

var person = {"name": "Alice", "age": 30};

// Access and modify (bracket notation)
print(person["name"]);  // Alice
person["city"] = "NYC";

// Access and modify (dot notation - cleaner syntax)
print(person.name);     // Alice
person.city = "NYC";
person.age = 31;

// Both notations are equivalent
assert(person.name == person["name"]);

// Dictionary properties (built-in)
print(person.length);   // 3 (number of key-value pairs)
print(person.empty);    // false
print(person.keys);     // ["name", "age", "city"]
print(person.values);   // ["Alice", 31, "NYC"]

Variables

var x = 10;       // Declaration
x = 20;           // Reassignment
y += 5;           // Compound assignment
z++;              // Increment (for array elements)

Operators

Arithmetic

+ - * / %         // Basic math
-x                // Unary minus

Comparison

== != < > <= >=   // Comparisons

Logical

&& || !           // AND, OR, NOT (short-circuit)

Bitwise

& | ^ << >> ~     // Bitwise operations

Compound Assignment

+= -= *= /= %=    // Arithmetic compound
&= |= ^= <<= >>=  // Bitwise compound

Ternary

var result = condition ? "yes" : "no";

Control Flow

If Statements

if (x > 0) {
    print("positive");
} else if (x < 0) {
    print("negative");
} else {
    print("zero");
}

While Loops

while (i < 10) {
    print(i);
    i = i + 1;
}

Do-While Loops

do {
    print(i);
    i = i + 1;
} while (i < 10);

For Loops

for (var i = 0; i < 10; i = i + 1) {
    print(i);
}

// Break and continue work in all loops
for (var i = 0; i < 10; i = i + 1) {
    if (i == 5) break;
    if (i % 2 == 0) continue;
    print(i);
}

Functions

Basic Functions

func greet(name) {
    return "Hello, " + name;
}

var message = greet("Alice");

Anonymous Functions

var square = func(x) { return x * x; };
var result = square(5);

Closures

func makeCounter() {
    var count = 0;
    return func() {
        count = count + 1;
        return count;
    };
}

var counter = makeCounter();
print(counter());  // 1
print(counter());  // 2

First-Class Functions

func apply(fn, x) {
    return fn(x);
}

var result = apply(square, 10);  // 100

Built-in Functions

I/O

print("Hello");           // Output with newline
printRaw("No newline");   // Output without newline
var input = input("Enter something: ");

Type Conversion

toString(42);        // "42"
toNumber("3.14");    // 3.14
toInt(3.9);         // 3
toBoolean(1);       // true
type(42);           // "number"

Arrays, Strings, and Dictionaries: Method style (preferred)

[1, 2, 3].len();          // 3
"hello".len();            // 5
var a = [1, 2]; a.push(3); // a is now [1, 2, 3]
var v = a.pop();           // v == 3

var d = {"a": 1, "b": 2};
d.len();                   // 2
d.keys();                  // ["a", "b"]
d.values();                // [1, 2]
d.has("a");               // true

Note: Global forms like len(x), push(arr, ...), pop(arr), keys(dict), values(dict), has(dict, key) have been removed. Use method style.

Numbers

toInt(3.9);        // 3 (global)
(3.9).toInt();     // 3 (method on number)

Utility

assert(condition, "message");  // Testing
time();                        // Current time in microseconds
sleep(1.5);                   // Sleep for 1.5 seconds
random();                     // Random number 0-1
eval("print('Hello');");     // Execute string as code
exit(0);                     // Exit program

File I/O

var content = readFile("data.txt");
writeFile("output.txt", "Hello");
var lines = readLines("config.txt");
var exists = fileExists("test.txt");

Standard Library Reference

The following built-ins are available by default. Unless specified, functions throw on invalid argument counts/types.

  • print(x): prints x with newline
  • printRaw(x): prints x without newline
  • input(prompt?): reads a line from stdin (optional prompt)
  • toString(x): returns string representation
  • toNumber(s): parses string to number or returns none
  • toInt(n): truncates number to integer
  • toBoolean(x): converts to boolean using truthiness rules
  • type(x): returns the type name as string
  • len(x) / x.len(): length of array/string/dict
  • push(arr, ...values) / arr.push(...values): appends values to array in place, returns arr
  • pop(arr) / arr.pop(): removes and returns last element
  • keys(dict) / dict.keys(): returns array of keys
  • values(dict) / dict.values(): returns array of values
  • has(dict, key) / dict.has(key): returns true if key exists
  • readFile(path): returns entire file contents as string
  • writeFile(path, content): writes content to file
  • readLines(path): returns array of lines
  • fileExists(path): boolean
  • time(): microseconds since Unix epoch
  • sleep(seconds): pauses execution
  • random(): float in [0,1)
  • eval(code): executes code string in current environment
  • exit(code?): terminates the program

Notes:

  • Arrays support properties: length, first, last, empty
  • Dicts support properties: length, empty, keys, values
  • Method-style builtins on arrays/strings/dicts are preferred; global forms remain for compatibility.

Advanced Features

Classes (Phase 1)

// Declare a class with fields and methods
class Person {
  var name;
  var age;

  // Methods can use implicit `this`
  func setName(n) { this.name = n; }
  func greet() { print("Hi, I'm " + this.name); }
}

// Construct via the class name
var p = Person();
p.setName("Bob");
p.greet();

// Fields are stored on the instance (a dictionary under the hood)
p.age = 30;

Notes:

  • Instances are plain dictionaries; methods are shared functions placed on the instance.
  • On a property call like obj.method(...), the interpreter injects this = obj into the call frame (no argument injection).
  • Taking a method reference and calling it later does not autobind this; call via obj.method(...) when needed.

Extensions (Builtins and Classes)

Extend existing types (including builtins) with new methods:

extension array {
  func sum() {
    var i = 0; var s = 0;
    while (i < len(this)) { s = s + this[i]; i = i + 1; }
    return s;
  }
}

extension dict { func size() { return len(this); } }
extension string { func shout() { return toString(this) + "!"; } }
extension any { func tag() { return "<" + type(this) + ">"; } }

assert([1,2,3].sum() == 6);
assert({"a":1,"b":2}.size() == 2);
assert("hi".shout() == "hi!");
assert(42.tag() == "<number>");

Notes:

  • Lookup order for obj.method(...): instance dictionary → class extensions (for user classes) → builtin extensions (string/array/dict) → any.
  • this is injected for property calls.

String Interpolation

var name = "Alice";
var age = 30;
var message = "Name: " + name + ", Age: " + age;

Tail Call Optimization

func factorial(n, acc) {
    if (n <= 1) return acc;
    return factorial(n - 1, n * acc);  // Tail call optimized
}

Assignment System

Bob has a unique assignment system that prevents common bugs:

// Assignment statements (everywhere)
var x = 5;
x = 10;
y += 5;

// Assignment expressions (only in for loops)
for (var i = 0; i < 5; i = i + 1) { }  // OK
for (j = 0; j < 5; j += 1) { }         // OK

// This prevents bugs like:
if (x = 10) { }  // PARSE ERROR - prevents accidental assignment

Memory Management

Bob automatically manages memory - no manual allocation or deallocation needed. Objects are cleaned up when no longer referenced.

Error Handling

Bob provides helpful error messages with context:

// Runtime errors show line numbers and context
var x = undefined_variable;  // Error: Undefined variable 'undefined_variable' at line 2

// Type errors are caught
var result = "hello" / 5;    // Error: Cannot divide string by number

Interactive Mode (REPL)

Bob includes an interactive mode for experimenting:

$ ./build/bin/bob
Bob Interactive Mode
> var x = 42;
> print(x * 2);
84
> exit();

Examples

Fibonacci with Tail Call Optimization

func fib(n, a, b) {
    if (n == 0) return a;
    if (n == 1) return b;
    return fib(n - 1, b, a + b);
}

print(fib(40, 0, 1));  // Fast even for large numbers

Working with Data Structures

var people = [
    {"name": "Alice", "age": 30},
    {"name": "Bob", "age": 25}
];

for (var i = 0; i < people.len(); i = i + 1) {
    var person = people[i];
    print(person["name"] + " is " + person["age"] + " years old");
}

File Processing

var lines = readLines("data.txt");
var processed = [];

for (var i = 0; i < lines.len(); i = i + 1) {
    var line = lines[i];
    if (line.len() > 0) {
        processed.push("Processed: " + line);
    }
}

var output = "";
for (var i = 0; i < processed.len(); i = i + 1) {
    output = output + processed[i];
    if (i < processed.len() - 1) {
        output = output + "\n";
    }
}
writeFile("output.txt", output);

For more examples, see the comprehensive test suite in test_bob_language.bob