From 6ddc8302105ae25184e21343d86c1b1545f90f95 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Thu, 4 Jun 2020 14:03:27 -0500 Subject: [PATCH] DEV: Some walker trait refactoring Enables chained visitors using the ChainedWalk implementation. --- src/ast/mod.rs | 161 +++++++++++++++++++++++++++++----- src/ast/rewrite.rs | 87 ++++++++++++++++++ src/ast/typecheck.rs | 52 +++++++++++ src/ast/walk.rs | 134 ++++++++++++++-------------- src/build/opcode/translate.rs | 3 +- 5 files changed, 349 insertions(+), 88 deletions(-) create mode 100644 src/ast/rewrite.rs create mode 100644 src/ast/typecheck.rs diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 807091c..6aa56b3 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -33,10 +33,10 @@ use crate::build::Val; use crate::error::{BuildError, ErrorType::TypeFail}; pub mod printer; +pub mod rewrite; +pub mod typecheck; pub mod walk; -pub use walk::Walker; - #[derive(Debug, PartialEq, Clone)] pub enum TemplatePart { Str(Vec), @@ -701,29 +701,144 @@ fn normalize_path(p: PathBuf) -> PathBuf { } impl walk::Walker for Rewriter { - fn visit_expression(&mut self, expr: &mut Expression) { - // Rewrite all paths except for stdlib paths to absolute. - let main_separator = format!("{}", std::path::MAIN_SEPARATOR); - if let Expression::Include(ref mut def) = expr { - let path = PathBuf::from(&def.path.fragment); - def.path.fragment = normalize_path(self.base.join(path)) - .to_string_lossy() - .to_string(); + fn walk_statement_list(&mut self, stmts: Vec<&mut Statement>) { + for v in stmts { + self.walk_statement(v); } - if let Expression::Import(ref mut def) = expr { - let path = PathBuf::from( - &def.path - .fragment - .replace("/", &main_separator) - .replace("\\", &main_separator), - ); - // std/ paths are special and do not get made into absolute paths. - if path.starts_with(format!("std{}", main_separator)) { - return; + } + + 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); + } + Statement::Print(_, _, 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::Cast(ref mut def) => { + self.walk_expression(&mut def.target); + } + 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.walk_value(val); + } + + Expression::Import(i) => { + self.visit_import(i); + } + Expression::Include(i) => { + self.visit_include(i); + } + Expression::Fail(f) => { + self.visit_fail(f); + } + Expression::Not(ref mut def) => { + self.walk_expression(def.expr.as_mut()); + } + Expression::Debug(ref mut def) => { + self.walk_expression(&mut def.expr); + } + } + } + + fn walk_value(&mut self, val: &mut Value) { + match val { + Value::Empty(_) + | Value::Symbol(_) + | Value::Boolean(_) + | Value::Int(_) + | Value::Float(_) + | Value::Str(_) => self.visit_value(val), + Value::Tuple(fs) => self.walk_fieldset(&mut fs.val), + Value::List(vs) => { + for e in &mut vs.elems { + self.walk_expression(e); + } } - def.path.fragment = normalize_path(self.base.join(path)) - .to_string_lossy() - .to_string(); } } } diff --git a/src/ast/rewrite.rs b/src/ast/rewrite.rs new file mode 100644 index 0000000..d0d940a --- /dev/null +++ b/src/ast/rewrite.rs @@ -0,0 +1,87 @@ +// Copyright 2020 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::path::PathBuf; + +use crate::ast::walk::{Visitor, Walker}; +use crate::ast::Expression; + +pub struct Rewriter { + base: PathBuf, +} + +impl Rewriter { + pub fn new>(base: P) -> Self { + Self { base: base.into() } + } +} + +impl Visitor for Rewriter { + fn visit_expression(&mut self, expr: &mut Expression) { + // Rewrite all paths except for stdlib paths to absolute. + let main_separator = format!("{}", std::path::MAIN_SEPARATOR); + if let Expression::Include(ref mut def) = expr { + let path = PathBuf::from(&def.path.fragment); + #[cfg(not(windows))] + { + if path.is_relative() { + def.path.fragment = self + .base + .join(path) + .canonicalize() + .unwrap() + .to_string_lossy() + .to_string(); + } + } + #[cfg(windows)] + { + if path.is_relative() { + def.path.fragment = self.base.join(path).to_string_lossy().to_string(); + } + } + } + if let Expression::Import(ref mut def) = expr { + let path = PathBuf::from( + &def.path + .fragment + .replace("/", &main_separator) + .replace("\\", &main_separator), + ); + // std/ paths are special and do not get made into absolute paths. + if path.starts_with(format!("std{}", main_separator)) { + return; + } + #[cfg(not(windows))] + { + if path.is_relative() { + def.path.fragment = self + .base + .join(path) + .canonicalize() + .unwrap() + .to_string_lossy() + .to_string(); + } + } + #[cfg(windows)] + { + if path.is_relative() { + def.path.fragment = self.base.join(path).to_string_lossy().to_string(); + } + } + } + } +} + +impl Walker for Rewriter {} diff --git a/src/ast/typecheck.rs b/src/ast/typecheck.rs new file mode 100644 index 0000000..8fc5c39 --- /dev/null +++ b/src/ast/typecheck.rs @@ -0,0 +1,52 @@ +// Copyright 2020 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. + +//! Implements typechecking for the parsed ucg AST. + +use std::collections::BTreeMap; + +use crate::ast::walk::Visitor; +use crate::ast::{Expression, Shape, Statement, Value}; + +use Expression::{ + Binary, Call, Cast, Copy, Debug, Fail, Format, Func, FuncOp, Grouped, Import, Include, Module, + Not, Range, Select, Simple, +}; +use Statement::Let; +use Value::{Boolean, Empty, Float, Int, List, Str, Symbol, Tuple}; + +pub struct Checker { + symbol_table: BTreeMap, +} + +impl Visitor for Checker { + fn visit_import(&mut self, _i: &mut super::ImportDef) { + // noop by default; + } + fn visit_include(&mut self, _i: &mut super::IncludeDef) { + // noop by default; + } + fn visit_fail(&mut self, _f: &mut super::FailDef) { + // noop by default; + } + fn visit_value(&mut self, _val: &mut Value) { + // noop by default + } + fn visit_expression(&mut self, _expr: &mut Expression) { + // noop by default + } + fn visit_statement(&mut self, _stmt: &mut Statement) { + // noop by default + } +} diff --git a/src/ast/walk.rs b/src/ast/walk.rs index 0a10b11..f76c0fb 100644 --- a/src/ast/walk.rs +++ b/src/ast/walk.rs @@ -1,6 +1,33 @@ use crate::ast::*; -pub trait Walker { +pub trait Visitor { + // TODO(jwall): Should this have exit versions as well? + fn visit_import(&mut self, _i: &mut ImportDef) { + // noop by default; + } + + fn visit_include(&mut self, _i: &mut IncludeDef) { + // noop by default; + } + + fn visit_fail(&mut self, _f: &mut FailDef) { + // noop by default; + } + + fn visit_value(&mut self, _val: &mut Value) { + // noop by default + } + + fn visit_expression(&mut self, _expr: &mut Expression) { + // noop by default + } + + fn visit_statement(&mut self, _stmt: &mut Statement) { + // noop by default + } +} + +pub trait Walker: Visitor { fn walk_statement_list(&mut self, stmts: Vec<&mut Statement>) { for v in stmts { self.walk_statement(v); @@ -141,81 +168,60 @@ pub trait Walker { } } } - - // TODO(jwall): Should this have exit versions as well? - fn visit_import(&mut self, _i: &mut ImportDef) { - // noop by default; - } - - fn visit_include(&mut self, _i: &mut IncludeDef) { - // noop by default; - } - - fn visit_fail(&mut self, _f: &mut FailDef) { - // noop by default; - } - - fn visit_value(&mut self, _val: &mut Value) { - // noop by default - } - - fn visit_expression(&mut self, _expr: &mut Expression) { - // noop by default - } - - fn visit_statement(&mut self, _stmt: &mut Statement) { - // noop by default - } } -// TODO this would be better implemented as a Trait I think. -pub struct AstWalker<'a> { - handle_value: Option<&'a dyn Fn(&mut Value)>, - handle_expression: Option<&'a dyn Fn(&mut Expression)>, - handle_statment: Option<&'a dyn Fn(&mut Statement)>, +pub struct ChainedWalk { + pub visitor_1: Visitor1, + pub visitor_2: Visitor2, } -impl<'a> AstWalker<'a> { - pub fn new() -> Self { - AstWalker { - handle_value: None, - handle_expression: None, - handle_statment: None, +impl ChainedWalk +where + Visitor1: Visitor, + Visitor2: Visitor, +{ + pub fn new(visitor_1: Visitor1, visitor_2: Visitor2) -> Self { + Self { + visitor_1, + visitor_2, } } - - pub fn with_value_handler(mut self, h: &'a dyn Fn(&mut Value)) -> Self { - self.handle_value = Some(h); - self - } - - pub fn with_expr_handler(mut self, h: &'a dyn Fn(&mut Expression)) -> Self { - self.handle_expression = Some(h); - self - } - - pub fn with_stmt_handler(mut self, h: &'a dyn Fn(&mut Statement)) -> Self { - self.handle_statment = Some(h); - self - } } -impl<'a> Walker for AstWalker<'a> { +impl Visitor for ChainedWalk +where + Visitor1: Visitor, + Visitor2: Visitor, +{ + fn visit_import(&mut self, i: &mut ImportDef) { + self.visitor_1.visit_import(i); + self.visitor_2.visit_import(i); + } + fn visit_include(&mut self, i: &mut IncludeDef) { + self.visitor_1.visit_include(i); + self.visitor_2.visit_include(i); + } + fn visit_fail(&mut self, f: &mut FailDef) { + self.visitor_1.visit_fail(f); + self.visitor_2.visit_fail(f); + } fn visit_value(&mut self, val: &mut Value) { - if let Some(h) = self.handle_value { - h(val); - } + self.visitor_1.visit_value(val); + self.visitor_2.visit_value(val); } - fn visit_expression(&mut self, expr: &mut Expression) { - if let Some(h) = self.handle_expression { - h(expr); - } + self.visitor_1.visit_expression(expr); + self.visitor_2.visit_expression(expr); } - fn visit_statement(&mut self, stmt: &mut Statement) { - if let Some(h) = self.handle_statment { - h(stmt); - } + self.visitor_1.visit_statement(stmt); + self.visitor_2.visit_statement(stmt); } } + +impl Walker for ChainedWalk +where + Visitor1: Visitor, + Visitor2: Visitor, +{ +} diff --git a/src/build/opcode/translate.rs b/src/build/opcode/translate.rs index 5c00c36..aed360c 100644 --- a/src/build/opcode/translate.rs +++ b/src/build/opcode/translate.rs @@ -14,10 +14,11 @@ use std::collections::BTreeMap; use std::path::Path; +use crate::ast::rewrite::Rewriter; use crate::ast::walk::Walker; use crate::ast::{ BinaryExprType, BinaryOpDef, Expression, FormatArgs, FuncOpDef, Position, PositionedItem, - Rewriter, SelectDef, Statement, TemplatePart, Token, TokenType, Value, + SelectDef, Statement, TemplatePart, Token, TokenType, Value, }; use crate::build::format::{ExpressionTemplate, SimpleTemplate, TemplateParser}; use crate::build::opcode::Primitive;