为 C++ 程序编写 BNFC 语法
Write BNFC grammar for a C++ program
所以我正在使用 BNF-Convertor (BNFC) 编写语法来解析 C++ 程序。 c++程序如下。
// a small C++ program
#include <iostream>
int main()
{
std::cout << "i";
return 0;
}
我为此写的BNF语法如下
PDefs. Program ::= [Def] ;
terminator Def "" ;
comment "//" ;
comment "/*" "*/" ;
comment "#" ;
DFun. Def ::= Type Id "(" [Arg] ")" "{" [Stm] "}" ;
separator Arg "," ;
terminator Stm "" ;
separator nonempty Id "::" ;
ADecl. Arg ::= Type Id ;
SExp. Stm ::= Exp ";" ;
Sids. Stm ::= Id ;
SDecl. Stm ::= Type Id ";" ;
SDecls. Stm ::= Type Id "," [Id] ";" ;
SInit. Stm ::= Type Id "=" Exp ";" ;
SReturn. Stm ::= "return" Exp ";" ;
SWhile. Stm ::= "while" "(" Exp ")" Stm ;
SBlock. Stm ::= "{" [Stm] "}" ;
SIfElse. Stm ::= "if" "(" Exp ")" Stm "else" Stm ;
EInt. Exp15 ::= Integer ;
EDouble. Exp15 ::= Double ;
ETrue. Exp15 ::= "true" ;
EFalse. Exp15 ::= "false" ;
EId. Exp15 ::= Id ;
EApp. Exp15 ::= Id "(" [Exp] ")" ;
EStr. Exp15 ::= "\"" Id "\"";
EPIncr. Exp14 ::= Exp15 "++" ;
EPDecr. Exp14 ::= Exp15 "--" ;
EIncr. Exp13 ::= "++" Exp14 ;
EDecr. Exp13 ::= "--" Exp14 ;
ETimes. Exp12 ::= Exp12 "*" Exp13 ;
EDiv. Exp12 ::= Exp12 "/" Exp13 ;
EPlus. Exp11 ::= Exp11 "+" Exp12 ;
EMinus. Exp11 ::= Exp11 "-" Exp12 ;
ELs. Exp9 ::= Exp9 "<<" Exp10 ;
ERs. Exp9 ::= Exp9 ">>" Exp10 ;
ELt. Exp9 ::= Exp9 "<" Exp10 ;
EGt. Exp9 ::= Exp9 ">" Exp10 ;
ELtEq. Exp9 ::= Exp9 "<=" Exp10 ;
EGtWq. Exp9 ::= Exp9 ">=" Exp10 ;
EEq. Exp8 ::= Exp8 "==" Exp9 ;
ENEq. Exp8 ::= Exp8 "!=" Exp9 ;
EAnd. Exp4 ::= Exp4 "&&" Exp5 ;
EOr. Exp3 ::= Exp3 "||" Exp4 ;
EAss. Exp2 ::= Exp3 "=" Exp2 ;
coercions Exp 15 ;
separator Exp "," ;
separator Id "," ;
Tbool. Type ::= "bool" ;
Tdouble. Type ::= "double" ;
Tint. Type ::= "int" ;
Tvoid. Type ::= "void" ;
token Id (letter (letter | digit | '_' )*) ;
token Ids (letter)* ;
我在此处为 ::
和左移运算符编写了规则 <<
和 >>
右移运算符,但由于某些原因,它无法正确解析。我做错了什么?
根据我的理解,这应该可行,但出现此错误。
syntax error at line 6 before << "i" ; return
你的问题是 std::cout
不是 Id
。它可能是一个 [Id]
—— 即 Id
的列表 —— 因为声明
separator nonempty Id "::" ;
但该声明与后面的声明冲突
separator Id "," ;
我对BNFC的了解还不够,无法预测那个冲突的结果,但很难想象结果是你想要的。
表达式的语法不使用[Id]
;它只允许 Id
:
EId. Exp15 ::= Id ;
EApp. Exp15 ::= Id "(" [Exp] ")" ;
因此限定名称 std::cout
不会被解析为 Exp15
。
您尝试使用 Id
列表的两个非常不同的上下文;它们在句法和语义上都不同。所以你真的不能指望对它们都使用 [Id]
。
由于您显然希望能够处理命名空间的名称,我建议将 Name
显式定义为非空 ::
分隔的 Id
列表.当然,您可以使用 [Id]
宏来代替您自己的定义;这真的是一种风格判断。对我来说,似乎不太清楚,但口味各不相同。
在另一种情况下——声明——使用[Id]
确实是不正确的,尽管它可能对你的简化语法来说已经足够了。在这里,我建议使用一个新的 Declarator
非终结符,您最初可以将其定义为 Id
,但您最终会希望将其扩展为包含指针声明符 (*foo
),数组声明符 (foo[3]
),甚至可能是函数声明符。使用 Declarator
非终端,您可以替换
SDecl. Stm ::= Type Id ";" ;
SDecls. Stm ::= Type Id "," [Id] ";" ;
与
separator nonempty Declarator ","
SDecls. Stm ::= Type [Declarator] ";" ;
注意:来自 "LBNF Grammar Formalism",在第 7 节(宏)中:
separator nonempty Stm ";" ;
means
(:[]). [Stm] ::= Stm ;
(:). [Stm] ::= Stm ";" [Stm] ;
所以我正在使用 BNF-Convertor (BNFC) 编写语法来解析 C++ 程序。 c++程序如下。
// a small C++ program
#include <iostream>
int main()
{
std::cout << "i";
return 0;
}
我为此写的BNF语法如下
PDefs. Program ::= [Def] ;
terminator Def "" ;
comment "//" ;
comment "/*" "*/" ;
comment "#" ;
DFun. Def ::= Type Id "(" [Arg] ")" "{" [Stm] "}" ;
separator Arg "," ;
terminator Stm "" ;
separator nonempty Id "::" ;
ADecl. Arg ::= Type Id ;
SExp. Stm ::= Exp ";" ;
Sids. Stm ::= Id ;
SDecl. Stm ::= Type Id ";" ;
SDecls. Stm ::= Type Id "," [Id] ";" ;
SInit. Stm ::= Type Id "=" Exp ";" ;
SReturn. Stm ::= "return" Exp ";" ;
SWhile. Stm ::= "while" "(" Exp ")" Stm ;
SBlock. Stm ::= "{" [Stm] "}" ;
SIfElse. Stm ::= "if" "(" Exp ")" Stm "else" Stm ;
EInt. Exp15 ::= Integer ;
EDouble. Exp15 ::= Double ;
ETrue. Exp15 ::= "true" ;
EFalse. Exp15 ::= "false" ;
EId. Exp15 ::= Id ;
EApp. Exp15 ::= Id "(" [Exp] ")" ;
EStr. Exp15 ::= "\"" Id "\"";
EPIncr. Exp14 ::= Exp15 "++" ;
EPDecr. Exp14 ::= Exp15 "--" ;
EIncr. Exp13 ::= "++" Exp14 ;
EDecr. Exp13 ::= "--" Exp14 ;
ETimes. Exp12 ::= Exp12 "*" Exp13 ;
EDiv. Exp12 ::= Exp12 "/" Exp13 ;
EPlus. Exp11 ::= Exp11 "+" Exp12 ;
EMinus. Exp11 ::= Exp11 "-" Exp12 ;
ELs. Exp9 ::= Exp9 "<<" Exp10 ;
ERs. Exp9 ::= Exp9 ">>" Exp10 ;
ELt. Exp9 ::= Exp9 "<" Exp10 ;
EGt. Exp9 ::= Exp9 ">" Exp10 ;
ELtEq. Exp9 ::= Exp9 "<=" Exp10 ;
EGtWq. Exp9 ::= Exp9 ">=" Exp10 ;
EEq. Exp8 ::= Exp8 "==" Exp9 ;
ENEq. Exp8 ::= Exp8 "!=" Exp9 ;
EAnd. Exp4 ::= Exp4 "&&" Exp5 ;
EOr. Exp3 ::= Exp3 "||" Exp4 ;
EAss. Exp2 ::= Exp3 "=" Exp2 ;
coercions Exp 15 ;
separator Exp "," ;
separator Id "," ;
Tbool. Type ::= "bool" ;
Tdouble. Type ::= "double" ;
Tint. Type ::= "int" ;
Tvoid. Type ::= "void" ;
token Id (letter (letter | digit | '_' )*) ;
token Ids (letter)* ;
我在此处为 ::
和左移运算符编写了规则 <<
和 >>
右移运算符,但由于某些原因,它无法正确解析。我做错了什么?
根据我的理解,这应该可行,但出现此错误。
syntax error at line 6 before << "i" ; return
你的问题是 std::cout
不是 Id
。它可能是一个 [Id]
—— 即 Id
的列表 —— 因为声明
separator nonempty Id "::" ;
但该声明与后面的声明冲突
separator Id "," ;
我对BNFC的了解还不够,无法预测那个冲突的结果,但很难想象结果是你想要的。
表达式的语法不使用[Id]
;它只允许 Id
:
EId. Exp15 ::= Id ;
EApp. Exp15 ::= Id "(" [Exp] ")" ;
因此限定名称 std::cout
不会被解析为 Exp15
。
您尝试使用 Id
列表的两个非常不同的上下文;它们在句法和语义上都不同。所以你真的不能指望对它们都使用 [Id]
。
由于您显然希望能够处理命名空间的名称,我建议将 Name
显式定义为非空 ::
分隔的 Id
列表.当然,您可以使用 [Id]
宏来代替您自己的定义;这真的是一种风格判断。对我来说,似乎不太清楚,但口味各不相同。
在另一种情况下——声明——使用[Id]
确实是不正确的,尽管它可能对你的简化语法来说已经足够了。在这里,我建议使用一个新的 Declarator
非终结符,您最初可以将其定义为 Id
,但您最终会希望将其扩展为包含指针声明符 (*foo
),数组声明符 (foo[3]
),甚至可能是函数声明符。使用 Declarator
非终端,您可以替换
SDecl. Stm ::= Type Id ";" ;
SDecls. Stm ::= Type Id "," [Id] ";" ;
与
separator nonempty Declarator ","
SDecls. Stm ::= Type [Declarator] ";" ;
注意:来自 "LBNF Grammar Formalism",在第 7 节(宏)中:
separator nonempty Stm ";" ;
means
(:[]). [Stm] ::= Stm ; (:). [Stm] ::= Stm ";" [Stm] ;