This project demonstrates how to create a compiler extension for the Onyx programming language. It includes an example of defining and using custom macros to extend the Onyx compiler's functionality.
The compiler extension is implemented in src/extension.onyx.
The project is organized as follows:
onyx-compiler-extension-example/
├── onyx-pkg.kdl # Package configuration file for the Onyx project
├── README.md # Documentation for the project
├── src/ # Source files for the extension and main program
│ ├── extension.onyx # Implementation of the compiler extension
│ └── main.onyx # Main program demonstrating the extension's usage
Contains the implementation of the compiler extension. Key features include:
- Macro Handling: Defines a macro named
define_functhat generates functions dynamically. - Message Handling: Processes messages from the compiler, including macro expansion requests.
Demonstrates the usage of the compiler extension. Key features include:
- Extension Declaration: Declares the extension and its supported macros (
double,define_func). - Macro Usage: Uses the
define_funcmacro to define multiple functions and thedoublemacro for arithmetic operations.
-
Initialization:
#load "core:onyx/compiler_extension" use onyx.compiler_extension {*} use core.conv use core {eprintf, tprintf, Result} main :: () { ext := ExtensionContext.make("My First Extension") // Shorthand way of handling a macro expansion request ext->handle_macro("define_func", handle_define_func) ext->start(message_handler) } -
Macro Handling:
- The
define_funcmacro generates functions dynamically based on the provided body. - Example implementation:
handle_define_func :: ( ext: &ExtensionContext, em: ExpansionInfo ) -> Result(str, ExpansionFailureReason) { em.body->strip_whitespace() code: dyn_str for line in em.body->split_iter("\n") { line->strip_whitespace() code->append(line) code->append(" :: () { println(\"Hello from ") code->append(line) code->append("\") }\n"); } return .{ Ok = code } }
- The
-
Message Handling:
- Processes messages from the compiler, such as macro expansion requests.
- Example:
message_handler :: (ext, msg) => { // Print debugging in compiler extensions requires you to print // to standard error, so you have to use 'eprintf' eprintf("Message: {p}\n", msg) switch msg { case .ExpandMacro as em { if em.macro_name == "double" { em.body->strip_whitespace() ext->send(.{ Expansion = .{ id = em.id, code = .{ Ok = tprintf("2 * ({})", em.body) } } }) break } ext->send(.{ Expansion = .{ id = em.id, code = .{ Err = .NotSupported } } }) } case _ {} } }
-
Extension Declaration:
Extension :: #compiler_extension "./extension.onyx" { double, define_func } -
Macro Usage:
- Defines multiple functions using the
define_funcmacro:Extension.define_func!{ foo bar qux dog } - Uses the
doublemacro for arithmetic:main :: () { dog() println(Extension.double!{ 12345 }) }
- Defines multiple functions using the
To run the project, execute the main.onyx file:
$ onyx run src/main.onyx