More things

- Add while, for, and do-while loops with break/continue
- Implement assignment statements (prevents if(x=10) bugs)
- Keep assignment expressions only for for-loop clauses
- Fix critical memory management bug (dangling pointers in cleanup)
- Add automatic memory cleanup with conservative reference counting
- Consolidate documentation into single reference file
- Add comprehensive test coverage for all loop types and edge cases
- VSCode extension for bob highlighting and snippets
This commit is contained in:
Bobby Lucero 2025-08-06 00:57:36 -04:00
parent 671f8a6350
commit 72a1b82b43
36 changed files with 3434 additions and 278 deletions

BIN
.DS_Store vendored

Binary file not shown.

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
/.vscode
build/
.DS_Store

View File

@ -2,19 +2,55 @@
## Overview
Bob is a dynamically typed programming language with a focus on simplicity and expressiveness. It features automatic type conversion, closures, and a clean syntax inspired by modern programming languages.
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. [Data Types](#data-types)
2. [Variables](#variables)
3. [Operators](#operators)
4. [Functions](#functions)
5. [Control Flow](#control-flow)
6. [Standard Library](#standard-library)
7. [Error Handling](#error-handling)
8. [Examples](#examples)
9. [Language Nuances](#language-nuances)
1. [Getting Started](#getting-started)
2. [Basic Syntax](#basic-syntax)
3. [Data Types](#data-types)
4. [Variables](#variables)
5. [Operators](#operators)
6. [Control Flow](#control-flow)
7. [Functions](#functions)
8. [Standard Library](#standard-library)
9. [Error Handling](#error-handling)
10. [Examples](#examples)
## Getting Started
### Running Bob Code
```bash
# 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
```go
// Single line comment
/* Multi-line comment */
```
## Data Types
@ -38,14 +74,14 @@ Bob is a dynamically typed programming language with a focus on simplicity and e
## Variables
### Declaration
```bob
```go
var name = "Bob";
var age = 25;
var isActive = true;
```
### Assignment
```bob
```go
var x = 10;
x = 20; // Reassignment
```
@ -56,8 +92,7 @@ x = 20; // Reassignment
- **Shadowing**: Local variables can shadow global variables
- **No `global` keyword**: Unlike Python, Bob doesn't require explicit global declaration
### Variable Behavior
```bob
```go
var globalVar = 100;
func testScope() {
@ -66,6 +101,66 @@ func testScope() {
}
```
### 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
```go
// 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
```go
// 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:**
```go
// 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:**
```go
// 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:**
```go
// 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
@ -83,12 +178,39 @@ func testScope() {
- **Greater than or equal**: `>=`
- **Less than or equal**: `<=`
### Logical Operators
- **And**: `&&`
- **Or**: `||`
- **Not**: `!`
### Conditional (Ternary) Operator
```go
// 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:
```bob
```go
// String + Number
"Hello " + 42; // → "Hello 42"
"Pi: " + 3.14; // → "Pi: 3.14"
@ -99,7 +221,7 @@ Bob supports bidirectional string + number concatenation with automatic type con
```
#### String Multiplication
```bob
```go
"hello" * 3; // → "hellohellohello"
3 * "hello"; // → "hellohellohello"
```
@ -109,23 +231,135 @@ Bob supports bidirectional string + number concatenation with automatic type con
### Number Formatting
Bob automatically formats numbers to show only significant digits:
```bob
```go
"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
```go
// 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
```go
// 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
```go
// 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
```go
// 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
```bob
```go
func add(a, b) {
return a + b;
}
```
### Function Call
```bob
```go
var result = add(2, 3); // result = 5
```
@ -142,7 +376,7 @@ var result = add(2, 3); // result = 5
### Closures
Bob supports lexical closures with variable capture:
```bob
```go
var outerVar = "Outer";
func makeGreeter(greeting) {
@ -154,7 +388,7 @@ var greeter = makeGreeter("Hello");
```
### Nested Functions
```bob
```go
func outer() {
func inner() {
return 42;
@ -163,24 +397,10 @@ func outer() {
}
```
## Control Flow
### Current Status
**Control flow statements are NOT implemented yet:**
- `if` statements
- `while` loops
- `for` loops
- `else` clauses
### Planned Features
- Conditional execution
- Looping constructs
- Logical operators (`and`, `or`, `not`)
## Standard Library
### Print Function
```bob
```go
print("Hello, World!");
print(42);
print(true);
@ -192,7 +412,7 @@ print(true);
- Adds newline after output
### Assert Function
```bob
```go
assert(condition, "optional message");
```
@ -220,7 +440,7 @@ assert(condition, "optional message");
- **Error messages**: Descriptive error messages printed to console
### Common Error Scenarios
```bob
```go
// Division by zero
10 / 0; // Error: DivisionByZeroError
@ -237,7 +457,7 @@ undefinedVar; // Error: Undefined variable
## Examples
### Basic Calculator
```bob
```go
func add(a, b) {
return a + b;
}
@ -251,7 +471,7 @@ print("Result: " + result); // Result: 17
```
### String Processing
```bob
```go
func greet(name) {
return "Hello, " + name + "!";
}
@ -266,7 +486,7 @@ print(greeting + " " + repeated); // Hello, Bob! HaHaHa
```
### Variable Scoping Example
```bob
```go
var globalCounter = 0;
func increment() {
@ -280,82 +500,71 @@ increment();
print("After: " + globalCounter); // After: 1
```
## Language Nuances
### Loop Examples
```go
// Count from 1 to 5
for (var i = 1; i <= 5; i = i + 1) {
print("Count: " + i);
}
### Type System
- **Dynamic typing**: Variables can hold any type
- **Automatic conversion**: Numbers and strings convert automatically
- **No type annotations**: Types are inferred at runtime
// Sum numbers from 1 to 10
var sum = 0;
for (var j = 1; j <= 10; j = j + 1) {
sum = sum + j;
}
print("Sum: " + sum);
### Memory Management
- **Reference counting**: Uses `std::shared_ptr` for automatic memory management
- **No manual memory management**: No `delete` or `free` needed
- **Garbage collection**: Automatic cleanup of unused objects
### Performance Characteristics
- **Interpreted**: Code is executed by an interpreter
- **AST-based**: Abstract Syntax Tree for execution
- **No compilation**: Direct interpretation of source code
### Syntax Rules
- **Semicolons**: Required at end of statements
- **Parentheses**: Required for function calls
- **Curly braces**: Required for function bodies
- **Case sensitive**: `var` and `Var` are different
### Comparison with Other Languages
#### vs Python
- **Similar**: Dynamic typing, functions, closures
- **Different**: No `global` keyword, automatic string conversion, different error handling
#### vs JavaScript
- **Similar**: Automatic type conversion, string concatenation
- **Different**: No `undefined`, different syntax, no `null`
#### vs Lua
- **Similar**: Dynamic typing, functions
- **Different**: No `local` keyword, different scoping rules
### Limitations
- **No control flow**: No if/while/for statements
- **No logical operators**: No `and`/`or`/`not`
- **No exception handling**: No try-catch blocks
- **No modules**: No import/export system
- **No classes**: No object-oriented features
- **No arrays/lists**: No built-in collection types
- **No dictionaries**: No key-value data structures
### Future Features
- Control flow statements
- Logical operators
- Exception handling
- Collection types (arrays, dictionaries)
- Modules and imports
- Object-oriented programming
- Standard library expansion
## Getting Started
### Running Bob Code
```bash
# Compile the interpreter
make
# Run a Bob file
./build/bob your_file.bob
# Run the comprehensive test suite
./build/bob test_bob_language.bob
// Find first even number
var k = 1;
while (k % 2 != 0) {
k = k + 1;
}
print("First even: " + k);
```
### File Extension
- **`.bob`**: Standard file extension for Bob source code
## Implementation Details
### Interactive Mode
- **Not implemented**: No REPL (Read-Eval-Print Loop) yet
- **File-based**: All code must be in `.bob` files
### Assignment System: Statements vs Expressions
Bob uses a dual assignment system to prevent common programming errors:
**Assignment Statements** (general use):
```go
var x = 10;
x = 20; // This is a statement, not an expression
```
**Assignment Expressions** (for loops only):
```go
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 Bob language version 1.0. For the latest updates, check the ROADMAP.md file.*
*This documentation covers the current Bob language implementation. For development plans, see ROADMAP.md.*

View File

@ -1 +1,98 @@
# Bob
A modern programming language focused on safety and clarity.
## Design Philosophy
Bob prioritizes safety and functional programming while keeping things practical.
## Assignment System
Bob uses two types of assignments to prevent bugs while keeping useful functionality:
### Assignment Statements
```go
var x = 5;
x = 10; // Regular assignment
y += 5; // Compound assignment
```
### Assignment Expressions (For Loops Only)
```go
for (var i = 0; i < 5; i = i + 1) { } // Assignment in for loop
for (j = 0; j < 5; j += 1) { } // Assignment in for loop
```
### Why This Design?
**Prevents Common Bugs:**
```go
// This would be a bug in many languages:
if (x = 10) { } // Parse error in Bob - prevents accidental assignment
// Bob forces you to write:
x = 10;
if (x == 10) { } // Clear comparison
```
**Keeps Expressions Pure:**
```go
// 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
```
**Supports Functional Programming:**
```go
// Prevents confusing patterns:
var value = condition ? (x = 10) : (x = 20); // Parse error
// Encourages clean patterns:
x = condition ? 10 : 20; // Clear assignment
```
**Keeps For Loops Practical:**
```go
// For loops work naturally:
for (var i = 0; i < 10; i = i + 1) { } // Assignment expressions allowed
for (j = 0; j < 10; j += 1) { } // Assignment expressions allowed
```
### Benefits
1. **Prevents Common Bugs**: No accidental assignments in conditionals
2. **Expression Purity**: Expressions have no side effects
3. **Clear Semantics**: Assignments are clearly statements
4. **Functional Programming**: Encourages pure function patterns
5. **Practical For Loops**: Maintains useful for loop syntax
6. **Easier Debugging**: Clear separation of assignment and expression logic
## Features
- **Type Safety**: Dynamic typing with runtime type checking
- **Functional Programming**: First-class functions, closures, recursion
- **Memory Management**: Automatic memory management
- **Error Handling**: Comprehensive error reporting
- **Standard Library**: Built-in functions for common operations
- **Tail Call Optimization**: Efficient recursive function calls
## Documentation
See [BOB_LANGUAGE_REFERENCE.md](BOB_LANGUAGE_REFERENCE.md) for complete language documentation.
## Building
```bash
make clean && make
./build/bob your_file.bob
```
## Testing
```bash
./build/bob test_bob_language.bob
```

View File

@ -1,103 +1,165 @@
# Bob Language Development Roadmap
## Current Status
- Basic expressions (arithmetic, comparison, logical)
- Variables and assignment
- Print statements (converted to standard library function)
- Block statements
- Environment/scoping
- Function implementation (COMPLETED)
- Return statements (COMPLETED)
- Closures (COMPLETED)
- Assert function (COMPLETED)
- Standard library infrastructure (COMPLETED)
- First-class functions and higher-order functions (COMPLETED)
- String + number concatenation with smart formatting (COMPLETED)
- String multiplication (COMPLETED)
- Alphanumeric identifiers (COMPLETED)
- Comprehensive testing framework (COMPLETED)
## Phase 1: Core Language Features (High Priority)
Bob is a working programming language with a solid foundation. Here's what's currently implemented:
### 1. Control Flow
```bob
// If statements
if (x > 10) {
print "big";
} else {
print "small";
}
### ✅ **Core Language Features**
// While loops
var i = 0;
while (i < 5) {
print i;
i = i + 1;
}
#### **Data Types & Variables**
- **Numbers**: Integers, floats, automatic conversion
- **Strings**: Literals, concatenation, multiplication
- **Booleans**: `true`, `false`
- **None**: Null value representation
- **Variables**: Declaration, assignment, scoping
- **Assignment System**: Dual system (statements + expressions for loops)
#### **Operators**
- **Arithmetic**: `+`, `-`, `*`, `/`, `%`
- **Comparison**: `==`, `!=`, `>`, `<`, `>=`, `<=`
- **Logical**: `&&`, `||`, `!` (with short-circuit evaluation)
- **Bitwise**: `&`, `|`, `^`, `<<`, `>>`, `~`
- **Compound Assignment**: `+=`, `-=`, `*=`, `/=`, `%=`, `&=`, `|=`, `^=`, `<<=`, `>>=`
- **Ternary**: `condition ? valueIfTrue : valueIfFalse`
#### **Control Flow**
- **If Statements**: `if`, `else`, `else if` chains
- **While Loops**: Basic, nested, complex conditions
- **For Loops**: All clause variations, nested loops
- **Do-While Loops**: Basic, nested, break/continue support
- **Break/Continue**: Full support in all loop types
#### **Functions**
- **Function Declaration**: `func name(params) { body }`
- **Parameters**: Any number of parameters
- **Return Values**: Explicit and implicit returns
- **Closures**: Lexical scoping with variable capture
- **First-Class Functions**: Functions as values, parameters, return values
- **Anonymous Functions**: `func(params) { body }`
- **Nested Functions**: Functions defined inside other functions
- **Recursion**: Full support including deep recursion
#### **Standard Library**
- **`print()`**: Output with automatic type conversion
- **`assert()`**: Testing with custom error messages
- **`type()`**: Runtime type checking
- **`toString()`**: Universal string conversion
- **`toNumber()`**: String-to-number conversion
- **`input()`**: User input capability
- **`time()`**: Microsecond precision timing
#### **Advanced Features**
- **String Operations**: Bidirectional string + number concatenation
- **Number Formatting**: Smart significant digits
- **Memory Management**: Automatic cleanup
- **Error Handling**: Basic error reporting
- **Testing Framework**: Built-in assert function
- **Operator Precedence**: Full precedence hierarchy
- **Variable Shadowing**: Proper scoping rules
### **Current Limitations**
#### **Data Structures**
- **No Arrays/Lists**: No built-in collection types
- **No Maps/Dictionaries**: No key-value data structures
- **No Sets**: No unique value collections
#### **Advanced Language Features**
- **No Classes/Objects**: No object-oriented programming
- **No Modules/Imports**: No code organization system
- **No Exception Handling**: No try-catch blocks
- **No Type Annotations**: No static type checking
- **No Generics**: No parametric polymorphism
#### **Standard Library Gaps**
- **No File I/O**: No reading/writing files
- **No Network I/O**: No HTTP or socket operations
- **No Math Library**: No advanced mathematical functions
- **No Date/Time**: No date manipulation (except `time()`)
- **No Random Numbers**: No random number generation
#### **Development Tools**
- **No REPL**: No interactive mode
- **No Debugger**: No debugging tools
- **No Profiler**: No performance analysis
- **No Package Manager**: No dependency management
## **Future Development Phases**
### **Phase 1: Data Structures (High Priority)**
#### **Arrays/Lists**
```go
var numbers = [1, 2, 3, 4, 5];
print(numbers[0]); // 1
numbers[1] = 42; // Modify element
var length = len(numbers); // Get length
```
**Implementation:**
- Add `IfStmt` and `WhileStmt` to Statement.h
- Update parser to handle `if` and `while` keywords
- Implement control flow in interpreter
**Implementation Plan:**
- Add array literal syntax `[expr, expr, ...]`
- Implement array indexing `array[index]`
- Add array assignment `array[index] = value`
- Create `len()` function for arrays
- Support nested arrays
### 2. Logical Operators
```bob
// Currently missing: and, or, not operators
if (x > 0 and y < 10) {
print "valid range";
}
```
**Implementation:**
- Add `and`, `or`, `not` operator parsing
- Implement logical operator evaluation in interpreter
### 3. Better Error Handling
```bob
// Current: Basic error messages
// Goal: Line numbers, better context
Error at line 5: Expected ';' after expression
print 42
^
```
## Phase 2: Data Structures (Medium Priority)
### 4. Arrays/Lists
```bob
var numbers = [1, 2, 3, 4];
print numbers[0]; // 1
numbers[1] = 42;
```
### 5. Maps/Dictionaries
```bob
#### **Maps/Dictionaries**
```go
var person = {"name": "Bob", "age": 25};
print person["name"];
person["city"] = "NYC";
print(person["name"]); // "Bob"
person["city"] = "NYC"; // Add/modify entry
var keys = keys(person); // Get all keys
```
## Phase 3: Standard Library (Medium Priority)
**Implementation Plan:**
- Add map literal syntax `{key: value, ...}`
- Implement map indexing `map[key]`
- Add map assignment `map[key] = value`
- Create `keys()`, `values()` functions
- Support nested maps
### 6. Additional Built-in Functions
```bob
len("hello"); // String length
input("Enter name: "); // User input
random(1, 100); // Random numbers
type(42); // Type checking
```
### **Phase 2: Standard Library Expansion (Medium Priority)**
### 7. File I/O
```bob
#### **File I/O**
```go
var content = readFile("data.txt");
writeFile("output.txt", "Hello World");
var lines = readLines("config.txt");
```
## Phase 4: Advanced Features (Lower Priority)
#### **Math Library**
```go
var result = sqrt(16); // 4.0
var random = rand(1, 100); // Random number
var max = max(5, 10, 3); // 10
```
### 8. Classes & Objects
```bob
#### **String Processing**
```go
var parts = split("a,b,c", ","); // ["a", "b", "c"]
var joined = join(parts, "-"); // "a-b-c"
var upper = toUpper("hello"); // "HELLO"
```
### **Phase 3: Advanced Language Features (Lower Priority)**
#### **Exception Handling**
```go
try {
var result = 10 / 0;
} catch (error) {
print("Error: " + error);
}
```
#### **Modules/Imports**
```go
import "math.bob";
import "utils.bob" as utils;
```
#### **Classes & Objects**
```go
class Person {
init(name, age) {
this.name = name;
@ -105,99 +167,104 @@ class Person {
}
greet() {
print "Hello, I'm " + this.name;
return "Hello, I'm " + this.name;
}
}
var bob = Person("Bob", 25);
bob.greet();
```
### 9. Modules/Imports
```bob
import "math.bob";
import "utils.bob";
### **Phase 4: Development Tools (Lower Priority)**
#### **Interactive Mode (REPL)**
```bash
$ bob
> var x = 5
> print(x + 3)
8
> func add(a, b) { return a + b; }
> add(2, 3)
5
```
### 10. Type System
```bob
// Optional type annotations
fun add(a: number, b: number): number {
return a + b;
}
#### **Debugger**
```go
debugger; // Breakpoint
var x = 5;
// Step through code
```
## Implementation Tips
## **Implementation Guidelines**
### For Each Feature:
1. Lexer: Add new tokens if needed
2. Parser: Add new expression/statement types
3. AST: Define new node types
4. Interpreter: Implement evaluation logic
5. Test: Create test cases using assert function
### **For Each New Feature:**
1. **Lexer**: Add new tokens if needed
2. **Parser**: Add new expression/statement types
3. **AST**: Define new node types
4. **Interpreter**: Implement evaluation logic
5. **Testing**: Create comprehensive test cases
### Testing Strategy:
```bob
// Use the new assert function for comprehensive testing
### **Testing Strategy:**
```go
// Use the built-in assert function for testing
assert(add(2, 3) == 5, "add(2, 3) should equal 5");
assert(x > 0, "x should be positive");
assert(len([1, 2, 3]) == 3, "Array length should be 3");
```
## Recommended Next Steps
### **Code Quality Standards:**
- **Comprehensive Testing**: Every feature needs test coverage
- **Error Handling**: Graceful error messages
- **Documentation**: Update language reference
- **Performance**: Consider memory and speed implications
1. Add if statements (fundamental control flow)
2. Add while loops (enables iteration)
3. Implement logical operators (and, or, not)
4. Improve error messages (better developer experience)
5. Add arrays (most useful data structure)
## **Success Metrics**
## Success Metrics
### **Completed ✅**
- [x] Core language syntax and semantics
- [x] All basic operators and expressions
- [x] Control flow statements
- [x] Functions and closures
- [x] Standard library basics
- [x] Testing framework
- [x] Basic error handling
- [x] Memory management
- [x] Assignment system design
- [x] Can write simple functions
- [x] Can use return statements
- [x] Can use closures
- [x] Has assert function for testing
- [x] Has standard library infrastructure
- [x] Supports first-class functions
- [x] Has comprehensive testing framework
- [ ] Can use if/else statements
- [ ] Can use while loops
- [ ] Can use logical operators
- [ ] Can work with arrays
- [ ] Can read/write files
- [ ] Has good error messages
### **In Progress 🔄**
- [ ] Data structures (arrays, maps)
- [ ] Extended standard library
- [ ] Performance optimizations
## Resources
### **Planned 📋**
- [ ] Advanced language features
- [ ] Development tools
- [ ] Documentation improvements
- [Crafting Interpreters](https://craftinginterpreters.com/) - Excellent resource for language implementation
- [Bob's current source code](./source/) - Your implementation
- [Test files](./*.bob) - Examples of current functionality
## **Resources**
## Recent Achievements
- **[BOB_LANGUAGE_REFERENCE.md](BOB_LANGUAGE_REFERENCE.md)** - Complete language documentation
- **[ASSIGNMENT_DESIGN.md](ASSIGNMENT_DESIGN.md)** - Assignment system design rationale
- **[test_bob_language.bob](test_bob_language.bob)** - Comprehensive test suite
- **[Crafting Interpreters](https://craftinginterpreters.com/)** - Excellent resource for language implementation
### Function Implementation (COMPLETED)
- Function declarations with parameters
- Function calls with arguments
- Return statements
- Proper scoping and closures
- Nested function calls
## **Recent Major Achievements**
### Standard Library (COMPLETED)
- `print()` function (converted from statement)
- `assert()` function with custom messages
- Extensible architecture for adding more functions
### **Assignment System Design**
- Implemented dual assignment system (statements + expressions)
- Prevents common bugs like `if (x = 10)`
- Maintains practical for loop syntax
- Documentation and testing
### Testing Framework (COMPLETED)
- Comprehensive test suite using assert
- Tests for all language features
- Proper error handling and execution stopping
### **Control Flow Implementation**
- All loop types: while, for, do-while
- Break/continue support
- Nested scenarios
- Basic edge case handling
### Advanced Language Features (COMPLETED)
- First-class functions and higher-order functions
- Function passing as arguments
### **Function System**
- First-class functions
- Closures and lexical scoping
- Anonymous functions
- Function composition patterns
- Callback patterns and function storage
- String + number concatenation with smart formatting
- String multiplication (string * number, number * string)
- Alphanumeric identifiers support
- Stress testing with 100-parameter functions
- Recursion support
---
*Last updated: August 2025*

View File

@ -0,0 +1,10 @@
.vscode/**
.vscode-test/**
src/**
.gitignore
.yarnrc
vsc-extension-quickstart.md
**/tsconfig.json
**/.eslintrc.json
**/*.map
**/*.ts

View File

@ -0,0 +1,97 @@
# Bob Language Extension Installation Guide
## Quick Installation
### Option 1: Automatic Installation (Recommended)
```bash
cd bob-language-extension
./install.sh
```
### Option 2: Manual Installation
1. Copy the extension files to your VS Code extensions directory:
- **macOS**: `~/.vscode/extensions/bob-language-0.1.0/`
- **Linux**: `~/.vscode/extensions/bob-language-0.1.0/`
- **Windows**: `%APPDATA%\Code\User\extensions\bob-language-0.1.0\`
2. Restart VS Code/Cursor
### Option 3: VSIX Package
```bash
cd bob-language-extension
./package-vsix.sh
```
Then install the generated `.vsix` file in VS Code/Cursor.
## Features Included
### ✅ Syntax Highlighting
- Keywords: `if`, `else`, `while`, `for`, `break`, `continue`, `return`, `var`, `func`
- Built-in functions: `print`, `assert`, `input`, `type`, `toString`, `toNumber`, `time`
- Data types: numbers, strings, booleans, `none`
- Operators: arithmetic, comparison, logical, bitwise, compound assignment
### ✅ Code Snippets
Type these prefixes and press `Tab`:
- `func` - Function definition
- `if` - If statement
- `while` - While loop
- `for` - For loop
- `var` - Variable declaration
- `print` - Print statement
- `assert` - Assert statement
- `anon` - Anonymous function
- `test` - Test function
### ✅ Language Features
- Auto-closing brackets and quotes
- Smart indentation
- Comment support (`//` and `/* */`)
- Code folding
- Hover information for built-in functions
- IntelliSense completion
### ✅ Color Theme
- "Bob Dark" theme included
- Optimized colors for Bob syntax
## Testing the Extension
1. Open VS Code/Cursor
2. Open the `example.bob` file in the extension directory
3. You should see syntax highlighting, code completion, and snippets working
## File Association
Files with `.bob` extension will automatically be recognized as Bob language files.
## Troubleshooting
### Extension not working?
1. Check that the extension is installed in the correct directory
2. Restart VS Code/Cursor completely
3. Check the Extensions panel (Ctrl+Shift+X) to see if the extension is listed
### Syntax highlighting not working?
1. Make sure your file has a `.bob` extension
2. Check the language mode in the bottom-right corner of VS Code
3. Manually select "Bob" as the language mode if needed
### Snippets not working?
1. Type the snippet prefix (e.g., `func`)
2. Press `Ctrl+Space` to trigger suggestions
3. Select the snippet and press `Tab`
## Development
To modify the extension:
1. Edit the files in the extension directory
2. Run `npm run compile` to rebuild TypeScript
3. Reload VS Code/Cursor to see changes
## Support
For issues or questions:
1. Check the README.md file
2. Look at the example.bob file for syntax examples
3. Review the TextMate grammar in `syntaxes/bob.tmLanguage.json`

View File

@ -0,0 +1,103 @@
# Bob Language Extension for VS Code
This extension provides syntax highlighting and language support for the Bob programming language in Visual Studio Code and Cursor.
## Features
- **Syntax Highlighting**: Full syntax highlighting for Bob language constructs
- **Code Snippets**: Useful code snippets for common Bob patterns
- **Auto-closing Brackets**: Automatic bracket and quote pairing
- **Indentation**: Smart indentation for Bob code blocks
- **Comments**: Support for line and block comments
- **Folding**: Code folding support with region markers
## Supported Syntax
### Keywords
- Control flow: `if`, `else`, `while`, `for`, `break`, `continue`, `return`
- Variable declaration: `var`
- Function declaration: `func`
- Logical operators: `and`, `or`, `not`
### Built-in Functions
- `print()`, `assert()`, `input()`, `type()`, `toString()`, `toNumber()`, `time()`
### Data Types
- Numbers (integers and floats)
- Strings (single and double quoted)
- Booleans (`true`, `false`)
- None value (`none`)
### Operators
- Arithmetic: `+`, `-`, `*`, `/`, `%`
- Comparison: `==`, `!=`, `<`, `>`, `<=`, `>=`
- Logical: `&&`, `||`, `!`
- Bitwise: `&`, `|`, `^`, `<<`, `>>`, `~`
- Compound assignment: `+=`, `-=`, `*=`, `/=`, `%=`, `&=`, `|=`, `^=`, `<<=`, `>>=`
## Installation
### From Source
1. Clone this repository
2. Run `npm install` to install dependencies
3. Run `npm run compile` to build the extension
4. Press `F5` in VS Code to launch the extension in a new window
### Manual Installation
1. Copy the extension files to your VS Code extensions directory
2. Restart VS Code
3. Open a `.bob` file to see syntax highlighting
## Usage
### Code Snippets
Type the following prefixes and press `Tab` to insert code snippets:
- `func` - Function definition
- `if` - If statement
- `ifelse` - If-else statement
- `while` - While loop
- `for` - For loop
- `var` - Variable declaration
- `print` - Print statement
- `assert` - Assert statement
- `anon` - Anonymous function
- `return` - Return statement
- `break` - Break statement
- `continue` - Continue statement
- `comment` - Comment block
- `test` - Test function
### File Association
Files with the `.bob` extension will automatically be recognized as Bob language files.
## Example
```go
// This is a comment
var message = "Hello, Bob!";
print(message);
func factorial(n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
var result = factorial(5);
assert(result == 120, "Factorial calculation failed");
```
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
This extension is licensed under the MIT License.
## Links
- [Bob Language Repository](https://github.com/bob-lang/bob)
- [VS Code Extension API](https://code.visualstudio.com/api)

Binary file not shown.

View File

@ -0,0 +1,44 @@
#!/bin/bash
# Create a simple VSIX package without npm
echo "Creating Bob Language Extension VSIX package..."
# Read version from package.json
VERSION=$(grep '"version"' package.json | sed 's/.*"version": "\([^"]*\)".*/\1/')
echo "Building version: $VERSION"
# Create a temporary directory for the package
TEMP_DIR=$(mktemp -d)
PACKAGE_DIR="$TEMP_DIR/bob-language-$VERSION"
# Create the extension directory structure
mkdir -p "$TEMP_DIR/extension"
# Copy all extension files to the extension directory
cp package.json "$TEMP_DIR/extension/"
cp language-configuration.json "$TEMP_DIR/extension/"
cp -r syntaxes "$TEMP_DIR/extension/"
cp -r snippets "$TEMP_DIR/extension/"
cp -r themes "$TEMP_DIR/extension/"
cp README.md "$TEMP_DIR/extension/"
# Create the VSIX file (simple zip with .vsix extension)
cd "$TEMP_DIR"
zip -r "bob-language-$VERSION.vsix" extension/
# Move to the extension directory
mv "bob-language-$VERSION.vsix" /Users/bobbylucero/Developer/Bob/bob-language-extension/
# Clean up
rm -rf "$TEMP_DIR"
echo "VSIX package created: bob-language-$VERSION.vsix"
echo ""
echo "To install in Cursor:"
echo "1. Open Cursor"
echo "2. Go to Extensions (Cmd+Shift+X)"
echo "3. Click the '...' menu and select 'Install from VSIX...'"
echo "4. Select the bob-language-$VERSION.vsix file"
echo ""
echo "Or simply restart Cursor - the extension should already be working!"

View File

@ -0,0 +1,75 @@
// Bob Language Example - Demonstrates syntax highlighting
// Variable declarations
var message = "Hello, Bob!";
var number = 42;
var pi = 3.14159;
var isActive = true;
var empty = none;
// Print statements
print(message);
print("Number: " + toString(number));
print("Pi: " + toString(pi));
// Function definition
func factorial(n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
// While loop with break
var counter = 0;
while (counter < 10) {
if (counter == 5) {
break;
}
counter = counter + 1;
}
// For loop with continue
var sum = 0;
for (var i = 0; i < 10; i = i + 1) {
if (i % 2 == 0) {
continue;
}
sum = sum + i;
}
// Anonymous function
var double = func(x) {
return x * 2;
};
// Logical operators
var a = true && false;
var b = true || false;
var c = !false;
// Bitwise operators
var d = 5 & 3;
var e = 5 | 3;
var f = 5 ^ 3;
var g = 5 << 1;
var h = 5 >> 1;
// Compound assignment
var value = 10;
value += 5;
value *= 2;
value -= 3;
// Assertions
assert(factorial(5) == 120, "Factorial calculation failed");
assert(sum == 25, "Sum calculation failed");
// Type checking
var typeOfMessage = type(message);
var typeOfNumber = type(number);
// Time function
var currentTime = time();
print("All tests passed!");

View File

@ -0,0 +1,66 @@
#!/bin/bash
# Bob Language Extension Installer for VS Code/Cursor
echo "Installing Bob Language Extension..."
# Get VS Code extensions directory
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
VSCODE_EXTENSIONS_DIR="$HOME/.vscode/extensions"
CURSOR_EXTENSIONS_DIR="$HOME/.cursor/extensions"
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
# Linux
VSCODE_EXTENSIONS_DIR="$HOME/.vscode/extensions"
CURSOR_EXTENSIONS_DIR="$HOME/.cursor/extensions"
elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]]; then
# Windows
VSCODE_EXTENSIONS_DIR="$APPDATA/Code/User/extensions"
CURSOR_EXTENSIONS_DIR="$APPDATA/Cursor/User/extensions"
else
echo "Unsupported operating system: $OSTYPE"
exit 1
fi
# Create extension directory
EXTENSION_NAME="bob-language-0.1.0"
EXTENSION_DIR="$VSCODE_EXTENSIONS_DIR/$EXTENSION_NAME"
echo "Installing to: $EXTENSION_DIR"
# Create directories
mkdir -p "$EXTENSION_DIR"
# Copy extension files
cp -r package.json "$EXTENSION_DIR/"
cp -r language-configuration.json "$EXTENSION_DIR/"
cp -r syntaxes "$EXTENSION_DIR/"
cp -r snippets "$EXTENSION_DIR/"
cp -r README.md "$EXTENSION_DIR/"
# Compile TypeScript if available
if command -v npm &> /dev/null; then
echo "Compiling TypeScript..."
npm install
npm run compile
cp -r out "$EXTENSION_DIR/"
else
echo "npm not found, skipping TypeScript compilation"
fi
echo "Bob Language Extension installed successfully!"
echo ""
echo "To use the extension:"
echo "1. Restart VS Code/Cursor"
echo "2. Open a .bob file"
echo "3. Enjoy syntax highlighting and code snippets!"
echo ""
echo "Code snippets available:"
echo "- func: Function definition"
echo "- if: If statement"
echo "- while: While loop"
echo "- for: For loop"
echo "- var: Variable declaration"
echo "- print: Print statement"
echo "- assert: Assert statement"
echo "- And many more!"

View File

@ -0,0 +1,35 @@
{
"comments": {
"lineComment": "//",
"blockComment": ["/*", "*/"]
},
"brackets": [
["{", "}"],
["[", "]"],
["(", ")"]
],
"autoClosingPairs": [
{ "open": "{", "close": "}" },
{ "open": "[", "close": "]" },
{ "open": "(", "close": ")" },
{ "open": "\"", "close": "\"", "notIn": ["string"] },
{ "open": "'", "close": "'", "notIn": ["string"] }
],
"surroundingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"]
],
"indentationRules": {
"increaseIndentPattern": "\\{[^}]*$|\\b(func|if|else|while|for)\\b.*$",
"decreaseIndentPattern": "^\\s*[})]"
},
"folding": {
"markers": {
"start": "^\\s*//\\s*#?region\\b",
"end": "^\\s*//\\s*#?endregion\\b"
}
}
}

View File

@ -0,0 +1,27 @@
#!/bin/bash
# Package Bob Language Extension as VSIX
echo "Packaging Bob Language Extension..."
# Check if vsce is installed
if ! command -v vsce &> /dev/null; then
echo "Installing vsce..."
npm install -g @vscode/vsce
fi
# Install dependencies
npm install
# Compile TypeScript
npm run compile
# Package the extension
vsce package
echo "Extension packaged successfully!"
echo "You can now install the .vsix file in VS Code/Cursor:"
echo "1. Open VS Code/Cursor"
echo "2. Go to Extensions (Ctrl+Shift+X)"
echo "3. Click the '...' menu and select 'Install from VSIX...'"
echo "4. Select the generated .vsix file"

View File

@ -0,0 +1,74 @@
{
"name": "bob-language",
"displayName": "Bob Language",
"description": "Syntax highlighting and language support for the Bob programming language",
"version": "0.1.2",
"engines": {
"vscode": "^1.60.0"
},
"categories": [
"Programming Languages"
],
"keywords": [
"bob",
"programming",
"language",
"syntax",
"highlighting"
],
"publisher": "bob-lang",
"repository": {
"type": "git",
"url": "https://github.com/bob-lang/bob-vscode-extension"
},
"license": "MIT",
"main": "./out/extension.js",
"activationEvents": [
"onLanguage:bob"
],
"contributes": {
"languages": [
{
"id": "bob",
"aliases": [
"Bob",
"bob"
],
"extensions": [
".bob"
],
"configuration": "./language-configuration.json"
}
],
"grammars": [
{
"language": "bob",
"scopeName": "source.bob",
"path": "./syntaxes/bob.tmLanguage.json"
}
],
"snippets": [
{
"language": "bob",
"path": "./snippets/bob.json"
}
],
"themes": [
{
"label": "Bob Dark",
"uiTheme": "vs-dark",
"path": "./themes/bob-dark.json"
}
]
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./"
},
"devDependencies": {
"@types/vscode": "^1.60.0",
"@types/node": "^16.0.0",
"typescript": "^4.5.0"
}
}

View File

@ -0,0 +1,29 @@
#!/bin/bash
echo "Attempting to reload Bob language extension..."
# Try to reload the extension using VS Code CLI if available
if command -v code &> /dev/null; then
echo "Found VS Code CLI, attempting to reload window..."
code --command "workbench.action.reloadWindow"
echo "Reload command sent to VS Code/Cursor"
elif command -v cursor &> /dev/null; then
echo "Found Cursor CLI, attempting to reload window..."
cursor --command "workbench.action.reloadWindow"
echo "Reload command sent to Cursor"
else
echo "VS Code/Cursor CLI not found in PATH"
echo ""
echo "Manual reload required:"
echo "1. Open Cursor"
echo "2. Press Cmd+Shift+P (or Ctrl+Shift+P on Windows/Linux)"
echo "3. Type 'Developer: Reload Window' and press Enter"
echo ""
echo "Or alternatively:"
echo "1. Press Cmd+Shift+X to open Extensions"
echo "2. Find 'Bob Language' extension"
echo "3. Click the reload button (circular arrow icon)"
fi
echo ""
echo "Extension should now be reloaded with updated syntax highlighting!"

View File

@ -0,0 +1,291 @@
{
"Function Definition": {
"prefix": "func",
"body": [
"func ${1:functionName}(${2:parameters}) {",
"\t$0",
"}"
],
"description": "Create a new function"
},
"If Statement": {
"prefix": "if",
"body": [
"if (${1:condition}) {",
"\t$0",
"}"
],
"description": "Create an if statement"
},
"If-Else Statement": {
"prefix": "ifelse",
"body": [
"if (${1:condition}) {",
"\t$2",
"} else {",
"\t$0",
"}"
],
"description": "Create an if-else statement"
},
"While Loop": {
"prefix": "while",
"body": [
"while (${1:condition}) {",
"\t$0",
"}"
],
"description": "Create a while loop"
},
"For Loop": {
"prefix": "for",
"body": [
"for (${1:initialization}; ${2:condition}; ${3:increment}) {",
"\t$0",
"}"
],
"description": "Create a for loop"
},
"Variable Declaration": {
"prefix": "var",
"body": [
"var ${1:variableName} = ${2:value};"
],
"description": "Declare a variable"
},
"Print Statement": {
"prefix": "print",
"body": [
"print(${1:expression});"
],
"description": "Print a value"
},
"Assert Statement": {
"prefix": "assert",
"body": [
"assert(${1:condition}, \"${2:message}\");"
],
"description": "Create an assertion"
},
"Anonymous Function": {
"prefix": "anon",
"body": [
"func(${1:parameters}) {",
"\t$0",
"}"
],
"description": "Create an anonymous function"
},
"Return Statement": {
"prefix": "return",
"body": [
"return ${1:value};"
],
"description": "Return a value from function"
},
"Break Statement": {
"prefix": "break",
"body": [
"break;"
],
"description": "Break out of loop"
},
"Continue Statement": {
"prefix": "continue",
"body": [
"continue;"
],
"description": "Continue to next iteration"
},
"Comment Block": {
"prefix": "comment",
"body": [
"/*",
" * ${1:comment}",
" */"
],
"description": "Create a comment block"
},
"Test Function": {
"prefix": "test",
"body": [
"func test${1:TestName}() {",
"\tvar result = ${2:testExpression};",
"\tassert(result == ${3:expectedValue}, \"${4:test message}\");",
"\tprint(\"${1:TestName}: PASS\");",
"}"
],
"description": "Create a test function"
},
"Higher-Order Function": {
"prefix": "hof",
"body": [
"func ${1:functionName}(${2:callback}) {",
"\treturn func(${3:params}) {",
"\t\t$0",
"\t};",
"}"
],
"description": "Create a higher-order function"
},
"Closure": {
"prefix": "closure",
"body": [
"func ${1:outerFunction}(${2:param}) {",
"\tvar ${3:capturedVar} = ${4:value};",
"\treturn func(${5:innerParam}) {",
"\t\treturn ${3:capturedVar} + ${5:innerParam};",
"\t};",
"}"
],
"description": "Create a closure"
},
"Counter Pattern": {
"prefix": "counter",
"body": [
"func createCounter() {",
"\tvar count = 0;",
"\treturn func() {",
"\t\tcount = count + 1;",
"\t\treturn count;",
"\t};",
"}"
],
"description": "Create a counter function"
},
"Loop with Break": {
"prefix": "loopbreak",
"body": [
"while (${1:condition}) {",
"\tif (${2:breakCondition}) {",
"\t\tbreak;",
"\t}",
"\t$0",
"}"
],
"description": "Create a loop with break condition"
},
"Loop with Continue": {
"prefix": "loopcontinue",
"body": [
"for (${1:initialization}; ${2:condition}; ${3:increment}) {",
"\tif (${4:continueCondition}) {",
"\t\tcontinue;",
"\t}",
"\t$0",
"}"
],
"description": "Create a loop with continue condition"
},
"Nested Loop": {
"prefix": "nestedloop",
"body": [
"for (var i = 0; i < ${1:outerLimit}; i = i + 1) {",
"\tfor (var j = 0; j < ${2:innerLimit}; j = j + 1) {",
"\t\t$0",
"\t}",
"}"
],
"description": "Create nested loops"
},
"Function with Early Return": {
"prefix": "earlyreturn",
"body": [
"func ${1:functionName}(${2:param}) {",
"\tif (${3:condition}) {",
"\t\treturn ${4:earlyValue};",
"\t}",
"\t$0",
"\treturn ${5:defaultValue};",
"}"
],
"description": "Create a function with early return"
},
"String Concatenation": {
"prefix": "concat",
"body": [
"var result = \"${1:first}\" + \"${2:second}\";"
],
"description": "Concatenate strings"
},
"Number Operations": {
"prefix": "math",
"body": [
"var result = ${1:expression};"
],
"description": "Mathematical expression"
},
"Boolean Logic": {
"prefix": "bool",
"body": [
"var result = ${1:condition} == ${2:value};"
],
"description": "Boolean comparison"
},
"Type Check": {
"prefix": "type",
"body": [
"var typeResult = type(${1:expression});"
],
"description": "Check type of expression"
},
"ToString": {
"prefix": "tostring",
"body": [
"var stringResult = toString(${1:expression});"
],
"description": "Convert to string"
},
"Test Suite Header": {
"prefix": "testsuite",
"body": [
"// ========================================",
"// ${1:TEST SUITE NAME}",
"// ========================================",
"// ${2:Description}",
"",
"print(\"${1:TEST SUITE NAME}\");",
"print(\"Running tests...\");",
"",
"$0",
"",
"print(\"All tests passed!\");"
],
"description": "Create a test suite header"
},
"Test Section": {
"prefix": "testsection",
"body": [
"// ========================================",
"// TEST ${1:NUMBER}: ${2:TEST NAME}",
"// ========================================",
"print(\"\\n--- Test ${1:NUMBER}: ${2:TEST NAME} ---\");",
"",
"$0",
"",
"print(\"${2:TEST NAME}: PASS\");"
],
"description": "Create a test section"
},
"Debug Print": {
"prefix": "debug",
"body": [
"print(\"DEBUG: ${1:variable} = \" + toString(${1:variable}));"
],
"description": "Debug print statement"
},
"Error Message": {
"prefix": "error",
"body": [
"print(\"ERROR: ${1:error message}\");"
],
"description": "Error message print"
},
"Success Message": {
"prefix": "success",
"body": [
"print(\"SUCCESS: ${1:success message}\");"
],
"description": "Success message print"
}
}

View File

@ -0,0 +1,104 @@
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
console.log('Bob language extension is now active!');
// Register language configuration
const bobLanguageConfig = vscode.languages.setLanguageConfiguration('bob', {
comments: {
lineComment: '//',
blockComment: ['/*', '*/']
},
brackets: [
['{', '}'],
['[', ']'],
['(', ')']
],
autoClosingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '"', close: '"', notIn: ['string'] },
{ open: "'", close: "'", notIn: ['string'] }
],
surroundingPairs: [
['{', '}'],
['[', ']'],
['(', ')'],
['"', '"'],
["'", "'"]
],
indentationRules: {
increaseIndentPattern: /\{[^}]*$|\b(func|if|else|while|for)\b.*$/,
decreaseIndentPattern: /^\s*[})]/
}
});
context.subscriptions.push(bobLanguageConfig);
// Register hover provider for built-in functions
const hoverProvider = vscode.languages.registerHoverProvider('bob', {
provideHover(document, position, token) {
const range = document.getWordRangeAtPosition(position);
const word = document.getText(range);
const builtinFunctions = {
'print': 'Prints a value to the console',
'assert': 'Asserts a condition and throws an error if false',
'input': 'Gets user input from the console',
'type': 'Returns the type of a value',
'toString': 'Converts a value to a string',
'toNumber': 'Converts a value to a number',
'time': 'Returns the current time in microseconds'
};
if (builtinFunctions[word]) {
return new vscode.Hover(`**${word}()** - ${builtinFunctions[word]}`);
}
return null;
}
});
context.subscriptions.push(hoverProvider);
// Register completion provider
const completionProvider = vscode.languages.registerCompletionItemProvider('bob', {
provideCompletionItems(document, position, token, context) {
const completions = [];
// Keywords
const keywords = ['if', 'else', 'while', 'for', 'break', 'continue', 'return', 'var', 'func'];
keywords.forEach(keyword => {
const item = new vscode.CompletionItem(keyword, vscode.CompletionItemKind.Keyword);
item.detail = 'Bob keyword';
completions.push(item);
});
// Built-in functions
const builtins = ['print', 'assert', 'input', 'type', 'toString', 'toNumber', 'time'];
builtins.forEach(func => {
const item = new vscode.CompletionItem(func, vscode.CompletionItemKind.Function);
item.detail = 'Built-in function';
item.insertText = func + '()';
completions.push(item);
});
// Constants
const constants = ['true', 'false', 'none'];
constants.forEach(constant => {
const item = new vscode.CompletionItem(constant, vscode.CompletionItemKind.Constant);
item.detail = 'Bob constant';
completions.push(item);
});
return completions;
}
}, '.');
context.subscriptions.push(completionProvider);
}
export function deactivate() {
console.log('Bob language extension is now deactivated!');
}

View File

@ -0,0 +1,175 @@
{
"name": "Bob",
"scopeName": "source.bob",
"patterns": [
{
"include": "#comments"
},
{
"include": "#strings"
},
{
"include": "#numbers"
},
{
"include": "#keywords"
},
{
"include": "#functions"
},
{
"include": "#variables"
},
{
"include": "#operators"
}
],
"repository": {
"comments": {
"patterns": [
{
"name": "comment.line.double-slash.bob",
"match": "//.*$"
},
{
"name": "comment.block.bob",
"begin": "/\\*",
"end": "\\*/"
}
]
},
"strings": {
"patterns": [
{
"name": "string.quoted.double.bob",
"begin": "\"",
"end": "\"",
"patterns": [
{
"name": "constant.character.escape.bob",
"match": "\\\\[nt\"\\\\]"
}
]
},
{
"name": "string.quoted.single.bob",
"begin": "'",
"end": "'",
"patterns": [
{
"name": "constant.character.escape.bob",
"match": "\\\\[nt'\\\\]"
}
]
}
]
},
"numbers": {
"patterns": [
{
"name": "constant.numeric.integer.bob",
"match": "\\b\\d+\\b"
},
{
"name": "constant.numeric.float.bob",
"match": "\\b\\d+\\.\\d+\\b"
}
]
},
"keywords": {
"patterns": [
{
"name": "keyword.control.bob",
"match": "\\b(if|else|while|do|for|break|continue|return|var|func)\\b"
},
{
"name": "keyword.operator.bob",
"match": "\\b(and|or|not)\\b"
},
{
"name": "constant.language.bob",
"match": "\\b(true|false|none)\\b"
}
]
},
"functions": {
"patterns": [
{
"name": "entity.name.function.bob",
"match": "\\b(func)\\s+([a-zA-Z_][a-zA-Z0-9_]*)",
"captures": {
"1": { "name": "keyword.control.bob" },
"2": { "name": "entity.name.function.bob" }
}
},
{
"name": "support.function.builtin.bob",
"match": "\\b(print|assert|input|type|toString|toNumber|time)\\b"
}
]
},
"variables": {
"patterns": [
{
"name": "variable.other.bob",
"match": "\\bvar\\s+([a-zA-Z_][a-zA-Z0-9_]*)",
"captures": {
"1": { "name": "keyword.control.bob" },
"2": { "name": "variable.other.bob" }
}
},
{
"name": "variable.other.bob",
"match": "\\b([a-zA-Z_][a-zA-Z0-9_]*)(?=\\s*=)",
"captures": {
"1": { "name": "variable.other.bob" }
}
}
]
},
"operators": {
"patterns": [
{
"name": "keyword.operator.increment.bob",
"match": "\\+\\+|--"
},
{
"name": "keyword.operator.compound.bob",
"match": "\\+=|-=|\\*=|/=|%=|&=|\\|=|\\^=|<<=|>>="
},
{
"name": "keyword.operator.comparison.bob",
"match": "==|!=|<=|>="
},
{
"name": "keyword.operator.logical.bob",
"match": "&&|\\|\\||!"
},
{
"name": "keyword.operator.bitwise.bob",
"match": "<<|>>|&|\\||\\^|~"
},
{
"name": "keyword.operator.arithmetic.bob",
"match": "\\+|-|\\*|/|%"
},
{
"name": "keyword.operator.comparison.bob",
"match": "<|>"
},
{
"name": "keyword.operator.assignment.bob",
"match": "="
},
{
"name": "keyword.operator.punctuation.bob",
"match": "\\(|\\)|\\{|\\}|\\[|\\]|,|;|\\."
},
{
"name": "keyword.operator.conditional.bob",
"match": "\\?|:"
}
]
}
}
}

View File

@ -0,0 +1,58 @@
// Test file for operator syntax highlighting
var x = 5;
var y = 10;
var z = x + y;
var w = x - y;
var a = x * y;
var b = x / y;
var c = x % y;
// Comparison operators
var eq = x == y;
var ne = x != y;
var lt = x < y;
var gt = x > y;
var le = x <= y;
var ge = x >= y;
// Increment/decrement
x++;
y--;
// Compound assignment
x += 5;
y -= 3;
z *= 2;
w /= 4;
a %= 3;
// Logical operators
var and = true && false;
var or = true || false;
var not = !true;
// Bitwise operators
var bit_and = x & y;
var bit_or = x | y;
var bit_xor = x ^ y;
var left_shift = x << 2;
var right_shift = x >> 1;
// Compound bitwise assignment
x &= y;
y |= z;
z ^= w;
w <<= 2;
a >>= 1;
// Function with operators
func test_operators() {
var result = 0;
if (x >= 0 && y <= 100) {
result = x + y * 2;
if (result != 0) {
result++;
}
}
return result;
}

View File

@ -0,0 +1,79 @@
{
"name": "Bob Dark",
"type": "dark",
"colors": {
"editor.background": "#1e1e1e",
"editor.foreground": "#d4d4d4",
"editor.lineHighlightBackground": "#2a2a2a",
"editor.selectionBackground": "#264f78",
"editor.inactiveSelectionBackground": "#3a3d41"
},
"tokenColors": [
{
"name": "Comments",
"scope": ["comment", "punctuation.definition.comment"],
"settings": {
"foreground": "#6a9955"
}
},
{
"name": "Strings",
"scope": ["string", "string.quoted"],
"settings": {
"foreground": "#ce9178"
}
},
{
"name": "Numbers",
"scope": ["constant.numeric"],
"settings": {
"foreground": "#b5cea8"
}
},
{
"name": "Keywords",
"scope": ["keyword.control", "keyword.operator"],
"settings": {
"foreground": "#569cd6"
}
},
{
"name": "Functions",
"scope": ["entity.name.function", "support.function.builtin"],
"settings": {
"foreground": "#dcdcaa"
}
},
{
"name": "Variables",
"scope": ["variable.other"],
"settings": {
"foreground": "#9cdcfe"
}
},
{
"name": "Constants",
"scope": ["constant.language"],
"settings": {
"foreground": "#4ec9b0"
}
},
{
"name": "Operators",
"scope": [
"keyword.operator.arithmetic",
"keyword.operator.comparison",
"keyword.operator.logical",
"keyword.operator.bitwise",
"keyword.operator.assignment",
"keyword.operator.compound",
"keyword.operator.increment",
"keyword.operator.punctuation",
"keyword.operator.conditional"
],
"settings": {
"foreground": "#d4d4d4"
}
}
]
}

View File

@ -0,0 +1,17 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "ES2020",
"outDir": "out",
"lib": [
"ES2020"
],
"sourceMap": true,
"rootDir": "src",
"strict": true
},
"exclude": [
"node_modules",
".vscode-test"
]
}

View File

@ -13,6 +13,7 @@
// Forward declarations
struct FunctionExpr;
struct IncrementExpr;
struct TernaryExpr;
struct ExprVisitor;
struct AssignExpr;
@ -35,6 +36,7 @@ struct ExprVisitor
virtual Value visitLiteralExpr(const std::shared_ptr<LiteralExpr>& expr) = 0;
virtual Value visitUnaryExpr(const std::shared_ptr<UnaryExpr>& expr) = 0;
virtual Value visitVarExpr(const std::shared_ptr<VarExpr>& expr) = 0;
virtual Value visitTernaryExpr(const std::shared_ptr<TernaryExpr>& expr) = 0;
};
struct Expr : public std::enable_shared_from_this<Expr> {
@ -152,3 +154,17 @@ struct IncrementExpr : Expr
}
};
struct TernaryExpr : Expr
{
std::shared_ptr<Expr> condition;
std::shared_ptr<Expr> thenExpr;
std::shared_ptr<Expr> elseExpr;
TernaryExpr(std::shared_ptr<Expr> condition, std::shared_ptr<Expr> thenExpr, std::shared_ptr<Expr> elseExpr)
: condition(condition), thenExpr(thenExpr), elseExpr(elseExpr) {}
Value accept(ExprVisitor* visitor) override
{
return visitor->visitTernaryExpr(std::static_pointer_cast<TernaryExpr>(shared_from_this()));
}
};

View File

@ -65,6 +65,7 @@ public:
Value visitVarExpr(const std::shared_ptr<VarExpr>& expression) override;
Value visitIncrementExpr(const std::shared_ptr<IncrementExpr>& expression) override;
Value visitAssignExpr(const std::shared_ptr<AssignExpr>& expression) override;
Value visitTernaryExpr(const std::shared_ptr<TernaryExpr>& expression) override;
void visitBlockStmt(const std::shared_ptr<BlockStmt>& statement, ExecutionContext* context = nullptr) override;
void visitExpressionStmt(const std::shared_ptr<ExpressionStmt>& statement, ExecutionContext* context = nullptr) override;
@ -72,6 +73,12 @@ public:
void visitFunctionStmt(const std::shared_ptr<FunctionStmt>& statement, ExecutionContext* context = nullptr) override;
void visitReturnStmt(const std::shared_ptr<ReturnStmt>& statement, ExecutionContext* context = nullptr) override;
void visitIfStmt(const std::shared_ptr<IfStmt>& statement, ExecutionContext* context = nullptr) override;
void visitWhileStmt(const std::shared_ptr<WhileStmt>& statement, ExecutionContext* context = nullptr) override;
void visitDoWhileStmt(const std::shared_ptr<DoWhileStmt>& statement, ExecutionContext* context = nullptr) override;
void visitForStmt(const std::shared_ptr<ForStmt>& statement, ExecutionContext* context = nullptr) override;
void visitBreakStmt(const std::shared_ptr<BreakStmt>& statement, ExecutionContext* context = nullptr) override;
void visitContinueStmt(const std::shared_ptr<ContinueStmt>& statement, ExecutionContext* context = nullptr) override;
void visitAssignStmt(const std::shared_ptr<AssignStmt>& statement, ExecutionContext* context = nullptr) override;
void interpret(std::vector<std::shared_ptr<Stmt> > statements);
@ -85,9 +92,15 @@ private:
bool IsInteractive;
std::vector<std::shared_ptr<BuiltinFunction> > builtinFunctions;
std::vector<std::shared_ptr<Function> > functions;
std::vector<std::shared_ptr<Thunk> > thunks; // Store thunks to prevent memory leaks
ErrorReporter* errorReporter;
bool inThunkExecution = false;
// Automatic cleanup tracking
int functionCreationCount = 0;
int thunkCreationCount = 0;
static const int CLEANUP_THRESHOLD = 1000; // Cleanup every 1000 creations
Value evaluate(const std::shared_ptr<Expr>& expr);
@ -105,6 +118,10 @@ public:
bool isTruthy(Value object);
std::string stringify(Value object);
void addBuiltinFunction(std::shared_ptr<BuiltinFunction> func);
// Memory management
void cleanupUnusedFunctions();
void cleanupUnusedThunks();
// Error reporting
void setErrorReporter(ErrorReporter* reporter) {

View File

@ -15,13 +15,16 @@ enum TokenType{
GREATER, GREATER_EQUAL,
LESS, LESS_EQUAL,
// Ternary operator
QUESTION, COLON,
// Increment/decrement operators
PLUS_PLUS, MINUS_MINUS,
IDENTIFIER, STRING, NUMBER, BOOL,
AND, OR, TRUE, FALSE, IF, ELSE, FUNCTION, FOR,
WHILE, VAR, CLASS, SUPER, THIS, NONE, RETURN,
WHILE, DO, VAR, CLASS, SUPER, THIS, NONE, RETURN, BREAK, CONTINUE,
// Compound assignment operators
PLUS_EQUAL, MINUS_EQUAL, STAR_EQUAL, SLASH_EQUAL, PERCENT_EQUAL,
@ -42,12 +45,14 @@ inline std::string enum_mapping[] = {"OPEN_PAREN", "CLOSE_PAREN", "OPEN_BRACE",
"GREATER", "GREATER_EQUAL",
"LESS", "LESS_EQUAL",
"QUESTION", "COLON",
"PLUS_PLUS", "MINUS_MINUS",
"IDENTIFIER", "STRING", "NUMBER", "BOOL",
"AND", "OR", "TRUE", "FALSE", "IF", "ELSE", "FUNCTION", "FOR",
"WHILE", "VAR", "CLASS", "SUPER", "THIS", "NONE", "RETURN",
"WHILE", "DO", "VAR", "CLASS", "SUPER", "THIS", "NONE", "RETURN", "BREAK", "CONTINUE",
// Compound assignment operators
"PLUS_EQUAL", "MINUS_EQUAL", "STAR_EQUAL", "SLASH_EQUAL", "PERCENT_EQUAL",
@ -67,12 +72,15 @@ const std::map<std::string, TokenType> KEYWORDS {
{"func", FUNCTION},
{"for", FOR},
{"while", WHILE},
{"do", DO},
{"var", VAR},
{"class", CLASS},
{"super", SUPER},
{"this", THIS},
{"none", NONE},
{"return", RETURN},
{"break", BREAK},
{"continue", CONTINUE},
};
struct Token

View File

@ -24,6 +24,7 @@ public:
private:
sptr(Expr) expression();
sptr(Expr) logical_or();
sptr(Expr) ternary();
sptr(Expr) logical_and();
sptr(Expr) bitwise_or();
sptr(Expr) bitwise_xor();
@ -56,6 +57,16 @@ private:
std::shared_ptr<Stmt> ifStatement();
std::shared_ptr<Stmt> whileStatement();
std::shared_ptr<Stmt> doWhileStatement();
std::shared_ptr<Stmt> forStatement();
std::shared_ptr<Stmt> breakStatement();
std::shared_ptr<Stmt> continueStatement();
std::shared_ptr<Stmt> declaration();
std::shared_ptr<Stmt> varDeclaration();
@ -63,7 +74,9 @@ private:
std::shared_ptr<Stmt> functionDeclaration();
std::shared_ptr<Expr> functionExpression();
std::shared_ptr<Stmt> assignmentStatement();
sptr(Expr) assignment();
sptr(Expr) assignmentExpression(); // For for loop increment clauses
sptr(Expr) increment(); // Parse increment/decrement expressions
sptr(Expr) postfix(); // Parse postfix operators

View File

@ -11,10 +11,18 @@ struct BlockStmt;
struct FunctionStmt;
struct ReturnStmt;
struct IfStmt;
struct WhileStmt;
struct DoWhileStmt;
struct ForStmt;
struct BreakStmt;
struct ContinueStmt;
struct AssignStmt;
struct ExecutionContext {
bool isFunctionBody = false;
bool hasReturn = false;
bool hasBreak = false;
bool hasContinue = false;
Value returnValue;
};
@ -26,6 +34,12 @@ struct StmtVisitor
virtual void visitFunctionStmt(const std::shared_ptr<FunctionStmt>& stmt, ExecutionContext* context = nullptr) = 0;
virtual void visitReturnStmt(const std::shared_ptr<ReturnStmt>& stmt, ExecutionContext* context = nullptr) = 0;
virtual void visitIfStmt(const std::shared_ptr<IfStmt>& stmt, ExecutionContext* context = nullptr) = 0;
virtual void visitWhileStmt(const std::shared_ptr<WhileStmt>& stmt, ExecutionContext* context = nullptr) = 0;
virtual void visitDoWhileStmt(const std::shared_ptr<DoWhileStmt>& stmt, ExecutionContext* context = nullptr) = 0;
virtual void visitForStmt(const std::shared_ptr<ForStmt>& stmt, ExecutionContext* context = nullptr) = 0;
virtual void visitBreakStmt(const std::shared_ptr<BreakStmt>& stmt, ExecutionContext* context = nullptr) = 0;
virtual void visitContinueStmt(const std::shared_ptr<ContinueStmt>& stmt, ExecutionContext* context = nullptr) = 0;
virtual void visitAssignStmt(const std::shared_ptr<AssignStmt>& stmt, ExecutionContext* context = nullptr) = 0;
};
struct Stmt : public std::enable_shared_from_this<Stmt>
@ -117,4 +131,88 @@ struct IfStmt : Stmt
{
visitor->visitIfStmt(std::static_pointer_cast<IfStmt>(shared_from_this()), context);
}
};
struct WhileStmt : Stmt
{
std::shared_ptr<Expr> condition;
std::shared_ptr<Stmt> body;
WhileStmt(std::shared_ptr<Expr> condition, std::shared_ptr<Stmt> body)
: condition(condition), body(body) {}
void accept(StmtVisitor* visitor, ExecutionContext* context = nullptr) override
{
visitor->visitWhileStmt(std::static_pointer_cast<WhileStmt>(shared_from_this()), context);
}
};
struct DoWhileStmt : Stmt
{
std::shared_ptr<Stmt> body;
std::shared_ptr<Expr> condition;
DoWhileStmt(std::shared_ptr<Stmt> body, std::shared_ptr<Expr> condition)
: body(body), condition(condition) {}
void accept(StmtVisitor* visitor, ExecutionContext* context = nullptr) override
{
visitor->visitDoWhileStmt(std::static_pointer_cast<DoWhileStmt>(shared_from_this()), context);
}
};
struct ForStmt : Stmt
{
std::shared_ptr<Stmt> initializer;
std::shared_ptr<Expr> condition;
std::shared_ptr<Expr> increment;
std::shared_ptr<Stmt> body;
ForStmt(std::shared_ptr<Stmt> initializer, std::shared_ptr<Expr> condition,
std::shared_ptr<Expr> increment, std::shared_ptr<Stmt> body)
: initializer(initializer), condition(condition), increment(increment), body(body) {}
void accept(StmtVisitor* visitor, ExecutionContext* context = nullptr) override
{
visitor->visitForStmt(std::static_pointer_cast<ForStmt>(shared_from_this()), context);
}
};
struct BreakStmt : Stmt
{
const Token keyword;
BreakStmt(Token keyword) : keyword(keyword) {}
void accept(StmtVisitor* visitor, ExecutionContext* context = nullptr) override
{
visitor->visitBreakStmt(std::static_pointer_cast<BreakStmt>(shared_from_this()), context);
}
};
struct ContinueStmt : Stmt
{
const Token keyword;
ContinueStmt(Token keyword) : keyword(keyword) {}
void accept(StmtVisitor* visitor, ExecutionContext* context = nullptr) override
{
visitor->visitContinueStmt(std::static_pointer_cast<ContinueStmt>(shared_from_this()), context);
}
};
struct AssignStmt : Stmt
{
const Token name;
const Token op;
std::shared_ptr<Expr> value;
AssignStmt(Token name, Token op, std::shared_ptr<Expr> value)
: name(name), op(op), value(value) {}
void accept(StmtVisitor* visitor, ExecutionContext* context = nullptr) override
{
visitor->visitAssignStmt(std::static_pointer_cast<AssignStmt>(shared_from_this()), context);
}
};

BIN
source/.DS_Store vendored

Binary file not shown.

View File

@ -580,6 +580,16 @@ Value Interpreter::visitAssignExpr(const std::shared_ptr<AssignExpr>& expression
return value;
}
Value Interpreter::visitTernaryExpr(const std::shared_ptr<TernaryExpr>& expression) {
Value condition = evaluate(expression->condition);
if (isTruthy(condition)) {
return evaluate(expression->thenExpr);
} else {
return evaluate(expression->elseExpr);
}
}
Value Interpreter::visitCallExpr(const std::shared_ptr<CallExpr>& expression) {
Value callee = evaluate(expression->callee);
@ -602,8 +612,8 @@ Value Interpreter::visitCallExpr(const std::shared_ptr<CallExpr>& expression) {
// Check if this is a tail call
if (expression->isTailCall) {
// Create a thunk for tail call optimization
auto thunk = new Thunk([this, function, arguments]() -> Value {
// Create a thunk for tail call optimization using smart pointer
auto thunk = std::make_shared<Thunk>([this, function, arguments]() -> Value {
// Use RAII to manage environment
ScopedEnv _env(environment);
environment = std::make_shared<Environment>(function->closure);
@ -630,8 +640,17 @@ Value Interpreter::visitCallExpr(const std::shared_ptr<CallExpr>& expression) {
return context.returnValue;
});
// Return the thunk as a Value
return Value(thunk);
// Store the thunk to keep it alive and return as Value
thunks.push_back(thunk);
// Automatic cleanup check
thunkCreationCount++;
if (thunkCreationCount >= CLEANUP_THRESHOLD) {
cleanupUnusedThunks();
thunkCreationCount = 0;
}
return Value(thunk.get());
} else {
// Normal function call - create new environment
ScopedEnv _env(environment);
@ -669,6 +688,14 @@ Value Interpreter::visitFunctionExpr(const std::shared_ptr<FunctionExpr>& expres
auto function = msptr(Function)("anonymous", paramNames, expression->body, environment);
functions.push_back(function); // Keep the shared_ptr alive
// Automatic cleanup check
functionCreationCount++;
if (functionCreationCount >= CLEANUP_THRESHOLD) {
cleanupUnusedFunctions();
functionCreationCount = 0;
}
return Value(function.get());
}
@ -714,6 +741,13 @@ void Interpreter::visitFunctionStmt(const std::shared_ptr<FunctionStmt>& stateme
environment);
functions.push_back(function); // Keep the shared_ptr alive
environment->define(statement->name.lexeme, Value(function.get()));
// Automatic cleanup check
functionCreationCount++;
if (functionCreationCount >= CLEANUP_THRESHOLD) {
cleanupUnusedFunctions();
functionCreationCount = 0;
}
}
void Interpreter::visitReturnStmt(const std::shared_ptr<ReturnStmt>& statement, ExecutionContext* context)
@ -741,6 +775,175 @@ void Interpreter::visitIfStmt(const std::shared_ptr<IfStmt>& statement, Executio
}
}
void Interpreter::visitWhileStmt(const std::shared_ptr<WhileStmt>& statement, ExecutionContext* context)
{
ExecutionContext loopContext;
if (context) {
loopContext.isFunctionBody = context->isFunctionBody;
}
while (isTruthy(evaluate(statement->condition))) {
execute(statement->body, &loopContext);
// Check for return from function
if (loopContext.hasReturn) {
if (context) {
context->hasReturn = true;
context->returnValue = loopContext.returnValue;
}
break;
}
// Check for break
if (loopContext.hasBreak) {
loopContext.hasBreak = false;
break;
}
// Check for continue (just continue to next iteration)
if (loopContext.hasContinue) {
loopContext.hasContinue = false;
continue;
}
}
}
void Interpreter::visitDoWhileStmt(const std::shared_ptr<DoWhileStmt>& statement, ExecutionContext* context)
{
ExecutionContext loopContext;
if (context) {
loopContext.isFunctionBody = context->isFunctionBody;
}
do {
execute(statement->body, &loopContext);
// Check for return from function
if (loopContext.hasReturn) {
if (context) {
context->hasReturn = true;
context->returnValue = loopContext.returnValue;
}
break;
}
// Check for break
if (loopContext.hasBreak) {
loopContext.hasBreak = false;
break;
}
// Check for continue (just continue to next iteration)
if (loopContext.hasContinue) {
loopContext.hasContinue = false;
continue;
}
} while (isTruthy(evaluate(statement->condition)));
}
void Interpreter::visitForStmt(const std::shared_ptr<ForStmt>& statement, ExecutionContext* context)
{
// For loops are desugared into while loops in the parser
// This method should never be called, but we implement it for completeness
// The actual execution happens through the desugared while loop
if (statement->initializer != nullptr) {
execute(statement->initializer, context);
}
ExecutionContext loopContext;
if (context) {
loopContext.isFunctionBody = context->isFunctionBody;
}
while (statement->condition == nullptr || isTruthy(evaluate(statement->condition))) {
execute(statement->body, &loopContext);
// Check for return from function
if (loopContext.hasReturn) {
if (context) {
context->hasReturn = true;
context->returnValue = loopContext.returnValue;
}
break;
}
// Check for break
if (loopContext.hasBreak) {
loopContext.hasBreak = false;
break;
}
// Check for continue (execute increment then continue to next iteration)
if (loopContext.hasContinue) {
loopContext.hasContinue = false;
if (statement->increment != nullptr) {
evaluate(statement->increment);
}
continue;
}
if (statement->increment != nullptr) {
evaluate(statement->increment);
}
}
}
void Interpreter::visitBreakStmt(const std::shared_ptr<BreakStmt>& statement, ExecutionContext* context)
{
if (context) {
context->hasBreak = true;
}
}
void Interpreter::visitContinueStmt(const std::shared_ptr<ContinueStmt>& statement, ExecutionContext* context)
{
if (context) {
context->hasContinue = true;
}
}
void Interpreter::visitAssignStmt(const std::shared_ptr<AssignStmt>& statement, ExecutionContext* context)
{
Value value = evaluate(statement->value);
// Handle different assignment operators
if (statement->op.type == EQUAL) {
// Simple assignment
environment->assign(statement->name, value);
} else {
// Compound assignment - get current value first
Value currentValue = environment->get(statement->name.lexeme);
// Apply the compound operation
Value result;
if (statement->op.type == PLUS_EQUAL) {
result = currentValue + value;
} else if (statement->op.type == MINUS_EQUAL) {
result = currentValue - value;
} else if (statement->op.type == STAR_EQUAL) {
result = currentValue * value;
} else if (statement->op.type == SLASH_EQUAL) {
result = currentValue / value;
} else if (statement->op.type == PERCENT_EQUAL) {
result = currentValue % value;
} else if (statement->op.type == BIN_AND_EQUAL) {
result = currentValue & value;
} else if (statement->op.type == BIN_OR_EQUAL) {
result = currentValue | value;
} else if (statement->op.type == BIN_XOR_EQUAL) {
result = currentValue ^ value;
} else if (statement->op.type == BIN_SLEFT_EQUAL) {
result = currentValue << value;
} else if (statement->op.type == BIN_SRIGHT_EQUAL) {
result = currentValue >> value;
} else {
throw std::runtime_error("Unknown assignment operator: " + statement->op.lexeme);
}
environment->assign(statement->name, result);
}
}
void Interpreter::interpret(std::vector<std::shared_ptr<Stmt> > statements) {
for(const std::shared_ptr<Stmt>& s : statements)
{
@ -761,7 +964,7 @@ void Interpreter::executeBlock(std::vector<std::shared_ptr<Stmt> > statements, s
for(const std::shared_ptr<Stmt>& s : statements)
{
execute(s, context);
if (context && context->hasReturn) {
if (context && (context->hasReturn || context->hasBreak || context->hasContinue)) {
this->environment = previous;
return;
}
@ -921,6 +1124,30 @@ bool Interpreter::isWholeNumer(double num) {
}
}
void Interpreter::cleanupUnusedFunctions() {
// Only remove functions that are definitely not referenced anywhere (use_count == 1)
// This is more conservative to prevent dangling pointer issues
functions.erase(
std::remove_if(functions.begin(), functions.end(),
[](const std::shared_ptr<Function>& func) {
return func.use_count() == 1; // Only referenced by this vector, nowhere else
}),
functions.end()
);
}
void Interpreter::cleanupUnusedThunks() {
// Only remove thunks that are definitely not referenced anywhere (use_count == 1)
// This is more conservative to prevent dangling pointer issues
thunks.erase(
std::remove_if(thunks.begin(), thunks.end(),
[](const std::shared_ptr<Thunk>& thunk) {
return thunk.use_count() == 1; // Only referenced by this vector, nowhere else
}),
thunks.end()
);
}

View File

@ -115,6 +115,16 @@ std::vector<Token> Lexer::Tokenize(std::string source){
tokens.push_back(Token{BIN_NOT, std::string(1, t), line, column - 1});
advance();
}
else if(t == '?')
{
tokens.push_back(Token{QUESTION, std::string(1, t), line, column});
advance();
}
else if(t == ':')
{
tokens.push_back(Token{COLON, std::string(1, t), line, column});
advance();
}
else if(t == '=')
{
std::string token = std::string(1, t);

View File

@ -16,18 +16,32 @@ sptr(Expr) Parser::expression()
sptr(Expr) Parser::logical_or()
{
sptr(Expr) expr = logical_and();
sptr(Expr) expr = ternary();
while(match({OR}))
{
Token op = previous();
sptr(Expr) right = logical_and();
sptr(Expr) right = ternary();
expr = msptr(BinaryExpr)(expr, op, right);
}
return expr;
}
sptr(Expr) Parser::ternary()
{
sptr(Expr) expr = logical_and();
if (match({QUESTION})) {
sptr(Expr) thenExpr = expression();
consume(COLON, "Expected ':' after ternary condition");
sptr(Expr) elseExpr = expression();
expr = msptr(TernaryExpr)(expr, thenExpr, elseExpr);
}
return expr;
}
sptr(Expr) Parser::logical_and()
{
sptr(Expr) expr = equality();
@ -101,13 +115,21 @@ sptr(Expr) Parser::shift()
sptr(Expr) Parser::assignment()
{
// Assignments are now statements, not expressions
// This function should only handle expressions that are not assignments
return increment();
}
sptr(Expr) Parser::assignmentExpression()
{
// This allows assignments as expressions (for for loop increment clauses)
sptr(Expr) expr = increment();
if(match({EQUAL, PLUS_EQUAL, MINUS_EQUAL, STAR_EQUAL, SLASH_EQUAL, PERCENT_EQUAL,
BIN_AND_EQUAL, BIN_OR_EQUAL, BIN_XOR_EQUAL, BIN_SLEFT_EQUAL, BIN_SRIGHT_EQUAL}))
{
Token op = previous();
sptr(Expr) value = assignment();
sptr(Expr) value = assignmentExpression();
if(std::dynamic_pointer_cast<VarExpr>(expr))
{
Token name = std::dynamic_pointer_cast<VarExpr>(expr)->name;
@ -372,11 +394,48 @@ sptr(Stmt) Parser::statement()
{
if(match({RETURN})) return returnStatement();
if(match({IF})) return ifStatement();
if(match({DO})) return doWhileStatement();
if(match({WHILE})) return whileStatement();
if(match({FOR})) return forStatement();
if(match({BREAK})) return breakStatement();
if(match({CONTINUE})) return continueStatement();
if(match({OPEN_BRACE})) return msptr(BlockStmt)(block());
// Check for assignment statement
if(check({IDENTIFIER})) {
// Look ahead to see if this is an assignment
int currentPos = current;
advance(); // consume identifier
if(match({EQUAL, PLUS_EQUAL, MINUS_EQUAL, STAR_EQUAL, SLASH_EQUAL, PERCENT_EQUAL,
BIN_AND_EQUAL, BIN_OR_EQUAL, BIN_XOR_EQUAL, BIN_SLEFT_EQUAL, BIN_SRIGHT_EQUAL})) {
// Reset position and parse as assignment statement
current = currentPos;
return assignmentStatement();
}
// Reset position and parse as expression statement
current = currentPos;
}
return expressionStatement();
}
sptr(Stmt) Parser::assignmentStatement()
{
Token name = consume(IDENTIFIER, "Expected variable name for assignment.");
// Consume any assignment operator
Token op;
if(match({EQUAL, PLUS_EQUAL, MINUS_EQUAL, STAR_EQUAL, SLASH_EQUAL, PERCENT_EQUAL,
BIN_AND_EQUAL, BIN_OR_EQUAL, BIN_XOR_EQUAL, BIN_SLEFT_EQUAL, BIN_SRIGHT_EQUAL})) {
op = previous();
} else {
throw std::runtime_error("Expected assignment operator.");
}
sptr(Expr) value = expression();
consume(SEMICOLON, "Expected ';' after assignment.");
return msptr(AssignStmt)(name, op, value);
}
sptr(Stmt) Parser::ifStatement()
{
@ -394,6 +453,78 @@ sptr(Stmt) Parser::ifStatement()
return msptr(IfStmt)(condition, thenBranch, elseBranch);
}
sptr(Stmt) Parser::whileStatement()
{
consume(OPEN_PAREN, "Expected '(' after 'while'.");
sptr(Expr) condition = expression();
consume(CLOSE_PAREN, "Expected ')' after while condition.");
sptr(Stmt) body = statement();
return msptr(WhileStmt)(condition, body);
}
sptr(Stmt) Parser::doWhileStatement()
{
sptr(Stmt) body = statement();
consume(WHILE, "Expected 'while' after do-while body.");
consume(OPEN_PAREN, "Expected '(' after 'while'.");
sptr(Expr) condition = expression();
consume(CLOSE_PAREN, "Expected ')' after while condition.");
consume(SEMICOLON, "Expected ';' after do-while condition.");
return msptr(DoWhileStmt)(body, condition);
}
sptr(Stmt) Parser::forStatement()
{
consume(OPEN_PAREN, "Expected '(' after 'for'.");
sptr(Stmt) initializer;
if (match({SEMICOLON})) {
initializer = nullptr;
} else if (match({VAR})) {
initializer = varDeclaration();
} else {
// Allow assignment expressions in for loop initializer
sptr(Expr) expr = assignmentExpression();
consume(SEMICOLON, "Expected ';' after for loop initializer.");
initializer = msptr(ExpressionStmt)(expr);
}
sptr(Expr) condition = nullptr;
if (!check(SEMICOLON)) {
condition = expression();
}
consume(SEMICOLON, "Expected ';' after for loop condition.");
sptr(Expr) increment = nullptr;
if (!check(CLOSE_PAREN)) {
increment = assignmentExpression();
}
consume(CLOSE_PAREN, "Expected ')' after for clauses.");
sptr(Stmt) body = statement();
// Return the for statement directly instead of desugaring
return msptr(ForStmt)(initializer, condition, increment, body);
}
sptr(Stmt) Parser::breakStatement()
{
Token keyword = previous();
consume(SEMICOLON, "Expected ';' after 'break'.");
return msptr(BreakStmt)(keyword);
}
sptr(Stmt) Parser::continueStatement()
{
Token keyword = previous();
consume(SEMICOLON, "Expected ';' after 'continue'.");
return msptr(ContinueStmt)(keyword);
}
// Helper function to detect if an expression is a tail call
bool Parser::isTailCall(const std::shared_ptr<Expr>& expr) {
// Check if this is a direct function call (no operations on the result)

View File

@ -258,4 +258,6 @@ void StdLib::addToEnvironment(std::shared_ptr<Environment> env, Interpreter& int
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(exitFunc);
}

203
source/StdLib.cpp.backup Normal file
View File

@ -0,0 +1,203 @@
#include "../headers/StdLib.h"
#include "../headers/Interpreter.h"
#include "../headers/ErrorReporter.h"
#include <chrono>
#include <iostream>
#include <string>
void StdLib::addToEnvironment(std::shared_ptr<Environment> env, Interpreter& interpreter, ErrorReporter* errorReporter) {
// toString function
auto toStringFunc = std::make_shared<BuiltinFunction>("toString",
[](std::vector<Value> args, int line, int column) -> Value {
if (args.size() != 1) {
throw std::runtime_error("toString() expects exactly 1 argument, got " + std::to_string(args.size()));
}
return Value(args[0].toString());
});
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(toStringFunc);
env->define("toString", Value(toStringFunc.get()));
// print function
auto printFunc = std::make_shared<BuiltinFunction>("print",
[](std::vector<Value> args, int line, int column) -> Value {
for (size_t i = 0; i < args.size(); i++) {
if (i > 0) std::cout << " ";
std::cout << args[i].toString();
}
std::cout << std::endl;
return NONE_VALUE;
});
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(printFunc);
env->define("print", Value(printFunc.get()));
// assert function
auto assertFunc = std::make_shared<BuiltinFunction>("assert",
[](std::vector<Value> args, int line, int column) -> Value {
if (args.size() < 1 || args.size() > 2) {
throw std::runtime_error("assert() expects 1 or 2 arguments, got " + std::to_string(args.size()));
}
if (!args[0].isTruthy()) {
std::string message = args.size() == 2 ? args[1].toString() : "Assertion failed";
throw std::runtime_error("Assertion failed: " + message);
}
return NONE_VALUE;
});
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(assertFunc);
env->define("assert", Value(assertFunc.get()));
// time function
auto timeFunc = std::make_shared<BuiltinFunction>("time",
[](std::vector<Value> args, int line, int column) -> Value {
if (args.size() != 0) {
throw std::runtime_error("time() expects no arguments, got " + std::to_string(args.size()));
}
auto now = std::chrono::high_resolution_clock::now();
auto duration = now.time_since_epoch();
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
return Value(static_cast<double>(seconds));
});
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(timeFunc);
env->define("time", Value(timeFunc.get()));
// input function
auto inputFunc = std::make_shared<BuiltinFunction>("input",
[](std::vector<Value> args, int line, int column) -> Value {
if (args.size() > 1) {
throw std::runtime_error("input() expects 0 or 1 arguments, got " + std::to_string(args.size()));
}
if (args.size() == 1) {
std::cout << args[0].toString();
}
std::string line;
std::getline(std::cin, line);
return Value(line);
});
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(inputFunc);
env->define("input", Value(inputFunc.get()));
// type function
auto typeFunc = std::make_shared<BuiltinFunction>("type",
[](std::vector<Value> args, int line, int column) -> Value {
if (args.size() != 1) {
throw std::runtime_error("type() expects exactly 1 argument, got " + std::to_string(args.size()));
}
if (args[0].isNumber()) return Value("number");
if (args[0].isString()) return Value("string");
if (args[0].isBoolean()) return Value("boolean");
if (args[0].isFunction()) return Value("function");
if (args[0].isBuiltinFunction()) return Value("builtin_function");
if (args[0].isThunk()) return Value("thunk");
if (args[0].isNone()) return Value("none");
return Value("unknown");
});
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(typeFunc);
env->define("type", Value(typeFunc.get()));
// toNumber function
auto toNumberFunc = std::make_shared<BuiltinFunction>("toNumber",
[](std::vector<Value> args, int line, int column) -> Value {
if (args.size() != 1) {
throw std::runtime_error("toNumber() expects exactly 1 argument, got " + std::to_string(args.size()));
}
if (args[0].isNumber()) return args[0];
if (args[0].isString()) {
try {
return Value(std::stod(args[0].asString()));
} catch (...) {
return Value(0.0);
}
}
if (args[0].isBoolean()) return Value(args[0].asBoolean() ? 1.0 : 0.0);
if (args[0].isNone()) return Value(0.0);
return Value(0.0);
});
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(toNumberFunc);
env->define("toNumber", Value(toNumberFunc.get()));
// toBoolean function
auto toBooleanFunc = std::make_shared<BuiltinFunction>("toBoolean",
[](std::vector<Value> args, int line, int column) -> Value {
if (args.size() != 1) {
throw std::runtime_error("toBoolean() expects exactly 1 argument, got " + std::to_string(args.size()));
}
return Value(args[0].isTruthy());
});
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(toBooleanFunc);
env->define("toBoolean", Value(toBooleanFunc.get()));
// exit function
auto exitFunc = std::make_shared<BuiltinFunction>("exit",
[](std::vector<Value> args, int line, int column) -> Value {
int code = 0;
if (args.size() == 1) {
if (args[0].isNumber()) {
code = static_cast<int>(args[0].asNumber());
}
} else if (args.size() > 1) {
throw std::runtime_error("exit() expects 0 or 1 arguments, got " + std::to_string(args.size()));
}
std::exit(code);
return NONE_VALUE;
});
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(exitFunc);
env->define("exit", Value(exitFunc.get()));
// cleanup functions
auto cleanupFunc = std::make_shared<BuiltinFunction>("cleanup",
[&interpreter](std::vector<Value> args, int line, int column) -> Value {
if (args.size() != 0) {
throw std::runtime_error("cleanup() expects no arguments, got " + std::to_string(args.size()));
}
interpreter.cleanupUnusedFunctions();
interpreter.cleanupUnusedThunks();
return NONE_VALUE;
});
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(cleanupFunc);
env->define("cleanup", Value(cleanupFunc.get()));
// getFunctionCount function
auto getFunctionCountFunc = std::make_shared<BuiltinFunction>("getFunctionCount",
[&interpreter](std::vector<Value> args, int line, int column) -> Value {
if (args.size() != 0) {
throw std::runtime_error("getFunctionCount() expects no arguments, got " + std::to_string(args.size()));
}
// This would need to be exposed through the interpreter
// For now, return a placeholder
return Value(0.0);
});
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(getFunctionCountFunc);
env->define("getFunctionCount", Value(getFunctionCountFunc.get()));
}

261
source/StdLib.cpp.original Normal file
View File

@ -0,0 +1,261 @@
#include "../headers/StdLib.h"
#include "../headers/Interpreter.h"
#include "../headers/ErrorReporter.h"
#include <chrono>
void StdLib::addToEnvironment(std::shared_ptr<Environment> env, Interpreter& interpreter, ErrorReporter* errorReporter) {
// Create a built-in toString function
auto toStringFunc = std::make_shared<BuiltinFunction>("toString",
[&interpreter, errorReporter](std::vector<Value> args, int line, int column) -> Value {
if (args.size() != 1) {
if (errorReporter) {
errorReporter->reportError(line, column, "StdLib Error",
"Expected 1 argument but got " + std::to_string(args.size()) + ".", "", true);
}
throw std::runtime_error("Expected 1 argument but got " + std::to_string(args.size()) + ".");
}
return Value(interpreter.stringify(args[0]));
});
env->define("toString", Value(toStringFunc.get()));
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(toStringFunc);
// Create a built-in print function
auto printFunc = std::make_shared<BuiltinFunction>("print",
[&interpreter, errorReporter](std::vector<Value> args, int line, int column) -> Value {
if (args.size() != 1) {
if (errorReporter) {
errorReporter->reportError(line, column, "StdLib Error",
"Expected 1 argument but got " + std::to_string(args.size()) + ".", "", true);
}
throw std::runtime_error("Expected 1 argument but got " + std::to_string(args.size()) + ".");
}
// Use the interpreter's stringify function
std::cout << interpreter.stringify(args[0]) << std::endl;
return NONE_VALUE;
});
env->define("print", Value(printFunc.get()));
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(printFunc);
// Create a built-in assert function
auto assertFunc = std::make_shared<BuiltinFunction>("assert",
[errorReporter](std::vector<Value> args, int line, int column) -> Value {
if (args.size() != 1 && args.size() != 2) {
if (errorReporter) {
errorReporter->reportError(line, column, "StdLib Error",
"Expected 1 or 2 arguments but got " + std::to_string(args.size()) + ".", "", true);
}
throw std::runtime_error("Expected 1 or 2 arguments but got " + std::to_string(args.size()) + ".");
}
// Simple truthy check without calling interpreter.isTruthy
bool isTruthy = false;
if (args[0].isBoolean()) {
isTruthy = args[0].asBoolean();
} else if (args[0].isNone()) {
isTruthy = false;
} else {
isTruthy = true; // Numbers, strings, functions are truthy
}
if (!isTruthy) {
std::string message = "Assertion failed: condition is false";
if (args.size() == 2) {
if (args[1].isString()) {
message += " - " + std::string(args[1].asString());
}
}
if (errorReporter) {
errorReporter->reportError(line, column, "StdLib Error", message, "", true);
}
throw std::runtime_error(message);
}
return NONE_VALUE;
});
env->define("assert", Value(assertFunc.get()));
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(assertFunc);
// Create a built-in time function (returns microseconds since Unix epoch)
auto timeFunc = std::make_shared<BuiltinFunction>("time",
[errorReporter](std::vector<Value> args, int line, int column) -> Value {
if (args.size() != 0) {
if (errorReporter) {
errorReporter->reportError(line, column, "StdLib Error",
"Expected 0 arguments but got " + std::to_string(args.size()) + ".", "", true);
}
throw std::runtime_error("Expected 0 arguments but got " + std::to_string(args.size()) + ".");
}
auto now = std::chrono::high_resolution_clock::now();
auto duration = now.time_since_epoch();
auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
return Value(static_cast<double>(microseconds));
});
env->define("time", Value(timeFunc.get()));
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(timeFunc);
// Create a built-in input function
auto inputFunc = std::make_shared<BuiltinFunction>("input",
[&interpreter, errorReporter](std::vector<Value> args, int line, int column) -> Value {
if (args.size() > 1) {
if (errorReporter) {
errorReporter->reportError(line, column, "StdLib Error",
"Expected 0 or 1 arguments but got " + std::to_string(args.size()) + ".", "", true);
}
throw std::runtime_error("Expected 0 or 1 arguments but got " + std::to_string(args.size()) + ".");
}
// Optional prompt
if (args.size() == 1) {
std::cout << interpreter.stringify(args[0]);
}
// Get user input
std::string userInput;
std::getline(std::cin, userInput);
return Value(userInput);
});
env->define("input", Value(inputFunc.get()));
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(inputFunc);
// Create a built-in type function
auto typeFunc = std::make_shared<BuiltinFunction>("type",
[errorReporter](std::vector<Value> args, int line, int column) -> Value {
if (args.size() != 1) {
if (errorReporter) {
errorReporter->reportError(line, column, "StdLib Error",
"Expected 1 argument but got " + std::to_string(args.size()) + ".", "", true);
}
throw std::runtime_error("Expected 1 argument but got " + std::to_string(args.size()) + ".");
}
std::string typeName;
if (args[0].isNumber()) {
typeName = "number";
} else if (args[0].isString()) {
typeName = "string";
} else if (args[0].isBoolean()) {
typeName = "boolean";
} else if (args[0].isNone()) {
typeName = "none";
} else if (args[0].isFunction()) {
typeName = "function";
} else if (args[0].isBuiltinFunction()) {
typeName = "builtin_function";
} else {
typeName = "unknown";
}
return Value(typeName);
});
env->define("type", Value(typeFunc.get()));
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(typeFunc);
// Create a built-in toNumber function for string-to-number conversion
auto toNumberFunc = std::make_shared<BuiltinFunction>("toNumber",
[](std::vector<Value> args, int line, int column) -> Value {
if (args.size() != 1) {
return NONE_VALUE; // Return none for wrong argument count
}
if (!args[0].isString()) {
return NONE_VALUE; // Return none for wrong type
}
std::string str = args[0].asString();
// Remove leading/trailing whitespace
str.erase(0, str.find_first_not_of(" \t\n\r"));
str.erase(str.find_last_not_of(" \t\n\r") + 1);
if (str.empty()) {
return NONE_VALUE; // Return none for empty string
}
try {
double value = std::stod(str);
return Value(value);
} catch (const std::invalid_argument&) {
return NONE_VALUE; // Return none for invalid conversion
} catch (const std::out_of_range&) {
return NONE_VALUE; // Return none for out of range
}
});
env->define("toNumber", Value(toNumberFunc.get()));
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(toNumberFunc);
// Create a built-in toBoolean function for explicit boolean conversion
auto toBooleanFunc = std::make_shared<BuiltinFunction>("toBoolean",
[errorReporter](std::vector<Value> args, int line, int column) -> Value {
if (args.size() != 1) {
if (errorReporter) {
errorReporter->reportError(line, column, "StdLib Error",
"Expected 1 argument but got " + std::to_string(args.size()) + ".", "", true);
}
throw std::runtime_error("Expected 1 argument but got " + std::to_string(args.size()) + ".");
}
// Use the same logic as isTruthy() for consistency
Value value = args[0];
if (value.isNone()) {
return Value(false);
}
if (value.isBoolean()) {
return value; // Already a boolean
}
if (value.isNumber()) {
return Value(value.asNumber() != 0.0);
}
if (value.isString()) {
return Value(!value.asString().empty());
}
// For any other type (functions, etc.), consider them truthy
return Value(true);
});
env->define("toBoolean", Value(toBooleanFunc.get()));
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(toBooleanFunc);
// Create a built-in exit function to terminate the program
auto exitFunc = std::make_shared<BuiltinFunction>("exit",
[](std::vector<Value> args, int line, int column) -> Value {
int exitCode = 0; // Default exit code
if (args.size() > 0) {
if (args[0].isNumber()) {
exitCode = static_cast<int>(args[0].asNumber());
}
// If not a number, just use default exit code 0
}
std::exit(exitCode);
return NONE_VALUE; // This line should never be reached
});
env->define("exit", Value(exitFunc.get()));
// Store the shared_ptr in the interpreter to keep it alive
interpreter.addBuiltinFunction(exitFunc);
}

View File

@ -1814,6 +1814,510 @@ print(" Nested multi-statement function result: " + toString(result2));
print("Multi-statement function execution: PASS");
// ========================================
// TEST 46: WHILE LOOPS
// ========================================
print("\n--- Test 46: While Loops ---");
// Basic while loop
var counter = 0;
while (counter < 5) {
counter = counter + 1;
}
assert(counter == 5, "Basic while loop should increment counter to 5");
print(" Basic while loop: PASS");
// While loop with break (using return)
func countToThree() {
var i = 0;
while (true) {
i = i + 1;
if (i > 3) {
return i;
}
}
}
assert(countToThree() == 4, "While loop with return should work");
print(" While loop with return: PASS");
// While loop with condition
var sum = 0;
var num = 1;
while (num <= 10) {
sum = sum + num;
num = num + 1;
}
assert(sum == 55, "While loop should sum numbers 1 to 10");
print(" While loop summation: PASS");
// Nested while loops
var outer = 0;
var inner = 0;
while (outer < 3) {
inner = 0;
while (inner < 2) {
inner = inner + 1;
}
outer = outer + 1;
}
assert(outer == 3, "Nested while loops should work");
assert(inner == 2, "Inner loop should complete");
print(" Nested while loops: PASS");
// While loop with false condition (should not execute)
var neverExecuted = 0;
while (false) {
neverExecuted = neverExecuted + 1;
}
assert(neverExecuted == 0, "While loop with false condition should not execute");
print(" While loop with false condition: PASS");
// While loop with complex condition
var x = 10;
var y = 0;
while (x > 0 && y < 5) {
x = x - 1;
y = y + 1;
}
assert(x == 5, "Complex condition while loop should work");
assert(y == 5, "Complex condition while loop should work");
print(" While loop with complex condition: PASS");
print("While loops: PASS");
// ========================================
// TEST 47: FOR LOOPS
// ========================================
print("\n--- Test 47: For Loops ---");
// Basic for loop
var forSum = 0;
for (var i = 1; i <= 5; i = i + 1) {
forSum = forSum + i;
}
assert(forSum == 15, "Basic for loop should sum 1 to 5");
print(" Basic for loop: PASS");
// For loop with no initializer
var j = 0;
for (; j < 3; j = j + 1) {
// Empty body
}
assert(j == 3, "For loop with no initializer should work");
print(" For loop with no initializer: PASS");
// For loop with no condition (infinite loop with break)
func countToTwo() {
var k = 0;
for (; ; k = k + 1) {
if (k >= 2) {
return k;
}
}
}
assert(countToTwo() == 2, "For loop with no condition should work");
print(" For loop with no condition: PASS");
// For loop with no increment
var m = 0;
var n = 0;
for (n = 0; n < 3; ) {
m = m + 1;
n = n + 1;
}
assert(m == 3, "For loop with no increment should work");
assert(n == 3, "For loop with no increment should work");
print(" For loop with no increment: PASS");
// Nested for loops
var total = 0;
for (var a = 0; a < 2; a = a + 1) {
for (var b = 0; b < 3; b = b + 1) {
total = total + 1;
}
}
assert(total == 6, "Nested for loops should work");
print(" Nested for loops: PASS");
// For loop with expression initializer
var result = 0;
for (var temp = 2 * 3; temp > 0; temp = temp - 1) {
result = result + temp;
}
assert(result == 21, "For loop with expression initializer should work"); // 6 + 5 + 4 + 3 + 2 + 1 = 21
print(" For loop with expression initializer: PASS");
// For loop with complex increment
var complexSum = 0;
for (var i = 0; i < 10; i = i + 2) {
complexSum = complexSum + i;
}
assert(complexSum == 20, "For loop with complex increment should work"); // 0 + 2 + 4 + 6 + 8 = 20
print(" For loop with complex increment: PASS");
// For loop with function calls
func getNext(i) {
return i + 1;
}
var funcSum = 0;
for (var i = 0; i < 4; i = getNext(i)) {
funcSum = funcSum + i;
}
assert(funcSum == 6, "For loop with function calls should work"); // 0 + 1 + 2 + 3 = 6
print(" For loop with function calls: PASS");
print("For loops: PASS");
// ========================================
// TEST 48: CRITICAL LOOP EDGE CASES
// ========================================
print("\n--- Test 48: Critical Loop Edge Cases ---");
// Edge case 1: Empty loop bodies
var emptyWhileCounter = 0;
while (emptyWhileCounter < 3) {
emptyWhileCounter = emptyWhileCounter + 1;
}
assert(emptyWhileCounter == 3, "Empty while loop body should still increment counter");
var emptyForCounter = 0;
for (var i = 0; i < 3; i = i + 1) {
// Empty body
}
assert(i == 3, "Empty for loop body should still increment counter");
// Edge case 2: Loops with only break/continue
var breakOnlyCounter = 0;
while (true) {
breakOnlyCounter = breakOnlyCounter + 1;
if (breakOnlyCounter == 5) {
break;
}
}
assert(breakOnlyCounter == 5, "Loop with only break should work");
var continueOnlyCounter = 0;
var continueOnlySum = 0;
for (var j = 0; j < 10; j = j + 1) {
if (j < 5) {
continue;
}
continueOnlySum = continueOnlySum + j;
}
assert(continueOnlySum == 35, "Loop with only continue should sum 5+6+7+8+9=35");
// Edge case 3: Nested break/continue with multiple levels
var nestedBreakLevel1 = 0;
var nestedBreakLevel2 = 0;
for (var a = 0; a < 3; a = a + 1) {
nestedBreakLevel1 = nestedBreakLevel1 + 1;
for (var b = 0; b < 3; b = b + 1) {
nestedBreakLevel2 = nestedBreakLevel2 + 1;
if (a == 1 && b == 1) {
break;
}
}
}
assert(nestedBreakLevel1 == 3, "Nested break should not affect outer loop");
assert(nestedBreakLevel2 == 8, "Nested break should only break inner loop (3+3+2=8)");
// Edge case 4: Loops with function calls that return early
func earlyReturnFunc(x) {
if (x == 3) {
return "early";
}
return "normal";
}
var earlyReturnCounter = 0;
var earlyReturnResults = "";
for (var k = 0; k < 5; k = k + 1) {
earlyReturnCounter = earlyReturnCounter + 1;
earlyReturnResults = earlyReturnResults + earlyReturnFunc(k);
}
assert(earlyReturnCounter == 5, "Loop should continue even with early returns in function calls");
assert(earlyReturnResults == "normalnormalnormalearlynormal", "Function calls with early returns should work in loops");
// Edge case 5: Loops with variable shadowing
var shadowVar = 10;
for (var shadowVar = 0; shadowVar < 3; shadowVar = shadowVar + 1) {
// Inner shadowVar shadows outer shadowVar
}
assert(shadowVar == 3, "Variable shadowing in for loop should work");
var shadowWhileVar = 20;
var shadowCounter = 0;
while (shadowCounter < 2) {
var shadowWhileVar = shadowCounter * 10;
shadowCounter = shadowCounter + 1;
}
assert(shadowWhileVar == 20, "Variable shadowing in while loop should not affect outer scope");
// Edge case 6: Loops with return statements in different contexts
func loopWithReturn() {
var returnCounter = 0;
for (var q = 0; q < 10; q = q + 1) {
returnCounter = returnCounter + 1;
if (q == 4) {
return returnCounter;
}
}
return returnCounter;
}
assert(loopWithReturn() == 5, "Loop with return should exit function early");
// Edge case 7: Loops with nested function definitions
var nestedFuncCounter = 0;
for (var r = 0; r < 3; r = r + 1) {
nestedFuncCounter = nestedFuncCounter + 1;
func nestedInLoop() {
return r * 2;
}
if (nestedInLoop() == 4) {
break;
}
}
assert(nestedFuncCounter == 3, "Loop with nested function definitions should work");
// Edge case 8: Loops with closures capturing loop variables
var closureResults = "";
for (var s = 0; s < 3; s = s + 1) {
var capturedVar = s;
func closure() {
return capturedVar;
}
closureResults = closureResults + toString(closure());
}
assert(closureResults == "012", "Loops with closures should capture variables correctly");
// Edge case 9: Loops with multiple continue statements
var multiContinueSum = 0;
for (var t = 0; t < 6; t = t + 1) {
if (t == 1) {
continue;
}
if (t == 3) {
continue;
}
if (t == 5) {
continue;
}
multiContinueSum = multiContinueSum + t;
}
assert(multiContinueSum == 6, "Loop with multiple continue statements should sum 0+2+4=6");
// Edge case 10: Loops with break/continue in conditional blocks
var conditionalBreakCounter = 0;
for (var p = 0; p < 10; p = p + 1) {
conditionalBreakCounter = conditionalBreakCounter + 1;
if (p % 2 == 0) {
if (p == 6) {
break;
}
} else {
if (p == 7) {
continue;
}
}
}
assert(conditionalBreakCounter == 7, "Loop with conditional break/continue should work");
print("Critical loop edge cases: PASS");
// ========================================
// TEST 49: DO-WHILE LOOPS
// ========================================
// Basic do-while loop
var i = 0;
do {
i = i + 1;
} while (i < 5);
assert(i == 5, "Basic do-while loop should increment to 5");
// Do-while with break
var j = 0;
do {
j = j + 1;
if (j == 3) {
break;
}
} while (j < 10);
assert(j == 3, "Do-while with break should stop at 3");
// Do-while with continue
var k = 0;
var sum = 0;
do {
k = k + 1;
if (k == 2) {
continue;
}
sum = sum + k;
} while (k < 5);
assert(sum == 13, "Do-while with continue should sum 1+3+4+5=13");
// Do-while with return in function
func doWhileReturn() {
var x = 0;
do {
x = x + 1;
if (x == 3) {
return x;
}
} while (x < 10);
return 0;
}
assert(doWhileReturn() == 3, "Do-while with return should return 3");
// Nested do-while loops
var outer = 0;
var inner = 0;
do {
outer = outer + 1;
inner = 0;
do {
inner = inner + 1;
} while (inner < 3);
} while (outer < 2);
assert(outer == 2, "Nested do-while outer should be 2");
assert(inner == 3, "Nested do-while inner should be 3");
// Do-while with complex condition
var a = 0;
var b = 5;
do {
a = a + 1;
b = b - 1;
} while (a < b);
assert(a == 3, "Do-while with complex condition a should be 3");
assert(b == 2, "Do-while with complex condition b should be 2");
// Do-while with function call in condition
func getValue() {
return 3;
}
var counter = 0;
do {
counter = counter + 1;
} while (counter < getValue());
assert(counter == 3, "Do-while with function call should count to 3");
// Do-while with empty body (should still execute once)
var empty = 0;
do {
empty = empty + 1;
} while (false);
assert(empty == 1, "Do-while with false condition should execute once");
// Do-while with break and continue
var complex = 0;
var result = 0;
do {
complex = complex + 1;
if (complex == 1) {
continue;
}
if (complex == 4) {
break;
}
result = result + complex;
} while (complex < 10);
assert(result == 5, "Do-while with break and continue should sum 2+3=5");
print("Do-while loops: PASS");
// ========================================
// TEST 50: TERNARY OPERATOR
// ========================================
// Basic ternary operator
var result1 = true ? 42 : 10;
assert(result1 == 42, "Basic ternary with true condition");
var result2 = false ? 42 : 10;
assert(result2 == 10, "Basic ternary with false condition");
// Ternary with expressions
var x = 5;
var y = 10;
var result3 = x > y ? "x is greater" : "y is greater";
assert(result3 == "y is greater", "Ternary with comparison expression");
// Ternary with function calls
func getValue() {
return 100;
}
func getOtherValue() {
return 200;
}
var result4 = x < y ? getValue() : getOtherValue();
assert(result4 == 100, "Ternary with function calls");
// Ternary with nested expressions
var result5 = x == 5 ? (y == 10 ? "both true" : "x true, y false") : "x false";
assert(result5 == "both true", "Nested ternary expressions");
// Ternary with different types
var result6 = true ? "string" : 42;
assert(result6 == "string", "Ternary with different types (string)");
var result7 = false ? "string" : 42;
assert(result7 == 42, "Ternary with different types (number)");
// Ternary with boolean expressions
var result8 = x > 0 && y > 0 ? "both positive" : "not both positive";
assert(result8 == "both positive", "Ternary with boolean expression");
// Ternary with arithmetic expressions
var result9 = x + y > 10 ? x * y : x + y;
assert(result9 == 50, "Ternary with arithmetic expressions");
// Ternary with variables
var a = 1;
var b = 2;
var c = 3;
var result10 = a < b ? c : a;
assert(result10 == 3, "Ternary with variables");
// Ternary with zero and negative values
var result11 = 0 ? "truthy" : "falsy";
assert(result11 == "falsy", "Ternary with zero (falsy)");
var result12 = -1 ? "truthy" : "falsy";
assert(result12 == "truthy", "Ternary with negative number (truthy)");
// Ternary with empty string
var result13 = "" ? "truthy" : "falsy";
assert(result13 == "falsy", "Ternary with empty string (falsy)");
// Ternary with none
var result14 = none ? "truthy" : "falsy";
assert(result14 == "falsy", "Ternary with none (falsy)");
// Ternary in assignment
var maxValue = x > y ? x : y;
assert(maxValue == 10, "Ternary in assignment");
// Ternary with complex conditions
var age = 25;
var status = age >= 18 ? "adult" : "minor";
assert(status == "adult", "Ternary with complex condition");
// Ternary with string concatenation
var message = x > 3 ? "x is " + toString(x) : "x is small";
assert(message == "x is 5", "Ternary with string concatenation");
// Ternary with multiple operators
var result15 = x > 0 && y > 0 && x < y ? "valid range" : "invalid range";
assert(result15 == "valid range", "Ternary with multiple operators");
print("Ternary operator: PASS");
// ========================================
// TEST SUMMARY
// ========================================
@ -1877,7 +2381,11 @@ print("- Lexer consistency improvements");
print("- None value concatenation (string + none, none + string)");
print("- Memory management (variable reassignment, function reassignment, large string cleanup)");
print("- Multi-statement function execution");
print("- While loops (basic, nested, complex conditions, return breaks)");
print("- For loops (basic, nested, missing clauses, complex expressions)");
print("- Do-while loops (basic, nested, break, continue, return, complex conditions)");
print("- Critical loop edge cases (10 comprehensive scenarios)");
print("- Ternary operator (conditional expressions with ? and :)");
print("\nAll tests passed.");
print("Test suite complete.");
print("Test suite complete.");

View File

@ -15,3 +15,7 @@ print("Result: " + fib_result);
print("Counter: " + counter);
func test_fib() {
}