From 3c7cd3c4c7f4a7ae07f444a5bfc24cc0942b57ed Mon Sep 17 00:00:00 2001 From: Seoxi Ryouko Date: Wed, 8 Jul 2026 05:22:32 -0500 Subject: [PATCH] We have blocks & scopes --- src/interpret.rs | 42 +++++++++++++++++++++++++++++------------ src/lex/keyword.rs | 2 ++ src/lex/symbol.rs | 4 ++++ src/parse.rs | 47 +++++++++++++++++++++++++++++++++++++++++++--- test.tea | 8 ++++++++ 5 files changed, 88 insertions(+), 15 deletions(-) create mode 100644 test.tea diff --git a/src/interpret.rs b/src/interpret.rs index 8604634..9334300 100644 --- a/src/interpret.rs +++ b/src/interpret.rs @@ -31,12 +31,16 @@ macro_rules! some_or_error { } } -fn get_value(v: Value, scope: &HashMap) -> Result { +fn get_value(v: Value, scopes: &mut [Scope]) -> Result { match v { Value::Lit(t) => Ok(t), - Value::Id(s) => match scope.get(&s) { - None => Err(InterpreterError::new("undefined variable")), - Some(VarType::Const(t)) => Ok(t.clone()), + Value::Id(s) => { + match scopes.iter().rev().skip_while(|scope| !scope.contains_key(&s)).next() { + None => Err(InterpreterError::new("undefined variable")), + Some(scope) => match scope.get(&s).unwrap() { + VarType::Const(t) => Ok(t.clone()), + }, + } }, } } @@ -58,20 +62,28 @@ macro_rules! apply_op { } } -pub fn interpret(ast: Vec) -> Result, InterpreterError> { +type Scope = HashMap; + +fn interpret_block(ast: Vec, scopes: &mut Vec) -> Result, InterpreterError> { let mut ast = ast.clone(); ast.reverse(); let mut stack = vec![]; - let mut global_scope: HashMap = HashMap::new(); + //let mut scope = Scope::new(); loop { match ast.pop() { None => return Ok(stack), + Some(ASTNode::Block(tokens)) => { + scopes.push(HashMap::new()); + let mut result = interpret_block(tokens, scopes)?; + scopes.pop(); + stack.append(&mut result); + }, Some(ASTNode::Val(v)) => stack.push(v), Some(ASTNode::BinOp(op)) => { let b = some_or_error!(stack.pop(), InterpreterError::new("right value needed")); let a = some_or_error!(stack.pop(), InterpreterError::new("left value needed")); - let b: TeaValue = get_value(b, &global_scope)?; - let a: TeaValue = get_value(a, &global_scope)?; + let b: TeaValue = get_value(b, scopes)?; + let a: TeaValue = get_value(a, scopes)?; stack.push(Value::Lit(match op { Operator::Plus => { apply_op!(a, b, +) @@ -89,7 +101,7 @@ pub fn interpret(ast: Vec) -> Result, InterpreterError> { }, Some(ASTNode::UnaryMinus) => { let x = some_or_error!(stack.pop(), InterpreterError::new("value needed")); - let x: TeaValue = get_value(x, &global_scope)?; + let x: TeaValue = get_value(x, scopes)?; stack.push(Value::Lit(match x { TeaValue::Float(f) => TeaValue::Float(-f), TeaValue::Int(i) => TeaValue::Int(-i), @@ -100,14 +112,15 @@ pub fn interpret(ast: Vec) -> Result, InterpreterError> { Some(ASTNode::ConstDecl) => { let rhs = some_or_error!(stack.pop(), InterpreterError::new("rhs value needed")); let lhs = some_or_error!(stack.pop(), InterpreterError::new("lhs value needed")); - let rhs: TeaValue = get_value(rhs, &global_scope)?; + let rhs: TeaValue = get_value(rhs, scopes)?; + //let current_scope = .unwrap(); match lhs { Value::Lit(_) => return Err(InterpreterError::new("invalid left hand side")), Value::Id(s) => { - if global_scope.contains_key(&s) { + if scopes.last().unwrap().contains_key(&s) { return Err(InterpreterError::new("already defined constant")); } else { - global_scope.insert(s, VarType::Const(rhs)); + scopes.last_mut().unwrap().insert(s, VarType::Const(rhs)); } } } @@ -120,3 +133,8 @@ pub fn interpret(ast: Vec) -> Result, InterpreterError> { } } } + +pub fn interpret(ast: Vec) -> Result, InterpreterError> { + let mut scopes = vec![HashMap::new()]; + interpret_block(ast, &mut scopes) +} diff --git a/src/lex/keyword.rs b/src/lex/keyword.rs index bec0445..de96411 100644 --- a/src/lex/keyword.rs +++ b/src/lex/keyword.rs @@ -3,6 +3,7 @@ use super::{ whitespace_chars, Token }; #[derive(Debug)] pub enum Keyword { Let, + Fn, } macro_rules! match_keyword { @@ -15,5 +16,6 @@ macro_rules! match_keyword { pub fn parse_keyword(input: &str) -> Option<(Token, &str)> { match_keyword!("let", Token::Kw(Keyword::Let), input); + match_keyword!("fn", Token::Kw(Keyword::Fn), input); None } diff --git a/src/lex/symbol.rs b/src/lex/symbol.rs index fa0fadf..ab44b3e 100644 --- a/src/lex/symbol.rs +++ b/src/lex/symbol.rs @@ -4,6 +4,8 @@ use super::Token; pub enum Symbol { EqualSign, Semicolon, + OpenBrace, + CloseBrace, } pub fn parse_symbol(input: &str) -> Option<(Token, &str)> { @@ -12,6 +14,8 @@ pub fn parse_symbol(input: &str) -> Option<(Token, &str)> { match c { '=' => Some((Token::Sym(Symbol::EqualSign), input)), ';' => Some((Token::Sym(Symbol::Semicolon), input)), + '{' => Some((Token::Sym(Symbol::OpenBrace), input)), + '}' => Some((Token::Sym(Symbol::CloseBrace), input)), _ => None } } diff --git a/src/parse.rs b/src/parse.rs index d7f4602..f5d1336 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -20,6 +20,7 @@ pub enum ASTNode { BinOp(Operator), // binary operation UnaryMinus, StatementEnd, + Block(Vec), } #[derive(Debug)] @@ -55,9 +56,28 @@ macro_rules! assert_match { fn next_expr_end(tokens: &[Token]) -> usize { let mut index = 0; + let mut block_count = 0usize; loop { - if matches!(tokens.get(index), Some(Token::Sym(Symbol::Semicolon)) | None) { - return index; + match tokens.get(index) { + Some(Token::Sym(Symbol::OpenBrace)) => block_count += 1, + Some(Token::Sym(Symbol::CloseBrace)) => block_count -= 1, + Some(Token::Sym(Symbol::Semicolon)) if block_count == 0 => return index, + None => return index, + _ => (), + } + index += 1; + } +} + +fn next_block_end(tokens: &[Token]) -> Result { + let mut index = 0; + loop { + let token = tokens.get(index); + if token.is_none() { + return Err(ParseError::new("} expected")); + } + if matches!(token, Some(Token::Sym(Symbol::CloseBrace))) { + return Ok(index); } index += 1; } @@ -82,10 +102,31 @@ fn parse_expr(tokens: &[Token]) -> Result<(Vec, usize), ParseError> { ast.push(ASTNode::ConstDecl); index += count + 3; // 4 + count - 1 }, + Some(Token::Kw(Keyword::Fn)) => { + /*let id = match_or_return!(tokens.get(index + 1), Token::Id(a), a, ParseError::new("identifier expected after 'let' keyword")); + match_or_return!(tokens.get(index + 2), Token::Sym(Symbol::EqualSign), (), ParseError::new("= expected after identifier")); + let tokens = &tokens[(3 + index)..]; + let next_end = next_expr_end(tokens); + let (mut expr, count) = parse_expr(&tokens[..next_end])?; + ast.push(ASTNode::Val(Value::Id(id.to_owned()))); + ast.append(&mut expr); + ast.push(ASTNode::ConstDecl); + index += count + 3; // 4 + count - 1*/ + todo!() + }, + Some(Token::Sym(Symbol::EqualSign)) => todo!(), + Some(Token::Sym(Symbol::OpenBrace)) => { + let end = next_block_end(&tokens[(index + 1)..])?; + let (expr, count) = parse_expr(&tokens[(index + 1)..(index + end + 1)])?; + ast.push(ASTNode::Block(expr)); + index += count + 1; + }, + Some(Token::Sym(Symbol::CloseBrace)) => { + return Err(ParseError::new("unexpected }")); + }, Some(Token::Sym(Symbol::Semicolon)) => { ast.push(ASTNode::StatementEnd); }, - Some(Token::Sym(Symbol::EqualSign)) => todo!(), Some(Token::Op(op)) => { if ast.is_empty() && matches!(op, Operator::Minus) { ast.push(match tokens.get(index + 1) { diff --git a/test.tea b/test.tea new file mode 100644 index 0000000..31e3119 --- /dev/null +++ b/test.tea @@ -0,0 +1,8 @@ + +let x = 5; +let y = { + let x = 1; + x + 1 +}; +x + y +