Token
パーサー自体、 s、およびs の3 つのクラスのオブジェクトを持つパーサーがありますState
。パーサーはレクサーからトークンを生成します。すべてがブラック ボックス化されているため、トークンはパーサーの状態やパーサーについて何も知らず、状態もトークンについて何も知りません。配置のかなり単純なバージョン:
class Parser {
public function parse() {
$this->state = new StEmpty;
while ($token = $this->lexer->get()) {
$this->state = $this->token->expect($this);
}
}
public function stateStart() {
return $this->state->stateStart();
}
}
class StartToken {
public function expect(Parser $parser) {
return $parser->stateStart();
}
}
class StEmpty {
public function stateStart() {
return new StStart;
}
}
私が直面している問題は、状態が変化したときに、パーサーが何らかのアクションを実行する必要がある場合があることです (終了規則トークンに到達したときにツリーに規則を追加するなど)。それを知っているのは だけState
なので、パーサーに何をすべきかを伝えるのは状態次第です。問題は を に取得するParser
ことState
です。Parser
状態コンストラクターにを挿入することはできますが、すべてState
がパーサーを必要とするわけではなく、多くの重複コードが発生します ( State
s の基本クラスParser
があり、保護されたメンバーである場合を除きますが、何かを拡張することは避けたいと考えています)。 )。必要なメソッドに を注入することもできParser
ますが、同様の問題があります。state
State
実装には、特定のメソッドのパーサーが必要です。
だから私の質問は、不必要な継承やコードの重複なしに、いつ必要なState
のかを知るにはどうすればよいですか? Parser
完全に受け入れられる別のクラスが必要な場合。
これを理解するのが難しい場合のために、「解きほぐした」バージョンを次に示します。
class Parser {
public function parse() {
$this->state = 'StEmpty';
while ($token = $this->lexer->get()) {
switch ($token) {
case 'StartToken':
switch ($this->state) {
case 'StEmpty':
$this->state = 'StStart';
break;
}
break;
}
}
}
}
この質問への答えは他の言語にも当てはまる可能性がありますが、オーバーロードを許可する言語でこれを行う方が簡単であることはわかっています。PHP はそうではありません。