ucg/src/ast/walk.rs
Jeremy Wall 2821d0953b 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.
2019-05-14 20:54:06 -05:00

173 lines
5.4 KiB
Rust

use crate::ast::*;
pub trait Walker {
fn walk_statement_list(&mut self, stmts: Vec<&mut Statement>) {
for v in stmts {
self.walk_statement(v);
}
}
fn walk_statement(&mut self, stmt: &mut Statement) {
self.visit_statement(stmt);
match stmt {
Statement::Let(ref mut def) => {
self.walk_expression(&mut def.value);
}
Statement::Expression(ref mut expr) => {
self.walk_expression(expr);
}
Statement::Assert(ref mut expr) => {
self.walk_expression(expr);
}
Statement::Output(_, _, ref mut expr) => {
self.walk_expression(expr);
}
}
}
fn walk_fieldset(&mut self, fs: &mut FieldList) {
for &mut (_, ref mut expr) in fs.iter_mut() {
self.walk_expression(expr);
}
}
fn walk_expression(&mut self, expr: &mut Expression) {
self.visit_expression(expr);
match expr {
Expression::Call(ref mut def) => {
for expr in def.arglist.iter_mut() {
self.walk_expression(expr);
}
}
Expression::Copy(ref mut def) => {
self.walk_fieldset(&mut def.fields);
}
Expression::Format(ref mut def) => match def.args {
FormatArgs::List(ref mut args) => {
for expr in args.iter_mut() {
self.walk_expression(expr);
}
}
FormatArgs::Single(ref mut expr) => {
self.walk_expression(expr);
}
},
Expression::FuncOp(ref mut def) => match def {
FuncOpDef::Reduce(ref mut def) => {
self.walk_expression(def.target.as_mut());
self.walk_expression(def.acc.as_mut())
}
FuncOpDef::Map(ref mut def) => {
self.walk_expression(def.target.as_mut());
}
FuncOpDef::Filter(ref mut def) => {
self.walk_expression(def.target.as_mut());
}
},
Expression::Binary(ref mut def) => {
self.walk_expression(def.left.as_mut());
self.walk_expression(def.right.as_mut());
}
Expression::Grouped(ref mut expr, _) => {
self.walk_expression(expr);
}
Expression::Func(ref mut def) => self.walk_expression(def.fields.as_mut()),
Expression::Module(ref mut def) => {
self.walk_fieldset(&mut def.arg_set);
for stmt in def.statements.iter_mut() {
self.walk_statement(stmt);
}
}
Expression::Range(ref mut def) => {
self.walk_expression(def.start.as_mut());
self.walk_expression(def.end.as_mut());
if let Some(ref mut expr) = def.step {
self.walk_expression(expr.as_mut());
}
}
Expression::Select(ref mut def) => {
match def.default {
Some(ref mut e) => {
self.walk_expression(e.as_mut());
}
None => {
// noop;
}
};
self.walk_expression(def.val.as_mut());
self.walk_fieldset(&mut def.tuple);
}
Expression::Simple(ref mut val) => {
self.visit_value(val);
}
Expression::Import(_) | Expression::Include(_) | Expression::Fail(_) => {
//noop
}
Expression::Not(ref mut def) => {
self.walk_expression(def.expr.as_mut());
}
Expression::Debug(ref mut def) => {
self.walk_expression(&mut def.expr);
}
}
}
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 {
h(val);
}
}
fn visit_expression(&mut self, expr: &mut Expression) {
if let Some(h) = self.handle_expression {
h(expr);
}
}
fn visit_statement(&mut self, stmt: &mut Statement) {
if let Some(h) = self.handle_statment {
h(stmt);
}
}
}