【问题标题】:How can I generate code from file at compile time using a macro?如何在编译时使用宏从文件生成代码?
【发布时间】:2021-11-17 21:31:17
【问题描述】:

我有一个如下所示的 CSV 文件:

CountryCode,CountryName
AD,Andorra
AE,United Arab Emirates
AF,Afghanistan
AG,Antigua and Barbuda
// -- snip -- //

还有一个看起来像这样的类:

module OpenData
  class Country
    def initialize(@code : String, @name : String)
    end
  end
end

我希望在编译时自动加载模块中的类变量,如下所示:

module OpenData
  @@countries : Array(Country) = {{ run "./sources/country_codes.cr" }}
end

我尝试通过以下代码使用上面的“运行”宏:

require "csv"
require "./country"

content = File.read "#{__DIR__}/country-codes.csv"
result = [] of OpenData::Country
CSV.new(content, headers: true).each do |row|
  result.push OpenData::Country.new(row["CountryCode"], row["CountryName"])
end
result

但这会导致

@@countries : Array(Country) = {{ run "./sources/country_codes.cr" }}
                                    ^
Error: class variable '@@countries' of OpenData must be Array(OpenData::Country), not Nil

由于各种原因,我的所有其他尝试都以某种方式失败了,例如无法在宏中调用.new 或类似的东西。这是我在 Elixir 和其他支持宏的语言中经常做的事情,而且我怀疑 Crystal 也可以实现......我也会采取任何其他方式在编译时完成任务!

基本上还有几个文件我想用这种方式处理,它们更长/更复杂......提前致谢!

编辑:

发现问题。看来我必须从“运行”宏返回一个包含实际水晶代码的字符串。于是,“run”文件中的代码就变成了:

require "csv"

content = File.read "#{__DIR__}/country-codes.csv"
lines = [] of String
CSV.new(content, headers: true).each do |row|
  lines << "Country.new(\"#{row["CountryCode"]}\", \"#{row["CountryName"]}\")"
end

puts "[#{lines.join(", ")}]"

一切正常!

【问题讨论】:

  • 您可以在 SO 中为您自己的问题添加答案 :)

标签: crystal-lang


【解决方案1】:

您已经找到了答案,但为了完整起见,以下是文档,来自:https://crystal-lang.org/api/1.2.2/Crystal/Macros.html#run%28filename%2C%2Aargs%29%3AMacroId-instance-method

编译并执行一个 Crystal 程序并返回它的输出 作为MacroId

文件名 表示的文件必须是有效的 Crystal 程序。 此宏调用将 args 以常规方式传递给程序 程序参数。程序必须输出一个有效的 Crystal 表达式。 此输出是此宏调用的结果,为 MacroId

run 宏在可用宏方法的子集时很有用 不足以满足您的目的,您需要更强大的东西。 使用run,您可以在编译时读取文件,连接到互联网 或数据库。

一个简单的例子:

# read.cr
puts File.read(ARGV[0])
# main.cr
macro read_file_at_compile_time(filename)
  {{ run("./read", filename).stringify }}
end

puts read_file_at_compile_time("some_file.txt")

上面生成的程序将具有some_file.txt 的内容。 但是,该文件是在编译时读取的,在运行时不需要。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-23
    相关资源
    最近更新 更多