如何寫乙個json語法解析器

2021-07-26 18:43:29 字數 3958 閱讀 1425

最近正在補習編譯原理的相關理論基礎。於是琢磨著寫個簡單的語言解析器。

1. python程式設計(ply庫)

2. 正規表示式

1. json裡的字典key必須是字串型,value可以是任意型別;

2. json根基點必須是字典或者陣列;

3. json支援的值包括:

- 數字(整數或浮點數)

- 字串(在雙引號中)

- 邏輯值(true 或 false)

- 陣列(在方括號中)

- 物件(在花括號中)

- null

4. 分割符為逗號",".

上下文無關文法;

避免二義性,或者使用規則解決二義性衝突;

判斷二義性存在的方法:某一段輸入可以用兩棵以上語法樹解釋的,我們認為存在二義性;

優先順序,需要n+1個非終結符號;

下面先祭出自己寫的json的文法–bnf正規化。

root_block : lsbracket block_item_list rsbracket

| lbrace block_item_list rbrace

| constant

| normstring colon root_block

| normstring

| keywords

block_item_list : block_item_list comma root_block

| root_block

ply 是純粹由 python 實現的 lex 和 yacc(流行的編譯器構建工具)。ply 的設計目標是盡可能的沿襲傳統 lex 和 yacc 工具的工作方式,包括支援 lalr(1)分析法、提供豐富的輸入驗證、錯誤報告和診斷。因此,如果你曾經在其他程式語言下使用過 yacc,你應該能夠很容易的遷移到 ply 上。英譯

token

標記context free grammar

上下文無關文法

syntax directed translation

語法制導的翻譯

ambiguity

二義性terminals

終結符non-terminals

非終結符

documentation string

文件字串(python中的docstring)

shift-reduce

移進-歸約

empty productions

空產生式

panic mode recovery

panic恢復模式

第一步. 詞法解析(lex)

宣告tokens,所有用到的token;

# list of token names. this is always required

tokens = (

'lsbracket',

'rsbracket',

'lbrace',

'rbrace',

'colon',

'normstring',

'constant',

'variable',

'comma',

'keywords',

)

在賦值表示式或者函式中,使用正則扣取token,並返回這個值。值得注意的是換行或者注釋是不再tokens裡面的,而且沒有返回值。

t_ignore = ' \t\r'

t_lsbracket = r'\['

t_rsbracket = r'\]'

t_lbrace = r'\'

t_colon = r':'

t_comma = r','

keywords = [

r'null',

]keyword = '|'.join(keyword.replace(' ', '\s+') for keyword in keywords)

@lex.token(keyword)

deft_keywords

(t):

# todo: to support false,true

t.value = none

return t

deft_newline

(t):

r'\n+'

t.lexer.lineno +=len(t.value)

錯誤控制代碼,在解析過程中報錯時,會呼叫這個函式。放一些除錯資訊進去會幫助你快速定位問題。

```

def t_error(t):

raise exception('error {} at line {}'.format(t.value[0], t.lineno))

```

解析,扣tokens的方法有了,解析就是給定一段輸入,輸出tokens。

```

# test it out

data = '''

3 + 4 * 10

+ -20 *2

'''# give the lexer some input

lexer.input(data)

# tokenize

while true:

tok = lexer.token()

if not tok: break # no more input

print tok

```

第二步. 語法解析(yacc)

說下大致思路: 使用bnf正規化,構建ast樹。輸出是你想把輸入翻譯成的樣子,比如計算器,你想要的是結果。json我想要一段python版的json資料結構。

第三步. 匯入標頭檔案

看很多博文,給**片段的博主都沒有加標頭檔案,天曉得你用的是什麼庫。

這裡祭出完整的** –>傳送門。

1. 詞法解析時,被處理的token沒有返回值

輸入為:

,

,null,,,

,null, # 這裡是第11行,報錯的位置,可以看到問題出在','前面,,

,null,,,

,,,,

,,

null,,]

}, "a": }

報錯資訊如下:

syntax error

in input! parser state:

[0, 1, 6, 10, 1, 8, 11, 6, 10, 3, 9, 11]

lbrace normstring colon lbrace block_item_list comma normstring colon lsbracket block_item_list comma . lextoken(comma,',',11,310)

[2017-02-09

15:30:56,725] file_parser.py:113-error: before: ',' ,line 11 column 13

解析:

第二行的列表是詞法解析的狀態跳轉棧,即從第0個狀態跳到第11個狀態。然而,在第11個狀態中,因為異常無法跳到下一狀態。

第三行是token的處理序列。重點是看序列的最後乙個tokenlextoken, 最後乙個token代表現在能正確處理的最後乙個位置,而lextoken表示現在處理的是哪個字元,他們中間的位置就是有問題的部分。可以看到第11行逗號前面是null, null在我的邏輯中應該歸為keywords這類token,為什麼沒有出現呢?答案只有乙個,就是在lex扣取null時沒有返回值。

如何寫乙個Stack?

1.棧是陣列 2.先進後出 3.出棧 4.入棧 手寫乙個雙向鍊錶 棧 public class stackpopandpush public stackpopandpush int lens 返回元素個數 public intsize 返回陣列長度,容量,棧資料長 private intcapaci...

如何寫乙個鍊錶

有的時候,處於記憶體中的資料並不是連續的。那麼這時候,我們就需要在 資料結構中新增乙個屬性,這個屬性會記錄下面乙個資料的位址。有了這個位址之後,所有的資料就像一條鍊子一樣串起來了,那麼這個位址屬性就起到了穿線鏈結的作用。相比較普通的線性結構,鍊錶結構的優勢是什麼呢?我們可以總結一下 1 單個節點建立...

如何寫乙個Vue元件

寫的是以.vue結尾的單檔案元件的寫法,是基於webpack構建的專案。template 模板 js 邏輯 css 樣式 每個元件都有屬於自己的模板,js和樣式。如果將乙個頁面比喻成一間房子的話,元件就是房子裡的客廳 臥室 廚房 廁所。如果把廚房單獨拿出來的話,元件又可以是刀 油煙機.等等。就是說頁...