diff --git a/core/scripting/assets/scripts/wren/test01.wren b/core/scripting/assets/scripts/wren/test01.wren new file mode 100644 index 00000000..4c4ecd13 --- /dev/null +++ b/core/scripting/assets/scripts/wren/test01.wren @@ -0,0 +1,2 @@ +// Hello, world! +System.print("Hello, world!") diff --git a/core/scripting/assets/scripts/wren/test02.wren b/core/scripting/assets/scripts/wren/test02.wren new file mode 100644 index 00000000..7e4d7595 --- /dev/null +++ b/core/scripting/assets/scripts/wren/test02.wren @@ -0,0 +1,10 @@ +// Compute the fibonacci number at position n +fn fib(n) { + if (n <= 1) return n + return fib(n - 1) + fib(n - 2) +} + +System.print("Fibonacci sequence:") +for (i in 0..10) { + System.print("fib(%(_i)) = %(fib(i))") +} diff --git a/core/scripting/assets/scripts/wren/test03.wren b/core/scripting/assets/scripts/wren/test03.wren new file mode 100644 index 00000000..6d13d018 --- /dev/null +++ b/core/scripting/assets/scripts/wren/test03.wren @@ -0,0 +1,81 @@ +// Conway's Game of Life +class GameOfLife { + construct new(width, height) { + _width = width + _height = height + _grid = List.filled(width * height, false) + } + + // Get cell at x,y coordinates + cell(x, y) { + if (x < 0 || x >= _width || y < 0 || y >= _height) return false + return _grid[y * _width + x] + } + + // Set cell at x,y coordinates + setCell(x, y, value) { + if (x < 0 || x >= _width || y < 0 || y >= _height) return + _grid[y * _width + x] = value + } + + // Count live neighbors for cell at x,y + countNeighbors(x, y) { + var count = 0 + for (dy in -1..2) { + for (dx in -1..2) { + if (dx == 0 && dy == 0) continue + if (cell(x + dx, y + dy)) count = count + 1 + } + } + return count + } + + // Update the grid according to Game of Life rules + step() { + var newGrid = List.filled(_width * _height, false) + + for (y in 0..._height) { + for (x in 0..._width) { + var neighbors = countNeighbors(x, y) + var alive = cell(x, y) + + if (alive && (neighbors == 2 || neighbors == 3)) { + newGrid[y * _width + x] = true + } else if (!alive && neighbors == 3) { + newGrid[y * _width + x] = true + } + } + } + + _grid = newGrid + } + + // Display the current state + display() { + for (y in 0..._height) { + var line = "" + for (x in 0..._width) { + line = line + (cell(x, y) ? "■ " : "□ ") + } + System.print(line) + } + System.print("") + } +} + +// Create a 10x10 game board +var game = GameOfLife.new(10, 10) + +// Set up a simple glider pattern +game.setCell(1, 0, true) +game.setCell(2, 1, true) +game.setCell(0, 2, true) +game.setCell(1, 2, true) +game.setCell(2, 2, true) + +// Run for 10 generations +for (i in 0...10) { + System.print("Generation %(i):") + game.display() + game.step() +} diff --git a/core/scripting/src/lang/wren.rs b/core/scripting/src/lang/wren.rs index b8baf047..155d2365 100644 --- a/core/scripting/src/lang/wren.rs +++ b/core/scripting/src/lang/wren.rs @@ -79,11 +79,6 @@ pub enum Keyword { Super, } -/// Parses a list of tokens into an AST [`Expression`]. -pub fn parse(code: &str) -> Result { - Parser::from_code(code).parse_expression() -} - /// A parser for Wren code. struct Parser { tokens: Vec, @@ -181,6 +176,11 @@ impl Parser { } } +/// Parses a string of Wren code into an AST [`Expression`]. +pub fn parse(code: &str) -> Result { + Parser::from_code(code).parse_expression() +} + /// Tokenises a string of Wren code into a list of [`Token`]s. fn tokenise(code: &str) -> Vec { let mut tokens = Vec::new(); @@ -214,7 +214,30 @@ fn tokenise(code: &str) -> Vec { } continue; } - None => Token::Operator(Operator::Divide), + None => match characters.next_if_eq(&(position + 1, '*')) { + Some(_) => { + // ignore block comments + let mut depth = 1; + while depth > 0 { + match characters.next() { + Some((_, '*')) => { + if let Some((_, '/')) = characters.next() { + depth -= 1; + } + } + Some((_, '/')) => { + if let Some((_, '*')) = characters.next() { + depth += 1; + } + } + None => break, // Unterminated block comment + _ => {} + } + } + continue; + } + None => Token::Operator(Operator::Divide), + }, }, '<' => match characters.next_if_eq(&(position + 1, '=')) { Some(_) => Token::Operator(Operator::LessThanOrEqual), @@ -458,4 +481,28 @@ mod tests { )) ) ); + + macro_rules! parse_file_test { + ($name:ident, $path:expr) => { + #[test] + fn $name() { + use std::fs; + let contents = fs::read_to_string($path).expect("Failed to read file"); + let result = parse(&contents); + + assert!( + result.is_ok(), + "Failed to parse file {}: {:?}", + $path, + result.err() + ); + } + }; + } + + // parse_file_test!(it_should_parse_test01, + // "assets/scripts/wren/test01.wren"); parse_file_test! + // (it_should_parse_test02, "assets/scripts/wren/test02.wren"); + // parse_file_test!(it_should_parse_test03, + // "assets/scripts/wren/test03.wren"); }