ast: allow definitions in loop head
This is a rather bad hack, but it makes parsing a lot easier, so i'll allow it for now.
This commit is contained in:
parent
57e74ab52a
commit
ec8c8916b7
3 changed files with 37 additions and 11 deletions
|
@ -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) {
|
||||
|
|
13
src/main.rs
13
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),
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in a new issue