使用bison将数据解析为结构

Parsing data into structures using bison

本文关键字:结构 数据 bison 使用      更新时间:2023-10-16

整个夏天我在学校学习了flex和bison,现在我想再深入一点。我很难理解Bison 3.0.2的文档。也许你们中的一些人可以帮我。我想解析一个表示方程的字符串,同时填写一个数据结构,其中包含有关解析内容的信息。例如,假设我有(ax+b)^2。我希望解析器生成一个包含字符串和整数常量的结构,如下所示。

(  BEGINGROUP
a  VARIABLE
x  VARIABLE
+  ADDITION
b  VARIABLE
)  ENDGROUP

我使用flex创建了一个语言规范,并使用bison创建了一种语法。所需要的只是让两者将信息放入结构中。我有一些代码可以按照我想要的方式工作,但我忍不住认为我错过了一些东西。在Bison文档示例中,我看到他们使用$$或$1,他们说这是为了检查语义值?当我打印语义值时,我总是得到零。不管怎样,我的代码发布在下面。

数学.l

%{
 #include "equation.h"
Equation* equ;
void setEquation(Equation* equation) {
    equ = equation;
}
%}
/* Definitions */
space     [ t]
digit     [0-9]
letter    [a-zA-Z]
number    ({digit}+|{digit}+"."{digit}+|"."{digit}+|{digit}+".")
variable  {letter}
/* actions */
%%
{space}      ;
{number}     equ->addElement(yytext, Equation::number); return(1);
{variable}   equ->addElement(yytext, Equation::variable); return(2);  
"+"          equ->addElement(yytext, Equation::addition); return(10); /* Basic operators */
"-"          return(11);
"*"          return(12);
"/"          return(13);
"^"          return(14);
"log"        return(15); 
"sin"        return(20); /* Trigonometric Functions */
"cos"        return(21);
"tan"        return(22);
"csc"        return(23);              
"sec"        return(24);
"cot"        return(25);
"arcsin"     return(26);
"arccos"     return(27);
"arctan"     return(28); 
"("          equ->addElement(yytext, Equation::begGroup); return(30); /* Grouping Operators */
")"          equ->addElement(yytext, Equation::endGroup); return(31);
"["          return(32);
"]"          return(33);
","          return(34);
.            fprintf(stderr, "Error on character %sn", yytext);

math.y

/*
* Implement grammer for equations
*/
%{
#include "lex.yy.c"
#include "equation.h"
#include <iostream>
int yylex(void);
int yyerror(const char *msg);
void output(const char* where) {
    std::cout << where << ": " << yytext << std::endl;
}
%}
%token e_num       1
%token e_var       2
%token e_plus     10
%token e_minus    11
%token e_mult     12
%token e_div      13
%token e_pow      14
%token e_log      15 
%token e_sin      20
%token e_cos      21
%token e_tan      22
%token e_csc      23              
%token e_sec      24
%token e_cot      25
%token e_asin     26
%token e_acos     27
%token e_atan     28 
%token lparen     30
%token rparen     31
%token slparen    32
%token srparen    33
%token comma      34
%start Expression
%%
Expression : Term  MoreTerms 
           | e_minus Term MoreTerms
           ;
MoreTerms  : /* add a term */
             e_plus Term MoreTerms
           | /* subtract a term */
             e_minus Term MoreTerms
           | /* add a negetive term */
             e_plus e_minus Term MoreTerms /* Add a negetive term */
           | /* minus a negetive term */
             e_minus e_minus Term MoreTerms /* Subtract a negetive term */
           | /* no extra terms */
           ;
Term       : Factor MoreFactors {equ->addElement("*", Equation::multiplication)};
           ;
MoreFactors: e_mult Factor MoreFactors
           | e_div  Factor MoreFactors
           | Factor MoreFactors
           |
           ;
Factor     : e_num { std::cout << $1 << std::endl; } //returns zero no matter where I put this
           | e_var
           | Group 
           | Function 
           ;
BeginGroup : lparen | slparen; 
EndGroup   : rparen | srparen;
Group      : BeginGroup Expression EndGroup 
              ;
Function   : TrigFuncs
           | PowerFunc
           ;
TrigFuncs  : e_sin lparen Expression rparen
           | e_cos lparen Expression rparen
           | e_tan lparen Expression rparen
           | e_csc lparen Expression rparen
           | e_sec lparen Expression rparen
           | e_cot lparen Expression rparen
           | e_asin lparen Expression rparen
           | e_acos lparen Expression rparen
           | e_atan lparen Expression rparen
           ;
PowerFunc  : e_num e_pow Factor
           | e_var e_pow Factor
           | Group e_pow Factor
           | TrigFuncs e_pow Factor
           ;

我觉得我在做什么很清楚。正如您所看到的,扫描程序将yytext及其代码存储到等式类中,但有时解析器必须向等式类添加信息,而这正是事情变得繁忙的地方。首先,试图在语句之前或中间添加代码可能会导致大量的移位/减少冲突。其次,将代码放在语句末尾的效果是记录无序的事情。看一下术语规则。如果我键入"ax",这隐含地意味着"a"乘以"x"或"a*x"。我希望解析器在中将乘法添加到我的结构中,但解析器这样做是无序的。那么,有没有更好的方法来实现我的目标呢?

您可能想知道为什么四个月内没有人回答您的问题;当它看起来像是一个简单的计算器问题。这是因为这不是一个简单的问题。这是一个问题的集合,有很多隐蔽的角落和缝隙供不小心的人使用。现在,有相当多的野牛专家在Stack Overflow上提供答案,他们真的很了解自己的东西,他们都像瘟疫一样避免了这种情况。如果你简化了这个问题,一次只问一件事,你可能会得到一些答案,但你只是粘贴了一大堆代码,然后加上了一些"哦,顺便说一句,在做的时候解决其他所有问题!"。然而,如果有人想调试你的代码,他们就不能,因为你错过了一大堆关键的东西,比如Equation对象。StackOverflow不是一堆免费的程序员,你知道!阅读一些指南。

让我们从一些简单的事情开始;这个$$$1的东西,你开始的。你称之为"语义价值"。不是。它们是一种通过解析树将值传递回的机制,也是词法分析器将令牌值关联给解析器的机制。您总是得到一个零的原因是,您从未在lexer中为其分配任何值。该值是通过引用内置变量yylval来分配的。这在文档中。让我们取一个返回e_num令牌的数字lexeme。我可以这样写(在math.l中):

{number}     {yylval = atoi(yytext);return(e_num);}

这允许我们现在打印数字的值:

Factor     : e_num { std::cout << $1 << std::endl; }

如果使用枚举常量的名称而不是那些绝对值,看起来会更好。很糟糕的编码把这些数字硬连接起来。

我还看到您已经创建了堆栈,并在lexer中对其进行推送。也许这不是一个好计划,但我们现在就这么做吧。如果需要,可以将令牌与这样的对象值相关联。%union构建体(野牛)用于:

%union {
Equation* TokenValue;
}

现在你必须开始键入语法的终端和非终端:

%token <TokenValue> e_enum;

但现在,如果你愿意,我们可以使用这些结构来获得价值:

{number}    {equ->addElement(yytext, Equation::number); 
            yylval.TokenValue = equ;
            return(e_num);
            }

野牛:

Factor     : e_num { std::cout << $1->ElementText << std::endl; }

在野牛语法中,有些地方您可能希望将几个值从规则的右手边传递回左手边。这里将使用$$ = $2形式的构造,但我将留给您阅读。

接下来要提到的是你描述算术表达式语法的方式。有两个问题,主要是由于否定词的处理和语法形式

我们这里需要一些计算机科学知识。您(主要)将表达式表示为运算符和操作数列表,这被视为正则语言乔姆斯基类型3形式。问题是算术表达式主要嵌套在结构中。嵌套需要上下文无关语法乔姆斯基类型2。这就是为什么所有计算器语法的例子都使用以下形式:

exp:exp OP exp

而不是您使用的列表形式。

现在,您已经使用规则层次结构来获取语法中运算符的一些优先级,但不幸的是,一元减号(或否定号)运算符的优先级将高于二元减法运算符,因此应该出现在Factor规则中。这些就是为什么你会得到如此多的转变/减少冲突的原因。这不是正确的方法。所有的课本都以不同于你的例子的方式来做计算器和表达式,这是有原因的。你需要回到课本上去。

这就是为什么人们在大学里学习这些东西。我希望这能帮助一些在未来关注类似问题的读者。