難解な言語の通訳を書く(パート2)

前回の記事で、Brainfuckのアドオン用のインタープリターを作成するタスクを設定したことを思い出させてください。 当然、最初にBrainfuck自体を実装する必要があり、それからアドインに進みます。 幸いなことに、前の記事でこのパートが実装されました。 実際、このパートで実装する必要があるものについて説明します。
  1. 関数の概念(手順)。
  2. コメント


コメント

これが最も簡単な部分です。 コメントとは、括弧で囲まれた一連の文字です。 コメントテキストのブラケットシーケンスの「フレーミング」は正しい必要があります。 これは明らかです。 それだけです。 そしてもう1つ:解析の開始時にコメントを「選別」する必要があります-最後に、実行する必要があるコードのみを取得する必要があります。 これは単純に行われます:

unsigned int skipComment( std::ifstream &file )
{
unsigned int skipped = 0;
unsigned int unclosedComments = 1;
while( file.good() && ( unclosedComments != 0 ) )
{
char tmp = (char) file.get();
if ( bf_cBeg == tmp )
unclosedComments++;
else if ( tmp == bf_cEnd )
unclosedComments--;
skipped++;
}
return skipped;
}


この関数は、「(」文字が検出されるとすぐに呼び出されます。コメントを「飲み込み」、飲み込んだ文字の数を返します。これで問題は解決しました。次のタスクに進みましょう。

関数/手順。

ここでは、もちろん、もっと難しくなります!
私達は要求します:

したがって、関数本体は次のようになります。
{%<name>%<code>}

名前には、開き括弧のない任意の文字列を使用できます。

関数の名前を検索するには、次の関数を使用します。

std::string parseName( std::ifstream &file, const char parsingAfter )
{
if ( file.bad() || ( bf_fNme != parsingAfter ) )
return std::string();

std::string res = std::string();

while ( file.good() )
{
char tmp = (char) file.get();
if ( tmp == bf_fNme )
return res;
res += tmp;

}

return res;
}


この関数は、呼び出すために関数の名前を決定する必要がある場合に役立ちます。 関数を保存するには、次の構造を使用します。

struct proc
{
char code[ maxCodeSize ]; //
unsigned int realCodeSize; //
};

そして、関数をstd :: map <std :: string、proc>プロシージャに保存します。キーとしてファイルを使用し、関数の識別子を入力します。 ファイルを解析し、コンテナプロシージャを埋める方法を学ぶために残ります。 ここに:

bool parseFunction( std::ifstream &file, const char parsingAfter )
{
if( ( bf_fBeg != parsingAfter ) || ( file.bad() ) )
return false;
if( bf_fNme != ( char ) file.get() )
return false;
std::string funcName = parseName( file, bf_fNme );

if( 0 == funcName.size() )
return false;

proc addProc;
addProc.realCodeSize = 0;

while( file.good() )
{
char tmp = (char) file.get();

if( bf_cBeg == tmp )
skipComment( file );
else if ( bf_cEnd == tmp )
; // Syntax error
else if ( bf_fEnd == tmp )
{
std::map<std::string, proc>::iterator it = procedures.find( funcName );
if( it == procedures.end() )
{
std::pair< std::string, proc > tmpP;
tmpP.first = funcName;
tmpP.second = addProc;
procedures.insert( tmpP );
std::cout << "Added proceudre: " << funcName << std::endl;
printCode( addProc.code, addProc.realCodeSize, funcName );
return true;
}
else
{
std::cerr << "Function with name " << funcName
<< " already exists. Ignoring all other definitions."
<< std::endl;
return false;
}
}
else
addProc.code[ addProc.realCodeSize++ ] = tmp;

}
return false;
}


そのため、これらすべてを行うための多くの関数、コメント、ループ関数があります。
言語にさらに2文字を追加します。



それだけです。 ここで、コード内で記号「@」に遭遇すると、関数の名前が見つかり、その名前で実行する関数の本体が見つかります。 そのためのレジスタをコピーし、ループ関数を呼び出します。

ソースコード。

Source: https://habr.com/ru/post/J120279/


All Articles