import crack.compiler CrackContext; void myann(CrackContext ctx) { ctx.inject('import crack.io cout; cout `hello world`;'.buffer); }And module B:
@import A myann; @myann
The code injected from myann()would be injected into the token stream of module B.
There are a number of things that you can do from an annotation, you can read tokens, put them back, set parser callbacks, and create other annotations. You can't yet do introspection (examine classes or functions or other compile-time objects) but that feature will be coming.
We currently use annotations to implement compiler-level macros:
import crack.exp.ann define; @define attr(type, name) { type name; type get_$$name() { return name; } void set_$$name(type newVal) { name = newVal; } }
class Foo {
@attr(String, bar)
}
To use a macro like this from another module, you need to export it:
import crack.exp.ann define, export, exporter; @exporter # import all of the stuff we need to define exporter functions @define attr(type, name) { type name; type get_$$name() { return name; } void set_$$name(type newVal) { name = newVal; } } @export attr
From another module we can import the macro as an annotation:
@import attrmod attr; class Foo { @attr(String, bar); }