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
|
/.vscode
|
||||||
build/
|
build/
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
|||||||
@ -2,19 +2,55 @@
|
|||||||
|
|
||||||
## Overview
|
## 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
|
## Table of Contents
|
||||||
|
|
||||||
1. [Data Types](#data-types)
|
1. [Getting Started](#getting-started)
|
||||||
2. [Variables](#variables)
|
2. [Basic Syntax](#basic-syntax)
|
||||||
3. [Operators](#operators)
|
3. [Data Types](#data-types)
|
||||||
4. [Functions](#functions)
|
4. [Variables](#variables)
|
||||||
5. [Control Flow](#control-flow)
|
5. [Operators](#operators)
|
||||||
6. [Standard Library](#standard-library)
|
6. [Control Flow](#control-flow)
|
||||||
7. [Error Handling](#error-handling)
|
7. [Functions](#functions)
|
||||||
8. [Examples](#examples)
|
8. [Standard Library](#standard-library)
|
||||||
9. [Language Nuances](#language-nuances)
|
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
|
## Data Types
|
||||||
|
|
||||||
@ -38,14 +74,14 @@ Bob is a dynamically typed programming language with a focus on simplicity and e
|
|||||||
## Variables
|
## Variables
|
||||||
|
|
||||||
### Declaration
|
### Declaration
|
||||||
```bob
|
```go
|
||||||
var name = "Bob";
|
var name = "Bob";
|
||||||
var age = 25;
|
var age = 25;
|
||||||
var isActive = true;
|
var isActive = true;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Assignment
|
### Assignment
|
||||||
```bob
|
```go
|
||||||
var x = 10;
|
var x = 10;
|
||||||
x = 20; // Reassignment
|
x = 20; // Reassignment
|
||||||
```
|
```
|
||||||
@ -56,8 +92,7 @@ x = 20; // Reassignment
|
|||||||
- **Shadowing**: Local variables can shadow global variables
|
- **Shadowing**: Local variables can shadow global variables
|
||||||
- **No `global` keyword**: Unlike Python, Bob doesn't require explicit global declaration
|
- **No `global` keyword**: Unlike Python, Bob doesn't require explicit global declaration
|
||||||
|
|
||||||
### Variable Behavior
|
```go
|
||||||
```bob
|
|
||||||
var globalVar = 100;
|
var globalVar = 100;
|
||||||
|
|
||||||
func testScope() {
|
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
|
## Operators
|
||||||
|
|
||||||
### Arithmetic Operators
|
### Arithmetic Operators
|
||||||
@ -83,12 +178,39 @@ func testScope() {
|
|||||||
- **Greater than or equal**: `>=`
|
- **Greater than or equal**: `>=`
|
||||||
- **Less 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
|
### String Operators
|
||||||
|
|
||||||
#### Concatenation
|
#### Concatenation
|
||||||
Bob supports bidirectional string + number concatenation with automatic type conversion:
|
Bob supports bidirectional string + number concatenation with automatic type conversion:
|
||||||
|
|
||||||
```bob
|
```go
|
||||||
// String + Number
|
// String + Number
|
||||||
"Hello " + 42; // → "Hello 42"
|
"Hello " + 42; // → "Hello 42"
|
||||||
"Pi: " + 3.14; // → "Pi: 3.14"
|
"Pi: " + 3.14; // → "Pi: 3.14"
|
||||||
@ -99,7 +221,7 @@ Bob supports bidirectional string + number concatenation with automatic type con
|
|||||||
```
|
```
|
||||||
|
|
||||||
#### String Multiplication
|
#### String Multiplication
|
||||||
```bob
|
```go
|
||||||
"hello" * 3; // → "hellohellohello"
|
"hello" * 3; // → "hellohellohello"
|
||||||
3 * "hello"; // → "hellohellohello"
|
3 * "hello"; // → "hellohellohello"
|
||||||
```
|
```
|
||||||
@ -109,23 +231,135 @@ Bob supports bidirectional string + number concatenation with automatic type con
|
|||||||
### Number Formatting
|
### Number Formatting
|
||||||
Bob automatically formats numbers to show only significant digits:
|
Bob automatically formats numbers to show only significant digits:
|
||||||
|
|
||||||
```bob
|
```go
|
||||||
"Count: " + 2.0; // → "Count: 2" (no trailing zeros)
|
"Count: " + 2.0; // → "Count: 2" (no trailing zeros)
|
||||||
"Pi: " + 3.14; // → "Pi: 3.14" (exact precision)
|
"Pi: " + 3.14; // → "Pi: 3.14" (exact precision)
|
||||||
"Integer: " + 42; // → "Integer: 42" (no decimal)
|
"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
|
## Functions
|
||||||
|
|
||||||
### Function Declaration
|
### Function Declaration
|
||||||
```bob
|
```go
|
||||||
func add(a, b) {
|
func add(a, b) {
|
||||||
return a + b;
|
return a + b;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Function Call
|
### Function Call
|
||||||
```bob
|
```go
|
||||||
var result = add(2, 3); // result = 5
|
var result = add(2, 3); // result = 5
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -142,7 +376,7 @@ var result = add(2, 3); // result = 5
|
|||||||
### Closures
|
### Closures
|
||||||
Bob supports lexical closures with variable capture:
|
Bob supports lexical closures with variable capture:
|
||||||
|
|
||||||
```bob
|
```go
|
||||||
var outerVar = "Outer";
|
var outerVar = "Outer";
|
||||||
|
|
||||||
func makeGreeter(greeting) {
|
func makeGreeter(greeting) {
|
||||||
@ -154,7 +388,7 @@ var greeter = makeGreeter("Hello");
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Nested Functions
|
### Nested Functions
|
||||||
```bob
|
```go
|
||||||
func outer() {
|
func outer() {
|
||||||
func inner() {
|
func inner() {
|
||||||
return 42;
|
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
|
## Standard Library
|
||||||
|
|
||||||
### Print Function
|
### Print Function
|
||||||
```bob
|
```go
|
||||||
print("Hello, World!");
|
print("Hello, World!");
|
||||||
print(42);
|
print(42);
|
||||||
print(true);
|
print(true);
|
||||||
@ -192,7 +412,7 @@ print(true);
|
|||||||
- Adds newline after output
|
- Adds newline after output
|
||||||
|
|
||||||
### Assert Function
|
### Assert Function
|
||||||
```bob
|
```go
|
||||||
assert(condition, "optional message");
|
assert(condition, "optional message");
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -220,7 +440,7 @@ assert(condition, "optional message");
|
|||||||
- **Error messages**: Descriptive error messages printed to console
|
- **Error messages**: Descriptive error messages printed to console
|
||||||
|
|
||||||
### Common Error Scenarios
|
### Common Error Scenarios
|
||||||
```bob
|
```go
|
||||||
// Division by zero
|
// Division by zero
|
||||||
10 / 0; // Error: DivisionByZeroError
|
10 / 0; // Error: DivisionByZeroError
|
||||||
|
|
||||||
@ -237,7 +457,7 @@ undefinedVar; // Error: Undefined variable
|
|||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### Basic Calculator
|
### Basic Calculator
|
||||||
```bob
|
```go
|
||||||
func add(a, b) {
|
func add(a, b) {
|
||||||
return a + b;
|
return a + b;
|
||||||
}
|
}
|
||||||
@ -251,7 +471,7 @@ print("Result: " + result); // Result: 17
|
|||||||
```
|
```
|
||||||
|
|
||||||
### String Processing
|
### String Processing
|
||||||
```bob
|
```go
|
||||||
func greet(name) {
|
func greet(name) {
|
||||||
return "Hello, " + name + "!";
|
return "Hello, " + name + "!";
|
||||||
}
|
}
|
||||||
@ -266,7 +486,7 @@ print(greeting + " " + repeated); // Hello, Bob! HaHaHa
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Variable Scoping Example
|
### Variable Scoping Example
|
||||||
```bob
|
```go
|
||||||
var globalCounter = 0;
|
var globalCounter = 0;
|
||||||
|
|
||||||
func increment() {
|
func increment() {
|
||||||
@ -280,82 +500,71 @@ increment();
|
|||||||
print("After: " + globalCounter); // After: 1
|
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
|
// Sum numbers from 1 to 10
|
||||||
- **Dynamic typing**: Variables can hold any type
|
var sum = 0;
|
||||||
- **Automatic conversion**: Numbers and strings convert automatically
|
for (var j = 1; j <= 10; j = j + 1) {
|
||||||
- **No type annotations**: Types are inferred at runtime
|
sum = sum + j;
|
||||||
|
}
|
||||||
|
print("Sum: " + sum);
|
||||||
|
|
||||||
### Memory Management
|
// Find first even number
|
||||||
- **Reference counting**: Uses `std::shared_ptr` for automatic memory management
|
var k = 1;
|
||||||
- **No manual memory management**: No `delete` or `free` needed
|
while (k % 2 != 0) {
|
||||||
- **Garbage collection**: Automatic cleanup of unused objects
|
k = k + 1;
|
||||||
|
}
|
||||||
### Performance Characteristics
|
print("First even: " + k);
|
||||||
- **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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### File Extension
|
## Implementation Details
|
||||||
- **`.bob`**: Standard file extension for Bob source code
|
|
||||||
|
|
||||||
### Interactive Mode
|
### Assignment System: Statements vs Expressions
|
||||||
- **Not implemented**: No REPL (Read-Eval-Print Loop) yet
|
|
||||||
- **File-based**: All code must be in `.bob` files
|
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
|
# 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
|
# Bob Language Development Roadmap
|
||||||
|
|
||||||
## Current Status
|
## 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
|
### ✅ **Core Language Features**
|
||||||
```bob
|
|
||||||
// If statements
|
|
||||||
if (x > 10) {
|
|
||||||
print "big";
|
|
||||||
} else {
|
|
||||||
print "small";
|
|
||||||
}
|
|
||||||
|
|
||||||
// While loops
|
#### **Data Types & Variables**
|
||||||
var i = 0;
|
- **Numbers**: Integers, floats, automatic conversion
|
||||||
while (i < 5) {
|
- **Strings**: Literals, concatenation, multiplication
|
||||||
print i;
|
- **Booleans**: `true`, `false`
|
||||||
i = i + 1;
|
- **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:**
|
**Implementation Plan:**
|
||||||
- Add `IfStmt` and `WhileStmt` to Statement.h
|
- Add array literal syntax `[expr, expr, ...]`
|
||||||
- Update parser to handle `if` and `while` keywords
|
- Implement array indexing `array[index]`
|
||||||
- Implement control flow in interpreter
|
- Add array assignment `array[index] = value`
|
||||||
|
- Create `len()` function for arrays
|
||||||
|
- Support nested arrays
|
||||||
|
|
||||||
### 2. Logical Operators
|
#### **Maps/Dictionaries**
|
||||||
```bob
|
```go
|
||||||
// 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
|
|
||||||
var person = {"name": "Bob", "age": 25};
|
var person = {"name": "Bob", "age": 25};
|
||||||
print person["name"];
|
print(person["name"]); // "Bob"
|
||||||
person["city"] = "NYC";
|
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
|
### **Phase 2: Standard Library Expansion (Medium Priority)**
|
||||||
```bob
|
|
||||||
len("hello"); // String length
|
|
||||||
input("Enter name: "); // User input
|
|
||||||
random(1, 100); // Random numbers
|
|
||||||
type(42); // Type checking
|
|
||||||
```
|
|
||||||
|
|
||||||
### 7. File I/O
|
#### **File I/O**
|
||||||
```bob
|
```go
|
||||||
var content = readFile("data.txt");
|
var content = readFile("data.txt");
|
||||||
writeFile("output.txt", "Hello World");
|
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
|
#### **String Processing**
|
||||||
```bob
|
```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 {
|
class Person {
|
||||||
init(name, age) {
|
init(name, age) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
@ -105,99 +167,104 @@ class Person {
|
|||||||
}
|
}
|
||||||
|
|
||||||
greet() {
|
greet() {
|
||||||
print "Hello, I'm " + this.name;
|
return "Hello, I'm " + this.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var bob = Person("Bob", 25);
|
|
||||||
bob.greet();
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 9. Modules/Imports
|
### **Phase 4: Development Tools (Lower Priority)**
|
||||||
```bob
|
|
||||||
import "math.bob";
|
#### **Interactive Mode (REPL)**
|
||||||
import "utils.bob";
|
```bash
|
||||||
|
$ bob
|
||||||
|
> var x = 5
|
||||||
|
> print(x + 3)
|
||||||
|
8
|
||||||
|
> func add(a, b) { return a + b; }
|
||||||
|
> add(2, 3)
|
||||||
|
5
|
||||||
```
|
```
|
||||||
|
|
||||||
### 10. Type System
|
#### **Debugger**
|
||||||
```bob
|
```go
|
||||||
// Optional type annotations
|
debugger; // Breakpoint
|
||||||
fun add(a: number, b: number): number {
|
var x = 5;
|
||||||
return a + b;
|
// Step through code
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Implementation Tips
|
## **Implementation Guidelines**
|
||||||
|
|
||||||
### For Each Feature:
|
### **For Each New Feature:**
|
||||||
1. Lexer: Add new tokens if needed
|
1. **Lexer**: Add new tokens if needed
|
||||||
2. Parser: Add new expression/statement types
|
2. **Parser**: Add new expression/statement types
|
||||||
3. AST: Define new node types
|
3. **AST**: Define new node types
|
||||||
4. Interpreter: Implement evaluation logic
|
4. **Interpreter**: Implement evaluation logic
|
||||||
5. Test: Create test cases using assert function
|
5. **Testing**: Create comprehensive test cases
|
||||||
|
|
||||||
### Testing Strategy:
|
### **Testing Strategy:**
|
||||||
```bob
|
```go
|
||||||
// Use the new assert function for comprehensive testing
|
// Use the built-in assert function for testing
|
||||||
assert(add(2, 3) == 5, "add(2, 3) should equal 5");
|
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)
|
## **Success Metrics**
|
||||||
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
|
### **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
|
### **In Progress 🔄**
|
||||||
- [x] Can use return statements
|
- [ ] Data structures (arrays, maps)
|
||||||
- [x] Can use closures
|
- [ ] Extended standard library
|
||||||
- [x] Has assert function for testing
|
- [ ] Performance optimizations
|
||||||
- [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
|
|
||||||
|
|
||||||
## Resources
|
### **Planned 📋**
|
||||||
|
- [ ] Advanced language features
|
||||||
|
- [ ] Development tools
|
||||||
|
- [ ] Documentation improvements
|
||||||
|
|
||||||
- [Crafting Interpreters](https://craftinginterpreters.com/) - Excellent resource for language implementation
|
## **Resources**
|
||||||
- [Bob's current source code](./source/) - Your implementation
|
|
||||||
- [Test files](./*.bob) - Examples of current functionality
|
|
||||||
|
|
||||||
## 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)
|
## **Recent Major Achievements**
|
||||||
- Function declarations with parameters
|
|
||||||
- Function calls with arguments
|
|
||||||
- Return statements
|
|
||||||
- Proper scoping and closures
|
|
||||||
- Nested function calls
|
|
||||||
|
|
||||||
### Standard Library (COMPLETED)
|
### **Assignment System Design**
|
||||||
- `print()` function (converted from statement)
|
- Implemented dual assignment system (statements + expressions)
|
||||||
- `assert()` function with custom messages
|
- Prevents common bugs like `if (x = 10)`
|
||||||
- Extensible architecture for adding more functions
|
- Maintains practical for loop syntax
|
||||||
|
- Documentation and testing
|
||||||
|
|
||||||
### Testing Framework (COMPLETED)
|
### **Control Flow Implementation**
|
||||||
- Comprehensive test suite using assert
|
- All loop types: while, for, do-while
|
||||||
- Tests for all language features
|
- Break/continue support
|
||||||
- Proper error handling and execution stopping
|
- Nested scenarios
|
||||||
|
- Basic edge case handling
|
||||||
|
|
||||||
### Advanced Language Features (COMPLETED)
|
### **Function System**
|
||||||
- First-class functions and higher-order functions
|
- First-class functions
|
||||||
- Function passing as arguments
|
- Closures and lexical scoping
|
||||||
|
- Anonymous functions
|
||||||
- Function composition patterns
|
- Function composition patterns
|
||||||
- Callback patterns and function storage
|
- Recursion support
|
||||||
- String + number concatenation with smart formatting
|
|
||||||
- String multiplication (string * number, number * string)
|
---
|
||||||
- Alphanumeric identifiers support
|
|
||||||
- Stress testing with 100-parameter functions
|
*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
|
// Forward declarations
|
||||||
struct FunctionExpr;
|
struct FunctionExpr;
|
||||||
struct IncrementExpr;
|
struct IncrementExpr;
|
||||||
|
struct TernaryExpr;
|
||||||
struct ExprVisitor;
|
struct ExprVisitor;
|
||||||
|
|
||||||
struct AssignExpr;
|
struct AssignExpr;
|
||||||
@ -35,6 +36,7 @@ struct ExprVisitor
|
|||||||
virtual Value visitLiteralExpr(const std::shared_ptr<LiteralExpr>& expr) = 0;
|
virtual Value visitLiteralExpr(const std::shared_ptr<LiteralExpr>& expr) = 0;
|
||||||
virtual Value visitUnaryExpr(const std::shared_ptr<UnaryExpr>& expr) = 0;
|
virtual Value visitUnaryExpr(const std::shared_ptr<UnaryExpr>& expr) = 0;
|
||||||
virtual Value visitVarExpr(const std::shared_ptr<VarExpr>& 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> {
|
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 visitVarExpr(const std::shared_ptr<VarExpr>& expression) override;
|
||||||
Value visitIncrementExpr(const std::shared_ptr<IncrementExpr>& expression) override;
|
Value visitIncrementExpr(const std::shared_ptr<IncrementExpr>& expression) override;
|
||||||
Value visitAssignExpr(const std::shared_ptr<AssignExpr>& 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 visitBlockStmt(const std::shared_ptr<BlockStmt>& statement, ExecutionContext* context = nullptr) override;
|
||||||
void visitExpressionStmt(const std::shared_ptr<ExpressionStmt>& 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 visitFunctionStmt(const std::shared_ptr<FunctionStmt>& statement, ExecutionContext* context = nullptr) override;
|
||||||
void visitReturnStmt(const std::shared_ptr<ReturnStmt>& 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 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);
|
void interpret(std::vector<std::shared_ptr<Stmt> > statements);
|
||||||
|
|
||||||
@ -85,9 +92,15 @@ private:
|
|||||||
bool IsInteractive;
|
bool IsInteractive;
|
||||||
std::vector<std::shared_ptr<BuiltinFunction> > builtinFunctions;
|
std::vector<std::shared_ptr<BuiltinFunction> > builtinFunctions;
|
||||||
std::vector<std::shared_ptr<Function> > functions;
|
std::vector<std::shared_ptr<Function> > functions;
|
||||||
|
std::vector<std::shared_ptr<Thunk> > thunks; // Store thunks to prevent memory leaks
|
||||||
ErrorReporter* errorReporter;
|
ErrorReporter* errorReporter;
|
||||||
bool inThunkExecution = false;
|
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);
|
Value evaluate(const std::shared_ptr<Expr>& expr);
|
||||||
@ -106,6 +119,10 @@ public:
|
|||||||
std::string stringify(Value object);
|
std::string stringify(Value object);
|
||||||
void addBuiltinFunction(std::shared_ptr<BuiltinFunction> func);
|
void addBuiltinFunction(std::shared_ptr<BuiltinFunction> func);
|
||||||
|
|
||||||
|
// Memory management
|
||||||
|
void cleanupUnusedFunctions();
|
||||||
|
void cleanupUnusedThunks();
|
||||||
|
|
||||||
// Error reporting
|
// Error reporting
|
||||||
void setErrorReporter(ErrorReporter* reporter) {
|
void setErrorReporter(ErrorReporter* reporter) {
|
||||||
errorReporter = reporter;
|
errorReporter = reporter;
|
||||||
|
|||||||
@ -15,13 +15,16 @@ enum TokenType{
|
|||||||
GREATER, GREATER_EQUAL,
|
GREATER, GREATER_EQUAL,
|
||||||
LESS, LESS_EQUAL,
|
LESS, LESS_EQUAL,
|
||||||
|
|
||||||
|
// Ternary operator
|
||||||
|
QUESTION, COLON,
|
||||||
|
|
||||||
// Increment/decrement operators
|
// Increment/decrement operators
|
||||||
PLUS_PLUS, MINUS_MINUS,
|
PLUS_PLUS, MINUS_MINUS,
|
||||||
|
|
||||||
IDENTIFIER, STRING, NUMBER, BOOL,
|
IDENTIFIER, STRING, NUMBER, BOOL,
|
||||||
|
|
||||||
AND, OR, TRUE, FALSE, IF, ELSE, FUNCTION, FOR,
|
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
|
// Compound assignment operators
|
||||||
PLUS_EQUAL, MINUS_EQUAL, STAR_EQUAL, SLASH_EQUAL, PERCENT_EQUAL,
|
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",
|
"GREATER", "GREATER_EQUAL",
|
||||||
"LESS", "LESS_EQUAL",
|
"LESS", "LESS_EQUAL",
|
||||||
|
|
||||||
|
"QUESTION", "COLON",
|
||||||
|
|
||||||
"PLUS_PLUS", "MINUS_MINUS",
|
"PLUS_PLUS", "MINUS_MINUS",
|
||||||
|
|
||||||
"IDENTIFIER", "STRING", "NUMBER", "BOOL",
|
"IDENTIFIER", "STRING", "NUMBER", "BOOL",
|
||||||
|
|
||||||
"AND", "OR", "TRUE", "FALSE", "IF", "ELSE", "FUNCTION", "FOR",
|
"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
|
// Compound assignment operators
|
||||||
"PLUS_EQUAL", "MINUS_EQUAL", "STAR_EQUAL", "SLASH_EQUAL", "PERCENT_EQUAL",
|
"PLUS_EQUAL", "MINUS_EQUAL", "STAR_EQUAL", "SLASH_EQUAL", "PERCENT_EQUAL",
|
||||||
@ -67,12 +72,15 @@ const std::map<std::string, TokenType> KEYWORDS {
|
|||||||
{"func", FUNCTION},
|
{"func", FUNCTION},
|
||||||
{"for", FOR},
|
{"for", FOR},
|
||||||
{"while", WHILE},
|
{"while", WHILE},
|
||||||
|
{"do", DO},
|
||||||
{"var", VAR},
|
{"var", VAR},
|
||||||
{"class", CLASS},
|
{"class", CLASS},
|
||||||
{"super", SUPER},
|
{"super", SUPER},
|
||||||
{"this", THIS},
|
{"this", THIS},
|
||||||
{"none", NONE},
|
{"none", NONE},
|
||||||
{"return", RETURN},
|
{"return", RETURN},
|
||||||
|
{"break", BREAK},
|
||||||
|
{"continue", CONTINUE},
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Token
|
struct Token
|
||||||
|
|||||||
@ -24,6 +24,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
sptr(Expr) expression();
|
sptr(Expr) expression();
|
||||||
sptr(Expr) logical_or();
|
sptr(Expr) logical_or();
|
||||||
|
sptr(Expr) ternary();
|
||||||
sptr(Expr) logical_and();
|
sptr(Expr) logical_and();
|
||||||
sptr(Expr) bitwise_or();
|
sptr(Expr) bitwise_or();
|
||||||
sptr(Expr) bitwise_xor();
|
sptr(Expr) bitwise_xor();
|
||||||
@ -56,6 +57,16 @@ private:
|
|||||||
|
|
||||||
std::shared_ptr<Stmt> ifStatement();
|
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> declaration();
|
||||||
|
|
||||||
std::shared_ptr<Stmt> varDeclaration();
|
std::shared_ptr<Stmt> varDeclaration();
|
||||||
@ -63,7 +74,9 @@ private:
|
|||||||
std::shared_ptr<Stmt> functionDeclaration();
|
std::shared_ptr<Stmt> functionDeclaration();
|
||||||
std::shared_ptr<Expr> functionExpression();
|
std::shared_ptr<Expr> functionExpression();
|
||||||
|
|
||||||
|
std::shared_ptr<Stmt> assignmentStatement();
|
||||||
sptr(Expr) assignment();
|
sptr(Expr) assignment();
|
||||||
|
sptr(Expr) assignmentExpression(); // For for loop increment clauses
|
||||||
sptr(Expr) increment(); // Parse increment/decrement expressions
|
sptr(Expr) increment(); // Parse increment/decrement expressions
|
||||||
sptr(Expr) postfix(); // Parse postfix operators
|
sptr(Expr) postfix(); // Parse postfix operators
|
||||||
|
|
||||||
|
|||||||
@ -11,10 +11,18 @@ struct BlockStmt;
|
|||||||
struct FunctionStmt;
|
struct FunctionStmt;
|
||||||
struct ReturnStmt;
|
struct ReturnStmt;
|
||||||
struct IfStmt;
|
struct IfStmt;
|
||||||
|
struct WhileStmt;
|
||||||
|
struct DoWhileStmt;
|
||||||
|
struct ForStmt;
|
||||||
|
struct BreakStmt;
|
||||||
|
struct ContinueStmt;
|
||||||
|
struct AssignStmt;
|
||||||
|
|
||||||
struct ExecutionContext {
|
struct ExecutionContext {
|
||||||
bool isFunctionBody = false;
|
bool isFunctionBody = false;
|
||||||
bool hasReturn = false;
|
bool hasReturn = false;
|
||||||
|
bool hasBreak = false;
|
||||||
|
bool hasContinue = false;
|
||||||
Value returnValue;
|
Value returnValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -26,6 +34,12 @@ struct StmtVisitor
|
|||||||
virtual void visitFunctionStmt(const std::shared_ptr<FunctionStmt>& stmt, ExecutionContext* context = nullptr) = 0;
|
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 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 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>
|
struct Stmt : public std::enable_shared_from_this<Stmt>
|
||||||
@ -118,3 +132,87 @@ struct IfStmt : Stmt
|
|||||||
visitor->visitIfStmt(std::static_pointer_cast<IfStmt>(shared_from_this()), context);
|
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;
|
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 Interpreter::visitCallExpr(const std::shared_ptr<CallExpr>& expression) {
|
||||||
Value callee = evaluate(expression->callee);
|
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
|
// Check if this is a tail call
|
||||||
if (expression->isTailCall) {
|
if (expression->isTailCall) {
|
||||||
// Create a thunk for tail call optimization
|
// Create a thunk for tail call optimization using smart pointer
|
||||||
auto thunk = new Thunk([this, function, arguments]() -> Value {
|
auto thunk = std::make_shared<Thunk>([this, function, arguments]() -> Value {
|
||||||
// Use RAII to manage environment
|
// Use RAII to manage environment
|
||||||
ScopedEnv _env(environment);
|
ScopedEnv _env(environment);
|
||||||
environment = std::make_shared<Environment>(function->closure);
|
environment = std::make_shared<Environment>(function->closure);
|
||||||
@ -630,8 +640,17 @@ Value Interpreter::visitCallExpr(const std::shared_ptr<CallExpr>& expression) {
|
|||||||
return context.returnValue;
|
return context.returnValue;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Return the thunk as a Value
|
// Store the thunk to keep it alive and return as Value
|
||||||
return Value(thunk);
|
thunks.push_back(thunk);
|
||||||
|
|
||||||
|
// Automatic cleanup check
|
||||||
|
thunkCreationCount++;
|
||||||
|
if (thunkCreationCount >= CLEANUP_THRESHOLD) {
|
||||||
|
cleanupUnusedThunks();
|
||||||
|
thunkCreationCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Value(thunk.get());
|
||||||
} else {
|
} else {
|
||||||
// Normal function call - create new environment
|
// Normal function call - create new environment
|
||||||
ScopedEnv _env(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);
|
auto function = msptr(Function)("anonymous", paramNames, expression->body, environment);
|
||||||
functions.push_back(function); // Keep the shared_ptr alive
|
functions.push_back(function); // Keep the shared_ptr alive
|
||||||
|
|
||||||
|
// Automatic cleanup check
|
||||||
|
functionCreationCount++;
|
||||||
|
if (functionCreationCount >= CLEANUP_THRESHOLD) {
|
||||||
|
cleanupUnusedFunctions();
|
||||||
|
functionCreationCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return Value(function.get());
|
return Value(function.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -714,6 +741,13 @@ void Interpreter::visitFunctionStmt(const std::shared_ptr<FunctionStmt>& stateme
|
|||||||
environment);
|
environment);
|
||||||
functions.push_back(function); // Keep the shared_ptr alive
|
functions.push_back(function); // Keep the shared_ptr alive
|
||||||
environment->define(statement->name.lexeme, Value(function.get()));
|
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)
|
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) {
|
void Interpreter::interpret(std::vector<std::shared_ptr<Stmt> > statements) {
|
||||||
for(const std::shared_ptr<Stmt>& s : 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)
|
for(const std::shared_ptr<Stmt>& s : statements)
|
||||||
{
|
{
|
||||||
execute(s, context);
|
execute(s, context);
|
||||||
if (context && context->hasReturn) {
|
if (context && (context->hasReturn || context->hasBreak || context->hasContinue)) {
|
||||||
this->environment = previous;
|
this->environment = previous;
|
||||||
return;
|
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});
|
tokens.push_back(Token{BIN_NOT, std::string(1, t), line, column - 1});
|
||||||
advance();
|
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 == '=')
|
else if(t == '=')
|
||||||
{
|
{
|
||||||
std::string token = std::string(1, t);
|
std::string token = std::string(1, t);
|
||||||
|
|||||||
@ -16,18 +16,32 @@ sptr(Expr) Parser::expression()
|
|||||||
|
|
||||||
sptr(Expr) Parser::logical_or()
|
sptr(Expr) Parser::logical_or()
|
||||||
{
|
{
|
||||||
sptr(Expr) expr = logical_and();
|
sptr(Expr) expr = ternary();
|
||||||
|
|
||||||
while(match({OR}))
|
while(match({OR}))
|
||||||
{
|
{
|
||||||
Token op = previous();
|
Token op = previous();
|
||||||
sptr(Expr) right = logical_and();
|
sptr(Expr) right = ternary();
|
||||||
expr = msptr(BinaryExpr)(expr, op, right);
|
expr = msptr(BinaryExpr)(expr, op, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
return expr;
|
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) Parser::logical_and()
|
||||||
{
|
{
|
||||||
sptr(Expr) expr = equality();
|
sptr(Expr) expr = equality();
|
||||||
@ -101,13 +115,21 @@ sptr(Expr) Parser::shift()
|
|||||||
|
|
||||||
sptr(Expr) Parser::assignment()
|
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();
|
sptr(Expr) expr = increment();
|
||||||
|
|
||||||
if(match({EQUAL, PLUS_EQUAL, MINUS_EQUAL, STAR_EQUAL, SLASH_EQUAL, PERCENT_EQUAL,
|
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}))
|
BIN_AND_EQUAL, BIN_OR_EQUAL, BIN_XOR_EQUAL, BIN_SLEFT_EQUAL, BIN_SRIGHT_EQUAL}))
|
||||||
{
|
{
|
||||||
Token op = previous();
|
Token op = previous();
|
||||||
sptr(Expr) value = assignment();
|
sptr(Expr) value = assignmentExpression();
|
||||||
if(std::dynamic_pointer_cast<VarExpr>(expr))
|
if(std::dynamic_pointer_cast<VarExpr>(expr))
|
||||||
{
|
{
|
||||||
Token name = std::dynamic_pointer_cast<VarExpr>(expr)->name;
|
Token name = std::dynamic_pointer_cast<VarExpr>(expr)->name;
|
||||||
@ -372,11 +394,48 @@ sptr(Stmt) Parser::statement()
|
|||||||
{
|
{
|
||||||
if(match({RETURN})) return returnStatement();
|
if(match({RETURN})) return returnStatement();
|
||||||
if(match({IF})) return ifStatement();
|
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());
|
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();
|
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()
|
sptr(Stmt) Parser::ifStatement()
|
||||||
{
|
{
|
||||||
@ -394,6 +453,78 @@ sptr(Stmt) Parser::ifStatement()
|
|||||||
return msptr(IfStmt)(condition, thenBranch, elseBranch);
|
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
|
// Helper function to detect if an expression is a tail call
|
||||||
bool Parser::isTailCall(const std::shared_ptr<Expr>& expr) {
|
bool Parser::isTailCall(const std::shared_ptr<Expr>& expr) {
|
||||||
// Check if this is a direct function call (no operations on the result)
|
// 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
|
// Store the shared_ptr in the interpreter to keep it alive
|
||||||
interpreter.addBuiltinFunction(exitFunc);
|
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");
|
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
|
// TEST SUMMARY
|
||||||
// ========================================
|
// ========================================
|
||||||
@ -1877,7 +2381,11 @@ print("- Lexer consistency improvements");
|
|||||||
print("- None value concatenation (string + none, none + string)");
|
print("- None value concatenation (string + none, none + string)");
|
||||||
print("- Memory management (variable reassignment, function reassignment, large string cleanup)");
|
print("- Memory management (variable reassignment, function reassignment, large string cleanup)");
|
||||||
print("- Multi-statement function execution");
|
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("\nAll tests passed.");
|
||||||
print("Test suite complete.");
|
print("Test suite complete.");
|
||||||
|
|
||||||
@ -15,3 +15,7 @@ print("Result: " + fib_result);
|
|||||||
|
|
||||||
print("Counter: " + counter);
|
print("Counter: " + counter);
|
||||||
|
|
||||||
|
|
||||||
|
func test_fib() {
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user