<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 練習 34:分析器 > 原文:[Exercise 34: Analyzers](https://learncodethehardway.org/more-python-book/ex34.html) > 譯者:[飛龍](https://github.com/wizardforcel) > 協議:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) > 自豪地采用[谷歌翻譯](https://translate.google.cn/) 你現在有了一個解析器,它應該生成一個語法產生式對象樹。我會將其稱為“解析樹”,這意味著你可以從“解析樹的頂部開始,然后“遍歷”它,直到你訪問每個節點來分析整個程序。當你了解`BSTree`和`TSTree`數據結構時,你已經做了這樣的事情。你從頂部開始訪問了每個節點,并且你訪問的順序(深度優先,廣度優先,順序遍歷等)確定了節點的處理方式。你的解析樹具有相同的功能,編寫微型 Python 解釋器的下一步是遍歷樹并分析它。 分析器的任務是,在你的語法中找到語義錯誤,并修復或添加下一階段需要的信息。語義錯誤是錯誤,雖然語法正確,但并不適合 Python 程序。這可以是一個尚未定義的遍歷,也可以是不符合邏輯的代碼,它根本沒有意義。一些語言語法是如此松散,分析器必須做更多的工作來修復解析樹。其他語言很容易解析和處理,甚至不需要分析器的步驟。 為了編寫分析器,你需要一種方法來訪問解析樹中的每個節點,分析錯誤,并修復任何缺少的信息。有三種通用方法可以用于實現它: + 你創建一個分析器,它知道如何更新每個語法產生式。它將以和解析器相似的方式遍歷解析樹,對每種生產式類型都擁有一個函數,但他的任務是更改,更新和檢查產生式。 + 你改變你的語法產生式,讓他們知道如何分析自己的狀態。那么你的分析器就僅僅是一個引擎,它遍歷解析樹,調用每個產生式的`analyze()`方法。使用這種風格,你將需要一些狀態,它們會傳遞給每個語法產生式類,這個類應該是第三個類。 + 你創建一組單獨的類來實現最終分析后的樹,你可以將其傳遞給解釋器。通過許多方式,你將使用一組新的類來映射語法分析器的語法產生式,這些新的類接受全局狀態,語法產生式,并配置其`__init__`,使其為分析后的結果。 我建議你現在使用 #2 或 #3 來完成挑戰練習。 ## 訪客模式 “訪問者模式”是面向對象語言中非常常見的技術,其中你可以創建一些類,它們知道被“訪問”時應該做什么。這可以讓你將處理某個類的代碼集成到這個類。這樣做的優點是,你不需要大型的`if`語句來檢查類上的類型,來了解該做什么。相反,你只需創建一個類似于這個的類: ```py class Foo(object): def visit(self, data): # do stuff to self for foo ``` 一旦你擁有了這個類(`visit `可以叫任何東西),你可以遍歷列表來調用它。 ```py for action in list_of_actions: action.visit(data) ``` 你將使用這種模式用于 #2 或 #3 風格的分析器;唯一的區別是: + 如果你決定,你的語法產生式也將是分析結果,那么你的`analyze()`函數(也就是我們的`visit()`)只會將該數據存儲在產生式類,或者在提供給它的狀態中。 + 如果你決定,你的語法產生式將為解釋器生成另一組類(請參閱練習 35),那么每次`analyze`的調用都將返回一個新對象,該對象將放入列表中以供以后使用,或將其作為子樹附加到當前對象。 我將介紹第一種情況,其中你的語法產生式也是你的分析器結果。這適用于我們簡單的微型 Python 腳本,你應該遵循這種風格。如果你想嘗試其他的設計,那么你可以之后嘗試。 ## 簡短的微型 Python 分析器 > 警告 > 如果你想自己嘗試,為你的語法產生式嘗試實現訪客模式,那么你應該停在這里。我將給出一個相當完整但簡單的例子,它充滿了障礙。 訪客模式背后的概念似乎是奇怪的,但它是完全有意義的。每個語法產生式都知道在不同階段應該做什么,所以你可以把這個階段代碼放在需要的數據附近。為了演示這個,我寫了一個小型的偽造的`PunyPyAnalyzer`,它僅僅使用訪客模式打印出解析。我只完成一個語法產生式的樣例,所以你可以理解這是如何完成的。我不想給你太多的線索。 我做的第一件事是,定義一個`Production`類,我的所有語法產生式都將繼承它。 ```py class Production(object): def analyze(self, world): """Implement your analyzer here.""" ``` 它擁有我的初始的`analyze()`方法,并接受我們隨后使用的`PunyPyWorld`。第一個語法產生式的示例使`FuncCall`產生式: ```py class FuncCall(Production): def __init__(self, name, params): self.name = name self.params = params def analyze(self, world): print("> FuncCall: ", self.name) self.params.analyze(world) ``` 函數調用有名稱和`params`,它是一個`Parameters`產生式類,用于函數調用的參數。看看`analyze()`方法,你會看到第一個訪客函數。當你訪問`PunyPyAnalyzer`時,你將看到如何運行,但是請注意,此函數之后在每個函數的參數上調用`param.analyze(world)`: ```py class Parameters(Production): def __init__(self, expressions): self.expressions = expressions def analyze(self, world): print(">> Parameters: ") for expr in self.expressions: expr.analyze(world) ``` 這就產生了`Parameters`類,它包含每個表達式,它們組成函數的參數。`Parameters.analyze`僅僅遍歷它的表達式列表,其中我們擁有兩個: ```py class Expr(Production): pass class IntExpr(Expr): def __init__(self, integer): self.integer = integer def analyze(self, world): print(">>>> IntExpr: ", self.integer) class AddExpr(Expr): def __init__(self, left, right): self.left = left self.right = right def analyze(self, world): print(">>> AddExpr: ") self.left.analyze(world) self.right.analyze(world) ``` 在這個例子中,我只添加了兩個數字,但是我創建一個基本的`Expr`類,然后創建`IntExpr`和`AddExpr`類。每個都僅僅擁有`analyze()`方法,打印出其內容。 因此,我們有用于分析樹的類,我們可以做一些分析。我們需要的第一件事是一個世界,它可以跟蹤變量定義、函數、以及我們的`Production.analyze()`方法所需的其他東西。 ```py class PunyPyWorld(object): def __init__(self, variables): self.variables = variables self.functions = {} ``` 當調用任何`Production.analyze()`方法時,`PunyPyWorld`對象被傳遞給它,因此`analyze()`方法知道世界的狀態。它可以更新變量,尋找函數,并在世界中執行任何所需的事情。 然后我們需要一個`PunyPyAnalyzer`,它可以接受解析樹和世界,并使所有的語法產生式運行: ```py class PunyPyAnalyzer(object): def __init__(self, parse_tree, world): self.parse_tree = parse_tree self.world = world def analyze(self): for node in self.parse_tree: node.analyze(self.world) ``` 函數的簡單調用`hello(10 + 20)`的配置相當簡單。 ```py variables = {} world = PunyPyWorld(variables) # simulate hello(10 + 20) script = [FuncCall("hello", Parameters( [AddExpr(IntExpr(10), IntExpr(20))]) )] analyzer = PunyPyAnalyzer(script, world) analyzer.analyze() ``` 要確保你理解了我構造`script`的方式。注意到第一個參數是一個列表了嘛? ## 解析器與分析器 在這個例子中,我假設`PunyPyParser`已將`NUMBER`記號轉換為整數。在其他語言中,你可能只擁有記號,并讓`PunyPyAnalyzer`進行轉換。這一切都取決于,你想讓錯誤發生在哪里,以及哪里可以做最有用的分析。如果你將工作放在解析器中,那么你可以馬上給出格式化方面的早期錯誤。如果將其放在分析器中,那么你可以給出錯誤,使用整個解析文件來有所幫助。 ## 挑戰練習 所有這些`analyze()`方法的要點不僅僅是打印出來,而是改變每個`Production`子類的內部狀態,以便解釋器可以像腳本一樣運行它。你在這個練習中的任務是,接受你的語法產生式類(可能與我的不同)并進行分析。 隨意借鑒我的出發點。如果需要,可以使用我的分析器和我的世界,但是你應該嘗試首先編寫自己的分析器。你還應該將練習 33 中的產生式類與我的比較。你的更好嗎?它們能支持這種設計嗎?如果他們不能則改變它們。 你的分析器需要做一些事情才能使解釋器正常工作: + 跟蹤變量定義。在一個實際的語言中,這將需要一些非常復雜的嵌套表,但是對于微型 Python 來說,只需假設有一個巨大的表(`TSTree`或`dict`),所有變量都在這里。這意味著`hello(x, y)`函數的`x`和`y`參數實際上是全局變量。 + 跟蹤函數的位置,以便以后運行它們。我們的微型 Python 只有簡單的函數,但是當`Interpreter`運行時,它需要“跳轉”到并運行它們。最好的辦法保留它們,便于之后使用。 + 檢查你可以想到的任何錯誤,例如使用中缺少的變量。這是棘手的,因為 Python 這樣的語言,在解釋器階段中進行更多的錯誤檢查。你應該決定在分析過程中,可能出現哪些錯誤并實現它們。例如,如果我嘗試使用未定義的變量,會發生什么? + 如果你正確地實現了 Python `INDENT`語法,那么你的`FuncCall`產生式應該有額外的代碼。解釋器將需要它來運行它,所以確保有一個實現它的方式。 ## 研究性學習 + 這個練習已經很難了,但是如何創建一個更好的方式,來存儲變量,至少實現一個額外的作用域層級?記得“作用域”的概念是,`hello(x, y)`中的`x, y`不影響`hello`函數之外的你定義`x`和`y`。 + 在`Scanner`,`Parser`和`Analyzer`中實現賦值。這意味著我應該可以執行`x = 10 + 14`,你可以處理它。 ## 深入學習 研究“基于表達式”和“基于語句”的編程語言之間的區別。較短版本是一些只有表達式的語言,所以任何東西都有與之相關的某種(返回)值。其他語言的表達式擁有值,語句沒有,因此把它們賦給變量會失敗。Python 是哪種語言?
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看