19

私は現在、C++ インタープリターの上にあるプログラムを書いています。ユーザーは実行時に C++ コマンドを入力し、それがインタープリターに渡されます。特定のパターンでは、追加機能を提供できるように、与えられたコマンドを変更した形式に置き換えたいと考えています。

フォームの何かを置き換えたい

A->Draw(B1, B2)

MyFunc(A, B1, B2).

私が最初に考えたのは正規表現でしたがA、 、B1、 またはのいずれかB2が任意の C++ 式である可能性があるため、これはかなりエラーが発生しやすいものです。これらの式には引用符で囲まれた文字列や括弧が含まれている可能性があるため、すべてのケースを正規表現と一致させることは非常に困難です。さらに、この式には複数のネストされた形式が存在する場合があります。

次に考えたのは、clang をサブプロセスとして呼び出し、「-dump-ast」を使用して抽象構文ツリーを取得し、それを変更してから、C++ インタープリターに渡すコマンドに再構築することでした。ただし、これには、式を解析するのに十分な情報をclangに提供するために、インクルードファイルや前方宣言などの環境の変更を追跡する必要があります。インタプリタはこの情報を公開しないため、これも実行不可能に思えます。

3 番目の考えは、C++ インタープリター独自の内部解析を使用して抽象構文ツリーに変換し、そこから構築することでした。ただし、このインタープリターは、私が見つけた方法で ast を公開しません。

記載されたルートの 1 つに沿って、または完全に別のルートに沿って進む方法について何か提案はありますか?

4

6 に答える 6

3

あなたが欲しいのはプログラム変換システムです。これらは、基本的に次のようなソース レベル パターンで記述された、ソース コードへの変更を一般的に表現できるツールです。

 if you see *this*, replace it by *that*

ただし、抽象構文ツリーで動作するため、一致と置換のプロセスは、文字列ハッキングで得られるものよりもはるかに信頼できます.

このようなツールには、対象のソース言語用のパーサーが必要です。ソース言語が C++ であるため、これはかなり困難です。

Clang のような資格があります。結局、C++ を解析できます。OP オブジェクトは、すべての環境コンテキストなしでは実行できません。OPが(整形式の)プログラムフラグメント(ステートメントなど)をインタープリターに入力している限り、Clangは[私自身はあまり経験がありません]フラグメントが何であるかに集中するのに苦労するかもしれません(ステートメント? 式? 宣言? ...)。最後に、Clang は実際には PTS ではありません。そのツリー変更手順は、ソースからソースへの変換ではありません。これは利便性のために重要ですが、OP がそれを使用するのを止めることはないかもしれません。表面的な構文書き換え規則は便利ですが、手続き型ツリーのハッキングをより多くの労力でいつでも置き換えることができます。いくつかのルールがある場合、これは非常に重要になります。

Melt を使用した GCC は、Clang と同じように修飾されます。私は、Melt が GCC をせいぜいこの種の作業に耐えられないものにするという印象を受けています。YMMV。

完全な C++14 [2018 年 7 月編集: C++17] フロント エンドを備えたDMS ソフトウェア リエンジニアリング ツールキットは、完全に資格があります。DMS は、大規模な C++ コード ベースで大規模な変換を実行するために使用されてきました。

DMS は、構文カテゴリが何であるかを事前に知らされることなく、C++ の任意の (整形式の) フラグメントを解析し、そのパターン解析機構を使用して、適切な文法非終端型の AST を返すことができます。[解決方法を決定しなければならない曖昧さなどの複数の解析が発生する可能性があります。なぜ C++ を LR(1) パーサーで解析できないのですか? を参照してください。詳細な議論のために] 解析中にマクロ展開なしで生きていき、プリプロセッサ ディレクティブ (それらも解析される) がコード フラグメント (#if foo{#endif は許可されていません) ですが、対話的に入力されたコード フラグメントでは、これが実際の問題になる可能性はほとんどありません。

次に、DMS は、解析されたツリーを操作 (検索、検査、変更、ビルド、置換) するための完全な手続き型 AST ライブラリを提供し、変更されたツリーからサーフェス ソース コードを再生成して、インタープリターに供給する OP テキストを提供します。

この場合、OP が変更のほとんどをソース間の構文規則として直接記述できる可能性が高いことが際立っています。彼の例では、DMS に書き換えルールを提供できます (テストされていませんが、ほぼ正しい)。

rule replace_Draw(A:primary,B1:expression,B2:expression):
        primary->primary
    "\A->Draw(\B1, \B2)"     -- pattern
rewrites to
    "MyFunc(\A, \B1, \B2)";  -- replacement

DMS は、左側の "...Draw..." パターンを含む解析済みの AST を取得し、A、B1、および B2 の一致を置き換えた後、そのサブツリーを右側に置き換えます。引用符はメタクォートであり、C++ テキストとルール構文テキストを区別するために使用されます。バックスラッシュは、メタ変数に名前を付けるためにメタクォート内で使用されるメタエスケープです。ルール構文で指定できる内容の詳細については、DMS Rewrite Rulesを参照してください。

OPがそのようなルールのセットを提供する場合、DMS はセット全体を適用するように依頼できます。

だから私はこれがOPのためにうまくいくと思う. 彼がサードパーティに提供したいパッケージに「追加」するのは、かなり重いメカニズムです。DMS とその C++ フロント エンドは、ほとんど「小さな」プログラムではありません。しかし、最新のマシンには多くのリソースがあるため、OPがこれを行う必要があるかどうかの問題だと思います.

于 2016-01-28T09:20:09.077 に答える
0

ヘッダーを変更してメソッドを抑制してから、コンパイルするとエラーが見つかり、すべてのコアを置き換えることができます。

C++ インタープリターを持っている限り (CERN のルートとして)、コンパイラーを使用してすべての Draw をインターセプトする必要があると思います。これを行う簡単でクリーンな方法は、ヘッダーで Draw メソッドをプライベートとして宣言し、いくつかの定義を使用することです。

 class ItemWithDrawMehtod
 {
 ....
 public:
 #ifdef CATCHTHEMETHOD
     private:
 #endif
 void Draw(A,B);
 #ifdef CATCHTHEMETHOD
     public:
 #endif
 ....
 };

次に、次のようにコンパイルします。

 gcc -DCATCHTHEMETHOD=1 yourfilein.cpp
于 2015-09-06T17:40:16.167 に答える
0

ユーザーが複雑なアルゴリズムをアプリケーションに入力したい場合は、スクリプト言語をアプリに統合することをお勧めします。ユーザーがコード [定義された方法で関数/アルゴリズム] を記述できるため、アプリはインタープリターでそれを実行し、最終結果を取得できます。例: Python、Perl、JS など

インタープリターで C++ が必要なため、http://chaiscript.com/をお勧めします。

于 2015-09-16T13:51:20.223 に答える
-1

1 つの方法は、ユーザー コードを DLL (プラグインのようなもの) としてロードすることです。この方法では、実際のアプリケーションをコンパイルする必要はありません。ユーザー コードだけがコンパイルされ、アプリケーションがそれを動的にロードします。

于 2016-02-16T13:22:24.723 に答える