自分の言語を作成することの難しさをより深く理解するために、私は小さなコンパイラーに取り組んでいます。現在、文法にポインタ機能を追加する段階にありますが、それを実行することで、reduce/reduceの競合が発生しました。
これは、によってコンパイルできる私の文法の簡略化されたバージョンですbnfc
。私はhappy
パーサジェネレータを使用していますが、これは、reduce/reduceの競合があることを通知するプログラムです。
entrypoints Stmt ;
-- Statements
-------------
SDecl. Stmt ::= Type Ident; -- ex: "int my_var;"
SExpr. Stmt ::= Expr; -- ex: "printInt(123); "
-- Types
-------------
TInt. Type ::= "int" ;
TPointer. Type ::= Type "*" ;
TAlias. Type ::= Ident ; -- This is how I implement typedefs
-- Expressions
--------------
EMult. Expr1 ::= Expr1 "*" Expr2 ;
ELitInt. Expr2 ::= Integer ;
EVariable. Expr2 ::= Ident ;
-- and the standard corecions
_. Expr ::= Expr1 ;
_. Expr1 ::= Expr2 ;
私は文法がどのように機能するかを学習している段階にあります。しかし、私は何が起こるかを知っていると思います。これらの2つのプログラムを検討してください
main(){
int a;
int b;
a * b;
}
と
typedef int my_type;
main(){
my_type * my_type_pointer_variable;
}
(typedef
とのmain(){}
部分は関連性がなく、私の文法ではありません。しかし、それらはいくつかの文脈を与えます)
a "*" b
最初のプログラムでは、それがとして解析されることを望みます。Stmt ==(SExpr)==> Expr ==(EMult)==> Expr * Expr ==(..)==> Ident "*" Ident
つまり、基本的にSExpr
ルールを使用してステップを開始します。
同時に、my_type * my_type_pointer_variable
ルールを使って拡張していきたいと思います。Stmt ==(SDecl)==> Type Ident ==(TPointer)==> Type "*" Ident ==(TAlias)==> Ident "*" Ident
。
しかし、文法段階では、識別子が元々型エイリアスなのか変数なのかわかりません。
(1)reduce / reduceの競合を取り除くにはどうすればよいですか?(2)この問題を抱えているのは私だけですか?明らかな解決策はありますか?c文法はこの問題をどのように解決しますか?
これまでのところ、「*」の代わりに「&」またはその他の記号を使用して言語の構文を変更することに成功しましたが、これは非常に望ましくありません。また、さまざまな公開文法からは理解できず、なぜこの問題が発生しないのかを調べようとしましたが、運が悪かったのです。
そして最後に、どうすればこのような問題を自分で解決できますか?より詳細な出力から私が理解したhappy
のは、競合がどのように発生するかということだけです。賢さは、これらの競合を回避する唯一の方法ですか?たとえば、紹介するときに、さらに多くの問題に遭遇するのではないかと心配しています。EIndir. Expr = '*' Expr;