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)<<
在>>
和<<
之间显示匹配的字符串。