mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
DEV: Some walker trait refactoring
Enables chained visitors using the ChainedWalk implementation.
This commit is contained in:
parent
a3a7ce57ee
commit
6ddc830210
161
src/ast/mod.rs
161
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<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
87
src/ast/rewrite.rs
Normal 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
52
src/ast/typecheck.rs
Normal 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
|
||||
}
|
||||
}
|
134
src/ast/walk.rs
134
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<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,
|
||||
{
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user