程式語言導論/意義的追尋
語義學是關注程式設計結構含義定義的領域。有幾種不同的符號來定義這種含義。以下是一些示例:
上述三種不同的技術提供了以形式化方式解釋程式語言中每個語法元素作用的方法。形式化意味著明確且可機械化。如果一個定義只有一種解釋方式,那麼它就是明確的。如果我們可以在機器上編寫該定義,那麼它就是可機械化的。因此,可以獲得程式語言的直譯器。
形式化有很多優點。首先,它有助於理解程式語言。程式的含義並不總是顯而易見的。即使是專家有時也會難以理解程式碼的行為。而且,在不同語言中實現的兩個非常相似的程式可能會有不同的行為。例如,讓我們考慮以下在 C 語言中實現的程式:
#include <stdio.h>
int main() {
int x = 1;
x += (x = 2);
printf("x = %d\n", x);
}
該程式列印x = 4。現在,讓我們看看另一個程式,這次在 Java 中實現:
public class T {
public static void main(String args[]) {
int x = 1;
x += (x = 2);
System.out.println("x = " + x);
}
}
該程式列印x = 3。令人驚訝,不是嗎?那麼,哪個程式是錯誤的?答案是:它們都沒有錯。它們都按照自己的語義做到了預期的效果。在 C 語言中,賦值x += (x = 2)被翻譯成類似以下的東西:x = (x = 2) + x。另一方面,在 Java 中,我們會得到類似以下的東西:tmp = x; x = 2; x = x + tmp.
我們將看到如何使用操作語義來描述程式語言的含義。操作語義透過抽象機來描述含義。抽象機是一個直譯器,即一臺機器。但是,這臺機器不是由螺栓和電線製成的。它是用數學構建的。這就是名稱中“抽象”的來源。
為了構建抽象機,我們需要一個數據結構來表示程式。我們已經知道這樣的資料結構:它被稱為語法樹,我們在解析過程中生成它。但是,語法樹中有許多元素對程式的含義沒有貢獻。例如,在 C 命令x = 1; x = x + 1;中,兩個分號對儲存在x中的最終值沒有太大影響。在這種情況下,分號的存在是為了幫助解析器識別命令的結束位置。同樣,表示式x * (y + z)中的括號等標記已經編碼在語法樹的結構中。在構建程式語言的直譯器時,我們實際上不需要跟蹤這些標記。因此,在設計直譯器時,我們需要一些我們稱為抽象語法的東西來表示程式。與具體語法不同,抽象語法只包含讓直譯器在程式結構中導航的必要元素。