1

私はプログラミング言語のインタープリターを実装しようとしていますが、後戻りする必要があるケースに出くわしましたが、私のパーサージェネレーター (ply、Python で書かれた lex&yacc クローン) はそれを許可しません。関係するルールは次のとおりです:

'var_access_start : super'
'var_access_start : NAME'
'var_access_name : DOT NAME'
'var_access_idx : OPSQR expression CLSQR'
'''callargs : callargs COMMA expression
            | expression
            | '''
'var_access_metcall : DOT NAME LPAREN callargs RPAREN'
'''var_access_token : var_access_name
                    | var_access_idx
                    | var_access_metcall'''
'''var_access_tokens : var_access_tokens var_access_token
                     | var_access_token'''
'''fornew_var_access_tokens : var_access_tokens var_access_name
                            | var_access_tokens var_access_idx
                            | var_access_name
                            | var_access_idx'''
'type_varref : var_access_start fornew_var_access_tokens'
'hard_varref : var_access_start var_access_tokens'
'easy_varref : var_access_start'
'varref : easy_varref'
'varref : hard_varref'
'typereference : NAME'
'typereference : type_varref'
'''expression : new typereference LPAREN callargs RPAREN'''

'var_decl_empty : NAME'
'var_decl_value : NAME EQUALS expression'
'''var_decl : var_decl_empty
            | var_decl_value'''
'''var_decls : var_decls COMMA var_decl
             | var_decl'''
    'statement : var var_decls SEMIC'

エラーは、フォームのステートメントで発生します

var x = new SomeGuy.SomeOtherGuy();

SomeGuy.SomeOtherGuy は、型を格納する有効な変数です (型はファースト クラス オブジェクトです)。その型には、引数のないコンストラクターがあります。

その式を解析すると、パーサーは var_access_start = SomeGuy var_access_metcall = を構築します。SomeOtherGuy ( ) を実行し、セミコロンを見つけてエラー状態で終了します - パーサーがバックトラックして、式 = new typereference(SomeGuy .SomeOtherGuy) LPAREN empty_list RPAREN を作成してみてください。var ステートメントの構文と完全に一致します

ただし、PLY はバックトラッキングをサポートしておらず、パーサー ジェネレーターで実際に自分で実装するのに十分な経験がないことを考えると、問題を回避するために文法に変更を加えることはできますか?

の代わりに -> を使用することを検討しました。「メソッド呼び出し」演算子としてですが、パーサーをなだめるためだけに言語を変更したくありません。また、「変数参照」の形式としてメソッドがあるため、 myObject.someMethod().aChildOfTheResult[0].doSomeOtherThing(1,2,3).helloWorld() を実行できますが、文法を修正して同じ効果、それも私のために働くだろう

ありがとう!

4

1 に答える 1

2

あなたの言語には、抜粋に含まれている表現以外の表現が含まれていると思います。newまた、 、 、superおよびvarは実際には端末であると仮定します。

以下は大まかな概要です。読みやすくするためにbison、引用符で囲まれたリテラルを使用した構文を使用していますが、変換に問題はないと思います。

「型はファーストクラスの値である」と言いますが、構文では、メソッド呼び出しを使用して型を返すことを明示的に排除しています。実際、関数を返すメソッド呼び出しを排除しているようにも見えますが、それは奇妙に思えます。なぜなら、型が第一級の値であるにもかかわらず、メソッドが第一級の値ではないことを意味するからです。そこで、次のような式を許可して文法を単純化しました。

 new foo.returns_method_which_returns_type()()()

制限を元に戻すのは簡単ですが、説明を理解するのが難しくなります。

基本的な考え方は、パーサーに時期尚早の決定を強制することを避けることです。一度new遭遇すると、メソッド呼び出しとコンストラクター呼び出しを先読みトークンから区別することしかできません。そのため、その時点まで同じ削減が使用されるようにする必要があります。つまり、開き括弧が検出された場合でも、両方の可能性を保持する必要があります。

primary: NAME
       | "super"
       ;

postfixed: primary
         | postfixed '.' NAME
         | postfixed '[' expression ']'
         | postfixed '(' call_args ')'            /* PRODUCTION 1 */
         ;

expression: postfixed
          | "new" postfixed '(' call_args ')'     /* PRODUCTION 2 */
       /* | other stuff not relevant here */
          ;

 /* Your callargs allows (,,,3). This one doesn't */
call_args : /* EMPTY */
          | expression_list
          ;
expression_list: expression
               | expression_list ',' expression
               ;

/* Another slightly simplified production */
var_decl: NAME
        | NAME '=' expression
        ;

var_decl_list: var_decl
             | var_decl_list ',' var_decl
             ;

statement: "var" var_decl_list ';'
      /* | other stuff not relevant here */
         ;

ここで、非常によく似たPRODUCTION 1とを見てください。PRODUCTION 2(コメントでマークされています。)これらは基本的に、バックトラックを求めたあいまいさです。ただし、この文法では問題はありません。一度 aに遭遇すると、先読みトークンがまたはの場合にのみnewの削減を実行でき、先読みトークンがおよびでのみ実行できるためです。PRODUCTION 2,;PRODUCTION 1.([

(競合がないことを確認するために、bison でテストされた文法。)

于 2014-02-09T03:49:32.420 に答える