跳轉到內容

鸚鵡虛擬機器/Squaak教程/總結和結論

來自華夏公益教科書,開放的書籍,為開放的世界

歡迎來到鸚鵡編譯器工具教程的最後一集!讓我們回顧一下之前的章節,並總結一下這個教程。

第1集中,我們介紹了鸚鵡編譯器工具(PCT),概述了Squaak的高階功能,Squaak是我們在這個教程中實現的案例研究語言,我們還生成了一個語言shell,作為實現Squaak的基礎。

第2集討論了基於PCT的編譯器的一般結構。在此之後,我們描述了四個預設編譯階段:解析階段、解析樹到PAST、PAST到POST和POST到PIR。我們還在互動式語言shell中添加了一個命令列橫幅和命令列提示符。

第3集中,我們介紹了Squaak語言的完整語法。之後,我們開始實現第一部分,然後我們能夠為(簡單的)賦值生成程式碼。

第4集中,我們更詳細地討論了鸚鵡抽象語法樹節點的構建,之後我們實現了if語句和throw語句。

第5集重點介紹了變數宣告和變數作用域。我們實現了必要的基礎設施來正確處理全域性和區域性變數。在第6集中,我們繼續討論作用域,但現在是在子程式的上下文中。之後我們實現了子程式呼叫。

第7集擴充套件了我們的語法以處理複雜的表示式,使我們能夠使用算術運算子和其他運算子。我們討論瞭如何使用PCT的內建支援來處理運算子優先順序。

在上一集第8集中,我們討論了處理Squaak的聚合資料型別的語法和操作方法:陣列和雜湊。我們還提到了按引用和按值傳遞引數的主題。

如果你完成了教程並做了練習,你的實現應該已經完成。雖然討論了很多實現,但有些部分留作了習題。這是為了鼓勵你親自動手,自己弄清楚事情,而文字中包含足夠的提示(在我看來)來解決給定的問題。當然,這種方法要求你花更多的時間,自己思考,但我認為你閱讀所有這些內容是為了學習一些東西。在我看來,花更多的時間是值得的。

現在是看看我們能用這種語言做什麼的時候了。Squaak不僅僅是初學者在解析器討論中經常提供的平均計算器示例;它是一種完整的程式語言。

下一步是什麼?

[編輯 | 編輯原始碼]

這是鸚鵡編譯器工具教程的最後一集。我們展示瞭如何在短短幾百行原始碼中為鸚鵡虛擬機器實現一種完整的語言。當然,這必須證明PCT確實是一個實現語言的有效工具包。在撰寫本文時,PCT仍然缺乏對某些語言結構的有效支援。因此,我們專注於那些使用PCT很容易構建的部分。一旦PCT功能完備,肯定會發布另一個關於高階功能的教程。想想面向物件程式設計、閉包、協程以及高階控制流,如return語句。大多數功能已經可以實現,但對於這個教程的水平來說太複雜了。

生命遊戲

[編輯 | 編輯原始碼]

你可能已經注意到Squaak有點像Lua,雖然它在某些方面有所不同。這並非完全偶然。在Lua原始碼的釋出中,有一個名為“life.lua”的示例,它實現了康威的“生命遊戲”。這是一個很好的演示程式,很容易移植到Squaak。它的實現如下所示。執行它,享受吧!

    ## John Conway's Game of Life
    ## Implementation based on life.lua, found in Lua's distribution.
    ##
    var width          = 40    # width of "board"
    var height         = 20    # height of "board"
    var generation     = 1     # generation couner{{typo help inline|reason=similar to cooner|date=September 2022}}
    var numgenerations = 50    # how often should we evolve?

    ## initialize board to all zeroes
    sub initboard(board)
        for var y = 0, height do
            for var x = 0, width do
                board[y][x] = 0
            end
        end
    end

    ## spawn new life in board, at position (left, top),
    ## the life data is stored in shapedata, and shape width and
    ## height are specified.
    sub spawn(board, left, top, shapew, shapeh, shapedata)
        for var y = 0, shapeh - 1 do
            for var x = 0, shapew - 1 do
                board[top + y][left + x] = shapedata[y * shapew + x]
            end
        end
    end

    ## calculate the next generation.
    sub evolve(thisgen, nextgen)
        var ym1 = height - 1
        var y   = height
        var yp1 = 1
        var yi  = height

        while yi > 0 do
            var xm1 = width-1
            var x   = width
            var xp1 = 1
            var xi  = width

            while xi > 0 do

                var sum = thisgen[ym1][xm1]
                        + thisgen[ym1][x]
                        + thisgen[ym1][xp1]
                        + thisgen[y][xm1]
                        + thisgen[y][xp1]
                        + thisgen[yp1][xm1]
                        + thisgen[yp1][x]
                        + thisgen[yp1][xp1]

                nextgen[y][x] = sum==2 and thisgen[y][x] or sum==3

                xm1 = x
                x   = xp1
                xp1 = xp1 + 1
                xi  = xi - 1
            end

            ym1 = y
            y   = yp1
            yp1 = yp1 + 1
            yi  = yi - 1

        end
    end

    ## display thisgen to stdout.
    sub display(thisgen)
        var line = ""
        for var y = 0, height do
            for var x = 0, width do
                if thisgen[y][x] == 0 then
                    line = line .. "-"
                else
                    line = line .. "O"
                end

            end
            line = line .. "\n"
        end
        print(line, "\nLife - generation: ", generation)
    end

    ## main program
    sub main()
        var heart   = [1,0,1,1,0,1,1,1,1]
        var glider  = [0,0,1,1,0,1,0,1,1]
        var explode = [0,1,0,1,1,1,1,0,1,0,1,0]

        var thisgen = []
        initboard(thisgen)

        var nextgen = []
        initboard(nextgen)

        spawn(thisgen,3,5,3,3,heart)
        spawn(thisgen,5,4,3,3,glider)
        spawn(thisgen,25,10,3,4,explode)

        while generation <= numgenerations do
            evolve(thisgen, nextgen)
            display(thisgen)
            generation = generation + 1

            ## prevent switching nextgen and thisgen around,
            ## just call evolve with arguments switched.
            evolve(nextgen, thisgen)
            display(nextgen)
            generation = generation + 1

        end
    end

    ## start here.
    main()

注意使用子程式“print”。檢視檔案src/builtins/say.pir,並將子程式“say”(由語言shell建立指令碼生成)重新命名為“print”。

解決方案

[編輯 | 編輯原始碼]

如果你不想做練習,或者只是想看看它是什麼樣子,而不想做任何麻煩,這裡就是它是什麼樣子(這是生命第9代)。

    -----------------------------------------
    -----------------------------------------
    -----------------------------------------
    -----O-----------------------------------
    ----OO-----------------------------------
    --OO--O----------------------------------
    -----OO----------------------------------
    --OOOOO------------------OOO-------------
    ------------------------O---O------------
    -----------------------O-----O-----------
    ----------------------O---O---O----------
    ----------------------O--O-O--O----------
    ----------------------O---O---O----------
    -----------------------O-----O-----------
    ------------------------O---O------------
    -------------------------OOO-------------
    -----------------------------------------
    -----------------------------------------
    -----------------------------------------
    -----------------------------------------
    -----------------------------------------

    Life - generation: 9

但實際上,它與在鸚鵡上執行這個程式相比是無法比擬的。:-)

Squaak被設計成一種簡單的語言,提供足夠的功能來完成一些工作,但同時保持簡單。當然,在閱讀完本教程之後,你也是專家了;-) 如果你想新增更多功能,這裡有一些建議。

  • 實現字首和字尾自增/自減運算子,允許你寫“generation++”而不是“generation = generation + 1”。
  • 實現自增賦值運算子,例如“+=”及其同類。
  • 擴充套件語法以允許在一個語句中宣告多個變數,允許你寫“var x = 1, y, z =3”。當然,初始化部分仍然是可選的。你如何確保識別符號和初始化表示式保持在一起?
  • 實現一種機制(如“import”語句)來包含或載入另一個Squaak檔案,這樣Squaak程式就可以拆分成多個檔案。PCT不支援此功能,因此你需要編寫一些PIR來實現它。
  • 改進for語句,以允許負步長。請注意,這樣做會使迴圈條件變得更加複雜。

請注意,這些只是建議,我沒有親自實現它們,因此在最後我不會提供解決方案。

結語和致謝

[編輯 | 編輯原始碼]

到目前為止,你應該對PCT有了很好的印象,並且應該能夠處理針對鸚鵡的其他語言。目前,已經針對ECMAScript、Python、Ruby以及當然還有Perl 6完成了工作。它們中的大多數尚未完成(提示,提示)。

我希望你喜歡閱讀本教程,並且學到了足夠的知識,讓你對處理其他(現有)針對鸚鵡的語言充滿信心。Perl 6的實現仍然需要更多貢獻者!

非常感謝所有閱讀本教程併為我提供提示、技巧和反饋的人!感謝你閱讀本教程!

華夏公益教科書