From 16eef6725a013cbfd07d30d745afde0c0fe1e353 Mon Sep 17 00:00:00 2001 From: Victor Lanvin Date: Tue, 20 Aug 2024 08:59:56 -0700 Subject: [PATCH] Implement AST transformer Summary: Implement an eqWAlizer AST transformer in ELP, based on the AST visitor already implemented. Add `preprocess` module implementing (for now) the default implementation of the transformer, which is called on every AST/stub going through ELP. Reviewed By: RobinMorisset Differential Revision: D60584098 fbshipit-source-id: 52f1a705e2c3616cc08921898276dc345dec9986 --- crates/eqwalizer/src/ast/mod.rs | 4 +- crates/eqwalizer/src/ast/preprocess.rs | 20 + crates/types_db/src/eqwalizer/mod.rs | 1 + crates/types_db/src/eqwalizer/transformer.rs | 773 +++++++++++++++++++ 4 files changed, 797 insertions(+), 1 deletion(-) create mode 100644 crates/eqwalizer/src/ast/preprocess.rs create mode 100644 crates/types_db/src/eqwalizer/transformer.rs diff --git a/crates/eqwalizer/src/ast/mod.rs b/crates/eqwalizer/src/ast/mod.rs index 7cb1eb2597..b2e98e43ca 100644 --- a/crates/eqwalizer/src/ast/mod.rs +++ b/crates/eqwalizer/src/ast/mod.rs @@ -31,6 +31,7 @@ pub mod convert; pub mod convert_types; pub mod db; pub mod expand; +pub mod preprocess; pub mod stub; pub mod subst; pub mod trans_valid; @@ -205,7 +206,8 @@ pub fn from_bytes(bytes: &Vec) -> Result { if let Term::Tuple(res) = term { if let [Term::Atom(ok), forms, _] = &res.elements[..] { if ok.name == "ok" { - return Ok(convert::convert_forms(forms, false, false)?); + let converted_forms = convert::convert_forms(forms, false, false)?; + return Ok(preprocess::preprocess(converted_forms)); } } } diff --git a/crates/eqwalizer/src/ast/preprocess.rs b/crates/eqwalizer/src/ast/preprocess.rs new file mode 100644 index 0000000000..6b81f324c7 --- /dev/null +++ b/crates/eqwalizer/src/ast/preprocess.rs @@ -0,0 +1,20 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under both the MIT license found in the + * LICENSE-MIT file in the root directory of this source tree and the Apache + * License, Version 2.0 found in the LICENSE-APACHE file in the root directory + * of this source tree. + */ + +use elp_types_db::eqwalizer::transformer::Transformer; +use elp_types_db::eqwalizer::AST; + +struct Preprocessor {} + +impl Transformer<()> for Preprocessor {} + +pub(crate) fn preprocess(ast: AST) -> AST { + let mut preprocessor = Preprocessor {}; + preprocessor.transform_ast(ast).unwrap() +} diff --git a/crates/types_db/src/eqwalizer/mod.rs b/crates/types_db/src/eqwalizer/mod.rs index 4f3f5234a1..2572f3a43a 100644 --- a/crates/types_db/src/eqwalizer/mod.rs +++ b/crates/types_db/src/eqwalizer/mod.rs @@ -23,6 +23,7 @@ pub mod guard; pub mod invalid_diagnostics; pub mod pat; pub mod tc_diagnostics; +pub mod transformer; pub mod types; pub mod visitor; diff --git a/crates/types_db/src/eqwalizer/transformer.rs b/crates/types_db/src/eqwalizer/transformer.rs new file mode 100644 index 0000000000..351fa620de --- /dev/null +++ b/crates/types_db/src/eqwalizer/transformer.rs @@ -0,0 +1,773 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under both the MIT license found in the + * LICENSE-MIT file in the root directory of this source tree and the Apache + * License, Version 2.0 found in the LICENSE-APACHE file in the root directory + * of this source tree. + */ + +use super::expr::BComprehension; +use super::expr::BGenerate; +use super::expr::BinOp; +use super::expr::Binary; +use super::expr::BinaryElem; +use super::expr::Block; +use super::expr::Body; +use super::expr::Case; +use super::expr::Catch; +use super::expr::Clause; +use super::expr::Cons; +use super::expr::DynCall; +use super::expr::DynRemoteFun; +use super::expr::DynRemoteFunArity; +use super::expr::Expr; +use super::expr::Filter; +use super::expr::If; +use super::expr::LComprehension; +use super::expr::LGenerate; +use super::expr::Lambda; +use super::expr::LocalCall; +use super::expr::MComprehension; +use super::expr::MGenerate; +use super::expr::MapCreate; +use super::expr::MapUpdate; +use super::expr::Match; +use super::expr::Maybe; +use super::expr::MaybeElse; +use super::expr::MaybeMatch; +use super::expr::Qualifier; +use super::expr::Receive; +use super::expr::ReceiveWithTimeout; +use super::expr::RecordCreate; +use super::expr::RecordField; +use super::expr::RecordFieldGen; +use super::expr::RecordSelect; +use super::expr::RecordUpdate; +use super::expr::RemoteCall; +use super::expr::TryCatchExpr; +use super::expr::TryOfCatchExpr; +use super::expr::Tuple; +use super::expr::UnOp; +use super::form::ExternalForm; +use super::form::ExternalRecDecl; +use super::form::ExternalRecField; +use super::form::FunDecl; +use super::guard::Guard; +use super::guard::Test; +use super::guard::TestBinOp; +use super::guard::TestCall; +use super::guard::TestCons; +use super::guard::TestMapCreate; +use super::guard::TestMapUpdate; +use super::guard::TestRecordCreate; +use super::guard::TestRecordField; +use super::guard::TestRecordFieldGen; +use super::guard::TestRecordFieldNamed; +use super::guard::TestRecordSelect; +use super::guard::TestTuple; +use super::guard::TestUnOp; +use super::pat::Pat; +use super::pat::PatBinOp; +use super::pat::PatBinary; +use super::pat::PatBinaryElem; +use super::pat::PatCons; +use super::pat::PatMap; +use super::pat::PatMatch; +use super::pat::PatRecord; +use super::pat::PatRecordFieldNamed; +use super::pat::PatTuple; +use super::pat::PatUnOp; +use super::AST; +use crate::eqwalizer::expr::RecordFieldNamed; + +pub trait Transformer: Sized { + fn transform_ast(&mut self, ast: AST) -> Result { + ast.into_iter() + .map(|form| self.transform_form(form)) + .collect::, _>>() + } + fn transform_expr(&mut self, expr: Expr) -> Result { + walk_expr(self, expr) + } + fn transform_pat(&mut self, pat: Pat) -> Result { + walk_pat(self, pat) + } + fn transform_test(&mut self, test: Test) -> Result { + walk_test(self, test) + } + fn transform_clause(&mut self, clause: Clause) -> Result { + walk_clause(self, clause) + } + fn transform_body(&mut self, body: Body) -> Result { + walk_body(self, body) + } + fn transform_guard(&mut self, guard: Guard) -> Result { + walk_guard(self, guard) + } + fn transform_form(&mut self, form: ExternalForm) -> Result { + walk_form(self, form) + } + fn transform_qualifier(&mut self, qualifier: Qualifier) -> Result { + walk_qualifier(self, qualifier) + } + fn transform_binary_elem(&mut self, elem: BinaryElem) -> Result { + walk_binary_elem(self, elem) + } + fn transform_pat_binary_elem(&mut self, elem: PatBinaryElem) -> Result { + walk_pat_binary_elem(self, elem) + } + fn transform_record_field(&mut self, field: RecordField) -> Result { + walk_record_field(self, field) + } + fn transform_test_record_field( + &mut self, + field: TestRecordField, + ) -> Result { + walk_test_record_field(self, field) + } +} + +pub fn walk_body>(transformer: &mut V, body: Body) -> Result { + body.exprs + .into_iter() + .map(|e| transformer.transform_expr(e)) + .collect::, _>>() + .map(|forms| Body { exprs: forms }) +} + +pub fn walk_clause>(transformer: &mut V, clause: Clause) -> Result { + let pats = clause + .pats + .into_iter() + .map(|p| transformer.transform_pat(p)) + .collect::, _>>()?; + let guards = clause + .guards + .into_iter() + .map(|g| transformer.transform_guard(g)) + .collect::, _>>()?; + let body = transformer.transform_body(clause.body)?; + Ok(Clause { + location: clause.location, + pats, + guards, + body, + }) +} + +pub fn walk_qualifier>( + transformer: &mut V, + qualifier: Qualifier, +) -> Result { + match qualifier { + Qualifier::LGenerate(g) => Ok(Qualifier::LGenerate(LGenerate { + pat: transformer.transform_pat(g.pat)?, + expr: transformer.transform_expr(g.expr)?, + })), + Qualifier::BGenerate(g) => Ok(Qualifier::BGenerate(BGenerate { + pat: transformer.transform_pat(g.pat)?, + expr: transformer.transform_expr(g.expr)?, + })), + Qualifier::MGenerate(g) => Ok(Qualifier::MGenerate(MGenerate { + k_pat: transformer.transform_pat(g.k_pat)?, + v_pat: transformer.transform_pat(g.v_pat)?, + expr: transformer.transform_expr(g.expr)?, + })), + Qualifier::Filter(f) => Ok(Qualifier::Filter(Filter { + expr: transformer.transform_expr(f.expr)?, + })), + } +} + +pub fn walk_binary_elem>( + transformer: &mut V, + elem: BinaryElem, +) -> Result { + Ok(BinaryElem { + location: elem.location, + specifier: elem.specifier, + expr: transformer.transform_expr(elem.expr)?, + size: elem + .size + .map_or(Ok(None), |s| transformer.transform_expr(s).map(Some))?, + }) +} + +pub fn walk_record_field>( + transformer: &mut V, + field: RecordField, +) -> Result { + match field { + RecordField::RecordFieldGen(f) => Ok(RecordField::RecordFieldGen(RecordFieldGen { + value: transformer.transform_expr(f.value)?, + })), + RecordField::RecordFieldNamed(f) => Ok(RecordField::RecordFieldNamed(RecordFieldNamed { + name: f.name, + value: transformer.transform_expr(f.value)?, + })), + } +} + +fn walk_cons>(transformer: &mut V, c: Cons) -> Result { + let mut cons = c; + let mut transformed_elems = vec![]; + loop { + let h = Box::new(transformer.transform_expr(*cons.h)?); + transformed_elems.push((cons.location, h)); + match *cons.t { + Expr::Cons(c) => cons = c, + exp => { + let transformed_t = transformer.transform_expr(exp)?; + return Ok(transformed_elems.into_iter().rev().fold( + transformed_t, + |t, (location, h)| { + Expr::Cons(Cons { + location, + h, + t: Box::new(t), + }) + }, + )); + } + } + } +} + +pub fn walk_expr>(transformer: &mut V, e: Expr) -> Result { + match e { + Expr::Var(v) => Ok(Expr::Var(v)), + Expr::AtomLit(a) => Ok(Expr::AtomLit(a)), + Expr::IntLit(i) => Ok(Expr::IntLit(i)), + Expr::FloatLit(f) => Ok(Expr::FloatLit(f)), + Expr::Block(b) => Ok(Expr::Block(Block { + location: b.location, + body: transformer.transform_body(b.body)?, + })), + Expr::Match(m) => Ok(Expr::Match(Match { + location: m.location, + pat: transformer.transform_pat(m.pat)?, + expr: Box::new(transformer.transform_expr(*m.expr)?), + })), + Expr::Tuple(t) => Ok(Expr::Tuple(Tuple { + location: t.location, + elems: t + .elems + .into_iter() + .map(|e| transformer.transform_expr(e)) + .collect::, _>>()?, + })), + Expr::StringLit(s) => Ok(Expr::StringLit(s)), + Expr::NilLit(n) => Ok(Expr::NilLit(n)), + Expr::Cons(c) => walk_cons(transformer, c), + Expr::Case(c) => Ok(Expr::Case(Case { + location: c.location, + expr: Box::new(transformer.transform_expr(*c.expr)?), + clauses: c + .clauses + .into_iter() + .map(|c| transformer.transform_clause(c)) + .collect::, _>>()?, + })), + Expr::If(i) => Ok(Expr::If(If { + location: i.location, + clauses: i + .clauses + .into_iter() + .map(|c| transformer.transform_clause(c)) + .collect::, _>>()?, + })), + Expr::LocalCall(c) => Ok(Expr::LocalCall(LocalCall { + location: c.location, + id: c.id, + args: c + .args + .into_iter() + .map(|e| transformer.transform_expr(e)) + .collect::, _>>()?, + })), + Expr::DynCall(c) => Ok(Expr::DynCall(DynCall { + location: c.location, + f: Box::new(transformer.transform_expr(*c.f)?), + args: c + .args + .into_iter() + .map(|e| transformer.transform_expr(e)) + .collect::, _>>()?, + })), + Expr::RemoteCall(c) => Ok(Expr::RemoteCall(RemoteCall { + location: c.location, + id: c.id, + args: c + .args + .into_iter() + .map(|e| transformer.transform_expr(e)) + .collect::, _>>()?, + })), + Expr::LocalFun(f) => Ok(Expr::LocalFun(f)), + Expr::RemoteFun(f) => Ok(Expr::RemoteFun(f)), + Expr::DynRemoteFun(f) => Ok(Expr::DynRemoteFun(DynRemoteFun { + location: f.location, + module: Box::new(transformer.transform_expr(*f.module)?), + name: Box::new(transformer.transform_expr(*f.name)?), + })), + Expr::DynRemoteFunArity(f) => Ok(Expr::DynRemoteFunArity(DynRemoteFunArity { + location: f.location, + module: Box::new(transformer.transform_expr(*f.module)?), + name: Box::new(transformer.transform_expr(*f.name)?), + arity: Box::new(transformer.transform_expr(*f.arity)?), + })), + Expr::Lambda(l) => Ok(Expr::Lambda(Lambda { + location: l.location, + clauses: l + .clauses + .into_iter() + .map(|c| transformer.transform_clause(c)) + .collect::, _>>()?, + name: l.name, + })), + Expr::UnOp(o) => Ok(Expr::UnOp(UnOp { + location: o.location, + op: o.op, + arg: Box::new(transformer.transform_expr(*o.arg)?), + })), + Expr::BinOp(o) => Ok(Expr::BinOp(BinOp { + location: o.location, + op: o.op, + arg_1: Box::new(transformer.transform_expr(*o.arg_1)?), + arg_2: Box::new(transformer.transform_expr(*o.arg_2)?), + })), + Expr::LComprehension(c) => Ok(Expr::LComprehension(LComprehension { + location: c.location, + template: Box::new(transformer.transform_expr(*c.template)?), + qualifiers: c + .qualifiers + .into_iter() + .map(|q| transformer.transform_qualifier(q)) + .collect::, _>>()?, + })), + Expr::BComprehension(c) => Ok(Expr::BComprehension(BComprehension { + location: c.location, + template: Box::new(transformer.transform_expr(*c.template)?), + qualifiers: c + .qualifiers + .into_iter() + .map(|q| transformer.transform_qualifier(q)) + .collect::, _>>()?, + })), + Expr::MComprehension(c) => Ok(Expr::MComprehension(MComprehension { + location: c.location, + k_template: Box::new(transformer.transform_expr(*c.k_template)?), + v_template: Box::new(transformer.transform_expr(*c.v_template)?), + qualifiers: c + .qualifiers + .into_iter() + .map(|q| transformer.transform_qualifier(q)) + .collect::, _>>()?, + })), + Expr::Binary(b) => Ok(Expr::Binary(Binary { + location: b.location, + elems: b + .elems + .into_iter() + .map(|e| transformer.transform_binary_elem(e)) + .collect::, _>>()?, + })), + Expr::Catch(c) => Ok(Expr::Catch(Catch { + location: c.location, + expr: Box::new(transformer.transform_expr(*c.expr)?), + })), + Expr::TryCatchExpr(e) => Ok(Expr::TryCatchExpr(TryCatchExpr { + location: e.location, + try_body: transformer.transform_body(e.try_body)?, + catch_clauses: e + .catch_clauses + .into_iter() + .map(|c| transformer.transform_clause(c)) + .collect::, _>>()?, + after_body: e + .after_body + .map_or(Ok(None), |b| transformer.transform_body(b).map(Some))?, + })), + Expr::TryOfCatchExpr(e) => Ok(Expr::TryOfCatchExpr(TryOfCatchExpr { + location: e.location, + try_body: transformer.transform_body(e.try_body)?, + try_clauses: e + .try_clauses + .into_iter() + .map(|c| transformer.transform_clause(c)) + .collect::, _>>()?, + catch_clauses: e + .catch_clauses + .into_iter() + .map(|c| transformer.transform_clause(c)) + .collect::, _>>()?, + after_body: e + .after_body + .map_or(Ok(None), |b| transformer.transform_body(b).map(Some))?, + })), + Expr::Receive(r) => Ok(Expr::Receive(Receive { + location: r.location, + clauses: r + .clauses + .into_iter() + .map(|c| transformer.transform_clause(c)) + .collect::, _>>()?, + })), + Expr::ReceiveWithTimeout(r) => Ok(Expr::ReceiveWithTimeout(ReceiveWithTimeout { + location: r.location, + clauses: r + .clauses + .into_iter() + .map(|c| transformer.transform_clause(c)) + .collect::, _>>()?, + timeout: Box::new(transformer.transform_expr(*r.timeout)?), + timeout_body: transformer.transform_body(r.timeout_body)?, + })), + Expr::RecordCreate(r) => Ok(Expr::RecordCreate(RecordCreate { + location: r.location, + rec_name: r.rec_name, + fields: r + .fields + .into_iter() + .map(|f| transformer.transform_record_field(f)) + .collect::, _>>()?, + })), + Expr::RecordUpdate(r) => Ok(Expr::RecordUpdate(RecordUpdate { + location: r.location, + rec_name: r.rec_name, + expr: Box::new(transformer.transform_expr(*r.expr)?), + fields: r + .fields + .into_iter() + .map(|f| { + transformer + .transform_expr(f.value) + .map(|value| RecordFieldNamed { + value, + name: f.name, + }) + }) + .collect::, _>>()?, + })), + Expr::RecordSelect(r) => Ok(Expr::RecordSelect(RecordSelect { + location: r.location, + rec_name: r.rec_name, + field_name: r.field_name, + expr: Box::new(transformer.transform_expr(*r.expr)?), + })), + Expr::RecordIndex(r) => Ok(Expr::RecordIndex(r)), + Expr::MapCreate(m) => Ok(Expr::MapCreate(MapCreate { + location: m.location, + kvs: m + .kvs + .into_iter() + .map(|(k, v)| { + transformer.transform_expr(k).and_then(|k_trans| { + transformer + .transform_expr(v) + .and_then(|v_trans| Ok((k_trans, v_trans))) + }) + }) + .collect::, _>>()?, + })), + Expr::MapUpdate(m) => Ok(Expr::MapUpdate(MapUpdate { + location: m.location, + map: Box::new(transformer.transform_expr(*m.map)?), + kvs: m + .kvs + .into_iter() + .map(|(k, v)| { + transformer.transform_expr(k).and_then(|k_trans| { + transformer + .transform_expr(v) + .and_then(|v_trans| Ok((k_trans, v_trans))) + }) + }) + .collect::, _>>()?, + })), + Expr::Maybe(m) => Ok(Expr::Maybe(Maybe { + location: m.location, + body: transformer.transform_body(m.body)?, + })), + Expr::MaybeElse(m) => Ok(Expr::MaybeElse(MaybeElse { + location: m.location, + body: transformer.transform_body(m.body)?, + else_clauses: m + .else_clauses + .into_iter() + .map(|c| transformer.transform_clause(c)) + .collect::, _>>()?, + })), + Expr::MaybeMatch(m) => Ok(Expr::MaybeMatch(MaybeMatch { + location: m.location, + pat: transformer.transform_pat(m.pat)?, + arg: Box::new(transformer.transform_expr(*m.arg)?), + })), + } +} + +pub fn walk_pat_binary_elem>( + transformer: &mut V, + elem: PatBinaryElem, +) -> Result { + let pat = transformer.transform_pat(elem.pat)?; + let size = elem + .size + .map_or(Ok(None), |s| transformer.transform_expr(s).map(Some))?; + Ok(PatBinaryElem { + pat, + size, + location: elem.location, + specifier: elem.specifier, + }) +} + +pub fn walk_pat>(transformer: &mut V, p: Pat) -> Result { + match p { + Pat::PatWild(p) => Ok(Pat::PatWild(p)), + Pat::PatMatch(m) => Ok(Pat::PatMatch(PatMatch { + pat: Box::new(transformer.transform_pat(*m.pat)?), + arg: Box::new(transformer.transform_pat(*m.arg)?), + location: m.location, + })), + Pat::PatTuple(t) => Ok(Pat::PatTuple(PatTuple { + location: t.location, + elems: t + .elems + .into_iter() + .map(|p| transformer.transform_pat(p)) + .collect::, _>>()?, + })), + Pat::PatString(s) => Ok(Pat::PatString(s)), + Pat::PatNil(n) => Ok(Pat::PatNil(n)), + Pat::PatCons(c) => Ok(Pat::PatCons(PatCons { + location: c.location, + h: Box::new(transformer.transform_pat(*c.h)?), + t: Box::new(transformer.transform_pat(*c.t)?), + })), + Pat::PatInt(i) => Ok(Pat::PatInt(i)), + Pat::PatNumber(n) => Ok(Pat::PatNumber(n)), + Pat::PatAtom(a) => Ok(Pat::PatAtom(a)), + Pat::PatVar(v) => Ok(Pat::PatVar(v)), + Pat::PatRecord(r) => Ok(Pat::PatRecord(PatRecord { + location: r.location, + rec_name: r.rec_name, + fields: r + .fields + .into_iter() + .map(|f| { + transformer + .transform_pat(f.pat) + .map(|pat| PatRecordFieldNamed { name: f.name, pat }) + }) + .collect::, _>>()?, + gen: r.gen.map_or(Ok(None), |g| { + transformer.transform_pat(*g).map(|pat| Some(Box::new(pat))) + })?, + })), + Pat::PatRecordIndex(r) => Ok(Pat::PatRecordIndex(r)), + Pat::PatUnOp(o) => Ok(Pat::PatUnOp(PatUnOp { + location: o.location, + op: o.op, + arg: Box::new(transformer.transform_pat(*o.arg)?), + })), + Pat::PatBinOp(o) => Ok(Pat::PatBinOp(PatBinOp { + location: o.location, + op: o.op, + arg_1: Box::new(transformer.transform_pat(*o.arg_1)?), + arg_2: Box::new(transformer.transform_pat(*o.arg_2)?), + })), + Pat::PatBinary(b) => Ok(Pat::PatBinary(PatBinary { + location: b.location, + elems: b + .elems + .into_iter() + .map(|e| transformer.transform_pat_binary_elem(e)) + .collect::, _>>()?, + })), + Pat::PatMap(m) => Ok(Pat::PatMap(PatMap { + location: m.location, + kvs: m + .kvs + .into_iter() + .map(|(k, v)| { + transformer.transform_test(k).and_then(|k_trans| { + transformer + .transform_pat(v) + .and_then(|v_trans| Ok((k_trans, v_trans))) + }) + }) + .collect::, _>>()?, + })), + } +} + +pub fn walk_guard>(transformer: &mut V, g: Guard) -> Result { + Ok(Guard { + tests: g + .tests + .into_iter() + .map(|t| transformer.transform_test(t)) + .collect::, _>>()?, + }) +} + +pub fn walk_test_record_field>( + transformer: &mut V, + f: TestRecordField, +) -> Result { + match f { + TestRecordField::TestRecordFieldNamed(f) => Ok(TestRecordField::TestRecordFieldNamed( + TestRecordFieldNamed { + value: transformer.transform_test(f.value)?, + name: f.name, + }, + )), + TestRecordField::TestRecordFieldGen(f) => { + Ok(TestRecordField::TestRecordFieldGen(TestRecordFieldGen { + value: transformer.transform_test(f.value)?, + })) + } + } +} + +pub fn walk_test>(transformer: &mut V, t: Test) -> Result { + match t { + Test::TestVar(v) => Ok(Test::TestVar(v)), + Test::TestAtom(a) => Ok(Test::TestAtom(a)), + Test::TestNumber(n) => Ok(Test::TestNumber(n)), + Test::TestTuple(t) => Ok(Test::TestTuple(TestTuple { + location: t.location, + elems: t + .elems + .into_iter() + .map(|t| transformer.transform_test(t)) + .collect::, _>>()?, + })), + Test::TestString(s) => Ok(Test::TestString(s)), + Test::TestNil(n) => Ok(Test::TestNil(n)), + Test::TestCons(c) => Ok(Test::TestCons(TestCons { + location: c.location, + h: Box::new(transformer.transform_test(*c.h)?), + t: Box::new(transformer.transform_test(*c.t)?), + })), + Test::TestCall(c) => Ok(Test::TestCall(TestCall { + location: c.location, + id: c.id, + args: c + .args + .into_iter() + .map(|a| transformer.transform_test(a)) + .collect::, _>>()?, + })), + Test::TestRecordCreate(r) => Ok(Test::TestRecordCreate(TestRecordCreate { + location: r.location, + rec_name: r.rec_name, + fields: r + .fields + .into_iter() + .map(|f| transformer.transform_test_record_field(f)) + .collect::, _>>()?, + })), + Test::TestRecordSelect(r) => Ok(Test::TestRecordSelect(TestRecordSelect { + location: r.location, + rec: Box::new(transformer.transform_test(*r.rec)?), + rec_name: r.rec_name, + field_name: r.field_name, + })), + Test::TestRecordIndex(r) => Ok(Test::TestRecordIndex(r)), + Test::TestMapCreate(m) => Ok(Test::TestMapCreate(TestMapCreate { + location: m.location, + kvs: m + .kvs + .into_iter() + .map(|(k, v)| { + transformer.transform_test(k).and_then(|k_trans| { + transformer + .transform_test(v) + .and_then(|v_trans| Ok((k_trans, v_trans))) + }) + }) + .collect::, _>>()?, + })), + Test::TestMapUpdate(m) => Ok(Test::TestMapUpdate(TestMapUpdate { + location: m.location, + map: Box::new(transformer.transform_test(*m.map)?), + kvs: m + .kvs + .into_iter() + .map(|(k, v)| { + transformer.transform_test(k).and_then(|k_trans| { + transformer + .transform_test(v) + .and_then(|v_trans| Ok((k_trans, v_trans))) + }) + }) + .collect::, _>>()?, + })), + Test::TestUnOp(o) => Ok(Test::TestUnOp(TestUnOp { + location: o.location, + op: o.op, + arg: Box::new(transformer.transform_test(*o.arg)?), + })), + Test::TestBinOp(o) => Ok(Test::TestBinOp(TestBinOp { + location: o.location, + op: o.op, + arg_1: Box::new(transformer.transform_test(*o.arg_1)?), + arg_2: Box::new(transformer.transform_test(*o.arg_2)?), + })), + Test::TestBinaryLit(b) => Ok(Test::TestBinaryLit(b)), + } +} + +pub fn walk_form>( + transformer: &mut V, + form: ExternalForm, +) -> Result { + match form { + ExternalForm::Module(m) => Ok(ExternalForm::Module(m)), + ExternalForm::CompileExportAll(c) => Ok(ExternalForm::CompileExportAll(c)), + ExternalForm::Export(e) => Ok(ExternalForm::Export(e)), + ExternalForm::Import(i) => Ok(ExternalForm::Import(i)), + ExternalForm::ExportType(e) => Ok(ExternalForm::ExportType(e)), + ExternalForm::FunDecl(decl) => Ok(ExternalForm::FunDecl(FunDecl { + location: decl.location, + id: decl.id, + clauses: decl + .clauses + .into_iter() + .map(|c| transformer.transform_clause(c)) + .collect::, _>>()?, + })), + ExternalForm::File(f) => Ok(ExternalForm::File(f)), + ExternalForm::ElpMetadata(m) => Ok(ExternalForm::ElpMetadata(m)), + ExternalForm::Behaviour(b) => Ok(ExternalForm::Behaviour(b)), + ExternalForm::EqwalizerNowarnFunction(e) => Ok(ExternalForm::EqwalizerNowarnFunction(e)), + ExternalForm::EqwalizerUnlimitedRefinement(e) => { + Ok(ExternalForm::EqwalizerUnlimitedRefinement(e)) + } + ExternalForm::TypingAttribute(t) => Ok(ExternalForm::TypingAttribute(t)), + ExternalForm::ExternalTypeDecl(decl) => Ok(ExternalForm::ExternalTypeDecl(decl)), + ExternalForm::ExternalOpaqueDecl(decl) => Ok(ExternalForm::ExternalOpaqueDecl(decl)), + ExternalForm::ExternalFunSpec(spec) => Ok(ExternalForm::ExternalFunSpec(spec)), + ExternalForm::ExternalCallback(cb) => Ok(ExternalForm::ExternalCallback(cb)), + ExternalForm::ExternalOptionalCallbacks(cb) => { + Ok(ExternalForm::ExternalOptionalCallbacks(cb)) + } + ExternalForm::ExternalRecDecl(decl) => Ok(ExternalForm::ExternalRecDecl(ExternalRecDecl { + location: decl.location, + name: decl.name, + file: decl.file, + fields: decl + .fields + .into_iter() + .map(|f| { + f.default_value + .map_or(Ok(None), |val| transformer.transform_expr(val).map(Some)) + .map(|default_value| ExternalRecField { default_value, ..f }) + }) + .collect::, _>>()?, + })), + } +}