FEATURE: First make our AST Walker a little more ergonomic.

Use a trait instead of callbacks to make mutable it possible to support
Walkers with mutable internal state when necessary.
This commit is contained in:
Jeremy Wall 2019-05-14 20:18:58 -05:00
parent c9b9d1b7ea
commit 2821d0953b
3 changed files with 170 additions and 34 deletions

View File

@ -31,8 +31,11 @@ use abortable_parser;
use crate::build::scope::Scope; use crate::build::scope::Scope;
use crate::build::Val; use crate::build::Val;
pub mod printer;
pub mod walk; pub mod walk;
pub use walk::Walker;
macro_rules! enum_type_equality { macro_rules! enum_type_equality {
( $slf:ident, $r:expr, $( $l:pat ),* ) => { ( $slf:ident, $r:expr, $( $l:pat ),* ) => {
match $slf { match $slf {
@ -593,7 +596,7 @@ impl ModuleDef {
} }
} }
}; };
let walker = walk::AstWalker::new().with_expr_handler(&rewrite_import); let mut walker = walk::AstWalker::new().with_expr_handler(&rewrite_import);
for stmt in self.statements.iter_mut() { for stmt in self.statements.iter_mut() {
walker.walk_statement(stmt); walker.walk_statement(stmt);
} }

116
src/ast/printer.rs Normal file
View File

@ -0,0 +1,116 @@
// Copyright 2019 Jeremy Wall
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::borrow::BorrowMut;
use std::error::Error;
use std::io::Write;
use crate::ast::walk::Walker;
use crate::ast::*;
// TODO(jwall): We really need a way to preserve comments for these.
// Perhaps for code formatting we actually want to work on the token stream instead?
pub struct Printer {
indent: u8,
curr_indent: u8,
w: Box<dyn Write>,
pub errs: Vec<Box<dyn Error>>,
}
impl Printer {
pub fn new(indent: u8, w: Box<dyn Write>) -> Self {
Printer {
indent: indent,
curr_indent: 0,
w: w,
errs: Vec::new(),
}
}
pub fn render_list_def(&mut self, def: &ListDef) -> std::io::Result<()> {
panic!("Unimplemented");
Ok(())
}
pub fn render_tuple_def(&mut self, def: &Vec<(Token, Expression)>) -> std::io::Result<()> {
panic!("Unimplemented");
Ok(())
}
pub fn render_value(&mut self, v: &Value) {
// TODO
let w: &mut Write = self.w.borrow_mut();
let result = match v {
Value::Boolean(b) => write!(w, "{}", b),
Value::Empty(_) => write!(w, "NULL"),
// TODO(jwall): Should we maintain precision for floats?
Value::Float(f) => write!(w, "{}", f),
Value::Int(i) => write!(w, "{}", i),
// TODO(jwall): Make sure that we properly escape quotes here when rendering this?
Value::Str(s) => write!(w, "\"{}\"", s),
Value::Symbol(s) => write!(w, "{}", s),
Value::List(l) => self.render_list_def(l),
Value::Tuple(tpl) => self.render_tuple_def(&tpl.val),
};
if let Err(e) = result {
self.errs.push(Box::new(e));
}
}
fn render_expr(&mut self, expr: &Expression) {
match expr {
Expression::Binary(_def) => {}
Expression::Call(_def) => {}
Expression::Copy(_def) => {}
Expression::Debug(_def) => {}
Expression::Fail(_def) => {}
Expression::Format(_def) => {}
Expression::Func(_def) => {}
Expression::FuncOp(_def) => {}
Expression::Grouped(_expr, _) => {}
Expression::Import(_def) => {}
Expression::Include(_def) => {}
Expression::Module(_def) => {}
Expression::Not(_def) => {}
Expression::Range(_def) => {}
Expression::Select(_def) => {}
Expression::Simple(_def) => {}
}
}
fn render_stmt(&mut self, stmt: &Statement) {
match stmt {
Statement::Let(_def) => {}
Statement::Expression(_expr) => {}
Statement::Assert(_def) => {}
Statement::Output(_, _tok, _expr) => {}
}
}
pub fn render(&mut self, stmts: Vec<&mut Statement>) {
self.walk_statement_list(stmts);
}
}
impl Walker for Printer {
fn visit_value(&mut self, val: &mut Value) {
self.render_value(val);
}
fn visit_expression(&mut self, expr: &mut Expression) {
self.render_expr(expr);
}
fn visit_statement(&mut self, stmt: &mut Statement) {
self.render_stmt(stmt);
}
}

View File

@ -1,36 +1,13 @@
use crate::ast::*; use crate::ast::*;
pub struct AstWalker<'a> { pub trait Walker {
handle_value: Option<&'a Fn(&mut Value)>, fn walk_statement_list(&mut self, stmts: Vec<&mut Statement>) {
handle_expression: Option<&'a Fn(&mut Expression)>, for v in stmts {
handle_statment: Option<&'a Fn(&mut Statement)>, self.walk_statement(v);
}
impl<'a> AstWalker<'a> {
pub fn new() -> Self {
AstWalker {
handle_value: None,
handle_expression: None,
handle_statment: None,
} }
} }
pub fn with_value_handler(mut self, h: &'a Fn(&mut Value)) -> Self { fn walk_statement(&mut self, stmt: &mut Statement) {
self.handle_value = Some(h);
self
}
pub fn with_expr_handler(mut self, h: &'a Fn(&mut Expression)) -> Self {
self.handle_expression = Some(h);
self
}
pub fn with_stmt_handler(mut self, h: &'a Fn(&mut Statement)) -> Self {
self.handle_statment = Some(h);
self
}
pub fn walk_statement(&self, stmt: &mut Statement) {
self.visit_statement(stmt); self.visit_statement(stmt);
match stmt { match stmt {
Statement::Let(ref mut def) => { Statement::Let(ref mut def) => {
@ -48,13 +25,13 @@ impl<'a> AstWalker<'a> {
} }
} }
fn walk_fieldset(&self, fs: &mut FieldList) { fn walk_fieldset(&mut self, fs: &mut FieldList) {
for &mut (_, ref mut expr) in fs.iter_mut() { for &mut (_, ref mut expr) in fs.iter_mut() {
self.walk_expression(expr); self.walk_expression(expr);
} }
} }
pub fn walk_expression(&self, expr: &mut Expression) { fn walk_expression(&mut self, expr: &mut Expression) {
self.visit_expression(expr); self.visit_expression(expr);
match expr { match expr {
Expression::Call(ref mut def) => { Expression::Call(ref mut def) => {
@ -135,19 +112,59 @@ impl<'a> AstWalker<'a> {
} }
} }
fn visit_value(&self, val: &mut Value) { fn visit_value(&mut self, val: &mut Value);
fn visit_expression(&mut self, expr: &mut Expression);
fn visit_statement(&mut self, stmt: &mut Statement);
}
// TODO this would be better implemented as a Trait I think.
pub struct AstWalker<'a> {
handle_value: Option<&'a Fn(&mut Value)>,
handle_expression: Option<&'a Fn(&mut Expression)>,
handle_statment: Option<&'a Fn(&mut Statement)>,
}
impl<'a> AstWalker<'a> {
pub fn new() -> Self {
AstWalker {
handle_value: None,
handle_expression: None,
handle_statment: None,
}
}
pub fn with_value_handler(mut self, h: &'a Fn(&mut Value)) -> Self {
self.handle_value = Some(h);
self
}
pub fn with_expr_handler(mut self, h: &'a Fn(&mut Expression)) -> Self {
self.handle_expression = Some(h);
self
}
pub fn with_stmt_handler(mut self, h: &'a Fn(&mut Statement)) -> Self {
self.handle_statment = Some(h);
self
}
}
impl<'a> Walker for AstWalker<'a> {
fn visit_value(&mut self, val: &mut Value) {
if let Some(h) = self.handle_value { if let Some(h) = self.handle_value {
h(val); h(val);
} }
} }
fn visit_expression(&self, expr: &mut Expression) { fn visit_expression(&mut self, expr: &mut Expression) {
if let Some(h) = self.handle_expression { if let Some(h) = self.handle_expression {
h(expr); h(expr);
} }
} }
fn visit_statement(&self, stmt: &mut Statement) { fn visit_statement(&mut self, stmt: &mut Statement) {
if let Some(h) = self.handle_statment { if let Some(h) = self.handle_statment {
h(stmt); h(stmt);
} }