Ruby Ruby 字符串解析引擎 Racc 和 Rex 分析

dengqinghua · 2018年05月29日 · 3087 次阅读

Racc

原文地址: http://blog.dengqinghua.net/racc.html

Github: https://github.com/dengqinghua/roses/blob/master/source/racc.md

这个是 Racc 的使用示例介绍.

阅读完该文档之后, 您将了解到:

  • Rex 和 Racc 的关系
  • 如何使用 Racc 进行自定义

TL;DR

我们在做数据中心的时候, 计算引擎都是基于 SQL 的.

在 Java 生态中, 我们有很多解析 SQL 的工具, 如 Druid.

Ruby 中我们使用 sql-parser

require 'sql-parser'
parser = SQLParser::Parser.new

# Build the AST from a SQL statement
ast = parser.scan_str('SELECT * FROM users WHERE id = 1')

# Find which columns where selected in the FROM clause
ast.select_list.to_sql
#=> "*"

# Output the table expression as SQL
ast.table_expression.to_sql
#=> "FROM users WHERE id = 1"

# Drill down into the WHERE clause, to examine every piece
ast.table_expression.where_clause.to_sql
#=> "WHERE id = 1"
ast.table_expression.where_clause.search_condition.to_sql
#=> "id = 1"
ast.table_expression.where_clause.search_condition.left.to_sql
#=> "id"

但是发现该功能的 SQL 解析能力有限, 我们使用了很多SQL函数, 该插件并不支持复杂的 SQL 函数.

我们希望可以对此插件进行扩展. 阅读源码后发现, 源码核心是由两个文件

阅读了一下相关资料, 得知 YaccRexical 的概念

也就是说 SQL 解析分为两部分

  1. patterns, 用 parser.rex 来配置, 如 SELECT, ORDER 等词汇
  2. grammar, 用 parser.racc 来配置, 定义了语法, 即 SELECT, ORDER 的组合方式

故遇到一条 SQL

SELECT * FROM orders WHERE id = 1

的时候, 会分析出 patterns

SELECT
*
FROM
WHERE

再根据 语法 进行正则匹配即可.

下面的文档是介绍了 Rex 和 Racc 的作用 和 使用方式.

示例代码可以在 这里 查看

Rex 和 Racc 的来源

Yacc 和 Lexical

Racc 和 Rex 分别来源于单词 YaccLexical Analyser

在我们定义一个语言的时候, 需要解决两个问题

  1. 有哪些词汇 (patterns)?
  2. 有哪些语法 (grammar)?

其中 Lexical 就是词汇的抽象, Yacc 就是语法的抽象

下面是一个语法解析的过程示例:

racc_example

图片和对应的文档来源于这里

对应 Racc 和 Rex, 也是做相同的事情, 仅仅是解析和定义的语言为 Ruby, 所以用 R 开头

Git 地址

Rex

示例

在示例代码 calculator.rex 定义了词汇

如规则

macro
  DIGIT_MACRO     \d+

rule
  {DIGIT_MACRO} { [:DIGIT, text.to_i] }

表示的是 遇到了数字, 那么解析成 [:DIGIT, 数字] 的形式, 即

输入 1024, 解析为词汇 [:DIGIT, 1024]

为什么要有一个 :DIGIT 的标识呢? 是因为我们需要将相同属性的东西进行标记, 下一步进行语法申明的时候可以用到

NOTE: 参考: a-tester-learns-rex-and-racc-part-1

Racc

示例

有了词汇之后, 我们就可以定义语法了.

在示例代码 calculator.y 定义了语法

rule
  expression
    :
    DIGIT
    | DIGIT ADD DIGIT { return val[0] + val[2] }
    | DIGIT SUBSTRACT DIGIT { return val[0] - val[2] }
    | DIGIT MULTIPLY DIGIT { return val[0] * val[2] }
    | DIGIT DIVIDE DIGIT { return val[0] / val[2] }
end

一些注释如下

  1. expression 仅仅是一个名词, 可以起名为 abc, bcd 都行, 他可以被复用, 即规则可以嵌套使用. ":"后面是代表各种不同的情况, | 代表 或
  2. DIGIT ADD SUBSTRACT 等, 均为 calculator.rex 中申明的词汇 (patterns)
  3. val 是指匹配成功之后, 对应的值

    如字符串 2 + 2, 匹配到了 DIGIT ADD DIGIT 这部分, 则

    val[0] = 2
    val[1] = "+"
    val[2] = 2
    
  4. {} 表示的是 ruby 代码, 即如何处理该语法.

编译

编译的过程是生成 ruby 代码的过程, 包括 rex 和 racc 两部分

执行

rex calculator.rex -o calculator.rex.rb
racc calculator.y -o calculator.racc.rb

NOTE: 在这之前需要安装 racc 这个 gem. 可以通过 gem install racc 进行安装

此时有一个 calculator.racc.rb 文件. 我们可以起一个 pry 进行测试

NOTE: 希望之后可以添加 Rspec 测试, 这样更加直观和规范

pry -r ./calculator.racc.rb

在 console 中输入

Calculator.new.parse("2 + 2") #=> 输出 4
Calculator.new.parse("2 - 2") #=> 输出 0
Calculator.new.parse("2 * 3") #=> 输出 6
Calculator.new.parse("2 / 1") #=> 输出 2
Calculator.new.parse("2 | 1") #=> 抛出异常, 因为 | 无法解析: parse error on value 2 (DIGIT)

可以看到对应的结果.

References

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册