興味深いことに、私は、一般的な仕組みを理解したら、簡単な言語のパーサーの書き方を学び、最終的には自分の小さなコードゴルフ言語のインタープリターを書きたいと思っています。
そこで私はダグラス・クロックフォードの記事を読み始めましたトップダウン演算子の優先順位。
注: 以下のコードスニペットのコンテキストをより深く理解したい場合は、この記事を読むことをお勧めします。
var
ステートメントと代入演算子がどのように連携して動作するのか理解できません=
。
DCは次のような代入演算子を定義する。
var assignment = function (id) {
return infixr(id, 10, function (left) {
if (left.id !== "." && left.id !== "[" &&
left.arity !== "name") {
left.error("Bad lvalue.");
}
this.first = left;
this.second = expression(9);
this.assignment = true;
this.arity = "binary";
return this;
});
};
assignment("=");
注: [[value]] はトークンを参照し、その値を簡略化します
ここで、式関数が例えば に達すると[[t]],[[=]],[[2]]
、 の結果は[[=]].led
次のようになります。
{
"arity": "binary",
"value": "=",
"assignment": true, //<-
"first": {
"arity": "name",
"value": "t"
},
"second": {
"arity": "literal",
"value": "2"
}
}
DCはassignment
関数を作るので
2つの追加処理を行いたい。左辺のオペランドを調べて、それが適切な左辺値であることを確認する。割り当てメンバーを設定する我々はできるように後ですぐに特定する代入文。
var
彼が次のように定義する声明を紹介するところまでは、私には意味が分かります。
var ステートメントは、現在のブロック内の 1 つ以上の変数を定義します。各名前の後にオプションで = と初期化式を続けることができます。
stmt("var", function () {
var a = [], n, t;
while (true) {
n = token;
if (n.arity !== "name") {
n.error("Expected a new variable name.");
}
scope.define(n);
advance();
if (token.id === "=") {
t = token;
advance("=");
t.first = n;
t.second = expression(0);
t.arity = "binary";
a.push(t);
}
if (token.id !== ",") {
break;
}
advance(",");
}
advance(";");
return a.length === 0 ? null : a.length === 1 ? a[0] : a;
});
ここで、パーサーが次のようなトークンのセットに到達すると、[[var]],[[t]],[[=]],[[1]]
生成されたツリーは次のようになります。
{
"arity": "binary",
"value": "=",
"first": {
"arity": "name",
"value": "t"
},
"second": {
"arity": "literal",
"value": "1"
}
}
私の質問の鍵となる部分は、そのif (token.id === "=") {...}
部分です。
なぜ電話するのか分からない
t = token;
advance("=");
t.first = n;
t.second = expression(0);
t.arity = "binary";
a.push(t);
それよりも
t = token;
advance("=");
t.led (n);
a.push(t);
その部分で...
。
[[=]]
これは演算子led
関数を呼び出す(代入関数)、これは
それが適切な左辺値であることを確認し、割り当てメンバーを設定する我々はできるように後ですぐに特定する代入文。例えば
{
"arity": "binary",
"value": "=",
"assignment": true,
"first": {
"arity": "name",
"value": "t"
},
"second": {
"arity": "literal",
"value": "1"
}
}
lbp
0 から 10 までの値を持つ演算子はないので、呼び出してexpression(0) vs. expression (9)
も違いはありません。( !(0<0) && !(9<0) && 0<10 && 9<10)
)
また、条件により、または のいずれかとしてtoken.id === "="
オブジェクト メンバーに代入されることが防止され、 は呼び出されなくなります。token.id
'['
'.'
t.led
私の質問は簡単に言うと次のとおりです。
led
変数宣言の後にオプションで実行できる代入演算子の使用可能な関数を呼び出さずに、代わりにステートメントのfirst
およびメンバーを手動で設定し、メンバー を設定しないのはなぜですか?second
assignment
ベストアンサー1
言語を解析するときには、意味と構文の 2 つが重要です。
意味的に、同一ではないにしてvar x=5;
もvar x;x=5
非常に似ているように見えます (どちらの場合も、最初に変数が宣言され、次にその宣言された変数に値が割り当てられるため)。これがあなたが観察したものであり、大部分は正しいです。
文法的にしかし、この 2 つは異なります (これは明らかにわかります)。
自然言語では、類似したものは次のようになります。
- その少年はリンゴを持っています。
- リンゴがあります。少年はそれを持っています。
では簡潔に!2つの例を見てみましょう。
これら 2 つは (ほぼ) 同じ意味ですが、明らかに同じ文ではありません。JavaScript に戻ります。
1つ目var x=5
は次のように読む:
var x = 5
-----------------------VariableStatement--------------------
var ------------------- VariableDeclarationList
var ------------------- VariableDeclaration
var Identifier ------- Initialiser(opt)
var ------------------- x = AssignmentExpression
var ------------------- x ------------ = LogicalORExpression
var ------------------- x ------------ = LogicalANDExpression
var ------------------- x ------------ = BitwiseORExpression
var ------------------- x ------------ = BitwiseXORExpression
var ------------------- x ------------ = BitwiseANDExpression
var ------------------- x ------------ = EqualityExpression
var ------------------- x ------------ = ShiftExpression
var ------------------- x ------------ = AdditiveExpression
var ------------------- x ------------ = MultiplicativeExpression
var ------------------- x ------------ = UnaryExpression
var ------------------- x ------------ = PostfixExpression
var ------------------- x ------------ = NewExpression
var ------------------- x ------------ = MemberExpression
var ------------------- x ------------ = PrimaryExpression
var ------------------- x ------------ = Literal
var ------------------- x ------------ = NumericLiteral
var ------------------- x ------------ = DecimalLiteral
var ------------------- x ------------ = DecimalDigit
var ------------------- x ------------ = 5
ふう!解析するには、これらすべてを構文的に行う必要がありましたvar x = 5
。確かに、その多くは式の処理ですが、それが現状です。他のバージョンを確認してみましょう。
これは 2 つのステートメントに分かれます。var x; x = 5
最初のステートメントは次のとおりです。
var x
--------VariableStatement---
var ---- VariableDeclarationList
var ---- VariableDeclaration
var Idenfifier (optional initializer not present)
var x
2 番目の部分はx=5
代入文です。同じ表現の狂気を続けることはできますが、ほとんど同じです。
結論として、公式言語の文法で指定されているように、意味的にも構文的にも 2 つは同じ結果を生成しますが、両者は異なります。この場合、結果は確かに同じです。