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:
parent
671f8a6350
commit
72a1b82b43
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
/.vscode
|
||||
build/
|
||||
|
||||
.DS_Store
|
||||
|
||||
@ -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.*
|
||||
97
README.md
97
README.md
@ -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
|
||||
```
|
||||
|
||||
373
ROADMAP.md
373
ROADMAP.md
@ -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*
|
||||
10
bob-language-extension/.vscodeignore
Normal file
10
bob-language-extension/.vscodeignore
Normal file
@ -0,0 +1,10 @@
|
||||
.vscode/**
|
||||
.vscode-test/**
|
||||
src/**
|
||||
.gitignore
|
||||
.yarnrc
|
||||
vsc-extension-quickstart.md
|
||||
**/tsconfig.json
|
||||
**/.eslintrc.json
|
||||
**/*.map
|
||||
**/*.ts
|
||||
97
bob-language-extension/INSTALLATION.md
Normal file
97
bob-language-extension/INSTALLATION.md
Normal 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`
|
||||
103
bob-language-extension/README.md
Normal file
103
bob-language-extension/README.md
Normal 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)
|
||||
BIN
bob-language-extension/bob-language-0.1.2.vsix
Normal file
BIN
bob-language-extension/bob-language-0.1.2.vsix
Normal file
Binary file not shown.
44
bob-language-extension/create-vsix.sh
Executable file
44
bob-language-extension/create-vsix.sh
Executable 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!"
|
||||
75
bob-language-extension/example.bob
Normal file
75
bob-language-extension/example.bob
Normal 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!");
|
||||
66
bob-language-extension/install.sh
Executable file
66
bob-language-extension/install.sh
Executable 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!"
|
||||
35
bob-language-extension/language-configuration.json
Normal file
35
bob-language-extension/language-configuration.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
27
bob-language-extension/package-vsix.sh
Executable file
27
bob-language-extension/package-vsix.sh
Executable 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"
|
||||
74
bob-language-extension/package.json
Normal file
74
bob-language-extension/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
29
bob-language-extension/reload-extension.sh
Executable file
29
bob-language-extension/reload-extension.sh
Executable 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!"
|
||||
291
bob-language-extension/snippets/bob.json
Normal file
291
bob-language-extension/snippets/bob.json
Normal 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"
|
||||
}
|
||||
}
|
||||
104
bob-language-extension/src/extension.ts
Normal file
104
bob-language-extension/src/extension.ts
Normal 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!');
|
||||
}
|
||||
175
bob-language-extension/syntaxes/bob.tmLanguage.json
Normal file
175
bob-language-extension/syntaxes/bob.tmLanguage.json
Normal 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": "\\?|:"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
58
bob-language-extension/test-operators.bob
Normal file
58
bob-language-extension/test-operators.bob
Normal 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;
|
||||
}
|
||||
79
bob-language-extension/themes/bob-dark.json
Normal file
79
bob-language-extension/themes/bob-dark.json
Normal 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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
17
bob-language-extension/tsconfig.json
Normal file
17
bob-language-extension/tsconfig.json
Normal 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"
|
||||
]
|
||||
}
|
||||
@ -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()));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
BIN
source/.DS_Store
vendored
Binary file not shown.
@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
203
source/StdLib.cpp.backup
Normal 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
261
source/StdLib.cpp.original
Normal 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);
|
||||
}
|
||||
@ -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.");
|
||||
@ -15,3 +15,7 @@ print("Result: " + fib_result);
|
||||
|
||||
print("Counter: " + counter);
|
||||
|
||||
|
||||
func test_fib() {
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user