まず、「コードの行」という言葉を考えてはいけません。パーサーへの入力は、トークンのストリームです。あなたdoにはレクサーがありますか? :-)
ASTが多くのステートメントにまたがっているのは完璧に(そして正常な)です。実際、それは通常、単一の関数やモジュール全体(読み込み:ソースファイル)など、コンパイル単位全体に及ぶでしょう。
たとえば、このプログラムを検討:
fun f(x, y) {
z = x + y;
if (z > 3) {
return z;
} else {
return g(x);
}
}
fun g(x) {
return x * 2;
}
コンパイラは次のようになりますASTを構築する可能性がある(詳細を無視して、一般的な構造に焦点を当ててください):
ModNode
FunNode[Name = "f"]
CompoundStmtNode
AssignStmtNode
VarNode[Name = "z"]
BinExprNode[Oper = "+"]
VarNode[Name = "x"]
VarNode[Name = "y"]
IfStmtNode
BinExprNode[Oper = ">"]
VarNode[Name = "z"]
ConstNode[Value = 3]
CompoundStmtNode
ReturnNode
VarNode[Name = "z"]
CompoundStmtNode
ReturnNode
CallNode[Name = "g"]
VarNode[Name = "x"]
FunNode[Name = "g"]
CompoundStmtNode
ReturnNode
BinExprNode[Oper = "*"]
VarNode[Name = "x"]
ConstNode[Value = 2]
ソースファイル全体のASTを構築する利点は、コードに対して複数の論理的なパスを簡単に実行できることです(たとえば、前方参照を解決するため)。もちろん、欠点は、ASTがかなり大きくなることです。
個々のステートメントASTをブロックステートメントまたは複合ステートメントASTに結合することができます。これは、他のサブツリーと関数/メソッドツリーを組み合わせて、プログラムツリー全体を完成させます(1つのAST全プログラム)。 –
アイデアは階層の上に1つのASTを持つことです。あなたが何らかのメソッドを呼びたくないときは、その大部分のASTで呼び出すことになります。再帰はその残りの部分を処理します。 –
One Big Treeアプローチを使用してください。ステートメントからなる左の子を持つ「sequential_statement」というツリー・ノードでこれを実装し、もう1つの「sequential_statement」ノードまたはダミーの「リストの終わり」ノードである右の子を実装します。 –