私の理解では、パーサーは解析ツリーを作成し、その後それを破棄します。ただし、抽象構文ツリーをポップアウトすることもできます。これは、コンパイラが使用すると思われます。
私は、解析ツリーと抽象構文ツリーの両方が解析段階で作成されているという印象を受けています。次に、これらが異なる理由を誰かが説明できますか?
私の理解では、パーサーは解析ツリーを作成し、その後それを破棄します。ただし、抽象構文ツリーをポップアウトすることもできます。これは、コンパイラが使用すると思われます。
私は、解析ツリーと抽象構文ツリーの両方が解析段階で作成されているという印象を受けています。次に、これらが異なる理由を誰かが説明できますか?
回答:
解析ツリーは、具象構文ツリーとも呼ばれます。
基本的に、抽象ツリーの情報はコンクリートツリーの情報よりも少なくなります。具象ツリーには言語の各要素が含まれていますが、抽象ツリーには関心のない部分が捨てられています。
たとえば、次の式: (2 + 5) * 8
コンクリートはこんな感じ
  ( 2  + 5 )  * 8
  |  \ | / |  | |
  |   \|/  |  | |
   \___|__/   | |
       \______|/
一方、抽象ツリーには次のものがあります。
2  5 
 \/   
  +  8
   \/
   *
具体的なケースでは、括弧と言語のすべての部分がツリーに組み込まれています。抽象の場合、括弧はなくなりました。これは、その情報がツリー構造に組み込まれているためです。
最初に理解する必要があるのは、特定の方法でパーサーやコンパイラを書くことを強制する人はいないということです。具体的には、パーサーの結果がツリーである必要は必ずしもありません。入力を表すのに適した任意のデータ構造を使用できます。
たとえば、次の言語:
prog:
      definition 
    | definition ';' prog
    ;
definition: .....
定義のリストとして表すことができます。(Nitpickersはリストが縮退ツリーであることを指摘しますが、とにかく。)
次に、解析ツリー(またはパーサーが返したデータ構造)を保持する必要はありません。それどころか、コンパイラは通常、一連のパスとして構築され、前のパスの結果を変換します。したがって、コンパイラの全体的なレイアウトは次のようになります。
parser :: String             -> Maybe [Definitions]      -- parser
pass1  :: [Definitions]      -> Maybe DesugaredProg      -- desugarer
pass2  :: DesugaredProg      -> Maybe TypedProg          -- type checker
pass3  :: TypedProg          -> Maybe AbstractTargetLang -- code generation
pass4  :: AbstractTargetLang -> Maybe String             -- pretty printer
compiler :: String           -> Maybe String    -- transform source code to target code
compiler source = do
   defs  <- parser source
   desug <- pass1 defs
   typed <- pass2 desug
   targt <- pass3 typed
   pass4 targt
結論:構文解析ツリー、抽象構文ツリー、具体的な構文ツリーなどについて人々が話すのを聞いた場合、常に特定の目的に適したデータ構造に置き換えてください。