3

私は Python 用のマクロ システムに取り組んでおり (ここで議論されているように)、私が検討してきたことの 1 つは測定単位です。測定単位はマクロを使用せずに実装することも、静的マクロを使用して実装することもできますが (たとえば、事前にすべての単位を定義するなど)、実行時に構文を動的に拡張できるようにするというアイデアをいじっています。

これを行うために、コンパイル時にコードに対して一種の部分評価を使用することを検討しています。構文のマクロが使用できないために特定の式の解析が失敗した場合、コンパイラは関数/ブロックの評価を停止し、未知の式があるスタブを使用して既に持っているコードを生成します。このスタブが実行時にヒットすると、関数は現在のマクロ セットに対して再コンパイルされます。このコンパイルが失敗すると、実行を続行できないため、解析エラーがスローされます。コンパイルが成功すると、古い関数が新しい関数に置き換えられ、実行が続行されます。

私が目にする最大の問題は、影響を受けるコードが実行されるまで解析エラーが見つからないことです。ただし、これは多くの場合には影響しません。たとえば、[]、{}、()、および `` などのグループ演算子は、ペアにする必要があります (トークナイザー/リスト パーサーの要件)、およびクラスや関数などのトップレベルの構文「実行時間」は実際にはロード時間であり、構文が評価され、オブジェクトが生成されるため、影響を受けません。

上記の実装の難しさと問題以外に、このアイデアにはどのような問題がありますか?

4

6 に答える 6

3

いくつかの考えられる問題を次に示します。

  • 問題が発生した場合に役立つエラー メッセージをユーザーに提供するのが難しい場合があります。コンパイル時の構文エラーは単なる構文の拡張である可能性があるため、これは可能性が高いようです。
  • パフォーマンスヒット。

Perl 6 での動的解析の長所、短所、および/または実装に関する議論を見つけようとしましたが、適切なものが見つかりませんでした。ただし、Nicklaus Wirth (Pascal およびその他の言語の設計者) からの次の引用は興味深いと思うかもしれません。

1960 年代のコンピューター科学者の空想には際限がありませんでした。自動構文解析とパーサー生成の成功に拍車をかけられて、柔軟な、または少なくとも拡張可能な言語のアイデアを提案した人もいました。概念は、プログラムの前に構文規則があり、それが後続のプログラムを構文解析しながら一般的なパーサーを導くというものでした。さらに一歩: 構文規則はプログラムの前にあるだけでなく、テキスト全体のどこにでも散在する可能性があります。たとえば、誰かが for ステートメントの特に派手なプライベート形式を使用したい場合、同じプログラムの異なるセクションで同じ概念の異なるバリアントを指定することでさえ、洗練された方法でそれを行うことができます。言語が人間同士のコミュニケーションに役立つという概念は完全にブレンドされていましたが、どうやら誰もがその場で自分の言語を定義できるようになったようです。しかし、これらの私的な建造物が何を意味するのかを特定しようとしたときに遭遇した困難によって、高い期待はすぐに弱められました. その結果、拡張可能な言語という魅力的なアイデアはすぐに消え去りました。

編集: Perl 6 のSynopsis 6: Subroutinesは、更新されたフォーマット済みのバージョンが見つからなかったため、残念ながらマークアップ形式です。「マクロ」で検索してください。残念ながら、あまり興味深いものではありませんが、Perl 6 のワンパス構文解析規則や抽象構文ツリーの構文など、関連するものを見つけることができます。Perl 6 がとるアプローチは、マクロは引数が解析された直後に実行され、AST または文字列のいずれかを返す関数であるというものです。Perl 6 は、ソースに実際に戻り値が含まれているかのように解析を続けます。エラー メッセージの生成について言及されていますが、マクロが AST を返す場合は問題ないように思われます。

于 2009-01-27T12:58:26.413 に答える
2

これをさらに一歩進めると、「遅延」解析を実行でき、常に次のステートメントを評価するのに十分なだけ解析できます。ある種のジャストインタイムパーサーのように。その場合、構文エラーは通常のランタイムエラーになり、周囲のコードで処理できる通常の例外が発生する可能性があります。

def fun():
   not implemented yet

try:
  fun()
except:
  pass

それは興味深い効果ですが、それが有用であるか望ましいかは別の問題です。一般に、現時点でコードを呼び出さなくても、エラーについて知っておくとよいでしょう。

マクロは、制御がマクロに到達するまで評価されず、当然、パーサーは以前のすべての定義をすでに知っています。また、マクロ定義では、プログラムがこれまでに計算した変数やデータを使用することもできます(以前に計算されたリストのすべての要素に構文を追加するなど)。しかし、これはおそらく、通常は言語で直接実行できることのために自己修正プログラムを書き始めるのは悪い考えです。これは混乱する可能性があります...

いずれの場合も、コードを1回だけ解析する必要があります。また、2回目に実行する場合は、パフォーマンスの問題が発生しないように、すでに解析済みの式を使用してください。

于 2009-01-27T21:22:02.730 に答える
2

以下は私の修士論文からのいくつかのアイデアです。役に立つかもしれないし、役に立たないかもしれません。論文は、自然言語の堅牢な解析に関するものでした。主なアイデア: 言語の文脈自由文法が与えられた場合、与えられたテキスト (または、あなたの場合は Python プログラム) を解析しようとします。解析が失敗した場合、部分的に生成された解析ツリーが作成されます。ツリー構造を使用して、解析されたテキストをより適切にカバーする新しい文法規則を提案します。私はあなたに論文を送ることができますが、あなたがヘブライ語を読まない限り、これはおそらく役に立たないでしょう.

一言で言えば、私はボトムアップ チャート パーサーを使用しました。このタイプのパーサーは、文法からプロダクションのエッジを生成します。各エッジは、消費されたツリーの部分でマークされます。各エッジは、完全なカバレッジにどれだけ近づいたかに応じてスコアを取得します。次に例を示します。

S -> NP . VP

スコアは 1/2 です (NP はカバーできましたが、VP はカバーできませんでした)。最高スコアのエッジは、新しいルール (X->NP など) を示唆しています。一般に、チャート パーサーは、一般的な LALR または LL パーサー (プログラミング言語で通常使用される型) よりも効率的ではありません - O(n) の複雑さではなく O(n^3) ですが、もう一度、より複雑なものを試しています。既存の言語を解析するだけです。そのアイデアで何かできることがあれば、詳細をお送りします。自然言語パーサーを見ると、別のアイデアが得られると思います。

于 2009-02-01T07:17:26.157 に答える
1

私が検討したもう1つのことは、これを全面的にデフォルトの動作にすることですが、言語(特定の言語を解析するためのマクロのセットを意味する)がコンパイル時に解析エラーをスローできるようにします。たとえば、私のシステムのPython2.5はこれを行います。

スタブのアイデアの代わりに、実行時にコンパイル時に完全に処理できなかった関数を再コンパイルするだけです。これにより、実行時にコードを変更して再コンパイルできるため、自己変更コードも簡単になります。

于 2009-01-23T21:09:05.950 に答える
0

あなたのアプローチはあまりうまくいかないと思います。疑似コードで書かれた簡単な例を見てみましょう:

define some syntax M1 with definition D1
if _whatever_:
    define M1 to do D2
else:
    define M1 to do D3
code that uses M1

そのため、実行時に構文の再定義を許可すると問題が発生する例が 1 つあります (このアプローチでは、M1 を使用するコードは定義 D1 によってコンパイルされるため)。構文の再定義が発生するかどうかを確認することは決定できないことに注意してください。過大近似は、ある種の型付けシステムまたは他の種類の静的分析によって計算される可能性がありますが、Python はこれについてはよく知られていません。

私を悩ませているもう1つのことは、あなたの解決策が正しく「感じられない」ということです。実行時に解析できるかもしれないという理由だけで、解析できないソースコードを保存するのは悪いことだと思います。

頭に浮かぶ別の例は次のとおりです。

...function definition fun1 that calls fun2...
define M1 (at runtime)
use M1
...function definition for fun2

技術的には、M1 を使用すると解析できないため、プログラムの残りの部分 (fun2 の関数定義を含む) をソース コードに保持する必要があります。プログラム全体を実行すると、fun2 が定義されていても呼び出すことができない呼び出しが表示されます。

于 2009-01-29T23:04:41.003 に答える
0

後で展開されるいくつかの文字シーケンス ノードを除いて、構文ツリーの残りの部分を解決できるように、入力テキストのビットを未知の構文で区切る必要があるでしょう。トップレベルの構文によっては、それで問題ない場合があります。

構文解析アルゴリズム、レクサー、およびそれらの間のインターフェースをすべて更新する必要があることに気付くかもしれません。これにより、ほとんどのコンパイラー作成ツールが除外される可能性があります。

(より一般的なアプローチは、この目的のために文字列定数を使用することです。これは、実行時に小さなインタープリターに解析できます)。

于 2009-01-27T13:52:40.563 に答える