자바 컴파일 과정
- 소스 작성
- 컴파일
- 문법검사
- 어휘 분석
- 토근
- 예약어
- 자바에서 미리 정의된 키워드들입니다 (예: class, if, while 등)
- 식별자
- 변수, 메소드, 클래스 등의 이름입니다.
- 리터럴
- 상수 값들입니다 (예: 정수, 실수, 문자열, 불린 값 등)
- 연산자
- 수학, 관계, 논리 등의 연산자입니다 (예: +, -, *, /, ==, !=, &&, || 등).
- 구분자
- 코드의 구조를 나타내는 문자들입니다 (예: 괄호, 중괄호, 세미콜론 등).
자바 코드 예제: java Copy code public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } } 이 코드에서 추출되는 토큰들 중 일부는 다음과 같습니다: public: 예약어(reserved word)로 접근 제어자(access modifier)를 나타냅니다. 여기서는 클래스와 메소드에 대한 접근 제어자로 사용됩니다. class: 예약어로 클래스를 정의할 때 사용됩니다. HelloWorld: 식별자(identifier)로 클래스 이름을 나타냅니다. static: 예약어로 메소드가 클래스 메소드임을 나타냅니다. void: 예약어로 메소드의 반환 타입이 없음을 나타냅니다. main: 식별자로 메소드 이름을 나타냅니다. String: 예약어로 문자열 데이터 타입을 나타냅니다. args: 식별자로 메소드의 매개변수 이름을 나타냅니다. System: 식별자로 자바의 내장 클래스를 나타냅니다. out: 식별자로 System 클래스의 정적 변수를 나타냅니다. println: 식별자로 out 변수의 메소드 이름을 나타냅니다. "Hello, World!": 리터럴(literal)로 문자열 상수 값을 나타냅니다. (, ), {, }: 구분자(separators)로 코드의 구조를 나타내는 괄호와 중괄호입니다. ;: 구분자로 문장의 끝을 나타내는 세미콜론입니다. 이렇게 추출된 토큰들은 구문 분석기에 전달되어 코드의 문법 구조를 검사하는 데 사용됩니다.
- 예약어
- 토근
- 구문 분석
- 구문 분석(syntax analysis)은 어휘 분석에서 생성된 토큰들을 사용하여 소스 코드의 문법 구조를 검사하는 과정
- 구문 분석기(parser)
- 구문 분석기(parser)는 이 작업을 수행하며, 이 과정에서 문법 오류가 발견되면 컴파일 오류를 발생시킵니다. 구문 분석의 결과로 추상 구문 트리(Abstract Syntax Tree, AST)가 생성
자바 코드 예제: java Copy code int sum(int a, int b) { return a + b; } 이 코드의 추상 구문 트리는 다음과 같습니다: markdown Copy code FunctionDeclaration - Type: int - Identifier: sum - Parameters - Parameter - Type: int - Identifier: a - Parameter - Type: int - Identifier: b - Body - ReturnStatement - Expression - BinaryExpression - Left: Identifier (a) - Operator: + - Right: Identifier (b)
- 어휘 분석
- 위에서 보여드린 추상 구문 트리는 각 코드 요소와 관계를 계층 구조로 표현하고 있습니다. 이 트리는 구문 분석기가 소스 코드의 문법 구조를 이해하고, 이를 바탕으로 타입 검사, 바인딩, 최적화 등의 작업을 수행할 수 있게 합니다.
- 이 경우 구문 분석기는 누락된 세미콜론을 발견하고 컴파일 오류를 발생시킵니다. 이렇게 구문 분석은 소스 코드가 문법 규칙을 따르는지 검사하는 핵심 컴파일 과정
- 상위-하위 구문 분석
- 구문 분석기가 상위 요소(루트 노드)에서 시작하여 하위 요소(리프 노드)로 이동하는 방식입니다.
- 알기 쉽게 비유로 들면
- 상위-하위 구문 분석을 이해하기 쉽게 비유를 들어 설명해 드리겠습니다. 상위-하위 구문 분석은 마치 문장을 구성하는 단어와 구절을 이해하는 것과 비슷합니다. 예를 들어, 다음 문장을 생각해보세요.
The dog barks at the cat.- 이 문장은 명사, 동사, 전치사 등 다양한 품사로 구성되어 있습니다. 문장을 이해하기 위해서는 각 단어가 어떤 역할을 하는지 파악해야 합니다. 이를 위해 상위-하위 구문 분석을 사용할 수 있습니다. 상위-하위 구문 분석은 문장의 전체 구조를 이해하기 위해 상위 수준의 문법 요소(구, 절 등)부터 시작해서 하위 수준의 요소(단어, 품사 등)로 내려가며 분석합니다.
- 이제 다시 상위-하위 구문 분석에 대한 상세한 설명을 들어가겠습니다.
- 상위-하위 구문 분석은 가장 높은 수준의 문법 요소, 즉 문법의 시작점에서 시작합니다.
- 시작점에서 가능한 모든 문법 규칙을 시도하여 입력 문자열과 일치하는 규칙을 찾습니다.
- 일치하는 규칙이 발견되면, 그 규칙에 따라 하위 수준의 문법 요소로 내려갑니다.
- 하위 수준의 문법 요소에서 다시 가능한 모든 규칙을 시도하여 일치하는 규칙을 찾습니다.
- 이 과정을 계속 반복하며, 모든 입력 문자열이 처리되거나 더 이상 일치하는 규칙이 없을 때까지 내려갑니다.
- 이 방법으로, 상위 수준의 구조부터 차례대로 하위 수준의 구조로 내려가며 문법 요소를 분석하고 구문 트리를 구성할 수 있습니다. 이 과정에서 문법 오류가 발견되면 구문 분석이 실패하고 오류 메시지가 출력됩니다.
- Q(나) 상위에서 하위로 까지 여러 단계의 구문의 단계가 있는데 맨 처음 최상위 수준의 구문을 찾고 그 다음 단계의 구문을 전부 찾고 없으면 그다음 단계 수준의 구문.... 끝까지 최하위 단계의 구문을 찾고 이에서 필수 구문이 빠지거나 순서다르면 에러다?
- A(CHATGTP)네, 맞습니다! 상위-하위 구문 분석은 최상위 수준의 구문부터 시작하여 하위 수준의 구문으로 내려가며 전체 문법 구조를 이해하고 검증합니다. 각 단계에서 입력 문자열과 일치하는 문법 규칙을 찾고, 모든 규칙이 적용되어야만 문법적으로 올바른 코드라고 판단할 수 있습니다. 만약 필수 구문이 빠지거나 순서가 잘못되면 구문 분석이 실패하고 에러 메시지가 출력됩니다. 이를 통해 프로그래머가 문법 오류를 수정할 수 있게 도와줍니다.
- 알기 쉽게 비유로 들면
- 대표적인 상위-하위 구문 분석 방법으로는 재귀 하향 구문 분석(Recursive Descent Parsing)가 있다.
- 재귀적 함수 호출: 재귀 하향 구문 분석은 각 문법 규칙에 대응하는 파서 함수를 호출하며, 이 함수들은 필요에 따라 서로를 재귀적으로 호출합니다. 이렇게 하여 문법 구조를 따라 파싱이 진행됩니다.
- 순차적 검사: 재귀 하향 구문 분석에서는 가능한 문법 규칙을 순차적으로 검사하며, 일치하는 규칙을 찾으면 해당 규칙을 따라 파싱을 진행합니다.
- 백트래킹: 재귀 하향 구문 분석은 일치하는 문법 규칙을 찾지 못하면 이전 단계로 돌아가서 다른 규칙을 시도하는 백트래킹을 사용할 수 있습니다. 하지만 백트래킹은 성능에 부정적인 영향을 줄 수 있기 때문에, 대부분의 경우 예측 파서(predictive parser)를 사용하여 백트래킹이 필요 없도록 문법을 설계합니다.
- LL(1) 문법: 재귀 하향 구문 분석은 주로 LL(1) 문법을 사용합니다. LL(1) 문법은 왼쪽에서 오른쪽으로 파싱하고, 왼쪽 재귀를 허용하지 않으며, 한 번의 룩어헤드(lookahead)로 어떤 규칙을 적용할지 결정할 수 있는 문법입니다.
- LL(1): LL(1) 문법은 왼쪽에서 오른쪽(Left-to-right)으로 스캔하며, 왼쪽에서 오른쪽으로 파싱하는 구문 분석 알고리즘입니다. 약어 'LL'에서 첫 번째 'L'은 스캔 방향을 나타내고, 두 번째 'L'은 파싱 방향을 나타냅니다. LL(1) 문법은 재귀 하향 구문 분석 알고리즘과 함께 사용되며, 왼쪽 재귀를 허용하지 않습니다. 즉, 한 번의 룩어헤드로 어떤 문법 규칙을 적용할지 결정할 수 있어야 합니다.
- 룩어헤드(lookahead)는 구문 분석 과정에서 파서가 미리 다음에 나올 토큰(혹은 토큰들)을 살펴보는 기법입니다. 룩어헤드를 사용하면 파서가 현재 처리 중인 토큰 이후의 입력을 고려하여 적절한 문법 규칙을 결정할 수 있습니다.
- 룩어헤드는 일반적으로 LL(k)와 같은 문법에서 사용됩니다. 여기서 'k'는 룩어헤드하는 토큰 수를 나타냅니다. 예를 들어, LL(1) 문법에서는 현재 토큰을 처리한 후 바로 다음 토큰만 미리 보고(lookahead) 문법 규칙을 결정합니다. 이를 통해 파서는 여러 가지 문법 규칙 중 어떤 것을 적용할지 결정할 수 있습니다.
- 룩어헤드를 사용함으로써 파서는 문법 규칙 간의 충돌을 피하고, 처리 과정에서 더 나은 성능과 정확도를 달성할 수 있습니다. 하지만 룩어헤드 값이 클수록 구문 분석기의 복잡성이 증가하므로, 적절한 룩어헤드 값을 선택하는 것이 중요합니다. 대부분의 경우 LL(1)이나 LR(1) 문법이 충분한 성능과 간결함을 제공합니다.
- LL(1) or LR(1) → 한 글자씩 미리 예측
- 대부분 이 경우를 채택함
- 대개의 경우 효율적
- LL(2) or LR(2) → 두 글자씩 미리 예측
- LL(1) or LR(1) → 한 글자씩 미리 예측
- LL(1): LL(1) 문법은 왼쪽에서 오른쪽(Left-to-right)으로 스캔하며, 왼쪽에서 오른쪽으로 파싱하는 구문 분석 알고리즘입니다. 약어 'LL'에서 첫 번째 'L'은 스캔 방향을 나타내고, 두 번째 'L'은 파싱 방향을 나타냅니다. LL(1) 문법은 재귀 하향 구문 분석 알고리즘과 함께 사용되며, 왼쪽 재귀를 허용하지 않습니다. 즉, 한 번의 룩어헤드로 어떤 문법 규칙을 적용할지 결정할 수 있어야 합니다.
- 직관적이고 구현이 간단: 재귀 하향 구문 분석은 직관적인 방식으로 문법 구조를 따라 파싱을 진행하며, 구현이 상대적으로 간단하기 때문에 손쉽게 작성할 수 있습니다. 그러나 복잡한 문법이나 큰 입력에 대해서는 성능이 떨어질 수 있습니다.
- 구문 분석기가 상위 요소(루트 노드)에서 시작하여 하위 요소(리프 노드)로 이동하는 방식입니다.
- 하위-상위 구문 분석(상향식 구문 분석)
- 위에서 아래로 왼쪽에서 오른쪽으로 스캔하면서 구문 분석을 한다
- 파싱은 오른쪽에서 왼쪽으로 진행한다.
- 주어진 소스 코드나 데이터를 읽고 이해하는 과정
- 입력을 스캔하면서 토큰을 스택에 넣습니다.
- 스택의 상태와 룩어헤드 토큰을 기반으로 문법 규칙을 적용할지 결정합니다.
- 문법 규칙을 적용할 때 스택에서 토큰을 꺼내고 새로운 구문 구조를 스택에 쌓습니다. 이 과정을 "reduce"라고 합니다.
- 이 과정을 반복하여 최종적으로 문법 규칙을 만족하는 하나의 구조를 얻습니다
- 알기 쉽게 설명하자면
- Q
- 하향식 구문 분석은 이해했는데 상향식 구문 분석도 바로 이해가 안되네... 비유나 알기 쉽게 설명해준뒤 다시 설명해 줄 수 있겠어?
- A
-
- 상향식 구문 분석을 수행하는 것은 퍼즐 맞추기와 비슷합니다. 퍼즐 조각들이 입력 토큰이라고 가정해봅시다. 퍼즐을 맞추는 과정에서 조각들을 하나씩 확인하고, 서로 연결되는 조각들을 찾아 하나의 큰 퍼즐 조각으로 합칩니다. 이렇게 조각들을 계속 연결해 나가다 보면, 최종적으로 완성된 퍼즐을 얻게 됩니다.
- -상향식 구문 분석도 마찬가지로, 입력 토큰들을 하나씩 처리하면서 서로 연결되는 부분 구조를 찾습니다. 부분 구조들이 문법 규칙을 만족하면, 이를 하나의 더 큰 구조로 합칩니다(reduce). 이 과정을 반복하여 최종적으로 주어진 문법을 만족하는 하나의 구조를 얻습니다.
- 상향식 구문 분석도 문법검사가 목적이므로 결국 미리 정해진 경우의 수에 해당 되는 CASE가 아니라면 문법에러 - 컴파일 에러가 난다.
- 문법의 경우의 수가 많긴 하지만 경우의 수로 나누지 못할 정도로 불가능한 수는 아니기 때문
-
- Q
- 타입 검사
- 바인딩
- 최적화
- 문법검사
- java Copy code int sum(int a, int b) { return a + b } // 세미콜론이 누락되어 문법 오류 발생
- JVM실행
- 인터프리팅
- JIT(Just In Time)컴파일러
'[F-Lab 66해빗 페이백 챌린지 ]' 카테고리의 다른 글
| [F-lab 페이백 모각코 66일차] Java 스레드와 Java 스레드 생명주기 (0) | 2023.09.12 |
|---|---|
| [F-Lab 페이백 모각코 65일차] 객체 직렬화(Serialization)와 역직렬화(Deserialization) (0) | 2023.09.12 |
| [F-Lab 페이백 모각코 63일차] 클래스 로더 (Class Loader) (0) | 2023.09.09 |
| [F-Lab 페이백 모각코 62일차] SQL join 사용예제 (inner, left, right, outer,full) (0) | 2023.09.09 |
| [F-Lab 페이백 모각코 61일차] 소프트웨어 테스팅 (0) | 2023.09.09 |