朱莉娅:将字符串传递给宏

时间:2018-09-18 08:04:10

标签: julia

假设我有一个定义为的宏:

macro foomacro(ex::Expr)
    dump(ex)
    ex
end

目前,我想将表达式作为已解析的字符串传递,以便可以传递通过字符串连接获得的相当复杂且视情况而定的表达式。

但是,尝试:

@foomacro 1+2+3

给出预期结果6而

@foomacro parse("1+2+3")

返回已解析的表达式:(1 + 2 + 3),而不是实际对其进行解析...

据我了解,这两个宏都应该接收相同的表达式,但事实并非如此。

如何使该MWE正常工作?

ps:我想出了此修复程序,但我感觉它很脏且“不正确”

macro foomacro(ex::Expr)
    if ex.head == :call
        #in this case the user is calling the macro via a parsed string
        dump(ex)
        return ex
    end
    dump(ex)
    ex
end

ps:如果与此相关,那么当前代码正在0.6.4上运行,并且如果可能的话,我宁愿不更新为1.0,因为这会将我的实际项目推迟很多...

1 个答案:

答案 0 :(得分:4)

您正在混合级别。为了清楚起见,我们来介绍一个中间函数:

function foomacro_impl(expr)
    dump(expr)
    expr
end

macro foomacro(expr)
    foomacro_impl(expr)
end

如果运行,将解析表达式@foomacro <someexpr>,将<someexpr>部分传递给foomacro_impl,并将结果视为表达式并插入而不是原始表达式。这意味着写@foomacro 1+2+3等同于写

let expr = :(1+2+3)
    dump(expr)
    expr
end

返回

Expr
  head: Symbol call
  args: Array{Any}((4,))
    1: Symbol +
    2: Int64 1
    3: Int64 2
    4: Int64 3
:(1 + 2 + 3)

一个Expr的值为6。

另一方面,在@foomacro Meta.parse("1+2+3")中,整个参数parse("1+2+3")用作expr

julia> let expr = :(Meta.parse("1+2+3"))
           dump(expr)
           expr
       end
Expr
  head: Symbol call
  args: Array{Any}((2,))
    1: Expr
      head: Symbol .
      args: Array{Any}((2,))
        1: Symbol Meta
        2: QuoteNode
          value: Symbol parse
    2: String "1+2+3"
:(Meta.parse("1+2+3"))

因此,宏调用的结果是表达式Meta.parse("1+2+3"),由于它是对:(1 + 2 + 3)的调用,因此它的结果为另一个表达式parse。因此,这两种形式 not 不能接收相同的表达式!

但是有一些方法可以手动解析表达式并将其传递给宏:

  1. 您可以像我一样做,并使用单独的“宏实现功能”。然后,@foomacro bla返回的表达式等于foomacro_impl(Meta.parse(bla))。 (BTW这种方法对于测试非常有用,我大多数时候都建议这样做。)
  2. 您可以使用宏@eval构造一个表达式,将其拼接起来并立即对其求值:

    julia> @eval @foomacro $(Meta.parse("1+2+3"))
    Expr
      head: Symbol call
      args: Array{Any}((4,))
    1: Symbol +
        2: Int64 1
        3: Int64 2
        4: Int64 3
    6
    

    (或类似地,使用eval并手动构造Expr值。)