使用Instaparse使用BNF解析序列化PHP数据
我有一个PHP序列化的值,需要在Clojure中解码。我用这个来反序列化它;它使用EBNF/ABNF符号来定义语法。以下是完整的定义供参考:使用Instaparse使用BNF解析序列化PHP数据,php,parsing,clojure,bnf,ebnf,Php,Parsing,Clojure,Bnf,Ebnf,我有一个PHP序列化的值,需要在Clojure中解码。我用这个来反序列化它;它使用EBNF/ABNF符号来定义语法。以下是完整的定义供参考: <S> = expr <expr> = (string | integer | double | boolean | null | array)+ <digit> = #'[0-9]' <number> = negative* (decimal-num | integer-num) <negative&
<S> = expr
<expr> = (string | integer | double | boolean | null | array)+
<digit> = #'[0-9]'
<number> = negative* (decimal-num | integer-num)
<negative> = '-'
<integer-num> = digit+
<decimal-num> = integer-num '.' integer-num
<zero-or-one> = '0'|'1'
size = digit+
key = (string | integer)
<val> = expr
array = <'a:'> <size> <':{'> (key val)+ <'}'> <';'>?
boolean = <'b:'> zero-or-one <';'>
null = <'N;'>
integer = <'i:'> number <';'>
double = <'d:'> number <';'>
string = <'s:'> <size> <':\\\"'> #'([^\"]|\\.)*' <'\\\";'>
string = <'s:'> <size> <':\\\"'> #'([^\"]|\\.)*' <'\\\";'>
使用库进行反序列化,当它找到第二个“
”时,它会爆炸:
语法定义的这一行存在问题:
<S> = expr
<expr> = (string | integer | double | boolean | null | array)+
<digit> = #'[0-9]'
<number> = negative* (decimal-num | integer-num)
<negative> = '-'
<integer-num> = digit+
<decimal-num> = integer-num '.' integer-num
<zero-or-one> = '0'|'1'
size = digit+
key = (string | integer)
<val> = expr
array = <'a:'> <size> <':{'> (key val)+ <'}'> <';'>?
boolean = <'b:'> zero-or-one <';'>
null = <'N;'>
integer = <'i:'> number <';'>
double = <'d:'> number <';'>
string = <'s:'> <size> <':\\\"'> #'([^\"]|\\.)*' <'\\\";'>
string = <'s:'> <size> <':\\\"'> #'([^\"]|\\.)*' <'\\\";'>
string=#'([^\“]|\\)*'
您会注意到字符串定义不包括“
字符。但这不正确,我可以在该字符串中包含任何字符;大小才是关键。我不是BNF专家,所以我正在尝试找出我的选项
是否可以使用大小作为要抓取的正确字符数?如果不可能,是否有人看到我可以调整语法定义以启用正确解析的方法?我合理地确定,您不能仅使用EBNF解析器编写该语法,因为据我所知,该语法不是上下文无关的。我认为clos在上下文无关语法中,您可能遇到的最大困难是显式枚举所有预期长度前缀——类似于ABNF:
string = 's:0:"";' /
's:1:"' CHAR '";' /
's:2:"' 2CHAR '";' /
's:3:"' 3CHAR '";' / ...
如果字符串的长度是有界的,那么这可能会很好地工作,但是对于任意大小的字符串显然不起作用
否则,要正确处理任意长度的字符串,最好的选择可能是手动解析。幸运的是,对于这种大小的语法来说,这应该不是一项太难的任务。As,这种语法是。尽管如此,它是一种简单的解析方法,只是不需要使用/EBNF。例如,使用:
方便的宏:
(defmacro tagged-sphp-expr [tag parser]
`(fn [] (between #(string ~(str tag ":")) #(~parser) #(string ";"))))
其余的:
(def sphp-integer (tagged-sphp-expr "i" integer))
(def sphp-decimal (tagged-sphp-expr "d" decimal))
(defn sphp-boolean []
(= \1 ((tagged-sphp-expr "b" #(chr-in "01")))))
(defn sphp-null [] (string "N;") :null)
(defn sphp-string []
(let [tag (string "s:")
size (integer)
open (no-trim #(string ":\""))
contents (read-n size)
close (string "\";")]
contents))
(declare sphp-array)
(defn sphp-expr []
(any #(sphp-integer) #(sphp-decimal) #(sphp-boolean) #(sphp-null) #(sphp-string) #(sphp-array)))
(defn sphp-key []
(any #(sphp-string) #(sphp-integer)))
(defn sphp-kv-pair []
(apply array-map (series #(sphp-key) #(sphp-expr))))
(defn sphp-array []
(let [size (between #(string "a:") #(integer) #(string ":{"))
contents (times size sphp-kv-pair)]
(chr \})
(attempt #(chr \;))
contents))
测试:
(def test-str "i:1;d:2;s:16:\"{\"key\": \"value\"}\";a:2:{s:3:\"php\";s:3:\"sux\";s:3:\"clj\";s:3:\"rox\";};b:1;")
(println test-str)
;=> i:1;d:2;s:16:"{"key": "value"}";a:2:{s:3:"php";s:3:"sux";s:3:"clj";s:3:"rox";};b:1;
(parse #(multi* sphp-expr) test-str)
;=> [1 2.0 "{\"key\": \"value\"}" [{"php" "sux"} {"clj" "rox"}] true]
我不是BNF方面的专家,但我还没有看到使用表达式中的另一个值定义发生次数的定义。您可能需要一些反向引用,如
string=ANYCHAR*size
-如果不可能,则无法使用BNF解码格式。如果需要,您可能应该使用更通用的格式,如JSON可以。我希望我可以使用JSON。这个应用程序需要在转换期间与PHP应用程序并行工作并共享会话,并且PHP的会话数据是序列化存储的。其他选项,如大修PHP应用程序的会话管理或将解码委托给PHP,都有其自身的规模。好吧,PHP端应该很容易更改。Y您可以定义自定义会话处理程序函数,并且可以以JSON格式存储数据。但您必须确保应用了正确的锁定,否则会出现问题。这也适用于需要锁定文件的替换代码,并使用与PHP兼容的机制来实现。说明了为什么无法使用BNF进行锁定并且提供了一个可行的解决方案;非常感谢!这应该有一天会被清理到一个合适的库中;而不是“N:”;“。此外,当我修复此问题并包含一个null时,我得到了一个解析错误。似乎不允许sphp null返回nil。如果我从nil更改为例如:null,它会工作。parse EZ函数可以将某个东西解析为nil吗?@terjesb感谢您的更正。根据您的评论,我怀疑parse EZ使用了nil
作为失败,但我没有这样做。”查看它。返回:null是有意义的,如果需要,可以将结果替换为nil
。