Поэтому я пишу грамматику, используя BNF-конвертер (BNFC) разобрать программу на с ++. Программа на С ++ выглядит следующим образом.
// a small C++ program
#include <iostream>
int main()
{
std::cout << "i";
return 0;
}
Грамматика БНФ, которую я написал для этого, выглядит следующим образом.
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
s — из-за объявления
separator nonempty Id "::" ;
но эта декларация противоречит более поздней декларации
separator Id "," ;
Я не знаю достаточно о BNFC, чтобы предсказать результат этого конфликта, но трудно представить, что результат — это то, что вы хотите.
Синтаксис для выражения не использует [Id]
; это позволяет только Id
:
EId. Exp15 ::= Id ;
EApp. Exp15 ::= Id "(" [Exp] ")" ;
Итак, квалифицированное имя std::cout
не будет разбираться как Exp15
,
У вас есть два очень разных контекста, в которых вы пытаетесь использовать списки Id
s; они отличаются как синтаксически, так и семантически. Таким образом, вы действительно не можете ожидать использовать [Id]
для них обоих.
Поскольку вы, очевидно, хотите иметь возможность обрабатывать имена в пространстве имен, я бы предложил явно определить Name
как непустой ::
разделенный список Id
s. Вы могли бы использовать [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», в разделе 7 (Макросы):
separator nonempty Stm ";" ;
средства
(:[]). [Stm] ::= Stm ; (:). [Stm] ::= Stm ";" [Stm] ;
Других решений пока нет …