解析程式碼的利器 Spp 語法描述語言

沙棗發表於2016-07-01

MySpp 是什麼?

這是一個處理文字,尤其是處理計算機程式碼的工具,它能根據描述的語法,將文字轉換成指定的資料結構。

它比 Antlr 更高效,也更靈活:

> myspp grammar text.file > ast.json

Myspp 不需要生成中間程式碼,直接生成資料結構。

Myspp 有什麼用

設計 Myspp 是為了描述計算機語言的語法,所以 Myspp 最適合解析程式碼。

當程式碼可以被解析成資料結構的時候,程式碼本身就成了資料。可以用各種演算法修改,計算,擴充套件,改變這個資料結構,然後再寫成程式碼的時候,我們就完成了一件讓人興奮的事情:

我們可以寫能寫程式碼的程式碼
演算法可以被儲存,交換

計算機語言有非常複雜的語法形式,所以說,能夠解析程式碼的工具,能解析大部分的文字。即使是自然語言,有許多的根據上下文而改變的語法,也能用 Myspp 來處理。

Myspp 已經做過什麼專案

這個專案本身就是用 Myspp 完成的,所有的演算法,都是用一種叫做 MyLang 的語言書寫,然後使用 Myspp 解析演算法,生成語法樹,再生成執行程式碼。

工具的安裝

從網站上下載一個可執行檔案 spp,根據不同的可執行檔案

https://www.gitee.com:str/myspp.git

> spp
This is Spp (String Prepare Parser) 
Copyright C 2018-2020 Songzhiquan

MySpp 包含兩種語言 Spp, MyLang.

Spp (String Prepare Parser) 字串前處理器

Spp 是一種用於描述文字的語法的語言,MySpp 會依據語法對文字進行解析,如果符合語法定義,就會輸出描述文字結構的 Json 資料格式。如果文字不符合語法,就會有一個匹配錯誤的提示。

匹配捕獲的可能資料格式

  1. Fail 匹配失敗
  2. Pass 匹配成功
  3. String 字串
  4. Atom 命名捕獲單元

    name + string | atom | atoms + at ["name", "value", "12"]

  5. Atoms 命名捕獲單元序列

Spp 的語法是一種上下文無關的語法,適合對計算機語言進行解析,生成語法樹。

Spp 的基本格式,這也是 Spp 的自我描述:

top = +[ :s _comment spec ] ; _comment = '#' ~ :v ; spec = @define [':' '='] @rules [ ';' $ ] ; @define = token ? token ; token = +[ '@' :a ] ; @rules = +[ :s @rule ] ; @rule = [ @order @group cclass not till str more many maybe token any eof ] ; @order = '[' +[ :s @rule ] ']' ; @group = '(' +[ :s @rule ] ')' ; str = [ ( "'" ~ "'") ( '"' ~ '"' ) ] ; cclass = ':' :a ; not = '!' @rule ; till = '~' @rule ; more = '+' @rule ; many = '*' @rule ; maybe = '?' @rule ; keyword = '<' token ':' token '>' any = '.' ; eof = '$' ;

Spp 語言描述的語法 ( grammar ) 由許多規則 ( specification ) 組成。

spec-one = rules-one ;
spec-two = rukes-two ;

規則包括規則名稱 (spec-name) 和規則描述 (rules):

規則名稱由一個可選的修飾符和規則名稱組成:

下面的組合是等價的:

a = :a ;
ntoken a = :a ;

_a = :a ;
rtoken _a = :a ;

@a = :a ;
gtoken @a = :a ;

Spp 語言的基本單元

Spp 語言的規則是由以下基本單元組成的:

rules 規則串

rule1 rule2 rule3

一個以上規則的串聯,逐一全部匹配才算匹配,規則之間忽略空白。

group 分組

(rule1 rule2 rule3)

和 rules 相同, 用於在 rules 改變規則的優先順序。

branch 分支

[case1 case2 case3]

一個以上的規則,按照先後順序,只要匹配之中的任何一個,就算匹配。

string 字串

"abc" 'abc'

token 規則名稱

token @token _token
  1. gtoken (group token) 以 @開始的 token

    @gtoken

如果匹配到的不是 atoms (atom 的序列), 就會直接返回匹配結果。

  1. rtoken (reject token)

如果匹配成功,不管匹配到的是什麼,都會丟棄匹配結果,返回匹配成功或失敗的標誌。常用來處理註釋。

迴圈匹配 rept match

  1. more 要至少匹配 1 次,儘可能多。

    +rule
    
  2. many 不匹配也算匹配,儘可能多

    *rule
    
  3. maybe 不匹配也算匹配,只能匹配一次

    ?rule
    

匹配否定 not

! rule

如果匹配成功,則返回失敗標誌。因為所有的規則都是非回溯的,所以 not 可以針對任何規則,包括 not 本身

對字元組的否定,就是正規表示式中常見的一種結構 [^...]

![ :a :b :c ]

any 任意字元 用點表示

  any = '.'

說是任意字元,其實它並不會匹配文字的末尾,只有 eof 可以匹配文字的末尾。除此之外,它會匹配任何文字內的字元。

eof 檔案結束標記 用美元符號表示

$

開始設計語法

> spp spp
>> Spp <top> Repl, type 'exit' to exit!
>>> 

這個專案定義了一種名字叫做 Spp 的語言,用來描述計算機語言的語法:

door  = +[ _s spec ] ;
_s    = [ +:s _comm ] ;
_comm = '#' ~ :v ;
spec  = name ':' rules ;

Spp 可以描述任何計算機語言,包括描述語法的語言,例如 Antl 的 g4, EBNF 等。

Spp 語言的基本單元

Spp 語言定義了一系列規則,用以描述文字。

rules 規則串

rule1 rule2 rule3

許多規則的串聯,逐一匹配才算匹配,規則之間忽略空白。

group 分組

(rule1 rule2 rule3)

和 rules 相同,不過 rule 之間不會自動忽略空白,也能改變規則的優先順序。

branch 分支

[ case1 case2 case3 ]

規則的分支,只要匹配之中的任何一個,就算匹配,沒有順序之分,因為可能會用並行演算法,同時對所有分支進行測試,最先返回的分支不一定是最前面的。

string 字串

"abc" 'abc '

cclass 字元類

:a   [ :l :u '_' ]
:b   ' '
:c   '`'
:d   '0' - '9'
:e   '\'
:h   [ ' ' '\t' ]
:l   'a' - 'z' 
:n   char \n
:r   char \r
:s   [ :b :n :r :t ]
:t   char \t
:u   'A' - 'Z'
:v   [ :n :r ]
:w   [ :a :d '-' ]
:x   [ :d 'a' - 'f' 'A' - 'F' ]

range 區間

標記一段字元的範圍是一種常見的字元規則定義方式。

digit = '0' - '9'
lower = 'a' - 'z'

token 規則變數

token @token _token Token

因著變數,讓語法描述能處理巢狀的和遞迴的結構,規則變數指向另外一個規則:

.. ntoken 命名token 字母開頭的 token

ntoken Ntoken

token 會對捕獲的內容進行命名,成為一種叫做 atom 的資料結構,它是用字串實現的。以為結構不能移植到許多動態語言上。

如果匹配成功,那麼就會返回:

[name match at] # at 是字串當前的位移

..gtoken (group token) 以 @開始的 token

@gtoken

如果匹配到的不是 atoms (atom 的陣列), 就會不命名直接返回。這種規則通常作為臨時規則,對其他規則的組合進行命名。但不會影響輸入的資料結構。

..rtoken (reject token)

如果匹配成功,不管匹配到的是什麼,都只會返回匹配成功的標誌。這種結構常用來處理註釋。

迴圈匹配 rept match

.. more 要至少匹配1次,儘可能多。

+ rule

.. many 不匹配也算匹配,儘可能多

* rule

.. maybe 不匹配也算匹配,只能匹配一次

匹配否定 not

! rule

如果匹配成功,則返回失敗標誌。因為所有的規則都是非回溯的,所以 not 可以針對任何規則,包括 not 本身

對字元組的否定,就是正規表示式中常見的一種結構 [^...]

![abc] == [^abc]

對字元類的否定: ! %w == %W

any 任意字元 用點表示

  any = '.'

說是任意字元,其實它並不會匹配文字的末尾,只有 eof 會匹配。除此之外,它會匹配所以文字內的字元。

eof 檔案結束標記 用美元符號表示

  $

eof 檔案結束標記,在匹配檔案的結尾。這樣就表示整個檔案都匹配完了。

相關文章