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
M1
macro 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
f1
in a non-macro package B. Note that themacro package
symbols 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-type
option 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
M2
macro in macro package C, which relies on the content of packages A and B. It can be observed thatmacro package
and non-macro package
symbols 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
M2
macro inmain.cj
import C.* main() { @M2(let a = 1) }
The compilation command is as follows.
cjc main.cj -o main -L. -lB
The result obtained by expanding the
M2
macro inmain.cj
is: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.