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.
|
/// A get expression.
|
||||||
Get(GetExpr),
|
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 ast;
|
||||||
|
mod dumper;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod interpreter;
|
mod interpreter;
|
||||||
mod parser;
|
mod parser;
|
||||||
|
@ -13,7 +14,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "dump_ast")]
|
#[cfg(feature = "dump_ast")]
|
||||||
use ast::AstDumper;
|
use dumper::dump_program;
|
||||||
use errors::{ErrorHandler, SloxResult};
|
use errors::{ErrorHandler, SloxResult};
|
||||||
use interpreter::{evaluate, Value};
|
use interpreter::{evaluate, Value};
|
||||||
use parser::Parser;
|
use parser::Parser;
|
||||||
|
@ -34,7 +35,7 @@ fn run(source: String) -> SloxResult<()> {
|
||||||
let parser = Parser::new(tokens);
|
let parser = Parser::new(tokens);
|
||||||
let ast = parser.parse(&mut error_handler)?;
|
let ast = parser.parse(&mut error_handler)?;
|
||||||
#[cfg(feature = "dump_ast")]
|
#[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 resolved_vars = error_handler.report_or_continue(resolve_variables(&ast))?;
|
||||||
let value = error_handler.report_or_continue(evaluate(&ast, resolved_vars))?;
|
let value = error_handler.report_or_continue(evaluate(&ast, resolved_vars))?;
|
||||||
|
|
Loading…
Reference in a new issue