8

字节码和解析树之间到底有什么区别,特别是 Perl 使用的那个?它们实际上是指相同的概念,还是有区别?

我熟悉 Python 和 Java 中字节码的概念,但是在阅读 Perl 时,我了解到它应该在其解释器中执行解析树(而不是字节码)。

如果确实有区别,那么 Perl 不使用字节码(或 Python 不使用解析树)的原因是什么?主要是历史原因,还是需要不同编译/执行模型的语言之间存在差异?Perl(通过合理的努力和执行性能)可以通过使用字节码解释器来实现吗?

4

2 回答 2

9

Perl 使用的不是解析树,至少不是 Wikipedia 如何定义它。这是一个操作码树。

>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

编译程序时,您首先需要解析源代码(通常生成解析树),然后将其转换为字节码。跳过第二步并直接从解析树执行会更容易。此外,如果语言语法非常复杂(例如它允许修改代码),那么生成字节码会变得更加复杂。如果有一个 eval 类型的函数来执行任何代码,则必须将整个编译器与应用程序一起分发,才能将虚拟机用于此类代码。只包括一个解析器更简单。

在 perl 的下一个版本 Perl 6 中,代码应该被编译为字节码并在 Parrot 虚拟机上运行。预计会提高性能。字节码可以相当简单地进一步编译为处理器的本机指令(这称为 JIT 编译器),以接近 C 等编译语言的速度。

于 2012-05-02T15:25:24.667 回答