是否可以定义在编译结束时执行的指令? (Crystal 语言)
Is it possible to define instructions to be executed at the end of compilation? (Crystal lang)
假设一个 Crystal 项目正在使用不同的分片。每个分片都希望在整个项目编译结束时进行清理。是否可以使用宏?
例如这样的事情:
{% at_end %}
{% system("rm 'tmp files'") %}
{% end %}
如果需要在多个宏之间共享数据,可以使用 Hash
或 Array
,并通过 Constant
访问它们,关联的 AST 节点可从宏访问。
这是一个演示此的示例(在 https://carc.in/#/r/372k 直播):
STORAGE = [] of _
macro add(node)
{% STORAGE << node %}
end
macro list
{% for elem in STORAGE %}
{% puts "Elem: #{elem}" %}
{% end %}
end
add 1
add 2
add "hello"
add :world
add({a: 1, b: 2})
add 3 + 4
add a_call(arg1, 2)
list
这里的重要部分是:
STORAGE = [] of _
在这里我声明了一个特定类型的数组(在这种情况下这是不相关的,因为数组只能通过宏访问,你不能在正常情况下使用这种类型(_
)代码)。宏系统只需要知道它是一个数组即可。
macro add(node)
{% STORAGE << node %}
end
然后我创建一个能够改变数组 AST 节点(在宏系统中是 ArrayLiteral 类型)的宏。
请注意,它允许您存储任何类型的 AST 节点,从 NumberLiteral
或 SymbolLiteral
等简单节点到 Call
、Def
甚至 ClassDef
.
macro list
{% for elem in STORAGE %}
{% puts "Elem: #{elem}" %}
{% end %}
end
最后我创建了一个宏,它将简单地打印(在编译时)STORAGE
数组的内容。
同样可以用 HashLiteral
:
STORAGE = {} of _ => _
和上面解释的一样,你可以使用任何类型的 AST 节点作为键或值。
注意你在宏系统中使用的 Constants
也可以被普通代码看到,并且可能会出现名称冲突(如果用户也想使用常量 STORAGE
怎么办为他自己的代码?)。
为了尽量减少这种可能性(如果不需要),我建议您将常量放在一个特殊的模块中,例如 MyShard::MacroStorage::SomeInterestingNodes
或使用一些复杂的命名模式,例如 M____ACRO_Storage____01234
(<-这不太可能用户使用 ;) )
假设一个 Crystal 项目正在使用不同的分片。每个分片都希望在整个项目编译结束时进行清理。是否可以使用宏?
例如这样的事情:
{% at_end %}
{% system("rm 'tmp files'") %}
{% end %}
如果需要在多个宏之间共享数据,可以使用 Hash
或 Array
,并通过 Constant
访问它们,关联的 AST 节点可从宏访问。
这是一个演示此的示例(在 https://carc.in/#/r/372k 直播):
STORAGE = [] of _
macro add(node)
{% STORAGE << node %}
end
macro list
{% for elem in STORAGE %}
{% puts "Elem: #{elem}" %}
{% end %}
end
add 1
add 2
add "hello"
add :world
add({a: 1, b: 2})
add 3 + 4
add a_call(arg1, 2)
list
这里的重要部分是:
STORAGE = [] of _
在这里我声明了一个特定类型的数组(在这种情况下这是不相关的,因为数组只能通过宏访问,你不能在正常情况下使用这种类型(_
)代码)。宏系统只需要知道它是一个数组即可。
macro add(node)
{% STORAGE << node %}
end
然后我创建一个能够改变数组 AST 节点(在宏系统中是 ArrayLiteral 类型)的宏。
请注意,它允许您存储任何类型的 AST 节点,从 NumberLiteral
或 SymbolLiteral
等简单节点到 Call
、Def
甚至 ClassDef
.
macro list
{% for elem in STORAGE %}
{% puts "Elem: #{elem}" %}
{% end %}
end
最后我创建了一个宏,它将简单地打印(在编译时)STORAGE
数组的内容。
同样可以用 HashLiteral
:
STORAGE = {} of _ => _
和上面解释的一样,你可以使用任何类型的 AST 节点作为键或值。
注意你在宏系统中使用的 Constants
也可以被普通代码看到,并且可能会出现名称冲突(如果用户也想使用常量 STORAGE
怎么办为他自己的代码?)。
为了尽量减少这种可能性(如果不需要),我建议您将常量放在一个特殊的模块中,例如 MyShard::MacroStorage::SomeInterestingNodes
或使用一些复杂的命名模式,例如 M____ACRO_Storage____01234
(<-这不太可能用户使用 ;) )