使用pegjs解析java程式碼

軟體開發隨心記發表於2020-10-20

使用pegjs解析java程式碼

pegjs是什麼

pegjs是peg文法的一種實現,peg文法是一種解析表示式文法,其具體的解析公式和常用的正規表示式很像,需要注意的是peg不允許解析存在二義性。

pegjs官網 https://pegjs.org/

pegjs的作用

在正則匹配不能實現或困難時可以選擇pegjs來處理解析,如sql語句的解析,在構造dsl時編寫自定義規則也非常方便。

pegjs的簡單應用

1.這裡以一段java程式碼的解析為例,準備一段需要解析的java程式碼

class Test {
  @tag(1)
  @label("名字")
  String name;

  @tag(2)
  @label("性別 0-男 1-女")
  Int sex;
}

2.定義根節點,包含了型別、版本和程式碼塊陣列,使用*號表示多個

CodeBlock = blocks:IdlBlock* {
    return {
        type: 'javaSchema',
        version: '1.0.0',
        blocks
    }
} 

3.使用_表示空白符,匹配class關鍵字,使用Identifier這條規則匹配類名並賦值給className後返回,
_ '{' children:Children* '}'解析空白符後兩個花括號中間的子節點

IdlBlock =
    _ 'class'
    _ className:Identifier
    _ '{' children:Children* '}'
    _ {
        return {
            className,
            children
        }
    }
    
    
_ "whitespace" = [ \t\r\n]*

Identifier = $([a-zA-Z_])+

4.在子節點中解析變數

Children =
    _ variable:Variable';'
    _ {
    	return {
            variable
        }
    }

5.定義變數解析規則

Variable = 
	_ tag:Tag?
    _ label:Label?
    _ type:DataType?
    _ name:Identifier?
    _ {
    	return  {
        	tag,
            label,
            type,
            name
        }
    }

6.解析@tag(1)並返回其中的引數

Tag = '@tag('tagColumn:TagColumn')' {
	return tagColumn
}

TagColumn = $([0-9])*

7.解析@label("名字")並返回引數中的描述資訊

Label = '@label("'labelColumn:LabelColumn'")' {
	return labelColumn
}

LabelColumn = $([^\r\n\t\"\)])*

8.解析變數的型別,這裡僅定義了程式碼塊中用到的兩種型別,也可以擴充套件更多型別

DataType = 'String' / 'Int'

完整的例項

CodeBlock = blocks:IdlBlock* {
    return {
        type: 'javaSchema',
        version: '1.0.0',
        blocks
    }
} 

IdlBlock =
    _ 'class'
    _ className:Identifier
    _ '{' children:Children* '}'
    _ {
        return {
            className,
            children
        }
    }
    
_ "whitespace" = [ \t\r\n]*

Identifier = $([a-zA-Z_])+

Children =
    _ variable:Variable';'
    _ {
    	return {
            variable
        }
    }

Variable = 
	_ tag:Tag?
    _ label:Label?
    _ type:DataType?
    _ name:Identifier?
    _ {
    	return  {
        	tag,
            label,
            type,
            name
        }
    }
    
    
Tag = '@tag('tagColumn:TagColumn')' {
	return tagColumn
}

TagColumn = $([0-9])*

Label = '@label("'labelColumn:LabelColumn'")' {
	return labelColumn
}

LabelColumn = $([^\r\n\t\"\)])*

DataType = 'String' / 'Int'

解析結果

{
   "type": "javaSchema",
   "version": "1.0.0",
   "blocks": [
      {
         "className": "Test",
         "children": [
            {
               "variable": {
                  "tag": "1",
                  "label": "名字",
                  "type": "String",
                  "name": "name"
               }
            },
            {
               "variable": {
                  "tag": "2",
                  "label": "性別 0-男 1-女",
                  "type": "Int",
                  "name": "sex"
               }
            }
         ]
      }
   ]
}

上面的規則可以直接在pegjs官網的網頁版中驗證,pegjs也提供了npm包,pegjs的js api比較簡單可以參考pegjs官方文件

相關文章