New Rust user here. As an exercise when I learn a new language, I tend to implement the front end of the dragon book (you can find the code here). So far everything has work well until I found an impase with implementing the break
statement.
The break
statement holds a reference to its enclosing while
or do-while
bloc. This reference is used in the code generation phase, so after building the AST. The code looks as follows:
// statement.rs
pub trait Statement {
fn generate(&mut self, b: &mut String, begin: i64, after: i64) -> Result<()>;
fn label_after(&self) -> i64;
}
struct WhileStmt {
cond: Box<dyn Expression>,
body: Box<dyn Statement>,
after: i64,
}
impl Statement for WhileStmt {
fn generate(&mut self, b: &mut String, begin: i64, after: i64) -> Result<()> {
self.after = after;
cond.generate(b, 0, after);
let lbl = new_label();
body.generate(b, lbl, after);
// ...
Ok(())
}
fn label_after(&self) -> i64 { self.label }
}
pub struct BreakStmt<'a> {
enclosing: &'a Box<dyn Statement>
}
impl Statement for BreakStmt<'_> {
fn generate(&mut self, b: &mut String, begin: i64, after: i64) -> Result<(), String> {
emit(b, format!("goto L{}", self.enclosing.after()));
Ok(())
}
}
Now my parser should do something like this
// parser.rs
pub struct Parser {}
impl Parser {
// ...
fn stmt(&mut self, enclosing: &Box<dyn Statement>) -> Result<Box<dyn Statement>> {
match self.lookahead.tag() {
WHILE => {
let wh = Box::new(WhileStmt::emtpy());
// ...
let body = self.stmt(wh)?; // THIS NEVER WORKS
// ...
Ok(wh)
},
BREAK => {
let brk = BreakStmt::new(encstmt)?;
Ok(Box::new(brk))
}
// ...
}
fn program() -> Result<()> {
let nullstmt: Box<dyn Statement> = Box::new(NullStmt::new());
self.stmt(&nullstmt)?
Ok(())
}
}
The code above never works for different reasons, depending on the changes I made either because wh
is not areference to a Box<dyn Statement>
but a Box<WhileStmt>
.
If I modify the code so no reference to a Box
is used, but &dyn Statement
then I cannot get the lifetimes parameters right no matter what I do.
I know I can modify the code, so that the after
label gets trickled down during code generation, but If I want to be trustful to the original implementation, what am I missing?