Bob/BOB_LANGUAGE_REFERENCE.md
Bobby Lucero eacb86ec77 feat: comprehensive language enhancements and code quality improvements
Major additions and improvements across the Bob language ecosystem:

Core Language Features:

- Add comprehensive dictionary support with full CRUD operations

- Implement built-in functions: keys(), values(), has() for dictionaries

- Add string multiplication operator (string * number)

- Enhance error reporting with detailed context and call stacks

- Add ternary operator support (condition ? true_expr : false_expr)

- Implement do-while loops with break/continue support

- Add array increment/decrement operators (++, --)

- Add cross-type comparison operators with proper type coercion

- Implement toInt() function for float-to-integer conversion

- Add float array index auto-truncation (like JavaScript/Lua)

Code Quality & Linter Fixes:

- Remove all "using namespace std;" statements (best practice)

- Add proper std:: prefixes throughout codebase

- Fix const correctness in helper functions

- Resolve class/struct declaration mismatches

- Fix sign comparison warnings in array indexing

- Remove unused lambda captures in built-in functions

- Fix brace initialization warnings in parser

Documentation & Tooling:

- Significantly expand BOB_LANGUAGE_REFERENCE.md with new features

- Update VS Code extension with enhanced syntax highlighting

- Add comprehensive code snippets for new language features

- Update version information and package metadata

Test Suite:

- Add extensive dictionary functionality tests

- Add tests for new operators and built-in functions

- Add comprehensive copy behavior tests (by value vs by reference)

- Add performance and edge case testing

Architecture Improvements:

- Enhance Value system with proper move semantics

- Improve memory management with shared_ptr for complex types

- Add trampoline-based tail call optimization

- Implement proper error context propagation

This represents a major milestone in Bob language development with production-ready dictionary support, comprehensive testing, and significantly improved code quality.
2025-08-07 00:12:04 -04:00

21 KiB

Bob Language Reference

Overview

Bob is a dynamically typed programming language focused on safety and clarity. It features automatic type conversion, closures, and a clean syntax inspired by modern programming languages.

Table of Contents

  1. Getting Started
  2. Basic Syntax
  3. Data Types
  4. Variables
  5. Operators
  6. Control Flow
  7. Functions
  8. Standard Library
  9. Error Handling
  10. Examples

Getting Started

Running Bob Code

# Compile the interpreter
make

# Run a Bob file
./build/bob your_file.bob

# Run the comprehensive test suite
./build/bob test_bob_language.bob

File Extension

  • .bob: Standard file extension for Bob source code

Interactive Mode

  • Not implemented: No REPL (Read-Eval-Print Loop) yet
  • File-based: All code must be in .bob files

Basic Syntax

Statements

  • Semicolons: Required at end of statements
  • Parentheses: Required for function calls
  • Curly braces: Required for function bodies
  • Case sensitive: var and Var are different

Comments

// Single line comment
/* Multi-line comment */

Data Types

Numbers

  • Integers: 42, -10, 0
  • Floats: 3.14, 2.718, -1.5
  • Automatic conversion: Numbers are stored as doubles internally

Strings

  • Literal strings: "Hello, World!"
  • Empty strings: ""
  • Escape sequences: Not currently supported

Booleans

  • True: true
  • False: false

None

  • Null value: none (represents absence of value)

Arrays

  • Array literals: [1, 2, 3, 4, 5]
  • Empty arrays: []
  • Mixed types: [1, "hello", true, 3.14]
  • Nested arrays: [[1, 2], [3, 4]]

Dictionaries

  • Dictionary literals: {"key": "value", "number": 42}
  • Empty dictionaries: {}
  • String keys only: Keys must be string literals
  • Any value types: Values can be any type including nested structures
  • Nested dictionaries: {"user": {"name": "Bob", "age": 30}}

Array Access

var arr = [10, 20, 30, 40, 50];

// Basic indexing
print(arr[0]);  // 10
print(arr[2]);  // 30

// Float indices auto-truncate (like JavaScript/Lua)
print(arr[3.14]);  // 40 (truncated to arr[3])
print(arr[2.99]);  // 30 (truncated to arr[2])

// Assignment
arr[1] = 25;
print(arr[1]);  // 25

// Increment/decrement on array elements
arr[0]++;
print(arr[0]);  // 11
++arr[1];
print(arr[1]);  // 26

Array Built-in Functions

var arr = [1, 2, 3];

// Get array length
print(len(arr));  // 3

// Add element to end
push(arr, 4);
print(arr);  // [1, 2, 3, 4]

// Remove and return last element
var last = pop(arr);
print(last);  // 4
print(arr);   // [1, 2, 3]

Dictionary Access

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

// Basic access
print(person["name"]);  // Alice
print(person["age"]);   // 30

// Missing keys return none
print(person["email"]); // none

// Assignment
person["email"] = "alice@example.com";
person["age"] = 31;

// Nested access
var gameState = {
    "player": {"health": 100, "level": 5},
    "world": {"name": "Bobland", "difficulty": "hard"}
};
print(gameState["player"]["health"]);  // 100

Dictionary Built-in Functions

var person = {"name": "Bob", "age": 25, "city": "San Francisco"};

// Get all keys
var keys = keys(person);
print(keys);  // [name, age, city]

// Get all values
var values = values(person);
print(values);  // [Bob, 25, San Francisco]

// Check if key exists
print(has(person, "name"));   // true
print(has(person, "email"));  // false

Variables

Declaration

var name = "Bob";
var age = 25;
var isActive = true;

Assignment

var x = 10;
x = 20;  // Reassignment

Scoping

  • Global scope: Variables declared at top level
  • Local scope: Variables declared inside functions
  • Shadowing: Local variables can shadow global variables
  • No global keyword: Unlike Python, Bob doesn't require explicit global declaration
var globalVar = 100;

func testScope() {
    var localVar = 50;  // Local variable
    return localVar + globalVar;  // Can access global
}

Assignment System

Bob uses two assignment systems for safety and practicality:

Assignment Statements

  • Purpose: Main way to assign values
  • Syntax: variable = value; (with semicolon)
  • Context: Standalone statements
// Assignment statements (most common)
var x = 5;
x = 10;           // Regular assignment
y += 5;           // Compound assignment
z *= 2;           // Compound assignment

Assignment Expressions (For Loops Only)

  • Purpose: For loop clauses only
  • Syntax: variable = value (without semicolon)
  • Context: For loop initializers and increment clauses
// Assignment expressions (for loops only)
for (var i = 0; i < 5; i = i + 1) { }  // Assignment in increment
for (j = 0; j < 5; j += 1) { }         // Assignment in initializer and increment

Why This Design?

Prevents Common Bugs:

// This would be a bug in many languages:
if (x = 10) { }  // Parse error - assignment in conditional

// Bob forces you to write:
x = 10;
if (x == 10) { }  // Clear comparison

Keeps Expressions Pure:

// Prevents side effects in expressions:
var result = (x = 10) + (y = 20);  // Parse error

// Forces clean code:
x = 10;
y = 20;
var result = x + y;  // Pure expression

What's Blocked:

// These all cause parse errors:
var result = x = 10;                   // AssignExpr not allowed in expressions
var result = true ? (x = 10) : 0;      // AssignExpr not allowed in ternary
var result = func(x = 10);             // AssignExpr not allowed in function calls
var result = (x = 10) + 5;             // AssignExpr not allowed in arithmetic

Operators

Arithmetic Operators

  • Addition: +
  • Subtraction: -
  • Multiplication: *
  • Division: /
  • Modulo: %

Compound Assignment Operators

  • Add and assign: +=
  • Subtract and assign: -=
  • Multiply and assign: *=
  • Divide and assign: /=
  • Modulo and assign: %=
var x = 10;
x += 5;   // x = 15
x -= 3;   // x = 12
x *= 2;   // x = 24
x /= 4;   // x = 6
x %= 4;   // x = 2

Increment and Decrement Operators

  • Prefix increment: ++x
  • Postfix increment: x++
  • Prefix decrement: --x
  • Postfix decrement: x--
var x = 5;
print(++x);   // 6 (increments first, then prints)
print(x++);   // 6 (prints first, then increments)
print(x);     // 7

// Works with array elements
var arr = [1, 2, 3];
arr[0]++;
print(arr[0]);  // 2
++arr[1];
print(arr[1]);  // 3

Comparison Operators

  • Equal: ==
  • Not equal: !=
  • Greater than: >
  • Less than: <
  • Greater than or equal: >=
  • Less than or equal: <=

Cross-Type Comparisons

Bob supports cross-type comparisons with intuitive behavior:

// Equality operators work with any types
print(none == "hello");    // false (different types)
print(42 == "42");         // false (different types)
print(true == true);       // true (same type and value)

// Comparison operators only work with numbers
print(5 > 3);              // true
print(5 > "3");            // Error: > not supported between number and string
print(none > 5);           // Error: > not supported between none and number

Logical Operators

  • And: &&
  • Or: ||
  • Not: !

Conditional (Ternary) Operator

// Basic ternary operator
var result = condition ? valueIfTrue : valueIfFalse;

// Examples
var max = x > y ? x : y;
var status = age >= 18 ? "adult" : "minor";
var message = x > 0 ? "positive" : "non-positive";

// Ternary with expressions
var result = x + y > 10 ? x * y : x + y;

// Ternary with function calls
var value = condition ? getValue() : getOtherValue();

// Nested ternary expressions
var result = x == 5 ? (y == 10 ? "both true" : "x true") : "x false";

Precedence: The ternary operator has lower precedence than logical operators (&&, ||) but higher than assignment operators.

String Operators

Concatenation

Bob supports bidirectional string + number concatenation with automatic type conversion:

// String + Number
"Hello " + 42;           // → "Hello 42"
"Pi: " + 3.14;           // → "Pi: 3.14"

// Number + String
42 + " items";           // → "42 items"
3.14 + " is pi";         // → "3.14 is pi"

String Multiplication

"hello" * 3;             // → "hellohellohello"
3 * "hello";             // → "hellohellohello"

Note: String multiplication requires whole numbers.

Number Formatting

Bob automatically formats numbers to show only significant digits:

"Count: " + 2.0;         // → "Count: 2" (no trailing zeros)
"Pi: " + 3.14;           // → "Pi: 3.14" (exact precision)
"Integer: " + 42;        // → "Integer: 42" (no decimal)

Control Flow

If Statements

// Basic if statement
if (x > 10) {
    print("x is greater than 10");
}

// If-else statement
if (x > 10) {
    print("x is greater than 10");
} else {
    print("x is 10 or less");
}

// If-else if-else chain
if (x > 10) {
    print("x is greater than 10");
} else if (x > 5) {
    print("x is greater than 5");
} else {
    print("x is 5 or less");
}

While Loops

// Basic while loop
var i = 0;
while (i < 5) {
    print(i);
    i = i + 1;
}

// While loop with break (using return)
func countToThree() {
    var i = 0;
    while (true) {
        i = i + 1;
        if (i > 3) {
            return i;
        }
    }
}

For Loops

// Basic for loop
for (var i = 0; i < 5; i = i + 1) {
    print(i);
}

// For loop with no initializer
var j = 0;
for (; j < 3; j = j + 1) {
    // Empty body
}

// For loop with no condition (infinite loop with break)
for (var k = 0; ; k = k + 1) {
    if (k >= 2) {
        break; // or return
    }
}

// For loop with no increment
for (var n = 0; n < 3; ) {
    n = n + 1;
}

Do-While Loops

// Basic do-while loop
var i = 0;
do {
    print(i);
    i = i + 1;
} while (i < 5);

// Do-while with break
var j = 0;
do {
    j = j + 1;
    if (j == 3) {
        break;
    }
} while (j < 10);

// Do-while with continue
var k = 0;
var sum = 0;
do {
    k = k + 1;
    if (k == 2) {
        continue;
    }
    sum = sum + k;
} while (k < 5);

Loop Features

  • Nested loops: While, for, and do-while loops can be nested
  • Complex conditions: Loops support any boolean expression
  • Function calls: Loop conditions and bodies can contain function calls
  • Return statements: Loops can be exited using return statements
  • Break and continue: All loop types support break and continue statements
  • Variable scoping: Variables declared in for loop initializers are scoped to the loop
  • Do-while behavior: Do-while loops always execute the body at least once before checking the condition

Functions

Function Declaration

func add(a, b) {
    return a + b;
}

Function Call

var result = add(2, 3);  // result = 5

Parameters

  • Any number of parameters supported
  • No default parameters (not implemented)
  • No keyword arguments (not implemented)

Return Values

  • Explicit return: return value;
  • Implicit return: Functions return none if no return statement
  • Early return: Functions can return from anywhere

Closures

Bob supports lexical closures with variable capture:

var outerVar = "Outer";

func makeGreeter(greeting) {
    return greeting + " " + outerVar;
}

var greeter = makeGreeter("Hello");
// greeter captures outerVar from its lexical scope

Nested Functions

func outer() {
    func inner() {
        return 42;
    }
    return inner();
}

Standard Library

Print Function

print("Hello, World!");
print(42);
print(true);

Usage: print(expression)

  • Prints the result of any expression
  • Automatically converts values to strings
  • Adds newline after output

Assert Function

assert(condition, "optional message");

Usage:

  • assert(true) - passes silently
  • assert(false) - throws error and stops execution
  • assert(condition, "message") - includes custom error message

Behavior:

  • Terminates program execution on failure
  • No exception handling mechanism (yet)
  • Useful for testing and validation

Type Function

type(value);

Usage:

  • Returns the type of a value as a string
  • Returns: "number", "string", "boolean", "none", "array", "function"

Examples:

print(type(42));        // "number"
print(type("hello"));   // "string"
print(type(true));      // "boolean"
print(type(none));      // "none"
print(type([1, 2, 3])); // "array"
print(type(func() {})); // "function"

ToString Function

toString(value);

Usage:

  • Converts any value to a string
  • Works with all data types

Examples:

print(toString(42));        // "42"
print(toString(3.14));      // "3.14"
print(toString(true));      // "true"
print(toString([1, 2, 3])); // "[1, 2, 3]"

ToNumber Function

toNumber(value);

Usage:

  • Converts a value to a number
  • Returns 0 for non-numeric strings
  • Returns 0 for boolean false, 1 for boolean true

Examples:

print(toNumber("42"));      // 42
print(toNumber("3.14"));    // 3.14
print(toNumber("hello"));   // 0
print(toNumber(true));      // 1
print(toNumber(false));     // 0

ToInt Function

toInt(value);

Usage:

  • Converts a number to an integer (truncates decimal part)
  • Throws error for non-numeric values
  • Same result as using bitwise OR with 0 (value | 0)

Examples:

print(toInt(3.7));   // 3
print(toInt(3.2));   // 3
print(toInt(-3.7));  // -3
print(toInt(3.0));   // 3

Input Function

input();
input("prompt");

Usage:

  • Reads a line from standard input
  • Optional prompt string
  • Returns the input as a string

Examples:

var name = input("Enter your name: ");
print("Hello, " + name + "!");

Time Function

time();

Usage:

  • Returns current Unix timestamp (seconds since epoch)
  • Useful for timing and random seed generation

Examples:

var start = time();
// ... do some work ...
var end = time();
print("Elapsed: " + (end - start) + " seconds");

Sleep Function

sleep(seconds);

Usage:

  • Pauses execution for specified number of seconds
  • Useful for animations and timing

Examples:

print("Starting...");
sleep(1);
print("One second later...");

PrintRaw Function

printRaw("text");

Usage:

  • Prints text without adding a newline
  • Supports ANSI escape codes for colors and cursor control
  • Useful for animations and formatted output

Examples:

// Simple output
printRaw("Hello");
printRaw(" World");  // Prints: Hello World

// ANSI colors
printRaw("\e[31mRed text\e[0m");  // Red text
printRaw("\e[32mGreen text\e[0m"); // Green text

// Cursor positioning
printRaw("\e[2J");   // Clear screen
printRaw("\e[H");    // Move cursor to top-left

Random Function

random();

Usage:

  • Returns a random number between 0.0 and 1.0
  • Uses current time as seed

Examples:

var randomValue = random();
print(randomValue);  // 0.0 to 1.0

// Generate random integer 1-10
var randomInt = toInt(random() * 10) + 1;
print(randomInt);

Eval Function

eval("code");

Usage:

  • Executes Bob code as a string
  • Returns the result of the last expression
  • Runs in the current scope (can access variables)

Examples:

var x = 10;
var result = eval("x + 5");
print(result);  // 15

var code = "2 + 3 * 4";
var result = eval(code);
print(result);  // 14

Array Functions

Len Function

len(array);

Usage:

  • Returns the length of an array
  • Also works with strings

Examples:

var arr = [1, 2, 3, 4, 5];
print(len(arr));  // 5

var str = "hello";
print(len(str));  // 5

Push Function

push(array, value);

Usage:

  • Adds a value to the end of an array
  • Modifies the array in place
  • Returns none

Examples:

var arr = [1, 2, 3];
push(arr, 4);
print(arr);  // [1, 2, 3, 4]

Pop Function

pop(array);

Usage:

  • Removes and returns the last element of an array
  • Throws error if array is empty
  • Modifies the array in place

Examples:

var arr = [1, 2, 3, 4];
var last = pop(arr);
print(last);  // 4
print(arr);   // [1, 2, 3]

Dictionary Functions

Keys Function

keys(dictionary);

Usage:

  • Returns an array of all keys in a dictionary
  • Keys are returned as strings

Examples:

var person = {"name": "Bob", "age": 25, "city": "SF"};
var keys = keys(person);
print(keys);  // [name, age, city]

Values Function

values(dictionary);

Usage:

  • Returns an array of all values in a dictionary
  • Values maintain their original types

Examples:

var person = {"name": "Bob", "age": 25, "city": "SF"};
var values = values(person);
print(values);  // [Bob, 25, SF]

Has Function

has(dictionary, key);

Usage:

  • Returns true if the key exists in the dictionary
  • Returns false if the key is missing
  • Key must be a string

Examples:

var person = {"name": "Bob", "age": 25};
print(has(person, "name"));   // true
print(has(person, "email"));  // false

Error Handling

Current Error Types

  • Division by zero: DivisionByZeroError
  • Type errors: Operands must be of same type
  • String multiplication: String multiplier must be whole number
  • Assertion failures: Assertion failed: condition is false
  • Function call errors: Can only call functions, got [type]
  • Argument count errors: Expected X arguments but got Y

Error Behavior

  • No try-catch: Exception handling not implemented
  • Program termination: Errors stop execution immediately
  • Error messages: Descriptive error messages printed to console

Common Error Scenarios

// Division by zero
10 / 0;  // Error: DivisionByZeroError

// Type mismatch
"hello" - "world";  // Error: Cannot use '-' on two strings

// Invalid string multiplication
"hello" * 3.5;  // Error: String multiplier must be whole number

// Undefined variable
undefinedVar;  // Error: Undefined variable

// Function call errors
var notAFunction = 42;
notAFunction();  // Error: Can only call functions, got number

// Argument count errors
func test(a, b) { return a + b; }
test(1);  // Error: Expected 2 arguments but got 1

Examples

Basic Calculator

func add(a, b) {
    return a + b;
}

func multiply(a, b) {
    return a * b;
}

var result = add(5, multiply(3, 4));
print("Result: " + result);  // Result: 17

String Processing

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

func repeat(str, count) {
    return str * count;
}

var greeting = greet("Bob");
var repeated = repeat("Ha", 3);
print(greeting + " " + repeated);  // Hello, Bob! HaHaHa

Variable Scoping Example

var globalCounter = 0;

func increment() {
    var localCounter = 1;
    globalCounter = globalCounter + localCounter;
    return globalCounter;
}

print("Before: " + globalCounter);  // Before: 0
increment();
print("After: " + globalCounter);   // After: 1

Loop Examples

// Count from 1 to 5
for (var i = 1; i <= 5; i = i + 1) {
    print("Count: " + i);
}

// Sum numbers from 1 to 10
var sum = 0;
for (var j = 1; j <= 10; j = j + 1) {
    sum = sum + j;
}
print("Sum: " + sum);

// Find first even number
var k = 1;
while (k % 2 != 0) {
    k = k + 1;
}
print("First even: " + k);

Implementation Details

Assignment System: Statements vs Expressions

Bob uses a dual assignment system to prevent common programming errors:

Assignment Statements (general use):

var x = 10;
x = 20;  // This is a statement, not an expression

Assignment Expressions (for loops only):

for (var i = 0; i < 10; i = i + 1) {  // Assignment in increment clause
    print(i);
}

Why This Design?

  • Prevents if (x = 10) bugs: Assignment statements can't be used in conditions
  • Maintains loop functionality: For loops still work with assignment expressions
  • Clear intent: Distinguishes between intentional assignments and accidental ones

Memory Management

Bob uses automatic memory management with conservative cleanup:

Reference Counting:

  • All objects use std::shared_ptr for automatic cleanup
  • Functions and thunks are tracked by reference count
  • Objects are freed when no longer referenced

Automatic Cleanup:

  • Functions created in loops are automatically cleaned up when unused
  • Cleanup occurs every 1000 function/thunk creations
  • Only removes objects with use_count() == 1 (conservative approach)

Safety Guarantees:

  • Functions referenced anywhere are never cleaned up
  • Only truly unused functions are removed
  • No dangling pointers or memory corruption

This documentation covers the current Bob language implementation. For development plans, see ROADMAP.md.