给出以下示例
"~a{b=1}&(a{b=1}|a{b=1})|a{b=1}|a{b=1}"
我使用Instaparse编写了以下解析器
((insta/parser
"
S = (group | exp)+
group = '~'? <'('> exp+ <')'> op?
exp = '~'? path <'='> (v | r) <'}'> op?
path = (p <'{'>)* p
op = '|' | '&'
<p> = #'[a-z]'
v = #'[a-z0-9]'
r = <'\\''> #'[^\\']*' <'\\''>
")
"~a{b=1}&(a{b=1}|a{b=1})|a{b=1}|a{b=1}")
运行上面的输出以下输出
[:S
[:exp "~" [:path "a" "b"] [:v "1"] [:op "&"]]
[:group
[:exp [:path "a" "b"] [:v "1"] [:op "|"]]
[:exp [:path "a" "b"] [:v "1"] [:op "|"]]
[:exp [:path "a" "b"] [:v "1"]]
[:op "|"]]
[:exp [:path "a" "b"] [:v "1"]]]
然而,从这个输出来看,我很难将转换写入 Clojure 表达式。为了进行更直接的转换,我需要更多类似的东西:
[:S
[:op "|"
[:op "&"
[:exp "~" [:path "a" "b"] [:v "1"]]
[:group
[:op "|"
[:exp [:path "a" "b"] [:v "1"]]
[:op "|"
[:exp [:path "a" "b"] [:v "1"]]
[:exp [:path "a" "b"] [:v "1"]]]]]]
[:exp [:path "a" "b"] [:v "1"]]]]
考虑到这种结构,将其转换为 Clojure 会容易得多。
您将如何编写一个通用解析器,将上述结构解析为 AST,然后使用简单的
insta/transfrom
将其转换为 Clojure 代码?
我会遵循 运算符优先级中的示例 解析器;这 会给你“单一”
and
/or
术语,但这应该很容易
在以下步骤中修剪/优化。
S = expr
<expr> = and
and = or ( <'&'> or ) *
or= primary ( <'|'> primary ) *
<primary> = ( group | not | term )
<group> = <'('> expr <')'>
not = <'~'> term
term = #'a\\{b=[0-9]\\}'
例如
((insta/parser
"
S = expr
<expr> = and
and = or ( <'&'> or ) *
or= primary ( <'|'> primary ) *
<primary> = ( group | not | term )
<group> = <'('> expr <')'>
not = <'~'> term
term = #'a\\{b=[0-9]\\}'
")
"~a{b=1}&(a{b=2}|a{b=3})|a{b=4}|a{b=5}")
; →
; [:S
; [:and
; [:or [:not [:term "a{b=1}"]]]
; [:or
; [:and [:or [:term "a{b=2}"] [:term "a{b=3}"]]]
; [:term "a{b=4}"]
; [:term "a{b=5}"]]]]