3

Clojure でデコードする必要がある PHP によってシリアル化された値があります。このライブラリを使用して逆シリアル化しています。EBNF/ABNF 表記法を使用して文法を定義するInstaparseを使用します。参考までに、完全な定義は次のとおりです。

<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> <':\\\"'> #'([^\"]|\\.)*' <'\\\";'>

このライブラリにバグが見つかりました - 文字を含むシリアル化された文字列を処理できません"

php > echo serialize('{"key":"value"}');
s:15:"{"key":"value"}";

ライブラリを使用して逆シリアル化され、その秒が見つかったときに爆発します":

> (deserialize-php "s:15:\"{\"key\":\"value\"}\";")
[:index 7]

問題は、文法定義の次の行にあります。

string = <'s:'> <size> <':\\\"'> #'([^\"]|\\.)*' <'\\\";'>

文字列定義で文字が除外されていることがわかります"。それは正しくありませんが、その文字列には任意の文字を含めることができます。サイズが重要です。私は BNF の専門家ではないので、ここでの選択肢を理解しようとしています。

つかむ正しい文字数としてサイズを使用することは可能ですか? それが不可能な場合、文法定義を微調整して正しい解析を有効にする方法を誰かが見ていますか?

4

3 に答える 3

2

私が理解している限り、この文法はコンテキストフリーではないため、EBNFパーサーだけでそれを書くことはできないと確信しています。

于 2013-08-29T20:26:48.843 に答える
2

Arthur Ulfeldt が述べたように、この文法は、コード化された文字列のために文脈自由ではありません。それにもかかわらず、A/EBNF を使用していないだけで、解析するのは簡単です。たとえば、代わりにParse-EZを使用します。

便利なマクロ:

(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]
于 2013-08-30T21:14:20.287 に答える
1

文脈自由文法で最も近いのは、予想されるすべての長さのプレフィックスを明示的に列挙することだと思います-ABNFの行に沿った何か:

 string = 's:0:"";' /
          's:1:"' CHAR '";' /
          's:2:"' 2CHAR '";' /
          's:3:"' 3CHAR '";' / ...

文字列の長さが制限されている場合、これはかなりうまく機能する可能性がありますが、任意のサイズの文字列では明らかに機能しません。

それ以外の場合、任意の長さの文字列を正しく処理するには、手動で解析するのが最善の方法です。幸いなことに、このサイズの文法では、それほど難しい作業ではないはずです。

于 2013-08-30T14:30:02.253 に答える