DEV: Some walker trait refactoring

Enables chained visitors using the ChainedWalk implementation.
This commit is contained in:
Jeremy Wall 2020-06-04 14:03:27 -05:00 committed by Jeremy Wall
parent a3a7ce57ee
commit 6ddc830210
5 changed files with 349 additions and 88 deletions

View File

@ -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<char>),
@ -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();
}
}
}

87
src/ast/rewrite.rs Normal file
View File

@ -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<P: Into<PathBuf>>(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 {}

52
src/ast/typecheck.rs Normal file
View File

@ -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<String, Shape>,
}
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
}
}

View File

@ -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<Visitor1, Visitor2> {
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<Visitor1, Visitor2> ChainedWalk<Visitor1, Visitor2>
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<Visitor1, Visitor2> Visitor for ChainedWalk<Visitor1, Visitor2>
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<Visitor1, Visitor2> Walker for ChainedWalk<Visitor1, Visitor2>
where
Visitor1: Visitor,
Visitor2: Visitor,
{
}

View File

@ -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;