AST - Replaced dumper
* Rewrote as a kind of "pretty" printer * It isn't actually pretty but it does restore Lox code from the AST.
This commit is contained in:
parent
728c7a3857
commit
1c5efe7e62
3 changed files with 332 additions and 204 deletions
202
src/ast.rs
202
src/ast.rs
|
@ -147,205 +147,3 @@ pub enum ExprNode {
|
|||
/// A get expression.
|
||||
Get(GetExpr),
|
||||
}
|
||||
|
||||
/* -------------------------------- *
|
||||
* Dumper trait and implementations *
|
||||
* -------------------------------- */
|
||||
|
||||
/// This trait should be implemented by nodes to allow AST dumps.
|
||||
pub trait AstDumper {
|
||||
/// Dump the node as a string.
|
||||
fn dump(&self) -> String;
|
||||
}
|
||||
|
||||
impl AstDumper for ProgramNode {
|
||||
fn dump(&self) -> String {
|
||||
self.0
|
||||
.iter()
|
||||
.map(|node| node.dump())
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ")
|
||||
}
|
||||
}
|
||||
|
||||
/// Function formatting macro.
|
||||
macro_rules! fmt_fun_decl {
|
||||
($fs:literal, $fd:expr) => {
|
||||
format!(
|
||||
$fs,
|
||||
$fd.name.lexeme,
|
||||
$fd.params
|
||||
.iter()
|
||||
.map(|token| &token.lexeme as &str)
|
||||
.collect::<Vec<&str>>()
|
||||
.join(" "),
|
||||
$fd.body
|
||||
.iter()
|
||||
.map(|stmt| stmt.dump())
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ")
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
impl AstDumper for StmtNode {
|
||||
fn dump(&self) -> String {
|
||||
match self {
|
||||
Self::VarDecl(name, Some(expr)) => format!("( var {} {} )", name.lexeme, expr.dump()),
|
||||
Self::VarDecl(name, None) => format!("( var {} nil )", name.lexeme),
|
||||
|
||||
Self::FunDecl(fun_decl) => fmt_fun_decl!("( fun {} ({}) {} )", fun_decl),
|
||||
|
||||
Self::ClassDecl(decl) => format!(
|
||||
"( class {} {} )",
|
||||
decl.name.lexeme,
|
||||
decl.methods
|
||||
.iter()
|
||||
.map(|fun_decl| fmt_fun_decl!("( {} ({}) {} )", fun_decl))
|
||||
.collect::<Vec<String>>()
|
||||
.join(" "),
|
||||
),
|
||||
|
||||
Self::Expression(expr) => expr.dump(),
|
||||
Self::Print(expr) => format!("(print {})", expr.dump()),
|
||||
|
||||
Self::Block(stmts) => format!(
|
||||
"( {} )",
|
||||
stmts
|
||||
.iter()
|
||||
.map(|s| s.dump())
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ")
|
||||
),
|
||||
|
||||
Self::If {
|
||||
condition,
|
||||
then_branch,
|
||||
else_branch,
|
||||
} => match else_branch {
|
||||
None => format!("( if {} {} () )", condition.dump(), then_branch.dump()),
|
||||
Some(stmt) => format!(
|
||||
"( if {} {} {} )",
|
||||
condition.dump(),
|
||||
then_branch.dump(),
|
||||
stmt.dump()
|
||||
),
|
||||
},
|
||||
|
||||
Self::Loop {
|
||||
label,
|
||||
condition,
|
||||
body,
|
||||
after_body,
|
||||
} => {
|
||||
let ltxt = if let Some(label) = label {
|
||||
format!("@{} ", label.lexeme)
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
let abtxt = if let Some(after_body) = after_body {
|
||||
format!("{} ", after_body.dump())
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
format!(
|
||||
"( {}loop {} {} {})",
|
||||
ltxt,
|
||||
condition.dump(),
|
||||
body.dump(),
|
||||
abtxt
|
||||
)
|
||||
}
|
||||
|
||||
Self::LoopControl {
|
||||
is_break,
|
||||
loop_name,
|
||||
} => {
|
||||
let stmt = if *is_break { "break" } else { "continue" };
|
||||
match loop_name {
|
||||
Some(name) => format!("( {} {} )", stmt, name.lexeme),
|
||||
None => format!("( {} )", stmt),
|
||||
}
|
||||
}
|
||||
|
||||
Self::Return { token: _, value } => match value {
|
||||
Some(expr) => format!("( return {} )", expr.dump()),
|
||||
None => "( return )".to_owned(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AstDumper for ExprNode {
|
||||
fn dump(&self) -> String {
|
||||
match self {
|
||||
Self::Assignment { name, value, id } => {
|
||||
format!("{{#{}}}( = {} {} )", id, name.lexeme, value.dump())
|
||||
}
|
||||
Self::Logical {
|
||||
left,
|
||||
operator,
|
||||
right,
|
||||
} => format!("( {} {} {} )", operator.lexeme, left.dump(), right.dump()),
|
||||
Self::Binary {
|
||||
left,
|
||||
operator,
|
||||
right,
|
||||
} => format!("( {} {} {} )", operator.lexeme, left.dump(), right.dump()),
|
||||
Self::Unary { operator, right } => format!("( {} {} )", operator.lexeme, right.dump()),
|
||||
Self::Grouping { expression } => format!("( {} )", expression.dump()),
|
||||
Self::Variable { name, id } => format!("{{#{}}}{}", id, name.lexeme),
|
||||
Self::Litteral { value } => {
|
||||
if value.is_litteral() {
|
||||
value.lexeme.clone()
|
||||
} else {
|
||||
panic!("Unexpected token type for token {:#?}", value)
|
||||
}
|
||||
}
|
||||
|
||||
ExprNode::Lambda { params, body } => {
|
||||
format!(
|
||||
"( fun ({}) {} )",
|
||||
params
|
||||
.iter()
|
||||
.map(|token| &token.lexeme as &str)
|
||||
.collect::<Vec<&str>>()
|
||||
.join(" "),
|
||||
body.iter()
|
||||
.map(|stmt| stmt.dump())
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ")
|
||||
)
|
||||
}
|
||||
|
||||
ExprNode::Call {
|
||||
callee,
|
||||
right_paren: _,
|
||||
arguments,
|
||||
} => {
|
||||
let callee = callee.dump();
|
||||
if arguments.is_empty() {
|
||||
format!("( call {} )", callee)
|
||||
} else {
|
||||
format!(
|
||||
"( call {} {} )",
|
||||
callee,
|
||||
arguments
|
||||
.iter()
|
||||
.map(|arg| arg.dump())
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ExprNode::Get(get_expr) => {
|
||||
format!(
|
||||
"( get {} {} )",
|
||||
get_expr.instance.dump(),
|
||||
get_expr.name.lexeme
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
329
src/dumper.rs
Normal file
329
src/dumper.rs
Normal file
|
@ -0,0 +1,329 @@
|
|||
use std::fmt::Write;
|
||||
|
||||
use crate::{
|
||||
ast::{ExprNode, ProgramNode, StmtNode},
|
||||
tokens::Token,
|
||||
};
|
||||
|
||||
/* -------------------------------- *
|
||||
* Dumper trait and implementations *
|
||||
* -------------------------------- */
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn dump_program(ast: &ProgramNode) -> String {
|
||||
let mut dumper = Dumper::default();
|
||||
dump_statement_list(&mut dumper, &ast.0);
|
||||
dumper
|
||||
.lines
|
||||
.iter()
|
||||
.map(DumperLine::to_string)
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
fn fun_decl_params(params: &[Token]) -> String {
|
||||
params
|
||||
.iter()
|
||||
.map(|token| &token.lexeme as &str)
|
||||
.collect::<Vec<&str>>()
|
||||
.join(" ")
|
||||
}
|
||||
|
||||
fn dump_statement_list(dumper: &mut Dumper, statements: &[StmtNode]) {
|
||||
for stmt in statements {
|
||||
dump_statement(dumper, stmt);
|
||||
}
|
||||
}
|
||||
|
||||
fn dump_substatement(dumper: &mut Dumper, statement: &StmtNode) {
|
||||
let depth_change = match statement {
|
||||
StmtNode::Block(_) => 0,
|
||||
_ => 1,
|
||||
};
|
||||
dumper.depth += depth_change;
|
||||
dump_statement(dumper, statement);
|
||||
dumper.depth -= depth_change;
|
||||
}
|
||||
|
||||
fn dump_statement(dumper: &mut Dumper, stmt: &StmtNode) {
|
||||
match stmt {
|
||||
StmtNode::VarDecl(name, Some(expr)) => {
|
||||
dumper.integrate(
|
||||
&format!("var {} = ", name.lexeme),
|
||||
dump_expression(expr),
|
||||
";",
|
||||
);
|
||||
}
|
||||
StmtNode::VarDecl(name, None) => {
|
||||
dumper.add_line(format!("var {};", name.lexeme));
|
||||
}
|
||||
|
||||
StmtNode::FunDecl(fun_decl) => {
|
||||
dumper.add_line(format!(
|
||||
"fun {} ({}) {{",
|
||||
fun_decl.name.lexeme,
|
||||
fun_decl_params(&fun_decl.params),
|
||||
));
|
||||
if !fun_decl.body.is_empty() {
|
||||
dumper.depth += 1;
|
||||
dump_statement_list(dumper, &fun_decl.body);
|
||||
dumper.depth -= 1;
|
||||
dumper.add_line(String::default());
|
||||
}
|
||||
dumper.current_line().push_str("}");
|
||||
}
|
||||
|
||||
StmtNode::ClassDecl(decl) => {
|
||||
dumper.add_line(format!("class {} {{", decl.name.lexeme));
|
||||
if !decl.methods.is_empty() {
|
||||
dumper.depth += 1;
|
||||
for method in decl.methods.iter() {
|
||||
dumper.add_line(format!(
|
||||
"{} ({}) {{",
|
||||
method.name.lexeme,
|
||||
fun_decl_params(&method.params)
|
||||
));
|
||||
dumper.depth += 1;
|
||||
dump_statement_list(dumper, &method.body);
|
||||
dumper.depth -= 1;
|
||||
dumper.add_line("}".to_owned());
|
||||
}
|
||||
dumper.depth -= 1;
|
||||
dumper.add_line(String::default());
|
||||
}
|
||||
dumper.current_line().push_str("}");
|
||||
}
|
||||
|
||||
StmtNode::Expression(expr) => {
|
||||
dumper.integrate("", dump_expression(expr), ";");
|
||||
}
|
||||
|
||||
StmtNode::Print(expr) => {
|
||||
dumper.integrate("print ", dump_expression(expr), ";");
|
||||
}
|
||||
|
||||
StmtNode::Block(stmts) => {
|
||||
dumper.add_line("{".to_owned());
|
||||
dumper.depth += 1;
|
||||
dump_statement_list(dumper, stmts);
|
||||
dumper.depth -= 1;
|
||||
dumper.add_line("}".to_owned());
|
||||
}
|
||||
|
||||
StmtNode::If {
|
||||
condition,
|
||||
then_branch,
|
||||
else_branch,
|
||||
} => {
|
||||
dumper.integrate("if (", dump_expression(condition), ")");
|
||||
dump_substatement(dumper, then_branch);
|
||||
if let Some(else_branch) = else_branch {
|
||||
dumper.add_line("else".to_owned());
|
||||
dump_substatement(dumper, else_branch);
|
||||
}
|
||||
}
|
||||
|
||||
StmtNode::Loop {
|
||||
label,
|
||||
condition,
|
||||
body,
|
||||
after_body,
|
||||
} => {
|
||||
if let Some(label) = label {
|
||||
dumper.add_line(format!("@{} ", label.lexeme));
|
||||
};
|
||||
dumper.integrate("while (", dump_expression(condition), ")");
|
||||
if let Some(after_body) = after_body {
|
||||
let mut statements = match body.as_ref() {
|
||||
StmtNode::Block(block) => block.clone(),
|
||||
other => vec![other.clone()],
|
||||
};
|
||||
match after_body.as_ref() {
|
||||
StmtNode::Block(block) => statements.extend(block.clone()),
|
||||
other => statements.push(other.clone()),
|
||||
};
|
||||
if statements.len() == 1 {
|
||||
dump_substatement(dumper, &statements[0]);
|
||||
} else {
|
||||
dump_substatement(dumper, &StmtNode::Block(statements));
|
||||
}
|
||||
} else {
|
||||
dump_substatement(dumper, body);
|
||||
}
|
||||
}
|
||||
|
||||
StmtNode::LoopControl {
|
||||
is_break,
|
||||
loop_name,
|
||||
} => {
|
||||
let stmt = if *is_break { "break" } else { "continue" };
|
||||
dumper.add_line(match loop_name {
|
||||
Some(name) => format!("{} {};", stmt, name.lexeme),
|
||||
None => format!("{};", stmt),
|
||||
});
|
||||
}
|
||||
|
||||
StmtNode::Return { token: _, value } => match value {
|
||||
Some(expr) => dumper.integrate("return ", dump_expression(expr), ";"),
|
||||
None => dumper.add_line("return;".to_owned()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------- *
|
||||
* Expressions *
|
||||
* ----------- */
|
||||
|
||||
fn dump_expression(expr: &ExprNode) -> Dumper {
|
||||
let mut dumper = Dumper::default();
|
||||
dumper.add_line(String::default());
|
||||
dump_expr_node(&mut dumper, expr);
|
||||
dumper
|
||||
}
|
||||
|
||||
fn dump_expr_node(dumper: &mut Dumper, expr: &ExprNode) {
|
||||
match expr {
|
||||
ExprNode::Assignment { name, value, id: _ } => {
|
||||
dumper
|
||||
.current_line()
|
||||
.write_fmt(format_args!("{} = ", name.lexeme))
|
||||
.unwrap();
|
||||
dump_expr_node(dumper, value);
|
||||
}
|
||||
|
||||
ExprNode::Logical {
|
||||
left,
|
||||
operator,
|
||||
right,
|
||||
} => {
|
||||
dump_expr_node(dumper, left);
|
||||
dumper.current_line().push_str(" ");
|
||||
dumper.current_line().push_str(&operator.lexeme);
|
||||
dumper.current_line().push_str(" ");
|
||||
dump_expr_node(dumper, right);
|
||||
}
|
||||
|
||||
ExprNode::Binary {
|
||||
left,
|
||||
operator,
|
||||
right,
|
||||
} => {
|
||||
dump_expr_node(dumper, left);
|
||||
dumper.current_line().push_str(" ");
|
||||
dumper.current_line().push_str(&operator.lexeme);
|
||||
dumper.current_line().push_str(" ");
|
||||
dump_expr_node(dumper, right);
|
||||
}
|
||||
|
||||
ExprNode::Unary { operator, right } => {
|
||||
dumper.current_line().push_str(&operator.lexeme);
|
||||
dump_expr_node(dumper, right);
|
||||
}
|
||||
|
||||
ExprNode::Grouping { expression } => {
|
||||
dumper.current_line().push_str("(");
|
||||
dump_expr_node(dumper, expression);
|
||||
dumper.current_line().push_str(")");
|
||||
}
|
||||
|
||||
ExprNode::Litteral { value } => {
|
||||
dumper.current_line().push_str(&value.lexeme);
|
||||
}
|
||||
|
||||
ExprNode::Variable { name, id: _ } => {
|
||||
dumper.current_line().push_str(&name.lexeme);
|
||||
}
|
||||
|
||||
ExprNode::Lambda { params, body } => {
|
||||
dumper
|
||||
.current_line()
|
||||
.write_fmt(format_args!("fun ({}) {{", fun_decl_params(params)))
|
||||
.unwrap();
|
||||
if !body.is_empty() {
|
||||
dumper.depth += 1;
|
||||
dump_statement_list(dumper, body);
|
||||
dumper.add_line(String::default());
|
||||
dumper.depth -= 1;
|
||||
}
|
||||
dumper.current_line().push_str("}");
|
||||
}
|
||||
|
||||
ExprNode::Call {
|
||||
callee,
|
||||
right_paren: _,
|
||||
arguments,
|
||||
} => {
|
||||
dump_expr_node(dumper, callee);
|
||||
dumper.current_line().push_str("(");
|
||||
for (i, argument) in arguments.iter().enumerate() {
|
||||
dump_expr_node(dumper, argument);
|
||||
if arguments.len() - 1 > i {
|
||||
dumper.current_line().push_str(", ");
|
||||
}
|
||||
}
|
||||
dumper.current_line().push_str(")");
|
||||
}
|
||||
|
||||
ExprNode::Get(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------ *
|
||||
* Dumper state *
|
||||
* ------------ */
|
||||
|
||||
/// The state of the program dumper.
|
||||
#[derive(Debug, Default)]
|
||||
struct Dumper {
|
||||
depth: usize,
|
||||
lines: Vec<DumperLine>,
|
||||
}
|
||||
|
||||
/// A line generated while dumping the program.
|
||||
#[derive(Debug)]
|
||||
struct DumperLine {
|
||||
text: String,
|
||||
depth: usize,
|
||||
}
|
||||
|
||||
// Convert dumped lines to strings.
|
||||
impl ToString for DumperLine {
|
||||
fn to_string(&self) -> String {
|
||||
"\t".repeat(self.depth) + &self.text
|
||||
}
|
||||
}
|
||||
|
||||
impl Dumper {
|
||||
fn integrate(&mut self, prefix: &str, other: Dumper, suffix: &str) {
|
||||
match other.lines.len() {
|
||||
0 => self.add_line(format!("{}{}", prefix, suffix)),
|
||||
1 => self.add_line(format!("{}{}{}", prefix, other.lines[0].text, suffix)),
|
||||
len => {
|
||||
other
|
||||
.lines
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.for_each(|(lnb, mut line)| match lnb {
|
||||
0 => self.add_line(format!("{}{}", prefix, line.text)),
|
||||
l if l == len - 1 => self.add_line(format!("{}{}", line.text, suffix)),
|
||||
_ => {
|
||||
line.depth += self.depth;
|
||||
self.lines.push(line);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_line(&mut self, line: String) {
|
||||
self.lines.push(DumperLine {
|
||||
text: line,
|
||||
depth: self.depth,
|
||||
})
|
||||
}
|
||||
|
||||
fn current_line(&mut self) -> &mut String {
|
||||
let line = self.lines.len() - 1;
|
||||
&mut self.lines[line].text
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
mod ast;
|
||||
mod dumper;
|
||||
mod errors;
|
||||
mod interpreter;
|
||||
mod parser;
|
||||
|
@ -13,7 +14,7 @@ use std::{
|
|||
};
|
||||
|
||||
#[cfg(feature = "dump_ast")]
|
||||
use ast::AstDumper;
|
||||
use dumper::dump_program;
|
||||
use errors::{ErrorHandler, SloxResult};
|
||||
use interpreter::{evaluate, Value};
|
||||
use parser::Parser;
|
||||
|
@ -34,7 +35,7 @@ fn run(source: String) -> SloxResult<()> {
|
|||
let parser = Parser::new(tokens);
|
||||
let ast = parser.parse(&mut error_handler)?;
|
||||
#[cfg(feature = "dump_ast")]
|
||||
println!("AST generated ! {}", ast.dump());
|
||||
println!("AST generated ! {}", dump_program(&ast));
|
||||
|
||||
let resolved_vars = error_handler.report_or_continue(resolve_variables(&ast))?;
|
||||
let value = error_handler.report_or_continue(evaluate(&ast, resolved_vars))?;
|
||||
|
|
Loading…
Reference in a new issue