宏和字符串插值 (Julia)

Macros and string interpolation (Julia)

假设我制作了这个简单的字符串宏

macro e_str(s)
    return string("I touched this: ",s)
end

如果我将它应用于带插值的字符串,我 获得:

julia> e"foobar $(log(2))"
"I touched this: foobar $(log(2))"

而我想获得:

julia> e"foobar $(log(2))"
"I touched this: foobar 0.6931471805599453"

我必须对宏声明进行哪些更改?

在编译时解析字符串比委托给 Julia 更好。基本上,将字符串放入 IOBuffer 中,扫描字符串中的 $ 符号,并在它们出现时使用 parse 函数。

macro e_str(s)
    components = []
    buf = IOBuffer(s)
    while !eof(buf)
        push!(components, rstrip(readuntil(buf, '$'), '$'))
        if !eof(buf)
            push!(components, parse(buf; greedy=false))
        end
    end
    quote
        string($(map(esc, components)...))
    end
end

这不适用于转义的 $ 字符,但可以通过一些小的更改来解决 \ 的问题。我在 post.

的底部包含了一个基本示例

我这样写是因为字符串宏通常不用于模拟 Julia 字符串——具有常规字符串文字的常规宏更适合该目的。所以自己编写解析并没有那么糟糕,尤其是因为它允许自定义扩展。如果您真的希望解析与 Julia 解析它的方式相同,您可以转义字符串然后重新解析它,正如@MattB 建议的那样:

macro e_str(s)
    esc(parse("\"$(escape_string(s))\""))
end

生成的表达式是一个 :string 表达式,您可以将其转储和检查,然后以通常的方式进行分析。



字符串宏没有内置插值功能。但是,可以手动实现此功能。请注意,如果不转义与周围字符串宏具有相同定界符的字符串文字,则无法嵌入;也就是说,虽然 """ $("x") """ 是可能的,但 " $("x") " 是不可能的。相反,这必须转义为 " $(\"x\") ".

手动实现插值有两种方法:手动实现解析,或让 Julia 进行解析。第一种方法更灵活,但第二种方法更容易。

手动解析

macro interp_str(s)
    components = []
    buf = IOBuffer(s)
    while !eof(buf)
        push!(components, rstrip(readuntil(buf, '$'), '$'))
        if !eof(buf)
            push!(components, parse(buf; greedy=false))
        end
    end
    quote
        string($(map(esc, components)...))
    end
end

Julia 解析

macro e_str(s)
    esc(parse("\"$(escape_string(s))\""))
end

此方法对字符串进行转义(但请注意 escape_string 不会 $ 符号进行转义)并将其传回 Julia 的解析器进行解析。转义字符串是必要的,以确保 "\ 不影响字符串的解析。生成的表达式是一个 :string 表达式,可以对其进行检查和分解以用于宏目的。