私の理解では、パーサーは解析ツリーを作成し、その後それを破棄します。ただし、コンパイラが使用していると思われる抽象構文ツリーをポップすることもできます。
構文解析ツリーと抽象構文ツリーの両方が構文解析段階で作成されているような印象を受けました。次に、なぜこれらが異なるのか誰かが説明できますか?
解析ツリーは、具象構文ツリーとも呼ばれます。
基本的に、抽象ツリーは具象ツリーよりも情報が少なくなります。具象ツリーには言語の各要素が含まれていますが、抽象ツリーには興味のない部分が破棄されています。
たとえば、次の式:(2 + 5) * 8
コンクリートはこんな感じ
( 2 + 5 ) * 8
| \ | / | | |
| \|/ | | |
\___|__/ | |
\______|/
一方、抽象ツリーには次のものがあります。
2 5
\/
+ 8
\/
*
具体的なケースでは、括弧と言語のすべての部分がツリーに組み込まれています。抽象ケースでは、情報がツリー構造に組み込まれているため、括弧がなくなりました。
最初に理解する必要があるのは、パーサーやコンパイラーを特定の方法で作成するように強制される人はいないということです。具体的には、パーサーの結果がツリーである必要があるとは限りません。入力を表すのに適した任意のデータ構造にすることができます。
たとえば、次の言語:
prog:
definition
| definition ';' prog
;
definition: .....
定義のリストとして表すことができます。 (Nitpickersはリストisが退化したツリーであることを指摘しますが、とにかく。)
第2に、解析ツリー(またはパーサーが返したデータ構造)を保持する必要はありません。逆に、コンパイラは通常、前のパスの結果を変換する一連のパスとして構築されます。したがって、コンパイラの全体的なレイアウトは次のようになります。
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
結論:人々がparse trees、abstract syntac trees、concrete syntax treesなどについて話すのを聞く場合は、常に与えられた目的に適したデータ構造、そしてあなたは元気です。