8

バイトコードと解析ツリー、特に Perl で使用される解析ツリーの違いは何ですか? それらは実際に同じ概念を参照していますか、それとも違いがありますか?

私は Python と Java のバイトコードの概念に精通していますが、Perl について読んでいると、Perl は (バイトコードではなく) 解析ツリーをインタープリターで実行すると思われることを知りました。

実際に違いがある場合、Perl がバイトコードを使用しない (または Python が解析ツリーを使用しない) 理由は何ですか? それは主に歴史的なものですか、それとも異なるコンパイル/実行モデルを必要とする言語間に違いがありますか? バイトコード インタープリターを使用して Perl を (合理的な努力と実行パフォーマンスで) 実装できますか?

4

2 に答える 2

9

Perl が使用するのは解析ツリーではなく、少なくともウィキペディアで定義されている方法ではありません。オペコードツリーです。

>perl -MO=Concise -E"for (1..10) { say $i }"
g  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 49 -e:1) v:%,{,2048 ->3
f     <2> leaveloop vK/2 ->g
7        <{> enteriter(next->c last->f redo->8) lKS/8 ->d
-           <0> ex-pushmark s ->3
-           <1> ex-list lK ->6
3              <0> pushmark s ->4
4              <$> const[IV 1] s ->5
5              <$> const[IV 10] s ->6
6           <#> gv[*_] s ->7
-        <1> null vK/1 ->f
e           <|> and(other->8) vK/1 ->f
d              <0> iter s ->e
-              <@> lineseq vK ->-
8                 <;> nextstate(main 47 -e:1) v:%,2048 ->9
b                 <@> say vK ->c
9                    <0> pushmark s ->a
-                    <1> ex-rv2sv sK/1 ->b
a                       <#> gvsv[*i] s ->b
c                 <0> unstack v ->d
-e syntax OK

ただし、木と呼ばれていますが、実際には木ではありません。矢印に注意してください。これは、実際にはオペコードのリストのようなグラフであるためです (他の実行可能ファイルと同様)。

>perl -MO=Concise,-exec -E"for (1..10) { say $i }"
1  <0> enter
2  <;> nextstate(main 49 -e:1) v:%,{,2048
3  <0> pushmark s
4  <$> const[IV 1] s
5  <$> const[IV 10] s
6  <#> gv[*_] s
7  <{> enteriter(next->c last->f redo->8) lKS/8
d  <0> iter s
e  <|> and(other->8) vK/1
8      <;> nextstate(main 47 -e:1) v:%,2048
9      <0> pushmark s
a      <#> gvsv[*i] s
b      <@> say vK
c      <0> unstack v
           goto d
f  <2> leaveloop vK/2
g  <@> leave[1 ref] vKP/REFC
-e syntax OK

Perl のオペコードと Java のバイトコードの違いは、Java のバイトコードはシリアライズ可能 (ファイルに格納される) ように設計されていることです。

于 2012-05-02T15:35:42.903 に答える
7

解析ツリーはプログラムのトークンであり、ネストを示す構造に格納されます (どの引数がどの関数呼び出しに属しているか、どのステートメントがどのループ内にあるかなど)。一方、バイトコードはバイナリ表記に変換されたプログラム コードであり、より迅速に実行できます。仮想マシン。たとえば、架空の言語で次のコードがあるとします。

loop i from 1 to 10 {
    print i
}

解析ツリーは、たとえば次のようになります。

loop
    variable
        i
    integer
        1
    integer
        10
    block
        print
            variable
                i

一方、スタック指向の仮想マシン用にコンパイルされた生のシンボリック形式のバイトコードは次のようになります。

0x01 0x01    PUSH 1
             START:
0x02         DUP
0x03         PRINT
0x05         INCREMENT
0x02         DUP
0x01 0x0a    PUSH 10
0x04         LESSTHAN
0x06 0xf9    JUMPCOND START

プログラムをコンパイルするときは、まずソース コードを解析し (通常は解析ツリーを生成)、それをバイトコードに変換する必要があります。2 番目のステップをスキップして、解析ツリーから直接実行する方が簡単な場合があります。また、言語の構文が非常に複雑な場合 (たとえば、コードを変更できる場合) は、バイトコードの生成がより複雑になります。コードを実行するための eval 型関数がある場合、そのようなコードに仮想マシンを使用するには、コンパイラ全体をアプリケーションと共に配布する必要があります。パーサーのみを含める方が簡単です。

Perl の次のバージョンである Perl 6 では、コードはバイトコードにコンパイルされ、Parrot 仮想マシンで実行されることになっています。パフォーマンスの向上が期待されます。バイトコードは、C などのコンパイル済み言語の速度に近づくために、プロセッサのネイティブ命令 (これを JIT コンパイラと呼びます) にさらにコンパイルするのはかなり簡単です。

于 2012-05-02T15:25:24.667 に答える