如何在yacc中获取规则文本


declaration: type instance SEMICOLON
{
//I want to get string here
}
type:...
instance:...

我想在声明动作中获得当前匹配的声明字符串。在yacc中有什么方法可以做到这一点吗?

如果对与语法规则相对应的输入字符串感兴趣,并且原始的空白无关,那么正如评论中提到的那样,一种可能性是扫描仪不仅提供值,还提供词素。

但这还不够。解析器必须在语义操作中将受影响的标记的各个词素连接起来。

为了避免内存泄漏,处理后的字符串也必须相应地释放。

扫描仪适应

假设您有一个NUMBER令牌的扫描器规则:

[0-9]+   { yylval = atoi(yytext);  return NUMBER; }

现在还必须另外提供词素。然而,这意味着不同的价值类型的联盟是不够的,而是一种结合了语义的结构和价值联盟是必要的。

只有一个int值的简单结构体可以如下所示:

struct my_token {
char *text;
union {
int ival;
} u;
};

那么词法分析器规则可能看起来像这样:

[0-9]+   { yylval->u.ival = atoi(yytext); yylval->text = string_dup(yytext);  return NUMBER; }

解析器适应

一个简单的Yacc语法分析器语法规则执行只有一些数值计算通常是这样的:

EXPR:    NUMBER         { $$ = $1; }
|   EXPR '+' EXPR  { $$ = $1 - $3; }
...

语义动作现在看起来更复杂:它需要访问值以更复杂的方式。string_merge函数将连接不同标记的词素。最有可能的情况是,只有在确实需要时才接受额外的复杂性。例如,如果只需要调试,则有更简单的选项。此外,string_merge函数还应该释放单个词汇的内存。

EXPR:    NUMBER         { $$ = $1; }
|   EXPR '-' EXPR  { $$.u.ival = $1.u.ival - $3.u.ival; $$.text = string_merge($1.text, $2.text, $3.text); }
|   EXPR '+' EXPR  { $$.u.ival = $1.u.ival + $3.u.ival; $$.text = string_merge($1.text, $2.text, $3.text); }

解析错误时释放内存

小附注:如果你使用Bison,并且还需要在解析错误时释放内存,例如,因为你有一个长期存在的解析器,如在shell中等,你应该阅读这里:https://www.gnu.org/software/bison/manual/html_node/Destructor-Decl.html.

完整的自包含示例

下面的示例使用flex和Bison说明了上述要点。

calc.l

%{
#include "calc.tab.h"
#include "string_util.h"
%}
%option noyywrap noinput nounput bison-locations
%%
[t n]+   {  }
[0-9]+   { yylval->u.ival = atoi(yytext); yylval->text = string_dup(yytext);  return NUMBER; }
";"      { return SEMICOLON; }
.        { yylval->text = string_dup(yytext); return yytext[0]; }
%%

calc.y

要使用上面定义的结构体,可以使用Bison中的以下行:

%define api.value.type {struct my_token}

见https://www.gnu.org/software/bison/manual/html_node/Value-Type.html

%locations
%define api.pure full
%define parse.error detailed
%code top {
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
}
%code requires {
struct my_token {
char *text;
union {
int ival;
} u;
};
}

%code {
#include "calc.tab.h"
#include "string_util.h"
void yyerror(YYLTYPE* yyllocp, const char* msg);
int yylex(YYSTYPE* yylvalp, YYLTYPE* yyllocp);
}
%define api.value.type {struct my_token}
%token NUMBER
%token SEMICOLON
%left  '-' '+'
%left  '*' '/'
%left  '(' ')'
%%
EXPR_LIST:                        { $$.u.ival = 0; }
| EXPR_LIST EXPR SEMICOLON { printf("%d >>%s<<n", $2.u.ival, $2.text); string_free($2.text); }
| EXPR_LIST SEMICOLON
;
EXPR:    NUMBER         { $$ = $1; }
|   EXPR '-' EXPR  { $$.u.ival = $1.u.ival - $3.u.ival; $$.text = string_merge($1.text, $2.text, $3.text); }
|   EXPR '+' EXPR  { $$.u.ival = $1.u.ival + $3.u.ival; $$.text = string_merge($1.text, $2.text, $3.text); }
|   EXPR '*' EXPR  { $$.u.ival = $1.u.ival * $3.u.ival; $$.text = string_merge($1.text, $2.text, $3.text); }
|   EXPR '/' EXPR  { $$.u.ival = $1.u.ival / $3.u.ival; $$.text = string_merge($1.text, $2.text, $3.text); }
|   '(' EXPR ')'   { $$.u.ival = $2.u.ival; $$.text = string_merge($1.text, $2.text, $3.text); }
;
%%
void yyerror(YYLTYPE *yyllocp, const char *str) {
fprintf(stderr, "error: %s in line %d, column %dn", str, yyllocp->first_line, yyllocp->first_column);
}
int main(void)
{
yyparse();
if(allocations != 0) {
fprintf(stderr, "%d allocations were not freedn", allocations);
}
return 0;
}

string_util.c

变量allcations在每次分配时递增,在释放内存时递减。当到达程序的末尾没有解析错误时,allcations应该是0。

#include <string.h>
#include <stdlib.h>
#include "string_util.h"
int allocations = 0;
char *string_dup(char *str) {
allocations++;
return strdup(str);
}
void string_free(const char *ptr) {
allocations--;
free((void *)ptr);
}
char *string_merge(const char *str1, const char *str2, const char *str3) {
size_t len = strlen(str1) + strlen(str2) + strlen(str3) + 1;
char *ptr = malloc(len);
allocations++;
strcpy(ptr, str1);
strcat(ptr, str2);
strcat(ptr, str3);
string_free(str1);
string_free(str2);
string_free(str3);
return ptr;
}

string_util.h

#ifndef STRING_UTIL_H
#define STRING_UTIL_H
extern int allocations;
char *string_dup(char *str);
void string_free(const char *ptr);
char *string_merge(const char *str1, const char *str2, const char *str3);
#endif //STRING_UTIL_H

输入

5 + 3 + 2 + 1;
5 + 2 + 1 + 1; (5 - 4) * (3 + 1) - 2;
5 - 4 + (1 - 2);

程序在调试控制台上打印以下内容:

11 >>5+3+2+1<<
9 >>5+2+1+1<<
2 >>(5-4)*(3+1)-2<<
0 >>5-4+(1-2)<<

>><<之间显示匹配的字符串。

相关内容

  • 没有找到相关文章

最新更新