Parboiled学习(一)

Parboiled概述

Parboiled是一个混合Java/ Scala库,提供了基于解析表达文法(PEGs)的轻量级、易用、功能强大的任意输入文本解析

图形数据库Neo4j使用Parboiled解析查询语言Cypher

特点

  1.  用户可以以某种方式指定解析语法,并使它快速,轻松地工作。
  2.  解析表达式语法的强大表达能力
  3.  支持强大而灵活的解析器操作
  4.  出色的解析错误报告和恢复
  5.  良好性能
  6.  易于集成
  7.  轻量级,易于使用

Parboiled提供了递归下降PEG解析器的实现,该实现可对用户指定的PEG规则进行操作。

解析表达文法(PEG)

以纯公式的形式展现递归下降解析器的基础语法,对这个具体的解析器采用的实现方法没有限定。

与上下文无关文法(CFG)很像,但存在区别:

  1. PEG不存在二义性,只产生一个确定的语法分析树。
  2. PEG的选择操作符是有序的。如果第一个可能成功了,那么第二个可能就忽略。

  文法的组成部分:

  •    一个有限的非终结符的集合N
  •    一个有限的终结符的集合\sum,和N没有交集
  •    一个有限的解析规则的集合P
  •    一个被称为起点表达式的解析表达式e_{S}

   P中每一个解析规则以A\leftarrow e的形式出现,这里A是一个非终结符,e是一个解析表达式。解析表达式是类似正则表达式的层次表达式。

  原子解析表达式

  •    任何的非终结符
  •    任何的终结符
  •    空字符串\varepsilon

  解析表达式操作符

   

  优点

  1.    PEG更加严格更加强大,可以很好地成为正则表达式的替代品。
  2.    PEG不存在二义性。

  缺点

  1.    PEG不能表达左递归的解析规则。
  2.    未能被广泛应用。

 

使用Parboiled的两个阶段

  • 规则构建

    以Parboiled的方式构建解析器规则的树 / 有向图。该阶段与实际的输入无关,且构建的规则树可重用。

    从BaseParser派生一个自定义的类,并定义返回Rule实例的方法。这些方法从其他规则、终端、预定义原语和动作表达式构建规则实例。对于Java,Parboiled采用了一种“解析器扩展”的过程来更加简洁地构造代码。

 

  • 规则执行

    为了使解析器不仅仅是一个“识别器”(确定给定的输入是否符合语法定义的程序)。解析器需要包含解析器操作,即在规则执行期间的特定执行点的自定义代码片段。除了检查解析器状态以外,解析器操作通常还会构造解析器“值”,并且可以作为语义谓词影响解析过程。

    规则针对特定的输入文本运行。该阶段的最终结果将得到以下信息:

  1. 确定输入是否匹配根规则的布尔标志
  2. 可能遇到的错误信息列表
  3. 由解析器操作构造一个或多个值的对象

 

值栈(The Value Stack) 

在规则执行阶段,解析器操作可以利用值栈来组织AST(抽象语法树)节点等自定义对象的构造。值栈通常用作自定义对象的临时存储。

解析树(The Parse Tree) 

在规则执行阶段,Parboiled可以选择构造一个解析树,其节点与识别的规则相对应。每个解析树的节点都包含一个对它的构造规则的匹配器的引用、匹配的输入文本位置和位于值栈顶部的当前元素。解析树可用来查看哪些规则已匹配给定输入,在调试期间很有用。

 

ParseRunner

负责监督解析运行并可选地应用额外的逻辑,最重要的是可以根据语法处理非法输入字符,即解析错误。

以下是ParserRunner的五个预定义的解析器:

  1. BasicParseRunner:不执行错误处理。(速度最快)
  2. ReportingParseRunner:为输入中的第一个分析错误创建一个适当的InvalidInputError对象
  3. RecoveringParseRunner:报告输入中所有的错误信息,并尝试恢复。(最复杂)
  4. TracingParseRunner:有选择地为每个匹配或不匹配的规则打印跟踪语句
  5. ProfilingParseRunner:产生关于解析器如何处理一个或多个输入

Parboiled for Java

安装

jdk 1.8

parboiled-core-1.3.1.jar

asm-all-5.2.jar

Maven配置

<dependency>
  <groupId>org.parboiled</groupId>
  <artifactId>parboiled-java</artifactId>
  <version>1.1.8</version>
</dependency>

具体步骤

1.   决定使用哪种类型V来参数化解析器value栈,并创建一个从BaseParser派生的自定义解析器类使用返回类型规则添加一个或多个规则方法。

2.   通过调用Parboiled.createParser来创建解析器的实例。

3.   调用语法分析器的规则方法,该方法构建语法的根规则来创建规则树。

4.   选择一个标准的ParseRunner实现,并调用它run”方法来传递根规则和要解析的输入文本。

5.   检查返回的ParsingResult对象的不同成员。

 

示例:AbcParser

AbcParser算法能够描述经典的非上下文无关文法 a^{^{n}}b^{^{n}}c^{^{n}}   n\geq 1

AbcParser.java

package com.parboiledlearn.demo;

import org.parboiled.BaseParser;
import org.parboiled.Rule;
import org.parboiled.annotations.BuildParseTree;

/**
 * S <- &(A c) a+ B !(a|b|c)
 * A <- a A? b
 * B <- b B? c
 */

@SuppressWarnings({"InfiniteRecursion"})
@BuildParseTree

public class AbcParser extends BaseParser<Object> {

    public Rule S() {
        return Sequence(
                Test(A(), 'c'),
                OneOrMore('a'),
                B(),
                TestNot(AnyOf("abc"))
        );
    }

    public Rule A() {
        return Sequence('a', Optional(A()), 'b');
    }

    public Rule B() {
        return Sequence('b', Optional(B()), 'c');
    }
}

 AbcMain.java

package com.parboiledlearn.demo;

import org.parboiled.Parboiled;
import org.parboiled.common.StringUtils;
import org.parboiled.errors.ErrorUtils;
import static org.parboiled.support.ParseTreeUtils.printNodeTree;
import org.parboiled.parserunners.ReportingParseRunner;
import org.parboiled.support.ParsingResult;
import java.util.Scanner;

public class AbcMain {
    public static void main(String[] args)  {
        AbcParser parser = Parboiled.createParser(AbcParser.class);
        while (true) {
            System.out.print("Enter an a^n b^n c^n expression (single RETURN to exit)!\n");
            String input = new Scanner(System.in).nextLine();
            if (StringUtils.isEmpty(input)) break;
            ParsingResult<?> result = new ReportingParseRunner(parser.S()).run(input);
            if (!result.parseErrors.isEmpty())
                System.out.println(ErrorUtils.printParseError(result.parseErrors.get(0)));
            else
                System.out.println(printNodeTree(result) + '\n');
        }
    }
}

运行结果

对于无效输入,ReportingParseRunner会报告第一个错误位置。

 

 

 

参考资料:

https://github.com/sirthias/parboiled/wiki 

https://zh.wikipedia.org/wiki/%E8%A7%A3%E6%9E%90%E8%A1%A8%E8%BE%BE%E6%96%87%E6%B3%95

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值