diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 6a08c1e..cf0d178 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -17,6 +17,7 @@ enum Scope { SourceList, Function, Loop, + ForLoopHead, } struct Parser { @@ -243,6 +244,7 @@ impl Parser { self.lexer.expect_kind(token::Kind::OParen)?; let terminators = [token::Kind::Semi, token::Kind::Semi, token::Kind::CParen]; let mut exprs = Vec::new(); + self.scope.push(Scope::ForLoopHead); for i in 0..3 { exprs.push(if self.lexer.next_if(terminators[i]).is_some() { None @@ -250,6 +252,7 @@ impl Parser { Some(self.parse_expr(&terminators[i..=i])?) }); } + self.scope.pop(); let body = self.parse_block_stmt()?; self.scope.pop(); let exec = exprs.pop().unwrap(); @@ -330,10 +333,15 @@ impl Parser { self.assert_scope(Scope::File)?; let expr = if let Some(result) = self.lexer.peek() { let token = result?; - if !token.kind.is_start_of_expr() { - self.syntax_error(String::from("Expected an expression"), &token) - } else { - self.parse_assignment_expr_or_higher(terminators) + match token.kind { + k if k.is_start_of_expr() => self.parse_assignment_expr_or_higher(terminators), + // special case: when we are in a for loop header, we allow + // declaration statements (this is technically a bug and we're + // only doing it because it makes parsing everything easier) + token::Kind::VarKeyword if self.is_scope(Scope::ForLoopHead) => { + self.parse_decl_stmt() + } + _ => self.syntax_error(String::from("Expected an expression"), &token), } } else { self.syntax_error( @@ -707,6 +715,10 @@ impl Parser { } } + fn is_scope(&self, scope: Scope) -> bool { + self.assert_scope(scope).is_ok() + } + /// Ensure that the `scope` stack does not contain a certain scope. fn assert_scope_not(&self, scope: Scope) -> Result<(), Error> { if self.scope.contains(&scope) { diff --git a/src/main.rs b/src/main.rs index 58a3d32..fefdc8a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,20 +3,29 @@ mod error; mod lex; mod rt; +use std::time::Instant; + use ast::parse; use rt::exec; fn main() { + let start_time = Instant::now(); let result = parse(String::from("test.gaybuild")).unwrap(); + let duration = start_time.elapsed(); match result { Ok(tree) => { println!("=== begin tree ==="); tree.walk(|n, d| println!("{}{}", " ".repeat(d as usize), n)); - println!("=== end tree ===\n"); + println!("=== end tree ==="); + println!("parsing took {:?}\n", duration); println!("=== begin eval ==="); - match exec(&tree) { + let start_time = Instant::now(); + let result = exec(&tree); + let duration = start_time.elapsed(); + match result { Ok(val) => { println!("=== end eval ==="); + println!("evaluation took {:?}", duration); println!("evaluation returned {}", val) } Err(e) => println!("{:?}", e), diff --git a/test.gaybuild b/test.gaybuild index 4914986..73caf17 100644 --- a/test.gaybuild +++ b/test.gaybuild @@ -31,7 +31,7 @@ target kern { print("2 <= 3"); } # ... so do while loops ... - y = 0; + var y = 0; while (true) { print(y); y += 1; @@ -40,19 +40,24 @@ target kern { } } # ... and even for loops - for (x = 0; x < 10; x += 1) { + for (var x = 0; x < 10; x += 1) { print(x); } # functions are first-class citizens; they can be defined as inline # anonymous functions that you can treat like any other value - fn_with_callback(fn(a, b) { return a + b; }); + var captured_val = "-.-"; + fn_with_callback(fn(a, b) { + captured_val = "OwO"; + return a + b; + }); + print(captured_val); (fn(a, b) { return a + b; })(1, 2); # math works exactly as you would expect it to - owo = 1 + 2 * 3 * 4 - 5; + var owo = 1 + 2 * 3 * 4 - 5; # and, naturally, there are also all the bitwise goodies - bitmask = 1 << 2 | 1 << 8 ^ get_bit() & 1; + var bitmask = 1 << 2 | 1 << 8 ^ get_bit() & 1; # this is exactly equal to the above, but with added # parentheses to make the operator precedences clear bitmask = (1 << 2) | ((1 << 8) ^ (get_bit() & 1));