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:
anna 2022-08-07 17:49:58 +02:00
parent 57e74ab52a
commit ec8c8916b7
Signed by: fef
GPG key ID: EC22E476DC2D3D84
3 changed files with 37 additions and 11 deletions

View file

@ -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) {

View file

@ -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),

View file

@ -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));