Reimplement the uniplate-derive macro to allow for Biplates and
"multiple type traversals" as discussed in [Niklas Dewally, 2024‐03:
Implementing Uniplates and Biplates with Structure Preserving Trees].
(currently at
https://github.com/conjure-cp/conjure-oxide/wiki/2024%E2%80%9003-Implementing-Uniplates-and-Biplates-with-Structure-Preserving-Trees).
Implementation overview
=======================
The library syn supports the creation of custom AST nodes. Using the
Parse trait, one can implement parsers for these custom nodes by
combining existing Rust AST nodes together as well as parsing Rust
tokens by hand.
The macro code has two parts: parsing into a custom AST, and using this
AST for code-gen. This simplifies code-gen, as only the data needed for
Uniplate is encoded in the AST. This also allows for better error
reporting, as Most errors are now reported during parsing, allowing
errors to be associated with the specific tokens that caused them.
The biggest change in Uniplate's AST compared to Rust's is the
representation of types. Uniplate has three kinds of type: boxed pointer
types, plateable types, and unpalatable types.
Plateable types include the current type being derived, or other types
explicitly specified through the walk_through attribute in the macro
invocation.
Boxed pointer types represent smart pointers such as Box and Rc. These
use the .borrow() function instead of & to pass the inner value to the
Biplate instance. Generating instances for Box<A> and Rc<A> would mean
that values are constantly moved from stack to heap - this avoids this.
Implementations for standard library iterator types and primitives are
provided by Uniplate. These implementations are generated by declarative
macro, allowing easy extension to more collection types in the future.
Testing
=======
Add trybuild, a library for UI testing derive macros. The Rust tester
does not run at-all when test code does not compile, but we need to test
whether our macro gives a compile error when it should. trybuild also
checks that the error messages given by the compiler have not changed.
Two test ASTs are used for Uniplate and Biplates: a toy
statement-expression found in the original paper, and the current
Conjure Oxide AST. Property-based testing in paper.rs shows that
children() and with_children() work for random homogenous AST's,
including with boxes and iterators. As these two methods essentially act
as wrappers for the child tree and context function returned by the
uniplate() / biplate() methods, this test is sufficient to show that
automatically derived Uniplate implementations act as intended.
Signed-off-by: Niklas Dewally <niklas@dewally.com>