1

入力

 1  -- Narrowing Variable Initialization  
 2  
 3  function main a: integer returns integer;  
 4      b: integer is a * 2.;  
 5  begin  
 6      if a <= 0 then  
 7          b + 3;  
 8      else  
 9          b * 4;  
10      endif;  
11  end;  

出力を生成しています

  1  -- Narrowing Variable Initialization
  2  
  3  function main a: integer returns integer;
  4      b: integer is a * 2.;
  5  begin
Narrowing Variable Initialization
  6      if a <= 0 then
  7          b + 3;
  8      else
  9          b * 4;
 10      endif;
 11  end;

そのエラー メッセージを、エラーが実際に発生する 4 行目の下に配置する代わりに。私は何時間もそれを見てきましたが、それを理解することはできません.

%union
{
    char* ident;
    Types types;
}

%token <ident> IDENTIFIER
%token <types> INTEGER_LITERAL
%token <types> REAL_LITERAL
%token  BEGIN_
%token  FUNCTION
%token  IS
%token  <types> INTEGER
%token  <types> REAL
%token  RETURNS

%type  <types> expression
%type  <types> factor
%type  <types> literal
%type  <types> term
%type  <types> statement
%type  <types> type
%type  <types> variable

%%

program:
    /* empty */ |
    functions ;

functions:
    function_header_recovery body ; |
    function_header_recovery body functions ;

function_header_recovery:
    function_header ';' |
    error ';' ;

function_header:
    FUNCTION {locals = new Locals();} IDENTIFIER optional_parameters RETURNS type {globals->insert($3,locals->tList);} ;

optional_parameters:
    /* empty */ |
    parameters;

parameters:
    IDENTIFIER ':' type {locals->insert($1, $3); locals->tList.push_back($3); } |
    IDENTIFIER ':' type {locals->insert($1, $3); locals->tList.push_back($3); } "," parameters;

type:
    INTEGER | REAL ;

body:
    optional_variables BEGIN_ statement END ';' ;

optional_variables:
    /* empty */ |
    variables ;

variables:
    variable IS statement {checkTypes($1, $3, 2);} |
    variable IS statement {checkTypes($1, $3, 2);} variables ;

variable:
    IDENTIFIER ':' type {locals->insert($1, $3);} {$$ = $3;} ;

statement:
    expression ';' |

...

Types checkTypes(Types left, Types right, int flag)
{
    if (left == right)
    {
        return left;
    }
    if (flag == 1)
    {
        Listing::appendError("Conditional Expression Type Mismatch", Listing::SEMANTIC);
    }
    else if (flag == 2)
    {
        if (left < right)
        {
            Listing::appendError("Narrowing Variable Initialization", Listing::SEMANTIC);
        }
    }
    return REAL_TYPE;
}

印刷は以下によって処理されます:

void Listing::nextLine()
{
printf("\n");
if (error == "")
{
    lineNo++;
    printf("%4d%s",lineNo,"  ");
}
else
{
    printf("%s", error.c_str());
error = "";
nextLine();
}
}

void Listing::appendError(const char* errText, int errEnum)
{
error = error + errText;

if (errEnum == 997)
{
    lexErrCount++;
}
else if (errEnum == 998)
{
    synErrCount++;
}
else if (errEnum == 999)
{
    semErrCount++;
}
}

void Listing::display()
{
printf( "\b\b\b\b\b\b    " );

if (lexErrCount + synErrCount + semErrCount > 0)
{
    printf("\n\n%s%d","Lexical Errors ",lexErrCount);
    printf("\n%s%d","Syntax Errors ",synErrCount);
    printf("\n%s%d\n","Semantic Errors ",semErrCount);
}
else
{
    printf("\nCompiled Successfully\n");
}
}
4

2 に答える 2

1

それがまさにその方法bisonです。1トークンの先読みパーサーを生成するため、本番に続いてトークンを読み取るまで、本番アクションはトリガーされません。したがって、begin関連するアクションが発生する前に読み取る必要がありますvariables。(bisonテキストが同一であっても、アクションを組み合わせようとはしません。したがってvariables、次のトークンが表示されるまで、どのプロダクションが適用され、どのアクションを実行するかを実際に知ることはできません。)

行番号や列の位置を各トークンに関連付け、エラーメッセージが生成されるときにその情報を使用するには、さまざまな方法があります。エラーや警告を入力テキストに散在させるには、一般に、入力をバッファリングする必要があります。構文エラーの場合、次のトークンまでバッファリングする必要があるだけですが、それは一般的な解決策ではありません。たとえば、エラーを演算子に関連付けたい場合がありますが、演算子の末尾の引数が解析されるまでエラーは検出されません。

エラー/警告をソースに正しく散在させる簡単な方法は、すべてのエラー/警告を一時ファイルに書き込み、各エラーの前にファイルオフセットを配置することです。次に、このファイルを並べ替えて、入力を再読み取りし、適切な場所にエラーメッセージを挿入します。この戦略の良いところは、エラーごとに行番号を維持する必要がないことです。これにより、字句解析が著しく遅くなります。もちろん、Cのような構成を許可すると、それほど簡単には機能しません#include

適切なエラーメッセージを生成することは困難であり、場所を追跡することでさえ解析がかなり遅くなる可能性があるため、エラーが検出された場合に入力を2回解析する戦略を使用することがあります。最初の解析はエラーを検出するだけであり、それ以上の合理的なことができない場合は早期に失敗します。エラーが検出された場合、入力はより精巧なパーサーで再解析され、ファイルの場所を注意深く追跡し、場合によってはインデントの深さなどのヒューリスティックを使用して、より適切なエラーメッセージを生成しようとします。

于 2013-02-25T18:18:40.560 に答える
0

rici が指摘しているように、bison は LALR(1) パーサーを生成するため、1 つの先読みトークンを使用して、実行するアクションを認識します。ただし、常に先読みのトークンを使用するとは限りません。場合によっては (先読みに関係なく 1 つの可能性しかない場合)、先読みなしでルールを削減 (および関連するアクションを実行) できるデフォルトの削減を使用します。

あなたの場合、それを利用して、本当に必要な場合は先読みなしでアクションを実行できます。問題の特定のルール (先読みの要件をトリガーする) は次のとおりです。

variables:
    variable IS statement {checkTypes($1, $3, 2);} |
    variable IS statement {checkTypes($1, $3, 2);} variables ;

この場合、 を確認した後、variable IS statement実行するアクション (最初または 2 番目) を知るために、さらに変数宣言があるかどうかを判断するために次のトークンを確認する必要があります。ただし、2 つのアクションは実際には同じであるため、これらを 1 つのアクションに組み合わせることができます。

variables: vardecl | vardecl variables ;
vardecl: variable IS statement {checkTypes($1, $3, 2);}

2 つのリダクション/アクションを決定するために先読みが必要ないため、デフォルトのリダクションを使用することになります。

上記は、statement先読みなしで a の終わりを見つけることができることに依存していることに注意してください。;

于 2013-02-25T18:47:58.233 に答える