Abstract Syntax Tree, 추상 구문 트리
코드의 구문적 요소를 노드로 표현
예제 코드:
let x: number = 5;
이 코드는 다음과 같은 구문적 요소로 분해
VariableDeclaration
)Identifier
, 즉 x
)TypeAnnotation
, 즉 number
)AssignmentExpression
)Literal
, 즉 5
)이 각 요소가 AST의 노드로 표현
VariableDeclaration
├── Identifier (name = "x")
├── TypeAnnotation (type = "number")
└── Literal (value = 5)
이 트리 구조에서 상위 노드는 하위 노드들을 포함하며, 이러한 노드들의 계층적 관계는 코드의 구문적 구조를 그대로 반영 (6.3 컴파일러의 구조가 이 얘기를 하는 것)
노드의 계층적 관계
AST는 트리 구조를 가지므로 계층적(hierarchical) 관계를 통해 코드의 구조적 요소들을 표현
이러한 계층 관계 덕분에 컴파일러는 코드의 구문을 쉽게 구분하고 이해 가능
상위 노드(Parent Node)는 특정 구문을 대표하고, 이 구문에 속한 하위 노드(Child Node)들이 존재
예제: 함수 호출
function greet(name: string) {
console.log("Hello, " + name);
}
greet("World");
이 코드를 AST로 표현하면:
FunctionDeclaration
├── Identifier (name = "greet")
├── ParameterList
│ └── Parameter (name = "name", type = "string")
├── BlockStatement
│ └── ExpressionStatement
│ └── CallExpression
│ ├── MemberExpression (object = "console", property = "log")
│ └── BinaryExpression
│ ├── Literal (value = "Hello, ")
│ └── Identifier (name = "name")
CallExpression
├── Identifier (name = "greet")
└── Literal (value = "World")
이 AST를 통해 컴파일러는:
FunctionDeclaration
이 함수 선언을 의미BlockStatement
)이 존재console.log
가 호출되는 것을 알 수 있음계층적 구조 덕분에 컴파일러는 어떤 코드가 어떤 구문에 속해 있는지, 어떤 코드가 어떤 작업을 수행하는지 쉽게 이해할 수 있음
AST에서 각 노드는 코드의 구문 요소(Syntax Element)에 해당하며, 특정한 타입을 가짐
예를 들어, if
문은 IfStatement
타입의 노드로, 함수 선언은 FunctionDeclaration
타입의 노드로 표현
일반적인 구문 요소와 이에 해당하는 AST 노드 타입의 예시:
VariableDeclaration
FunctionDeclaration
CallExpression
IfStatement
ForStatement
, WhileStatement
Literal
BinaryExpression
(예: +
, `` 등의 연산자)예제: 조건문
if (x > 10) {
console.log("x is greater than 10");
}
AST는 다음과 같이 구문적 구조를 표현:
IfStatement
├── BinaryExpression (operator = ">")
│ ├── Identifier (name = "x")
│ └── Literal (value = 10)
└── BlockStatement
└── ExpressionStatement
└── CallExpression
├── MemberExpression (object = "console", property = "log")
└── Literal (value = "x is greater than 10")
IfStatement
노드는 조건문을 표현하고, 그 하위에는 BinaryExpression
(이진 연산자) 노드와 BlockStatement
(코드 블록) 노드가 위치
구문적 구조를 통한 의미 이해
컴파일러가 AST를 통해 코드의 구문적 구조를 이해할 수 있는 이유는 AST가 코드의 구문적 요소를 명확하게 구분하고, 그 관계를 트리 형태로 표현하기 때문
이는 구문을 해석하고 그 의미를 이해하기 위한 가장 효율적인 방법
예: 복잡한 함수 호출
let result = add(2, multiply(3, 4));
이 코드는 다음과 같이 AST로 표현:
VariableDeclaration
├── Identifier (name = "result")
└── CallExpression (callee = "add")
├── Literal (value = 2)
└── CallExpression (callee = "multiply")
├── Literal (value = 3)
└── Literal (value = 4)
CallExpression
노드를 통해 컴파일러는 add
함수가 호출되었음을 알고, 그 인자로 2
와 multiply(3, 4)
가 전달된 것을 파악