OpenDSA 完整目录

Chapter 27 Grammars

| 关于   «  4. 解析器生成器   ::   目录   ::   1. 线性表的构建与析构  »

5. 使用解析器生成器解释语言

5.1. 为 Jison 文法添加注解

本节我们概述如何为 Jison 文法添加注解,从而生成解释器而不仅仅是解析器。所谓解释器,是指我们不是仅仅返回一个表示输入表达式在语法上是否合法的信号,而是希望以某种方式“求值”合法表达式,并返回该求值的结果。

考虑下面的 Jison 文法,它是 https://gerhobbelt.github.io/jison/demos/calc/ 上演示的一个精简版本,由 Jison 开发者 Zach Carter 提供:

/*
    description: Parse and interpret/evaluate simple arithmetic expressions
    source:      this grammar is adapted from:
                 https://gerhobbelt.github.io/jison/demos/calc/
*/

// lexical section of the grammar
// ==============================

%lex
%%
\s+                   /* no return statement, so skip whitespace */
[0-9]+("."[0-9]+)?    return "NUMBER"
"*"                   return "TIMES"
"/"                   return "DIV"
"-"                   return "MINUS"
"+"                   return "PLUS"
"("                   return "LPAREN"
")"                   return "RPAREN"
<<EOF>>               return "EOF"
.                     return "INVALID"

/lex

%start program

// phrase-structure section of the grammar
// =======================================

%%

program
    : exp "EOF"
      { return $1; }
    ;
exp
    : term
      { $$ = $1; }
    | exp "PLUS" term
      { $$ = $1 + $3; }
    | exp "MINUS" term
      { $$ = $1 - $3; }
    ;

 项
     : 因子
       { $$ = $1; }
     | 项 "TIMES" 因子
       { $$ = $1 * $3; }
     | 项 "DIV" 因子
       { $$ = $1 / $3; }
     ;
 因子
     :  "NUMBER"
       { $$ = Number( $1 ); }
     | "LPAREN" 表达式 "RPAREN"
       { $$ = $2; }
     ;

这个文法生成一个解析器/解释器,用来对简单算术表达式求值。要试用它,只需把它剪切粘贴到 https://gerhobbelt.github.io/jison/try/ 上的 Jison 在线版本中。让 Jison 为你生成解析器/解释器,然后看看输入 \(6.4 - 3.2 * (42.6/13)\) 这样的表达式并点击 Jison 的 Parse 按钮时会发生什么。把这个文法交给 Jison 后,你会得到一个作为简单计算器的解释器。

这个文法中引入了 Jison 的一些“新”特性。首先,考虑由下面这行给出的词法规范:

[0-9]+("."[0-9]+)?    return "NUMBER"

回想一下,"return" 关键字之前的字符串正式名称是 正则表达式。事实上,从我们对 EBNF 的讨论中,我们已经熟悉了它使用的符号。记住,EBNF 中的 \(+\) 表示一个或多个,\(?\) 表示零个或一个,括号用于分组。因此上面的正则表达式表示,一个数字是“一个或多个数字,后面可选地跟一个由小数点和一个或多个数字组成的组”。

其次,考虑下面这部分文法中用花括号界定的注解:

program
    : exp "EOF"
      { return $1; }
    ;
exp
    : term
      { $$ = $1; }
    | exp "PLUS" term
      { $$ = $1 + $3; }
    | exp "MINUS" term
      { $$ = $1 - $3; }
    ;

在上面的例子中,每条产生式有一个注解。该注解插入在产生式的末尾。

这些注解由 JavaScript 代码组成。这里的 $1 和 $3 记法指的是解析器/解释器对 exp 产生式右端第一个和第三个非终结符求值所返回的结果。 $$ 记法是解析器/解释器递归返回给上一层分析的结果。因此 { $$ = $1 + $3; } 表示解释器应当把分析一个 exp (第一个非终结符)和一个 term (第三个非终结符)所返回的结果相加,并把该和返回给分析中上一层的递归。在与 program 非终结符相关联的顶层,记法 { return $1; } 表示当遇到输入结束时,解析器/解释器应当返回 $1 ,也就是分析/求值 program 产生式右端的 exp 所得到的结果。

本节的复习题将帮助你理解如何使用带注解的 Jison 文法来生成解释器。

   «  4. 解析器生成器   ::   目录   ::   1. 线性表的构建与析构  »

关闭窗口