宏和字符串插值(Julia)

时间:2016-09-14 15:02:21

标签: metaprogramming julia string-interpolation

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

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"

我必须对我的宏声明做出哪些更改?

1 个答案:

答案 0 :(得分:8)

最好在编译时解析字符串,而不是委托给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

这不适用于转义的$个字符,但可以通过一些小的更改来解决,以处理\。我在这篇文章的底部附上了一个基本的例子。

我这样编写它是因为字符串宏通常不用于模拟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

朱莉娅解析

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

此方法会转义字符串(但请注意escape_string }并将其传递回Julia的解析器进行解析。转义字符串是必要的,以确保$"不会影响字符串的解析。结果表达式是\表达式,可以为宏观目的检查和分解。