原文地址:http://blog.dengqinghua.net/racc.html
Github: https://github.com/dengqinghua/roses/blob/master/source/racc.md
这个是 Racc 的使用示例介绍。
阅读完该文档之后,您将了解到:
我们在做数据中心的时候,计算引擎都是基于 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 函数.
我们希望可以对此插件进行扩展。阅读源码后发现,源码核心是由两个文件
阅读了一下相关资料,得知 Yacc
和 Rexical
的概念
也就是说 SQL 解析分为两部分
parser.rex
来配置,如 SELECT, ORDER 等词汇parser.racc
来配置,定义了语法,即 SELECT, ORDER 的组合方式故遇到一条 SQL
SELECT * FROM orders WHERE id = 1
的时候,会分析出 patterns
SELECT
*
FROM
WHERE
再根据 语法
进行正则匹配即可。
下面的文档是介绍了 Rex 和 Racc 的作用 和 使用方式。
示例代码可以在 这里 查看
Racc 和 Rex 分别来源于单词 Yacc 和 Lexical Analyser
在我们定义一个语言的时候,需要解决两个问题
其中 Lexical 就是词汇
的抽象,Yacc 就是语法
的抽象
下面是一个语法解析的过程示例:
图片和对应的文档来源于这里
对应 Racc 和 Rex, 也是做相同的事情,仅仅是解析和定义的语言为 Ruby, 所以用 R 开头
在示例代码 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
有了词汇之后,我们就可以定义语法了。
在示例代码 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
一些注释如下
val 是指匹配成功之后,对应的值
如字符串 2 + 2
, 匹配到了 DIGIT ADD DIGIT
这部分,则
val[0] = 2
val[1] = "+"
val[2] = 2
{} 表示的是 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)
可以看到对应的结果。