PEG
PEG 解析示例
下面为你提供一些 PEG(解析表达式文法)规范的具体实例,帮助你理解其语法和应用场景。
一、简单算术表达式解析
这个例子展示了如何用 PEG 解析包含加减乘除和括号的算术表达式,同时处理运算符优先级。
# 算术表达式语法
Expression <- Term (AddOp Term)\*
Term <- Factor (MulOp Factor)\*
Factor <- Number / '(' Expression ')'
Number <- \[0-9]+ / '-' \[0-9]+
AddOp <- '+' / '-'
MulOp <- '\*' / '/'
解释:
-
Expression由Term后跟零个或多个AddOp Term组成(加法 / 减法)。 -
Term由Factor后跟零个或多个MulOp Factor组成(乘法 / 除法)。 -
Factor可以是数字或括号内的表达式。 -
Number支持整数和负数(如123或-456)。 -
优先级:通过规则嵌套实现(
MulOp在Term中,比AddOp优先级高)。
二、JSON 格式解析(简化版)
PEG 非常适合解析 JSON 这种无歧义的数据格式。
JSONValue <- JSONObject / JSONArray / JSONString / JSONNumber / "true" / "false" / "null"
JSONObject <- '{' (JSONPair (',' JSONPair)\*)? '}'
JSONPair <- JSONString ':' JSONValue
JSONArray <- '\[' (JSONValue (',' JSONValue)\*)? ']'
JSONString <- '"' \[^"]\* '"'
JSONNumber <- \[0-9]+ ('.' \[0-9]+)? (\[eE] \[+-]? \[0-9]+)?
解释:
-
JSONValue可以是对象、数组、字符串、数字或字面量(true/false/null)。 -
JSONObject是键值对的集合,用逗号分隔。 -
JSONArray是值的有序列表,用逗号分隔。 -
JSONString是双引号包裹的任意字符(简化版,实际需处理转义)。 -
JSONNumber支持整数、小数和科学计数法(如123、3.14、1e-10)。
三、CSV 格式解析
CSV(逗号分隔值)是一种简单的表格数据格式。
CSVFile <- Row ('\n' Row)\*
Row <- Value (',' Value)\* '\n'?
Value <- QuotedValue / UnquotedValue
QuotedValue <- '"' ('""' / \[^"])\* '"'
UnquotedValue <- \[^",\n]\*
解释:
-
CSVFile由多行Row组成,行之间用换行符分隔。 -
Row由多个Value组成,用逗号分隔,行末可选换行符。 -
Value可以是带引号的值或不带引号的值。 -
QuotedValue支持双引号转义(如"a""b"表示a"b)。
四、标识符和关键字解析
这个例子展示了如何区分关键字和普通标识符,避免匹配冲突。
Identifier <- !Keyword \[a-zA-Z\_] \[a-zA-Z0-9\_]\*
Keyword <- "if" / "else" / "while" / "function"
解释:
-
!Keyword是负向断言,确保当前位置不会匹配关键字。 -
例如,
if会匹配Keyword,而if_else会匹配Identifier。
五、HTML 标签解析(简化版)
解析 HTML 标签结构,处理嵌套关系。
HTML <- Element\*
Element <- OpenTag Content CloseTag
OpenTag <- '<' TagName Attribute\* '>'
CloseTag <- '\</' TagName '>'
TagName <- \[a-zA-Z]+
Attribute <- ' ' AttrName '=' '"' AttrValue '"'
AttrName <- \[a-zA-Z]+
AttrValue <- \[^"]\*
Content <- Text / Element
Text <- \[^<]\*
解释:
-
Element由开始标签、内容和结束标签组成。 -
Attribute是键值对(如id="main")。 -
Content可以是文本或嵌套的元素。 -
注意:这是简化版,实际 HTML 解析需处理自闭合标签(如
<br>)和更多复杂情况。
六、使用正向断言和负向断言
PEG 的断言运算符 & 和 ! 允许在不消耗输入的情况下检查匹配。
// 匹配以 "http" 开头的 URL
URL <- &"http" "http" ("s" / "") "://" Domain Path?
// 匹配非空行(至少包含一个非空白字符)
NonEmptyLine <- !'\n' \[^\n]\* '\n'
// 匹配非零数字
NonZeroDigit <- !'0' \[0-9]
解释:
-
&"http"确保后续字符是http,但不消耗输入。 -
!'0'排除0,只允许1-9。
七、PEG.js 中的实际应用
如果你使用 JavaScript 的 PEG.js 库,可以这样定义语法:
// PEG.js 语法文件示例
start = expression
expression
  \= additive
additive
  \= left:multiplicative "+" right:additive { return left + right; }
  / multiplicative
multiplicative
  \= left:primary "\*" right:multiplicative { return left \* right; }
  / primary
primary
  \= integer
  / "(" expr:expression ")" { return expr; }
integer "integer"
  \= digits:\[0-9]+ { return parseInt(digits.join(""), 10); }
解释:
-
这是一个可执行的 PEG 语法,包含动作代码(大括号内的 JavaScript)。
-
解析时会自动生成抽象语法树(AST)节点。
总结
PEG 的核心优势在于:
-
无歧义性:规则顺序决定匹配优先级,无需额外优先级规则。
-
高效解析:无回溯,适合处理复杂语法。
-
灵活断言:通过
&和!实现预检查,不消耗输入。
以上实例覆盖了常见场景,你可以根据需要调整规则或添加新的运算符 / 结构。