Bison 解析语义值
Bison parsing semantic values
我有下面的代码,我正在验证匹配类型。例如,关系操作仅使用布尔类型执行。我已经得到了大部分。我知道 $$ 是操作的结果, $n 是右手。但是当我将它应用于函数头时我很困惑,我需要确保指定的 return 类型正在被 returned。根据我下面的语法,return 类型将是 $5,但 returned 的实际值应该是 $$。我的问题是我将它应用到语法的正确部分吗?关于如何验证该函数是正确的类型,我的逻辑是否正确?return?
%{
#include <string>
#include <vector>
#include <map>
using namespace std;
#include "types.h"
#include "listing.h"
#include "symbols.h"
int yylex();
void yyerror(const char* message);
Symbols<Types> symbols;
vector<Types> case_statements;
%}
%define parse.error verbose
%union
{
CharPtr iden;
Types type;
}
%token <iden> IDENTIFIER
%token <type>INT_LITERAL REAL_LITERAL BOOL_LITERAL NOTOP CASE CASES TRUE FALSE ELSE ENDIF IF
%token ADDOP MULOP RELOP ANDOP EXPOP OROP REMOP
%token BEGIN_ BOOLEAN END ENDREDUCE INTEGER IS FUNCTION REDUCE RETURNS ENDCASE OTHERS REAL THEN WHEN ARROW
%type <type> type function_header statement statement_ reductions expression binary exponent unary relation term factor primary case cases
%%
function:
function_header optional_variable body;
function_header:
FUNCTION IDENTIFIER parameters RETURNS type ';' {checkAssignment($$,, "Narrowing Function Returns");}|
FUNCTION IDENTIFIER RETURNS type ';' {checkAssignment($$,, "Narrowing Function Returns");}|
error ';' ;
optional_variable:
optional_variable variable |
error ';' |
;
variable:
IDENTIFIER ':' type IS statement_
{checkAssignment(, , "Variable Initialization");
{if (symbols.find(, )) appendError(DUPLICATE_IDENTIFIER, );
symbols.insert(, );} };
parameters:
parameter optional_parameter;
optional_parameter:
optional_parameter ',' parameter|
;
parameter:
IDENTIFIER ':' type {symbols.insert(, );}
type:
INTEGER {$$ = INT_TYPE;} |
REAL {$$ = REAL_TYPE;} |
BOOLEAN {$$ = BOOL_TYPE;} ;
body:
BEGIN_ statement_ END ';' ;
statement_:
statement ';' |
error ';' {$$ = MISMATCH;} ;
statement:
expression |
REDUCE operator reductions ENDREDUCE {$$ = ;} ; |
IF expression THEN statement_ ELSE statement_ ENDIF {$$ = checkIfThen($$, , );} |
CASE expression IS cases OTHERS ARROW statement_ ENDCASE {$$ =checkExpression(); case_statements.push_back();
int size = case_statements.size();
if (!case_statements.empty()) {
int org;
for (int i = 0; i < size; i++){
for (int j = 0; j < case_statements.size(); j++) {
if (org == j) {
$$ = checkCases(case_statements[i], case_statements[j]);
}
}
org = i;
}
case_statements.clear();
}
} ;
operator:
ADDOP |
RELOP |
EXPOP |
MULOP ;
cases:
| cases case {$$ = ;} ;
case:
WHEN INT_LITERAL ARROW statement_ { case_statements.push_back();} ;
reductions:
reductions statement_ {$$ = checkArithmetic(, );} |
{$$ = INT_TYPE;} ;
expression:
expression OROP binary {$$ = checkLogical(, );} |
binary ;
binary:
binary ANDOP relation {$$ = checkLogical(, );} |
relation
relation:
relation RELOP term {$$ = checkRelational(, );}|
term ;
term:
term ADDOP factor {$$ = checkArithmetic(, );} |
factor ;
factor:
factor MULOP primary {$$ = checkArithmetic(, );} |
factor REMOP exponent {$$ = checkInteger(, );} |
exponent ;
exponent:
unary |
unary EXPOP exponent {$$ = checkArithmetic(,);};
unary:
NOTOP primary {$$ = ;}|
primary;
primary:
'(' expression ')' {$$ = ;} |
INT_LITERAL |
REAL_LITERAL |
IDENTIFIER {if (!symbols.find(, $$)) appendError(UNDECLARED, );} ;
%%
void yyerror(const char* message)
{
appendError(SYNTAX, message);
}
int main(int argc, char *argv[])
{
firstLine();
yyparse();
lastLine();
return 0;
}
编辑:根据以下信息更新了代码
function:
function_header optional_variable body {checkAssignment(,, "Narrowing Function Return");};;
function_header:
FUNCTION IDENTIFIER parameters RETURNS type ';' {$$ = ;} ;|
FUNCTION IDENTIFIER RETURNS type ';' {$$ = ;} ;|
error ';' {$$ = MISMATCH;};
optional_variable:
optional_variable variable |
error ';' |
;
variable:
IDENTIFIER ':' type IS statement_
{checkAssignment(, , "Variable Initialization");
{if (symbols.find(, )) appendError(DUPLICATE_IDENTIFIER, );
symbols.insert(, );} }
parameters:
parameter optional_parameter;
optional_parameter:
optional_parameter ',' parameter|
;
parameter:
IDENTIFIER ':' type {symbols.insert(, );}
type:
INTEGER {$$ = INT_TYPE;} |
REAL {$$ = REAL_TYPE;} |
BOOLEAN {$$ = BOOL_TYPE;} ;
body:
BEGIN_ statement_ END ';' {$$ = ;}
statement_:
statement ';' {$$ = ;}|
error ';' {$$ = MISMATCH;} ;
statement:
expression {$$ = ;}|
REDUCE operator reductions ENDREDUCE {$$ = ;} ; |
IF expression THEN statement_ ELSE statement_ ENDIF {$$ = checkIfThen(, , );} |
CASE expression IS cases OTHERS ARROW statement_ ENDCASE {$$ =checkCaseExpression();case_statements.push_back();
int size = case_statements.size();
if (!case_statements.empty()) {
int org;
for (int i = 0; i < size; i++){
for (int j = 0; j < case_statements.size(); j++) {
if (org == j) {
$$ = checkCases(case_statements[i], case_statements[j]);
}
}
org = i;
}
case_statements.clear();
};} ;
operator:
ADDOP |
RELOP |
EXPOP |
MULOP ;
cases:
%empty {$$ = EMPTY; }|
cases case ;
case:
WHEN INT_LITERAL ARROW statement_ { case_statements.push_back();}|
%empty {$$ = EMPTY;} ;
检查器功能
void checkAssignment(Types lValue, Types rValue, string message)
{
if (lValue != MISMATCH && rValue != MISMATCH && lValue != rValue)
appendError(GENERAL_SEMANTIC, "Type Mismatch on " + message);
}
每个语法符号(记号或 non-terminal)都有一个关联的“语义”值。 “语义”一词用于表示这些值不是句法的;也就是说,它们不是解析的一部分。 bison/yacc 解析器不会为您计算这些值,因为它只关心解析输入。但是,它确实安排了两件事:
- 词法分析器设置的语义值与对应的token关联;
- 任何与产生式(“语义动作”)相关联的代码都会在该产生式被识别时执行。
您可以编写语义动作来计算那些 non-terminal 其语义值在使用它们的产品中需要的语义值。您可以通过 * 分配给特殊符号 $$
来做到这一点,解析器将使用它作为产生式已被识别的 non-terminal 的语义值
在一个语义动作中,可以引用产生式right-hand端文法符号的语义值,使用特殊符号</code>、<code>
等. 数字是 right-hand 边上符号的索引。如果那个符号是一个记号,它的值一定是由词法扫描器设置的,通过放入yylval
。如果引用的符号是 non-terminal,则该值必须由 every 产品为 non-terminal 设置(其操作必须将一些值分配给 $$
.
Bison 手册中有一章是关于 Language semantics 的,其中包含更多详细信息和示例。 (如果您还没有阅读该手册中的介绍性内容 material,可能会有所帮助。)
就其价值而言,我建议不要在解析期间尝试执行 type-checking。您可能会发现 分离关注点 更容易,方法是在解析期间构建语义树 ("AST"),然后对该树进行单独传递以完成分析。
我有下面的代码,我正在验证匹配类型。例如,关系操作仅使用布尔类型执行。我已经得到了大部分。我知道 $$ 是操作的结果, $n 是右手。但是当我将它应用于函数头时我很困惑,我需要确保指定的 return 类型正在被 returned。根据我下面的语法,return 类型将是 $5,但 returned 的实际值应该是 $$。我的问题是我将它应用到语法的正确部分吗?关于如何验证该函数是正确的类型,我的逻辑是否正确?return?
%{
#include <string>
#include <vector>
#include <map>
using namespace std;
#include "types.h"
#include "listing.h"
#include "symbols.h"
int yylex();
void yyerror(const char* message);
Symbols<Types> symbols;
vector<Types> case_statements;
%}
%define parse.error verbose
%union
{
CharPtr iden;
Types type;
}
%token <iden> IDENTIFIER
%token <type>INT_LITERAL REAL_LITERAL BOOL_LITERAL NOTOP CASE CASES TRUE FALSE ELSE ENDIF IF
%token ADDOP MULOP RELOP ANDOP EXPOP OROP REMOP
%token BEGIN_ BOOLEAN END ENDREDUCE INTEGER IS FUNCTION REDUCE RETURNS ENDCASE OTHERS REAL THEN WHEN ARROW
%type <type> type function_header statement statement_ reductions expression binary exponent unary relation term factor primary case cases
%%
function:
function_header optional_variable body;
function_header:
FUNCTION IDENTIFIER parameters RETURNS type ';' {checkAssignment($$,, "Narrowing Function Returns");}|
FUNCTION IDENTIFIER RETURNS type ';' {checkAssignment($$,, "Narrowing Function Returns");}|
error ';' ;
optional_variable:
optional_variable variable |
error ';' |
;
variable:
IDENTIFIER ':' type IS statement_
{checkAssignment(, , "Variable Initialization");
{if (symbols.find(, )) appendError(DUPLICATE_IDENTIFIER, );
symbols.insert(, );} };
parameters:
parameter optional_parameter;
optional_parameter:
optional_parameter ',' parameter|
;
parameter:
IDENTIFIER ':' type {symbols.insert(, );}
type:
INTEGER {$$ = INT_TYPE;} |
REAL {$$ = REAL_TYPE;} |
BOOLEAN {$$ = BOOL_TYPE;} ;
body:
BEGIN_ statement_ END ';' ;
statement_:
statement ';' |
error ';' {$$ = MISMATCH;} ;
statement:
expression |
REDUCE operator reductions ENDREDUCE {$$ = ;} ; |
IF expression THEN statement_ ELSE statement_ ENDIF {$$ = checkIfThen($$, , );} |
CASE expression IS cases OTHERS ARROW statement_ ENDCASE {$$ =checkExpression(); case_statements.push_back();
int size = case_statements.size();
if (!case_statements.empty()) {
int org;
for (int i = 0; i < size; i++){
for (int j = 0; j < case_statements.size(); j++) {
if (org == j) {
$$ = checkCases(case_statements[i], case_statements[j]);
}
}
org = i;
}
case_statements.clear();
}
} ;
operator:
ADDOP |
RELOP |
EXPOP |
MULOP ;
cases:
| cases case {$$ = ;} ;
case:
WHEN INT_LITERAL ARROW statement_ { case_statements.push_back();} ;
reductions:
reductions statement_ {$$ = checkArithmetic(, );} |
{$$ = INT_TYPE;} ;
expression:
expression OROP binary {$$ = checkLogical(, );} |
binary ;
binary:
binary ANDOP relation {$$ = checkLogical(, );} |
relation
relation:
relation RELOP term {$$ = checkRelational(, );}|
term ;
term:
term ADDOP factor {$$ = checkArithmetic(, );} |
factor ;
factor:
factor MULOP primary {$$ = checkArithmetic(, );} |
factor REMOP exponent {$$ = checkInteger(, );} |
exponent ;
exponent:
unary |
unary EXPOP exponent {$$ = checkArithmetic(,);};
unary:
NOTOP primary {$$ = ;}|
primary;
primary:
'(' expression ')' {$$ = ;} |
INT_LITERAL |
REAL_LITERAL |
IDENTIFIER {if (!symbols.find(, $$)) appendError(UNDECLARED, );} ;
%%
void yyerror(const char* message)
{
appendError(SYNTAX, message);
}
int main(int argc, char *argv[])
{
firstLine();
yyparse();
lastLine();
return 0;
}
编辑:根据以下信息更新了代码
function:
function_header optional_variable body {checkAssignment(,, "Narrowing Function Return");};;
function_header:
FUNCTION IDENTIFIER parameters RETURNS type ';' {$$ = ;} ;|
FUNCTION IDENTIFIER RETURNS type ';' {$$ = ;} ;|
error ';' {$$ = MISMATCH;};
optional_variable:
optional_variable variable |
error ';' |
;
variable:
IDENTIFIER ':' type IS statement_
{checkAssignment(, , "Variable Initialization");
{if (symbols.find(, )) appendError(DUPLICATE_IDENTIFIER, );
symbols.insert(, );} }
parameters:
parameter optional_parameter;
optional_parameter:
optional_parameter ',' parameter|
;
parameter:
IDENTIFIER ':' type {symbols.insert(, );}
type:
INTEGER {$$ = INT_TYPE;} |
REAL {$$ = REAL_TYPE;} |
BOOLEAN {$$ = BOOL_TYPE;} ;
body:
BEGIN_ statement_ END ';' {$$ = ;}
statement_:
statement ';' {$$ = ;}|
error ';' {$$ = MISMATCH;} ;
statement:
expression {$$ = ;}|
REDUCE operator reductions ENDREDUCE {$$ = ;} ; |
IF expression THEN statement_ ELSE statement_ ENDIF {$$ = checkIfThen(, , );} |
CASE expression IS cases OTHERS ARROW statement_ ENDCASE {$$ =checkCaseExpression();case_statements.push_back();
int size = case_statements.size();
if (!case_statements.empty()) {
int org;
for (int i = 0; i < size; i++){
for (int j = 0; j < case_statements.size(); j++) {
if (org == j) {
$$ = checkCases(case_statements[i], case_statements[j]);
}
}
org = i;
}
case_statements.clear();
};} ;
operator:
ADDOP |
RELOP |
EXPOP |
MULOP ;
cases:
%empty {$$ = EMPTY; }|
cases case ;
case:
WHEN INT_LITERAL ARROW statement_ { case_statements.push_back();}|
%empty {$$ = EMPTY;} ;
检查器功能
void checkAssignment(Types lValue, Types rValue, string message)
{
if (lValue != MISMATCH && rValue != MISMATCH && lValue != rValue)
appendError(GENERAL_SEMANTIC, "Type Mismatch on " + message);
}
每个语法符号(记号或 non-terminal)都有一个关联的“语义”值。 “语义”一词用于表示这些值不是句法的;也就是说,它们不是解析的一部分。 bison/yacc 解析器不会为您计算这些值,因为它只关心解析输入。但是,它确实安排了两件事:
- 词法分析器设置的语义值与对应的token关联;
- 任何与产生式(“语义动作”)相关联的代码都会在该产生式被识别时执行。
您可以编写语义动作来计算那些 non-terminal 其语义值在使用它们的产品中需要的语义值。您可以通过 * 分配给特殊符号 $$
来做到这一点,解析器将使用它作为产生式已被识别的 non-terminal 的语义值
在一个语义动作中,可以引用产生式right-hand端文法符号的语义值,使用特殊符号</code>、<code>
等. 数字是 right-hand 边上符号的索引。如果那个符号是一个记号,它的值一定是由词法扫描器设置的,通过放入yylval
。如果引用的符号是 non-terminal,则该值必须由 every 产品为 non-terminal 设置(其操作必须将一些值分配给 $$
.
Bison 手册中有一章是关于 Language semantics 的,其中包含更多详细信息和示例。 (如果您还没有阅读该手册中的介绍性内容 material,可能会有所帮助。)
就其价值而言,我建议不要在解析期间尝试执行 type-checking。您可能会发现 分离关注点 更容易,方法是在解析期间构建语义树 ("AST"),然后对该树进行单独传递以完成分析。