0

私は現在、Ironyを使用して簡単な表現言語を作成する方法を学んでいます。関数のシグネチャを定義する最良の方法を見つけ出し、それらの関数への入力を検証するのが誰の責任であるかを判断するのに少し問題があります。

これまでのところ、私の言語の基本的な要素を定義する簡単な文法があります。これには、少数の二項演算子、括弧、数値、識別子、および関数呼び出しが含まれます。私の文法のBNFは次のようになります。

<expression> ::= <number> | <parenexp> | <binexp> | <fncall> | <identifier>
<parenexp>   ::= ( <expression> )
<fncall>     ::= <identifier> ( <argumentlist> )
<binexp>     ::= <expression> <binop> <expression>
<binop>      ::= + - * / %
... the rest of the grammar definition

Ironyパーサーを使用して、さまざまな入力文字列の構文を検証し、それらがこの文法に準拠していることを確認できます。

x + y / z * AVG(a + b, p)   -> Valid Syntax
x +/ AVG(x                  -> Invalid Syntax

それはすべてうまくいっていますが、今度はさらに一歩進んで、各関数に必要なパラメーターの数とともに、使用可能な関数を定義したいと思います。たとえば、 1つのパラメーターを受け入れ、2つのパラメーターをFOO受け入れる関数が必要です。BAR

FOO(a + b) * BAR(x + y, p + q)    -> Valid
FOO(a + b, 13)                    ->  Invalid

2番目のステートメントが解析されるときに、この関数の予想される入力を認識しているエラーメッセージを出力できるようにしたいと思います。

Too many arguments specified for function 'FOO'

これらのステートメントを実際に評価する必要はありません。ステートメントの構文を検証し、それらが有効な式であるかどうかを判断するだけです。

これをどの程度正確に行う必要がありますか?技術的には、次のように関数を文法に追加するだけでよいことを知っています。

<foofncall> ::= FOO( <expression> )
<barfncall> ::= BAR( <expression>, <expression> )

しかし、これについての何かは完全に正しく感じられません。私には、文法はジェネリック関数呼び出しのみを定義する必要があり、言語で使用可能なすべての関数を定義する必要はないようです。

  • これは通常、他の言語でどのように達成されますか?
  • 言語文法の基本的な構文と関数定義のようなより具体的な要素を分析する責任を処理する必要があると呼ばれるコンポーネントは何ですか?両方の責任を同じコンポーネントで処理する必要がありますか?
4

1 に答える 1

2

文法で直接タイプチェックインを実行できるため、パーサーで強制されますが、一般的にはそうすることはお勧めできません。代わりに、パーサーは基本構文を解析するだけでよく、タイプチェックには別のタイプチェックコードを使用する必要があります。

コンパイラの通常の場合、パーサーは抽象構文木またはプログラムの同等の表現を生成するだけです。次に、タイプチェックパスがASTに対して実行され、すべての型が適切に一致することを確認します。関数に適切な数の引数があり、それらの引数に適切な型があることを確認します。また、変数に割り当てられたものに対して適切な型があることを確認します。それらとそれらがどのように使用されるか。

一般的に単純であることに加えて、これは通常、より良いエラーメッセージを与えることを可能にします-単に「無効」の代わりに、「FOOに対する引数が多すぎる」またはあなたが何を持っているかを言うことができます。

于 2012-11-07T01:08:54.557 に答える