Defining and Importing Macro Packages
The definition of a Cangjie macro must be placed in the package declared as a macro package. The package constrained by the macro package allows only the macro definition to be visible to external systems.
Note:
Re-exported declarations are also visible to external systems. For details about package management and re-export, see Package Import.
// file define.cj
macro package define // Compile define.cjo with the macro attributes.
import std.ast.*
public func A() {} // Error, macro package does not allow external visible non-macro definitions. An error is reported.
public macro M(input: Tokens): Tokens { // macro M is visible to external systems.
return input
}
It should be specifically noted that in a macro package, symbols from both macro packages and non-macro packages can be re-exported. In a non-macro package, only symbols from non-macro packages can be re-exported.
See the following example.
-
Define the
M1macro in a macro package Amacro package A import std.ast.* public macro M1(input: Tokens): Tokens { return input }The compilation command is as follows.
cjc A.cj --compile-macro -
Define a public function
f1in a non-macro package B. Note that themacro packagesymbols cannot be re-exported in a non-macro package.package B // public import A.* // Error, it is not allowed to re-export a macro package in a package. public func f1(input: Int64): Int64 { return input }The following is the compilation command. Here we use the
--output-typeoption to compile the B package into the dynamic library. For details about the cjc compilation options, see cjc Compilation Options.cjc B.cj --output-type=dylib -o libB.so -
Define the
M2macro in macro package C, which relies on the content of packages A and B. It can be observed thatmacro packageand non-macro packagesymbols can be re-exported in themacro package.macro package C public import A.* // correct: macro package is allowed to re-export in a macro package. public import B.* // correct: non-macro package is also allowed to re-export in a macro package. import std.ast.* public macro M2(input: Tokens): Tokens { return @M1(input) + Token(TokenKind.NL) + quote(f1(1)) }The following shows the compilation command. Note that the dynamic library of the B package needs to be explicitly linked.
cjc C.cj --compile-macro -L. -lB -
Use the
M2macro inmain.cjimport C.* main() { @M2(let a = 1) }The compilation command is as follows.
cjc main.cj -o main -L. -lBThe result obtained by expanding the
M2macro inmain.cjis:import C.* main() { let a = 1 f1(1) }
It is obvious that the symbol f1 from package B appears in main.cj. The macro compiler can re-export the symbols in the B package in the C package, enabling users to correctly compile the code after macro expansion by importing only the macro package. If only import C.M2 is used to import macro symbols in main.cj, the error message undeclared identifier 'f1' is reported.