From 01ffdb67a6f7508c5c45a0361aea17d73549925f Mon Sep 17 00:00:00 2001 From: John Date: Fri, 19 Apr 2024 03:21:07 -0500 Subject: [PATCH] cl-ast: Add Fold and Visit traits, to more easily map or collect nodes in the AST typeck.rs: Since this is apparently my testbed now, add a new mode. TODO: replace the main `conlang` binary with this, since it's so much better. --- cl-ast/src/ast_visitor.rs | 8 + cl-ast/src/ast_visitor/fold.rs | 493 ++++++++++++++++++++++++++++ cl-ast/src/ast_visitor/visit.rs | 411 +++++++++++++++++++++++ cl-ast/src/desugar.rs | 5 + cl-ast/src/desugar/squash_groups.rs | 14 + cl-ast/src/desugar/while_else.rs | 34 ++ cl-ast/src/lib.rs | 2 + cl-repl/examples/typeck.rs | 24 +- cl-repl/src/lib.rs | 10 +- 9 files changed, 998 insertions(+), 3 deletions(-) create mode 100644 cl-ast/src/ast_visitor.rs create mode 100644 cl-ast/src/ast_visitor/fold.rs create mode 100644 cl-ast/src/ast_visitor/visit.rs create mode 100644 cl-ast/src/desugar.rs create mode 100644 cl-ast/src/desugar/squash_groups.rs create mode 100644 cl-ast/src/desugar/while_else.rs diff --git a/cl-ast/src/ast_visitor.rs b/cl-ast/src/ast_visitor.rs new file mode 100644 index 0000000..a7cabea --- /dev/null +++ b/cl-ast/src/ast_visitor.rs @@ -0,0 +1,8 @@ +//! Contains an [immutable visitor](Visit) and an [owned folder](Fold) trait, +//! with default implementations across the entire AST + +pub mod fold; +pub mod visit; + +pub use fold::Fold; +pub use visit::Visit; diff --git a/cl-ast/src/ast_visitor/fold.rs b/cl-ast/src/ast_visitor/fold.rs new file mode 100644 index 0000000..8ca6bd2 --- /dev/null +++ b/cl-ast/src/ast_visitor/fold.rs @@ -0,0 +1,493 @@ +//! A folder (implementer of the [Fold] trait) maps ASTs to ASTs + +use crate::ast::*; +use cl_structures::span::Span; + +/// Deconstructs the entire AST, and reconstructs it from scratch. +/// +/// Each method acts as a customization point. +/// +/// There are a set of default implementations for enums +/// under the name [`or_fold_`*](or_fold_expr_kind), +/// provided for ease of use. +/// +/// For all other nodes, traversal is *explicit*. +pub trait Fold { + fn fold_span(&mut self, extents: Span) -> Span { + extents + } + fn fold_mutability(&mut self, mutability: Mutability) -> Mutability { + mutability + } + fn fold_visibility(&mut self, visibility: Visibility) -> Visibility { + visibility + } + fn fold_identifier(&mut self, ident: Identifier) -> Identifier { + ident + } + fn fold_literal(&mut self, lit: Literal) -> Literal { + or_fold_literal(self, lit) + } + fn fold_bool(&mut self, b: bool) -> bool { + b + } + fn fold_char(&mut self, c: char) -> char { + c + } + fn fold_int(&mut self, i: u128) -> u128 { + i + } + fn fold_string(&mut self, s: String) -> String { + s + } + fn fold_file(&mut self, f: File) -> File { + let File { items } = f; + File { items: items.into_iter().map(|i| self.fold_item(i)).collect() } + } + fn fold_attrs(&mut self, a: Attrs) -> Attrs { + let Attrs { meta } = a; + Attrs { meta: meta.into_iter().map(|m| self.fold_meta(m)).collect() } + } + fn fold_meta(&mut self, m: Meta) -> Meta { + let Meta { name, kind } = m; + Meta { name: self.fold_identifier(name), kind: self.fold_meta_kind(kind) } + } + fn fold_meta_kind(&mut self, kind: MetaKind) -> MetaKind { + or_fold_meta_kind(self, kind) + } + fn fold_item(&mut self, i: Item) -> Item { + let Item { extents, attrs, vis, kind } = i; + Item { + extents: self.fold_span(extents), + attrs: self.fold_attrs(attrs), + vis: self.fold_visibility(vis), + kind: self.fold_item_kind(kind), + } + } + fn fold_item_kind(&mut self, kind: ItemKind) -> ItemKind { + or_fold_item_kind(self, kind) + } + fn fold_alias(&mut self, a: Alias) -> Alias { + let Alias { to, from } = a; + Alias { to: self.fold_identifier(to), from: from.map(|from| Box::new(self.fold_ty(*from))) } + } + fn fold_const(&mut self, c: Const) -> Const { + let Const { name, ty, init } = c; + Const { + name: self.fold_identifier(name), + ty: Box::new(self.fold_ty(*ty)), + init: Box::new(self.fold_expr(*init)), + } + } + fn fold_static(&mut self, s: Static) -> Static { + let Static { mutable, name, ty, init } = s; + Static { + mutable: self.fold_mutability(mutable), + name: self.fold_identifier(name), + ty: Box::new(self.fold_ty(*ty)), + init: Box::new(self.fold_expr(*init)), + } + } + fn fold_module(&mut self, m: Module) -> Module { + let Module { name, kind } = m; + Module { name: self.fold_identifier(name), kind: self.fold_module_kind(kind) } + } + fn fold_module_kind(&mut self, m: ModuleKind) -> ModuleKind { + match m { + ModuleKind::Inline(f) => ModuleKind::Inline(self.fold_file(f)), + ModuleKind::Outline => ModuleKind::Outline, + } + } + fn fold_function(&mut self, f: Function) -> Function { + let Function { name, sign, bind, body } = f; + Function { + name: self.fold_identifier(name), + sign: self.fold_ty_fn(sign), + bind: bind.into_iter().map(|p| self.fold_param(p)).collect(), + body: body.map(|b| self.fold_block(b)), + } + } + fn fold_param(&mut self, p: Param) -> Param { + let Param { mutability, name } = p; + Param { mutability: self.fold_mutability(mutability), name: self.fold_identifier(name) } + } + fn fold_struct(&mut self, s: Struct) -> Struct { + let Struct { name, kind } = s; + Struct { name: self.fold_identifier(name), kind: self.fold_struct_kind(kind) } + } + fn fold_struct_kind(&mut self, kind: StructKind) -> StructKind { + match kind { + StructKind::Empty => StructKind::Empty, + StructKind::Tuple(tys) => { + StructKind::Tuple(tys.into_iter().map(|t| self.fold_ty(t)).collect()) + } + StructKind::Struct(mem) => StructKind::Struct( + mem.into_iter() + .map(|m| self.fold_struct_member(m)) + .collect(), + ), + } + } + fn fold_struct_member(&mut self, m: StructMember) -> StructMember { + let StructMember { vis, name, ty } = m; + StructMember { + vis: self.fold_visibility(vis), + name: self.fold_identifier(name), + ty: self.fold_ty(ty), + } + } + fn fold_enum(&mut self, e: Enum) -> Enum { + let Enum { name, kind } = e; + Enum { name: self.fold_identifier(name), kind: self.fold_enum_kind(kind) } + } + fn fold_enum_kind(&mut self, kind: EnumKind) -> EnumKind { + or_fold_enum_kind(self, kind) + } + fn fold_variant(&mut self, v: Variant) -> Variant { + let Variant { name, kind } = v; + + Variant { name: self.fold_identifier(name), kind: self.fold_variant_kind(kind) } + } + fn fold_variant_kind(&mut self, kind: VariantKind) -> VariantKind { + or_fold_variant_kind(self, kind) + } + fn fold_impl(&mut self, i: Impl) -> Impl { + let Impl { target, body } = i; + Impl { target: self.fold_impl_kind(target), body: self.fold_file(body) } + } + fn fold_impl_kind(&mut self, kind: ImplKind) -> ImplKind { + or_fold_impl_kind(self, kind) + } + fn fold_ty(&mut self, t: Ty) -> Ty { + let Ty { extents, kind } = t; + Ty { extents: self.fold_span(extents), kind: self.fold_ty_kind(kind) } + } + fn fold_ty_kind(&mut self, kind: TyKind) -> TyKind { + or_fold_ty_kind(self, kind) + } + fn fold_ty_tuple(&mut self, t: TyTuple) -> TyTuple { + let TyTuple { types } = t; + TyTuple { + types: types + .into_iter() + .map(|kind| self.fold_ty_kind(kind)) + .collect(), + } + } + fn fold_ty_ref(&mut self, t: TyRef) -> TyRef { + let TyRef { mutable, count, to } = t; + TyRef { mutable: self.fold_mutability(mutable), count, to: self.fold_path(to) } + } + fn fold_ty_fn(&mut self, t: TyFn) -> TyFn { + let TyFn { args, rety } = t; + TyFn { + args: Box::new(self.fold_ty_kind(*args)), + rety: rety.map(|t| Box::new(self.fold_ty(*t))), + } + } + fn fold_path(&mut self, p: Path) -> Path { + let Path { absolute, parts } = p; + Path { absolute, parts: parts.into_iter().map(|p| self.fold_path_part(p)).collect() } + } + fn fold_path_part(&mut self, p: PathPart) -> PathPart { + match p { + PathPart::SuperKw => PathPart::SuperKw, + PathPart::SelfKw => PathPart::SelfKw, + PathPart::Ident(i) => PathPart::Ident(self.fold_identifier(i)), + } + } + fn fold_stmt(&mut self, s: Stmt) -> Stmt { + let Stmt { extents, kind, semi } = s; + Stmt { + extents: self.fold_span(extents), + kind: self.fold_stmt_kind(kind), + semi: self.fold_semi(semi), + } + } + fn fold_stmt_kind(&mut self, kind: StmtKind) -> StmtKind { + or_fold_stmt_kind(self, kind) + } + fn fold_semi(&mut self, s: Semi) -> Semi { + s + } + fn fold_let(&mut self, l: Let) -> Let { + let Let { mutable, name, ty, init } = l; + Let { + mutable: self.fold_mutability(mutable), + name: self.fold_identifier(name), + ty: ty.map(|t| Box::new(self.fold_ty(*t))), + init: init.map(|e| Box::new(self.fold_expr(*e))), + } + } + fn fold_expr(&mut self, e: Expr) -> Expr { + let Expr { extents, kind } = e; + Expr { extents: self.fold_span(extents), kind: self.fold_expr_kind(kind) } + } + fn fold_expr_kind(&mut self, kind: ExprKind) -> ExprKind { + or_fold_expr_kind(self, kind) + } + fn fold_assign(&mut self, a: Assign) -> Assign { + let Assign { kind, parts } = a; + let (head, tail) = *parts; + Assign { + kind: self.fold_assign_kind(kind), + parts: Box::new((self.fold_expr_kind(head), self.fold_expr_kind(tail))), + } + } + fn fold_assign_kind(&mut self, kind: AssignKind) -> AssignKind { + kind + } + fn fold_binary(&mut self, b: Binary) -> Binary { + let Binary { kind, parts } = b; + let (head, tail) = *parts; + Binary { + kind: self.fold_binary_kind(kind), + parts: Box::new((self.fold_expr_kind(head), self.fold_expr_kind(tail))), + } + } + fn fold_binary_kind(&mut self, kind: BinaryKind) -> BinaryKind { + kind + } + fn fold_unary(&mut self, u: Unary) -> Unary { + let Unary { kind, tail } = u; + Unary { kind: self.fold_unary_kind(kind), tail: Box::new(self.fold_expr_kind(*tail)) } + } + fn fold_unary_kind(&mut self, kind: UnaryKind) -> UnaryKind { + kind + } + fn fold_index(&mut self, i: Index) -> Index { + let Index { head, indices } = i; + Index { + head: Box::new(self.fold_expr_kind(*head)), + indices: indices.into_iter().map(|e| self.fold_expr(e)).collect(), + } + } + fn fold_array(&mut self, a: Array) -> Array { + let Array { values } = a; + Array { values: values.into_iter().map(|e| self.fold_expr(e)).collect() } + } + fn fold_array_rep(&mut self, a: ArrayRep) -> ArrayRep { + let ArrayRep { value, repeat } = a; + ArrayRep { + value: Box::new(self.fold_expr_kind(*value)), + repeat: Box::new(self.fold_expr_kind(*repeat)), + } + } + fn fold_addrof(&mut self, a: AddrOf) -> AddrOf { + let AddrOf { count, mutable, expr } = a; + AddrOf { + count, + mutable: self.fold_mutability(mutable), + expr: Box::new(self.fold_expr_kind(*expr)), + } + } + fn fold_block(&mut self, b: Block) -> Block { + let Block { stmts } = b; + Block { stmts: stmts.into_iter().map(|s| self.fold_stmt(s)).collect() } + } + fn fold_group(&mut self, g: Group) -> Group { + let Group { expr } = g; + Group { expr: Box::new(self.fold_expr_kind(*expr)) } + } + fn fold_tuple(&mut self, t: Tuple) -> Tuple { + let Tuple { exprs } = t; + Tuple { exprs: exprs.into_iter().map(|e| self.fold_expr(e)).collect() } + } + fn fold_loop(&mut self, l: Loop) -> Loop { + let Loop { body } = l; + Loop { body: Box::new(self.fold_expr(*body)) } + } + fn fold_while(&mut self, w: While) -> While { + let While { cond, pass, fail } = w; + While { + cond: Box::new(self.fold_expr(*cond)), + pass: Box::new(self.fold_block(*pass)), + fail: self.fold_else(fail), + } + } + fn fold_if(&mut self, i: If) -> If { + let If { cond, pass, fail } = i; + If { + cond: Box::new(self.fold_expr(*cond)), + pass: Box::new(self.fold_block(*pass)), + fail: self.fold_else(fail), + } + } + fn fold_for(&mut self, f: For) -> For { + let For { bind, cond, pass, fail } = f; + For { + bind: self.fold_identifier(bind), + cond: Box::new(self.fold_expr(*cond)), + pass: Box::new(self.fold_block(*pass)), + fail: self.fold_else(fail), + } + } + fn fold_else(&mut self, e: Else) -> Else { + let Else { body } = e; + Else { body: body.map(|e| Box::new(self.fold_expr(*e))) } + } + fn fold_break(&mut self, b: Break) -> Break { + let Break { body } = b; + Break { body: body.map(|e| Box::new(self.fold_expr(*e))) } + } + fn fold_return(&mut self, r: Return) -> Return { + let Return { body } = r; + Return { body: body.map(|e| Box::new(self.fold_expr(*e))) } + } + fn fold_continue(&mut self, c: Continue) -> Continue { + let Continue = c; + Continue + } +} + +#[inline] +/// Folds a [Literal] in the default way +pub fn or_fold_literal(folder: &mut F, lit: Literal) -> Literal { + match lit { + Literal::Bool(b) => Literal::Bool(folder.fold_bool(b)), + Literal::Char(c) => Literal::Char(folder.fold_char(c)), + Literal::Int(i) => Literal::Int(folder.fold_int(i)), + Literal::String(s) => Literal::String(folder.fold_string(s)), + } +} + +#[inline] +/// Folds a [MetaKind] in the default way +pub fn or_fold_meta_kind(folder: &mut F, kind: MetaKind) -> MetaKind { + match kind { + MetaKind::Plain => MetaKind::Plain, + MetaKind::Equals(l) => MetaKind::Equals(folder.fold_literal(l)), + MetaKind::Func(lits) => { + MetaKind::Func(lits.into_iter().map(|l| folder.fold_literal(l)).collect()) + } + } +} + +#[inline] +/// Folds an [ItemKind] in the default way +pub fn or_fold_item_kind(folder: &mut F, kind: ItemKind) -> ItemKind { + match kind { + ItemKind::Module(m) => ItemKind::Module(folder.fold_module(m)), + ItemKind::Alias(a) => ItemKind::Alias(folder.fold_alias(a)), + ItemKind::Enum(e) => ItemKind::Enum(folder.fold_enum(e)), + ItemKind::Struct(s) => ItemKind::Struct(folder.fold_struct(s)), + ItemKind::Const(c) => ItemKind::Const(folder.fold_const(c)), + ItemKind::Static(s) => ItemKind::Static(folder.fold_static(s)), + ItemKind::Function(f) => ItemKind::Function(folder.fold_function(f)), + ItemKind::Impl(i) => ItemKind::Impl(folder.fold_impl(i)), + } +} + +#[inline] +/// Folds a [ModuleKind] in the default way +pub fn or_fold_module_kind(folder: &mut F, kind: ModuleKind) -> ModuleKind { + match kind { + ModuleKind::Inline(f) => ModuleKind::Inline(folder.fold_file(f)), + ModuleKind::Outline => ModuleKind::Outline, + } +} + +#[inline] +/// Folds a [StructKind] in the default way +pub fn or_fold_struct_kind(folder: &mut F, kind: StructKind) -> StructKind { + match kind { + StructKind::Empty => StructKind::Empty, + StructKind::Tuple(tys) => { + StructKind::Tuple(tys.into_iter().map(|t| folder.fold_ty(t)).collect()) + } + StructKind::Struct(mem) => StructKind::Struct( + mem.into_iter() + .map(|m| folder.fold_struct_member(m)) + .collect(), + ), + } +} + +#[inline] +/// Folds an [EnumKind] in the default way +pub fn or_fold_enum_kind(folder: &mut F, kind: EnumKind) -> EnumKind { + match kind { + EnumKind::NoVariants => EnumKind::NoVariants, + EnumKind::Variants(v) => { + EnumKind::Variants(v.into_iter().map(|v| folder.fold_variant(v)).collect()) + } + } +} + +#[inline] +/// Folds a [VariantKind] in the default way +pub fn or_fold_variant_kind(folder: &mut F, kind: VariantKind) -> VariantKind { + match kind { + VariantKind::Plain => VariantKind::Plain, + VariantKind::CLike(n) => VariantKind::CLike(n), + VariantKind::Tuple(t) => VariantKind::Tuple(folder.fold_ty(t)), + VariantKind::Struct(mem) => VariantKind::Struct( + mem.into_iter() + .map(|m| folder.fold_struct_member(m)) + .collect(), + ), + } +} + +#[inline] +/// Folds an [ImplKind] in the default way +pub fn or_fold_impl_kind(folder: &mut F, kind: ImplKind) -> ImplKind { + match kind { + ImplKind::Type(t) => ImplKind::Type(folder.fold_ty(t)), + ImplKind::Trait { impl_trait, for_type } => ImplKind::Trait { + impl_trait: folder.fold_path(impl_trait), + for_type: Box::new(folder.fold_ty(*for_type)), + }, + } +} + +#[inline] +/// Folds a [TyKind] in the default way +pub fn or_fold_ty_kind(folder: &mut F, kind: TyKind) -> TyKind { + match kind { + TyKind::Never => TyKind::Never, + TyKind::Empty => TyKind::Empty, + TyKind::SelfTy => TyKind::SelfTy, + TyKind::Path(p) => TyKind::Path(folder.fold_path(p)), + TyKind::Tuple(t) => TyKind::Tuple(folder.fold_ty_tuple(t)), + TyKind::Ref(t) => TyKind::Ref(folder.fold_ty_ref(t)), + TyKind::Fn(t) => TyKind::Fn(folder.fold_ty_fn(t)), + } +} + +#[inline] +/// Folds a [StmtKind] in the default way +pub fn or_fold_stmt_kind(folder: &mut F, kind: StmtKind) -> StmtKind { + match kind { + StmtKind::Empty => StmtKind::Empty, + StmtKind::Local(l) => StmtKind::Local(folder.fold_let(l)), + StmtKind::Item(i) => StmtKind::Item(Box::new(folder.fold_item(*i))), + StmtKind::Expr(e) => StmtKind::Expr(Box::new(folder.fold_expr(*e))), + } +} +#[inline] +/// Folds an [ExprKind] in the default way +pub fn or_fold_expr_kind(folder: &mut F, kind: ExprKind) -> ExprKind { + match kind { + ExprKind::Empty => ExprKind::Empty, + ExprKind::Assign(a) => ExprKind::Assign(folder.fold_assign(a)), + ExprKind::Binary(b) => ExprKind::Binary(folder.fold_binary(b)), + ExprKind::Unary(u) => ExprKind::Unary(folder.fold_unary(u)), + ExprKind::Index(i) => ExprKind::Index(folder.fold_index(i)), + ExprKind::Path(p) => ExprKind::Path(folder.fold_path(p)), + ExprKind::Literal(l) => ExprKind::Literal(folder.fold_literal(l)), + ExprKind::Array(a) => ExprKind::Array(folder.fold_array(a)), + ExprKind::ArrayRep(a) => ExprKind::ArrayRep(folder.fold_array_rep(a)), + ExprKind::AddrOf(a) => ExprKind::AddrOf(folder.fold_addrof(a)), + ExprKind::Block(b) => ExprKind::Block(folder.fold_block(b)), + ExprKind::Group(g) => ExprKind::Group(folder.fold_group(g)), + ExprKind::Tuple(t) => ExprKind::Tuple(folder.fold_tuple(t)), + ExprKind::Loop(l) => ExprKind::Loop(folder.fold_loop(l)), + ExprKind::While(w) => ExprKind::While(folder.fold_while(w)), + ExprKind::If(i) => ExprKind::If(folder.fold_if(i)), + ExprKind::For(f) => ExprKind::For(folder.fold_for(f)), + ExprKind::Break(b) => ExprKind::Break(folder.fold_break(b)), + ExprKind::Return(r) => ExprKind::Return(folder.fold_return(r)), + ExprKind::Continue(c) => ExprKind::Continue(folder.fold_continue(c)), + } +} diff --git a/cl-ast/src/ast_visitor/visit.rs b/cl-ast/src/ast_visitor/visit.rs new file mode 100644 index 0000000..b5ee6be --- /dev/null +++ b/cl-ast/src/ast_visitor/visit.rs @@ -0,0 +1,411 @@ +//! A [visitor](Visit) (implementer of the [Visit] trait) walks the immutable AST, mutating itself. + +use crate::ast::*; +use cl_structures::span::Span; + +/// Immutably walks the entire AST +/// +/// Each method acts as a customization point. +/// +/// There are a set of default implementations for enums +/// under the name [`or_visit_`*](or_visit_expr_kind), +/// provided for ease of use. +/// +/// For all other nodes, traversal is *explicit*. +pub trait Visit<'a>: Sized { + fn visit_span(&mut self, _extents: &'a Span) {} + fn visit_mutability(&mut self, _mutable: &'a Mutability) {} + fn visit_visibility(&mut self, _vis: &'a Visibility) {} + fn visit_identifier(&mut self, _name: &'a Identifier) {} + fn visit_literal(&mut self, l: &'a Literal) { + or_visit_literal(self, l) + } + fn visit_bool(&mut self, _b: &'a bool) {} + fn visit_char(&mut self, _c: &'a char) {} + fn visit_int(&mut self, _i: &'a u128) {} + fn visit_string(&mut self, _s: &'a str) {} + fn visit_file(&mut self, f: &'a File) { + let File { items } = f; + items.iter().for_each(|i| self.visit_item(i)); + } + fn visit_attrs(&mut self, a: &'a Attrs) { + let Attrs { meta } = a; + meta.iter().for_each(|m| self.visit_meta(m)); + } + fn visit_meta(&mut self, m: &'a Meta) { + let Meta { name, kind } = m; + self.visit_identifier(name); + self.visit_meta_kind(kind); + } + fn visit_meta_kind(&mut self, kind: &'a MetaKind) { + or_visit_meta_kind(self, kind) + } + fn visit_item(&mut self, i: &'a Item) { + let Item { extents, attrs, vis, kind } = i; + self.visit_span(extents); + self.visit_attrs(attrs); + self.visit_visibility(vis); + self.visit_item_kind(kind); + } + fn visit_item_kind(&mut self, kind: &'a ItemKind) { + or_visit_item_kind(self, kind) + } + fn visit_alias(&mut self, a: &'a Alias) { + let Alias { to, from } = a; + self.visit_identifier(to); + if let Some(t) = from { + self.visit_ty(t) + } + } + fn visit_const(&mut self, c: &'a Const) { + let Const { name, ty, init } = c; + self.visit_identifier(name); + self.visit_ty(ty); + self.visit_expr(init); + } + fn visit_static(&mut self, s: &'a Static) { + let Static { mutable, name, ty, init } = s; + self.visit_mutability(mutable); + self.visit_identifier(name); + self.visit_ty(ty); + self.visit_expr(init); + } + fn visit_module(&mut self, m: &'a Module) { + let Module { name, kind } = m; + self.visit_identifier(name); + self.visit_module_kind(kind); + } + fn visit_module_kind(&mut self, kind: &'a ModuleKind) { + or_visit_module_kind(self, kind) + } + fn visit_function(&mut self, f: &'a Function) { + let Function { name, sign, bind, body } = f; + self.visit_identifier(name); + self.visit_ty_fn(sign); + bind.iter().for_each(|p| self.visit_param(p)); + if let Some(b) = body { + self.visit_block(b) + } + } + fn visit_param(&mut self, p: &'a Param) { + let Param { mutability, name } = p; + self.visit_mutability(mutability); + self.visit_identifier(name); + } + fn visit_struct(&mut self, s: &'a Struct) { + let Struct { name, kind } = s; + self.visit_identifier(name); + self.visit_struct_kind(kind); + } + fn visit_struct_kind(&mut self, kind: &'a StructKind) { + or_visit_struct_kind(self, kind) + } + fn visit_struct_member(&mut self, m: &'a StructMember) { + let StructMember { vis, name, ty } = m; + self.visit_visibility(vis); + self.visit_identifier(name); + self.visit_ty(ty); + } + fn visit_enum(&mut self, e: &'a Enum) { + let Enum { name, kind } = e; + self.visit_identifier(name); + self.visit_enum_kind(kind); + } + fn visit_enum_kind(&mut self, kind: &'a EnumKind) { + or_visit_enum_kind(self, kind) + } + fn visit_variant(&mut self, v: &'a Variant) { + let Variant { name, kind } = v; + self.visit_identifier(name); + self.visit_variant_kind(kind); + } + fn visit_variant_kind(&mut self, kind: &'a VariantKind) { + or_visit_variant_kind(self, kind) + } + fn visit_impl(&mut self, i: &'a Impl) { + let Impl { target, body } = i; + self.visit_impl_kind(target); + self.visit_file(body) + } + fn visit_impl_kind(&mut self, target: &'a ImplKind) { + or_visit_impl_kind(self, target) + } + fn visit_ty(&mut self, t: &'a Ty) { + let Ty { extents, kind } = t; + self.visit_span(extents); + self.visit_ty_kind(kind); + } + fn visit_ty_kind(&mut self, kind: &'a TyKind) { + or_visit_ty_kind(self, kind) + } + fn visit_ty_tuple(&mut self, t: &'a TyTuple) { + let TyTuple { types } = t; + types.iter().for_each(|kind| self.visit_ty_kind(kind)) + } + fn visit_ty_ref(&mut self, t: &'a TyRef) { + let TyRef { mutable, count: _, to } = t; + self.visit_mutability(mutable); + self.visit_path(to); + } + fn visit_ty_fn(&mut self, t: &'a TyFn) { + let TyFn { args, rety } = t; + self.visit_ty_kind(args); + if let Some(rety) = rety { + self.visit_ty(rety); + } + } + fn visit_path(&mut self, p: &'a Path) { + let Path { absolute: _, parts } = p; + parts.iter().for_each(|p| self.visit_path_part(p)) + } + fn visit_path_part(&mut self, p: &'a PathPart) { + match p { + PathPart::SuperKw => {} + PathPart::SelfKw => {} + PathPart::Ident(i) => self.visit_identifier(i), + } + } + fn visit_stmt(&mut self, s: &'a Stmt) { + let Stmt { extents, kind, semi } = s; + self.visit_span(extents); + self.visit_stmt_kind(kind); + self.visit_semi(semi); + } + fn visit_stmt_kind(&mut self, kind: &'a StmtKind) { + or_visit_stmt_kind(self, kind) + } + fn visit_semi(&mut self, _s: &'a Semi) {} + fn visit_let(&mut self, l: &'a Let) { + let Let { mutable, name, ty, init } = l; + self.visit_mutability(mutable); + self.visit_identifier(name); + if let Some(ty) = ty { + self.visit_ty(ty); + } + if let Some(init) = init { + self.visit_expr(init) + } + } + fn visit_expr(&mut self, e: &'a Expr) { + let Expr { extents, kind } = e; + self.visit_span(extents); + self.visit_expr_kind(kind) + } + fn visit_expr_kind(&mut self, e: &'a ExprKind) { + or_visit_expr_kind(self, e) + } + fn visit_assign(&mut self, a: &'a Assign) { + let Assign { kind, parts } = a; + let (head, tail) = parts.as_ref(); + self.visit_assign_kind(kind); + self.visit_expr_kind(head); + self.visit_expr_kind(tail); + } + fn visit_assign_kind(&mut self, _kind: &'a AssignKind) {} + fn visit_binary(&mut self, b: &'a Binary) { + let Binary { kind, parts } = b; + let (head, tail) = parts.as_ref(); + self.visit_binary_kind(kind); + self.visit_expr_kind(head); + self.visit_expr_kind(tail); + } + fn visit_binary_kind(&mut self, _kind: &'a BinaryKind) {} + fn visit_unary(&mut self, u: &'a Unary) { + let Unary { kind, tail } = u; + self.visit_unary_kind(kind); + self.visit_expr_kind(tail); + } + fn visit_unary_kind(&mut self, _kind: &'a UnaryKind) {} + fn visit_index(&mut self, i: &'a Index) { + let Index { head, indices } = i; + self.visit_expr_kind(head); + indices.iter().for_each(|e| self.visit_expr(e)); + } + fn visit_array(&mut self, a: &'a Array) { + let Array { values } = a; + values.iter().for_each(|e| self.visit_expr(e)) + } + fn visit_array_rep(&mut self, a: &'a ArrayRep) { + let ArrayRep { value, repeat } = a; + self.visit_expr_kind(value); + self.visit_expr_kind(repeat); + } + fn visit_addrof(&mut self, a: &'a AddrOf) { + let AddrOf { count: _, mutable, expr } = a; + self.visit_mutability(mutable); + self.visit_expr_kind(expr); + } + fn visit_block(&mut self, b: &'a Block) { + let Block { stmts } = b; + stmts.iter().for_each(|s| self.visit_stmt(s)); + } + fn visit_group(&mut self, g: &'a Group) { + let Group { expr } = g; + self.visit_expr_kind(expr) + } + fn visit_tuple(&mut self, t: &'a Tuple) { + let Tuple { exprs } = t; + exprs.iter().for_each(|e| self.visit_expr(e)) + } + fn visit_loop(&mut self, l: &'a Loop) { + let Loop { body } = l; + self.visit_expr(body) + } + fn visit_while(&mut self, w: &'a While) { + let While { cond, pass, fail } = w; + self.visit_expr(cond); + self.visit_block(pass); + self.visit_else(fail); + } + fn visit_if(&mut self, i: &'a If) { + let If { cond, pass, fail } = i; + self.visit_expr(cond); + self.visit_block(pass); + self.visit_else(fail); + } + fn visit_for(&mut self, f: &'a For) { + let For { bind, cond, pass, fail } = f; + self.visit_identifier(bind); + self.visit_expr(cond); + self.visit_block(pass); + self.visit_else(fail); + } + fn visit_else(&mut self, e: &'a Else) { + let Else { body } = e; + if let Some(body) = body { + self.visit_expr(body) + } + } + fn visit_break(&mut self, b: &'a Break) { + let Break { body } = b; + if let Some(body) = body { + self.visit_expr(body) + } + } + fn visit_return(&mut self, r: &'a Return) { + let Return { body } = r; + if let Some(body) = body { + self.visit_expr(body) + } + } + fn visit_continue(&mut self, c: &'a Continue) { + let Continue = c; + } +} + +pub fn or_visit_literal<'a, V: Visit<'a>>(visitor: &mut V, l: &'a Literal) { + match l { + Literal::Bool(b) => visitor.visit_bool(b), + Literal::Char(c) => visitor.visit_char(c), + Literal::Int(i) => visitor.visit_int(i), + Literal::String(s) => visitor.visit_string(s), + } +} + +pub fn or_visit_meta_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a MetaKind) { + match kind { + MetaKind::Plain => {} + MetaKind::Equals(l) => visitor.visit_literal(l), + MetaKind::Func(lits) => lits.iter().for_each(|l| visitor.visit_literal(l)), + } +} + +pub fn or_visit_item_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a ItemKind) { + match kind { + ItemKind::Module(m) => visitor.visit_module(m), + ItemKind::Alias(a) => visitor.visit_alias(a), + ItemKind::Enum(e) => visitor.visit_enum(e), + ItemKind::Struct(s) => visitor.visit_struct(s), + ItemKind::Const(c) => visitor.visit_const(c), + ItemKind::Static(s) => visitor.visit_static(s), + ItemKind::Function(f) => visitor.visit_function(f), + ItemKind::Impl(i) => visitor.visit_impl(i), + } +} + +pub fn or_visit_module_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a ModuleKind) { + match kind { + ModuleKind::Inline(f) => visitor.visit_file(f), + ModuleKind::Outline => {} + } +} + +pub fn or_visit_struct_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a StructKind) { + match kind { + StructKind::Empty => {} + StructKind::Tuple(ty) => ty.iter().for_each(|t| visitor.visit_ty(t)), + StructKind::Struct(m) => m.iter().for_each(|m| visitor.visit_struct_member(m)), + } +} + +pub fn or_visit_enum_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a EnumKind) { + match kind { + EnumKind::NoVariants => {} + EnumKind::Variants(variants) => variants.iter().for_each(|v| visitor.visit_variant(v)), + } +} + +pub fn or_visit_variant_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a VariantKind) { + match kind { + VariantKind::Plain => {} + VariantKind::CLike(_) => {} + VariantKind::Tuple(t) => visitor.visit_ty(t), + VariantKind::Struct(m) => m.iter().for_each(|m| visitor.visit_struct_member(m)), + } +} + +pub fn or_visit_impl_kind<'a, V: Visit<'a>>(visitor: &mut V, target: &'a ImplKind) { + match target { + ImplKind::Type(t) => visitor.visit_ty(t), + ImplKind::Trait { impl_trait, for_type } => { + visitor.visit_path(impl_trait); + visitor.visit_ty(for_type) + } + } +} + +pub fn or_visit_ty_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a TyKind) { + match kind { + TyKind::Never => {} + TyKind::Empty => {} + TyKind::SelfTy => {} + TyKind::Path(p) => visitor.visit_path(p), + TyKind::Tuple(t) => visitor.visit_ty_tuple(t), + TyKind::Ref(t) => visitor.visit_ty_ref(t), + TyKind::Fn(t) => visitor.visit_ty_fn(t), + } +} + +pub fn or_visit_stmt_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a StmtKind) { + match kind { + StmtKind::Empty => {} + StmtKind::Local(l) => visitor.visit_let(l), + StmtKind::Item(i) => visitor.visit_item(i), + StmtKind::Expr(e) => visitor.visit_expr(e), + } +} + +pub fn or_visit_expr_kind<'a, V: Visit<'a>>(visitor: &mut V, e: &'a ExprKind) { + match e { + ExprKind::Empty => {} + ExprKind::Assign(a) => visitor.visit_assign(a), + ExprKind::Binary(b) => visitor.visit_binary(b), + ExprKind::Unary(u) => visitor.visit_unary(u), + ExprKind::Index(i) => visitor.visit_index(i), + ExprKind::Path(p) => visitor.visit_path(p), + ExprKind::Literal(l) => visitor.visit_literal(l), + ExprKind::Array(a) => visitor.visit_array(a), + ExprKind::ArrayRep(a) => visitor.visit_array_rep(a), + ExprKind::AddrOf(a) => visitor.visit_addrof(a), + ExprKind::Block(b) => visitor.visit_block(b), + ExprKind::Group(g) => visitor.visit_group(g), + ExprKind::Tuple(t) => visitor.visit_tuple(t), + ExprKind::Loop(l) => visitor.visit_loop(l), + ExprKind::While(w) => visitor.visit_while(w), + ExprKind::If(i) => visitor.visit_if(i), + ExprKind::For(f) => visitor.visit_for(f), + ExprKind::Break(b) => visitor.visit_break(b), + ExprKind::Return(r) => visitor.visit_return(r), + ExprKind::Continue(c) => visitor.visit_continue(c), + } +} diff --git a/cl-ast/src/desugar.rs b/cl-ast/src/desugar.rs new file mode 100644 index 0000000..2b5e631 --- /dev/null +++ b/cl-ast/src/desugar.rs @@ -0,0 +1,5 @@ +//! Desugaring passes for Conlang + +pub mod while_else; + +pub mod squash_groups; diff --git a/cl-ast/src/desugar/squash_groups.rs b/cl-ast/src/desugar/squash_groups.rs new file mode 100644 index 0000000..36fe56b --- /dev/null +++ b/cl-ast/src/desugar/squash_groups.rs @@ -0,0 +1,14 @@ +//! Squashes group expressions +use crate::{ast::*, ast_visitor::fold::*}; + +/// Squashes group expressions +pub struct SquashGroups; + +impl Fold for SquashGroups { + fn fold_expr_kind(&mut self, kind: ExprKind) -> ExprKind { + match kind { + ExprKind::Group(Group { expr }) => self.fold_expr_kind(*expr), + _ => or_fold_expr_kind(self, kind), + } + } +} diff --git a/cl-ast/src/desugar/while_else.rs b/cl-ast/src/desugar/while_else.rs new file mode 100644 index 0000000..0cac483 --- /dev/null +++ b/cl-ast/src/desugar/while_else.rs @@ -0,0 +1,34 @@ +//! Desugars `while {...} else` expressions +//! into `loop if {...} else break` expressions + +use crate::{ast::*, ast_visitor::fold::Fold}; +use cl_structures::span::Span; + +/// Desugars while-else expressions +/// into loop-if-else-break expressions +pub struct WhileElseDesugar; + +impl Fold for WhileElseDesugar { + fn fold_expr(&mut self, e: Expr) -> Expr { + let Expr { extents, kind } = e; + let kind = desugar_while(extents, kind); + Expr { extents: self.fold_span(extents), kind: self.fold_expr_kind(kind) } + } +} + +/// Desugars while(-else) expressions into loop-if-else-break expressions +fn desugar_while(extents: Span, kind: ExprKind) -> ExprKind { + match kind { + // work backwards: fail -> break -> if -> loop + ExprKind::While(While { cond, pass, fail: Else { body } }) => { + // Preserve the else-expression's extents, if present, or use the parent's extents + let fail_span = body.as_ref().map(|body| body.extents).unwrap_or(extents); + let break_expr = Expr { extents: fail_span, kind: ExprKind::Break(Break { body }) }; + + let loop_body = If { cond, pass, fail: Else { body: Some(Box::new(break_expr)) } }; + let loop_body = Expr { extents, kind: ExprKind::If(loop_body) }; + ExprKind::Loop(Loop { body: Box::new(loop_body) }) + } + _ => kind, + } +} diff --git a/cl-ast/src/lib.rs b/cl-ast/src/lib.rs index b525acd..d0eab33 100644 --- a/cl-ast/src/lib.rs +++ b/cl-ast/src/lib.rs @@ -16,4 +16,6 @@ pub use ast::*; pub mod ast; pub mod ast_impl; +pub mod ast_visitor; +pub mod desugar; pub mod format; diff --git a/cl-repl/examples/typeck.rs b/cl-repl/examples/typeck.rs index ebcb9d0..5d38823 100644 --- a/cl-repl/examples/typeck.rs +++ b/cl-repl/examples/typeck.rs @@ -1,3 +1,7 @@ +use cl_ast::{ + ast_visitor::fold::Fold, + desugar::{squash_groups::SquashGroups, while_else::WhileElseDesugar}, +}; use cl_lexer::Lexer; use cl_parser::Parser; use cl_repl::repline::{error::Error as RlError, Repline}; @@ -33,6 +37,7 @@ fn main() -> Result<(), Box> { Err(e)? } }; + unsafe { TREES.push(code) }.collect_in_root(&mut prj)?; main_menu(&mut prj) @@ -81,6 +86,7 @@ fn main_menu(prj: &mut Project) -> Result<(), Box> { "l" | "list" => list_types(prj), "q" | "query" => query_type_expression(prj), "r" | "resolve" => resolve_all(prj), + "d" | "desugar" => live_desugar(), "h" | "help" => { println!( "Valid commands are: @@ -88,6 +94,7 @@ fn main_menu(prj: &mut Project) -> Result<(), Box> { list (l): List all known types query (q): Query the type system resolve (r): Perform type resolution + desugar (d): WIP: Test the experimental desugaring passes help (h): Print this list exit (e): Exit the program" ); @@ -105,7 +112,7 @@ fn enter_code(prj: &mut Project) -> Result<(), Box> { return Ok(Response::Break); } let code = Parser::new(Lexer::new(line)).file()?; - + let code = WhileElseDesugar.fold_file(code); // Safety: this is totally unsafe unsafe { TREES.push(code) }.collect_in_root(prj)?; @@ -113,6 +120,21 @@ fn enter_code(prj: &mut Project) -> Result<(), Box> { }) } +fn live_desugar() -> Result<(), Box> { + read_and(C_RESV, "se>", |line| { + let code = Parser::new(Lexer::new(line)).stmt()?; + println!("Raw, as parsed:\n{C_LISTING}{code}\x1b[0m"); + + let code = SquashGroups.fold_stmt(code); + println!("SquashGroups\n{C_LISTING}{code}\x1b[0m"); + + let code = WhileElseDesugar.fold_stmt(code); + println!("WhileElseDesugar\n{C_LISTING}{code}\x1b[0m"); + + Ok(Response::Accept) + }) +} + fn query_type_expression(prj: &mut Project) -> Result<(), Box> { read_and(C_RESV, "ty>", |line| { if line.trim().is_empty() { diff --git a/cl-repl/src/lib.rs b/cl-repl/src/lib.rs index c904c03..7d3ca07 100644 --- a/cl-repl/src/lib.rs +++ b/cl-repl/src/lib.rs @@ -112,10 +112,16 @@ pub mod program { self.parse_file().or_else(|_| self.parse_stmt()) } pub fn parse_stmt(&self) -> PResult> { - Ok(Program { data: Parsed::Stmt(Parser::new(self.lex()).stmt()?), text: self.text }) + let stmt = Parser::new(self.lex()).stmt()?; + // let stmt = WhileElseDesugar.fold_stmt(stmt); + + Ok(Program { data: Parsed::Stmt(stmt), text: self.text }) } pub fn parse_file(&self) -> PResult> { - Ok(Program { data: Parsed::File(Parser::new(self.lex()).file()?), text: self.text }) + let file = Parser::new(self.lex()).file()?; + // let file = WhileElseDesugar.fold_file(file); + + Ok(Program { data: Parsed::File(file), text: self.text }) } }