最好在编译时解析字符串而不是委托给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
Julia解析
macro e_str(s)
esc(parse("\"$(escape_string(s))\""))
end
这种方法对字符串进行转义(但请注意,escape_string
不会转义 $
符号),然后将其传回 Julia 的解析器进行解析。转义字符串是必要的,以确保 "
和 \
不会影响字符串的解析。生成的表达式是一个 :string
表达式,可以用于宏目的的检查和分解。
return string("我触碰了这个:",eval(parse("\""*s*"\"")))
。如果$
表达式使用变量,则需要小心获取评估的上下文。 - Dan Getzreturn :(string("I touched this: ", $(parse("\""*s*"\""))))
,但如果s
包含任何"
字面量,它将会出错。 - mbaumaneval
时会响起。也许做"\"\"\""*s*"\"\"\""
可以减少"
的风险(甚至可以更详细地将"
替换为\"
)。 - Dan Getz