From 6e02a1a6441af728c2b7a1987acc5b1f51a89c96 Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Tue, 27 May 2025 09:22:29 +0200 Subject: [PATCH] add ast printer --- lox/bf.lox | 100 +++++++++++++++++++++ lox/error.lox | 7 ++ lox/for_closure_in_body_lox | 22 +++++ lox/shadow_local.lox | 8 ++ src/ast_printer.rs | 173 ++++++++++++++++++++++++++++++++++++ src/lib.rs | 5 +- 6 files changed, 314 insertions(+), 1 deletion(-) create mode 100644 lox/bf.lox create mode 100644 lox/error.lox create mode 100644 lox/for_closure_in_body_lox create mode 100644 lox/shadow_local.lox create mode 100644 src/ast_printer.rs diff --git a/lox/bf.lox b/lox/bf.lox new file mode 100644 index 0000000..cb6deab --- /dev/null +++ b/lox/bf.lox @@ -0,0 +1,100 @@ +class Cell { + init(previous, next, data) { + this.previous = previous; + this.next = next; + this.data = data; + } +} + +class Tape { + init() { + this.current = Cell(nil, nil, 0); + } + + backward() { + if (this.current.previous == nil) { + this.current.previous = Cell(nil, this.current, 0); + } + + this.current = this.current.previous; + } + + + forward() { + if (this.current.next == nil) { + this.current.next = Cell(this.current, nil, 0); + } + + this.current = this.current.next; + } + + increment() { + this.current.data = this.current.data + 1; + } + + decrement() { + this.current.data = this.current.data - 1; + } +} + +class Interpreter { + init(bfpath) { + this.bfpath = bfpath; + } + + run() { + var tape = Tape(); + var instruction = read(this.bfpath); + + while (instruction != nil) { + if (instruction == ">") { + tape.forward(); + } + + if (instruction == "<") { + tape.backward(); + } + + if (instruction == "+") { + tape.increment(); + } + + if (instruction == "-") { + tape.decrement(); + } + + if (instruction == ".") { + asciiOut(tape.current.data); + } + + if (instruction == ",") { + + } + + if (instruction == "[" and tape.current.data < 0.0001) { + instruction = read(this.bfpath); + var bracketCount = 1; + while (bracketCount > 0 and instruction != nil) { + if (instruction == "[") { bracketCount = bracketCount + 1; } + if (instruction == "]") { bracketCount = bracketCount - 1; } + if (bracketCount > 0) { instruction = read(this.bfpath); } + } + } + + if (instruction == "]" and tape.current.data >= 0.0001) { + instruction = read(this.bfpath, true); + var bracketCount = 1; + while (bracketCount > 0 and instruction != nil) { + if (instruction == "]") { bracketCount = bracketCount + 1; } + if (instruction == "[") { bracketCount = bracketCount - 1; } + if (bracketCount > 0) { instruction = read(this.bfpath, true); } + } + } + + instruction = read(this.bfpath); + } + } +} + +var interpreter = Interpreter("bf/helloworld.bf"); +interpreter.run(); diff --git a/lox/error.lox b/lox/error.lox new file mode 100644 index 0000000..a1ecd73 --- /dev/null +++ b/lox/error.lox @@ -0,0 +1,7 @@ +var a = 1; +while (a < 10) { + a = a + 1; +} + +print a; + diff --git a/lox/for_closure_in_body_lox b/lox/for_closure_in_body_lox new file mode 100644 index 0000000..01587e6 --- /dev/null +++ b/lox/for_closure_in_body_lox @@ -0,0 +1,22 @@ +var f1; +var f2; +var f3; + +for (var i = 1; i < 4; i = i + 1) { + var j = i; + fun f() { + print i; + print j; + } + + if (j == 1) f1 = f; + else if (j == 2) f2 = f; + else f3 = f; +} + +f1(); // expect: 4 + // expect: 1 +f2(); // expect: 4 + // expect: 2 +f3(); // expect: 4 + // expect: 3 diff --git a/lox/shadow_local.lox b/lox/shadow_local.lox new file mode 100644 index 0000000..bdbfd10 --- /dev/null +++ b/lox/shadow_local.lox @@ -0,0 +1,8 @@ +{ + var a = "local"; + { + var a = "shadow"; + print a; // expect: shadow + } + print a; // expect: local +} diff --git a/src/ast_printer.rs b/src/ast_printer.rs new file mode 100644 index 0000000..d48728c --- /dev/null +++ b/src/ast_printer.rs @@ -0,0 +1,173 @@ +use crate::{expression::Expression, statement::Statement, token::Literal}; + +pub fn print(statements: &[Statement]) -> String { + let mut result = String::new(); + for statement in statements { + result.push_str(&format!("{}\n", print_statement(statement, 0))); + } + result +} + +fn print_statement(statement: &Statement, indent: usize) -> String { + let indent_str = " ".repeat(indent); + match statement { + Statement::Block(statements) => { + let mut result = format!("{}Block:\n", indent_str); + for stmt in statements { + result.push_str(&format!("{}\n", print_statement(stmt, indent + 1))); + } + result.trim_end().to_string() + } + Statement::Print(expr) => { + format!("{}Print: {}", indent_str, print_expression(expr)) + } + Statement::Expression(expr) => { + format!("{}Expr: {}", indent_str, print_expression(expr)) + } + Statement::Var { name, initializer } => { + let init_str = match **initializer { + Some(ref expr) => print_expression(expr), + None => "nil".to_string(), + }; + format!("{}Var {}: {}", indent_str, name.lexeme, init_str) + } + Statement::If { + condition, + then_branch, + else_branch, + } => { + let mut result = format!( + "{}If ({})\n{}", + indent_str, + print_expression(condition), + print_statement(then_branch, indent + 1) + ); + if let Some(else_branch) = else_branch { + result.push_str(&format!( + "\n{}Else:\n{}", + indent_str, + print_statement(else_branch, indent + 1) + )); + } + result + } + Statement::While { condition, body } => { + format!( + "{}While ({})\n{}", + indent_str, + print_expression(condition), + print_statement(body, indent + 1) + ) + } + Statement::Function { name, params, body } => { + let params_str = params + .iter() + .map(|p| p.lexeme.clone()) + .collect::>() + .join(", "); + let mut result = format!("{}Function {}({}):\n", indent_str, name.lexeme, params_str); + for stmt in body { + result.push_str(&format!("{}\n", print_statement(stmt, indent + 1))); + } + result.trim_end().to_string() + } + Statement::Return { keyword: _, value } => { + let value_str = match value { + Some(expr) => print_expression(expr), + None => "nil".to_string(), + }; + format!("{}Return: {}", indent_str, value_str) + } + Statement::Class { + name, + superclass, + methods, + } => { + let superclass_str = match superclass { + Some(expr) => format!(" < {}", print_expression(expr)), + None => "".to_string(), + }; + let mut result = format!("{}Class {}{}:\n", indent_str, name.lexeme, superclass_str); + for method in methods { + result.push_str(&format!("{}\n", print_statement(method, indent + 1))); + } + result.trim_end().to_string() + } + } +} + +fn print_expression(expr: &Expression) -> String { + match expr { + Expression::Assign { name, value } => { + format!("(= {} {})", name.lexeme, print_expression(value)) + } + Expression::Binary { + left, + operator, + right, + } => { + format!( + "({} {} {})", + operator.lexeme, + print_expression(left), + print_expression(right) + ) + } + Expression::Call { + callee, + paren: _, + args, + } => { + let args_str = args + .iter() + .map(print_expression) + .collect::>() + .join(", "); + format!("(call {} [{}])", print_expression(callee), args_str) + } + Expression::Get { object, name } => { + format!("(get {} {})", print_expression(object), name.lexeme) + } + Expression::Grouping { expression } => { + format!("(group {})", print_expression(expression)) + } + Expression::Literal { value } => match value { + Literal::String(s) => format!("\"{}\"", s), + Literal::Number(n) => format!("{}", n), + Literal::Boolean(b) => format!("{}", b), + Literal::Nil => "nil".to_string(), + }, + Expression::Logical { + left, + operator, + right, + } => { + format!( + "({} {} {})", + operator.lexeme, + print_expression(left), + print_expression(right) + ) + } + Expression::Set { + object, + name, + value, + } => { + format!( + "(set {} {} {})", + print_expression(object), + name.lexeme, + print_expression(value) + ) + } + Expression::Super { keyword: _, method } => { + format!("(super {})", method.lexeme) + } + Expression::This { keyword: _ } => "this".to_string(), + Expression::Unary { operator, right } => { + format!("({} {})", operator.lexeme, print_expression(right)) + } + Expression::Variable { name } => name.lexeme.clone(), + } +} diff --git a/src/lib.rs b/src/lib.rs index 7cebf0b..d27e546 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,8 +11,9 @@ use interpreter::{Interpreter, InterpreterError}; use parser::ParserError; use resolver::{Resolver, ResolverError}; use thiserror::Error; -use tracing::error; +use tracing::{debug, error}; +pub mod ast_printer; pub mod callable; pub mod class; pub mod cli; @@ -90,6 +91,8 @@ fn run_rox(input: &str, interpreter: &mut Interpreter) -> Result<(), RoxError> { let mut resolver = Resolver::new(interpreter); let ast = parser::ast(tokens)?; + debug!("AST:\n{}", crate::ast_printer::print(&ast)); + resolver.resolve(&ast)?; interpreter.run(ast)?;