Definition
Compiler là một chương trình giúp biên dịch những đoạn code ở ngôn ngữ cấp cao thành ngôn ngữ máy. Input của compiler là source program và output được gọi là object code. Một vài compiler còn sử dụng ngôn ngữ assembly như một ngôn ngữ trung gian.
Compilation Phases
Các giai đoạn trong quá trình biên dịch:
Analysis Phase
Lexical Analysis
Scan qua mã nguồn để tạo thành các lexeme, là các đơn vị trừu tượng nhỏ nhất của ngôn ngữ lập trình.
Ví dụ:
String greeting = "hello";
Trong ví dụ trên có 5 lexeme là:
String
greeting
=
"hello"
;
Các lexeme sau đó sẽ được nhóm thành các token, là một đối tượng giúp mô tả một lexeme. Cụ thể, nó sẽ cho biết lexeme mang ý nghĩa gì, chẳng hạn như cho biết lexeme đó là một keyword, tên biến hoặc string literal.
Syntax Analysis (parsing)
Syntax analyzer (parser) kiểm tra các syntax error và tạo ra AST (abstract syntax tree) từ các token giúp biểu diễn cấu trúc logic của một chương trình.
Semantic Analysis
Duyệt qua AST ở bước trên để kiểm tra các lỗi logic chẳng hạn như:
- Gán dữ liệu có kiểu khác với kiểu dữ liệu của biến.
- Dùng các biến chưa khai báo.
- Dùng các keyword của ngôn ngữ làm biến.
Output của nó là một AST có thêm các annotation giúp mô tả giá trị của các node trong AST.
Synthesis Phase
Intermediate Code Generation
Tạo ra intermediate code (hay intermediate representation - IR). IR phải có các tính chất:
- Dễ tạo ra.
- Dễ biên dịch thành ngôn ngữ máy.
Optimization
Optimize code bằng cách xóa bỏ những dòng không cần thiết hoặc sắp xếp lại code. Ý nghĩa của source code sẽ không bị thay đổi.
Quá trình này cần:
- Tập trung vào việc giảm thiểu tài nguyên sử dụng cũng như là giúp tăng tốc quá trình thực thi của chương trình.
- Không ảnh hưởng quá nhiều đến tổng thời gian biên dịch.
Code Generation
Chuyển đổi AST thành dạng machine-readable code.
Minh họa:
Symbol Table and Error Handler
Tất cả các phase của compiler đều tương tác với:
- Symbol table: là một bảng giúp lưu những thông tin quan trọng phục vụ cho việc truy xuất nhanh chẳng hạn như tên biến, tên hàm hoặc tên interface.
- Error handler: có nhiệm vụ phát hiện và báo cáo lỗi có trong source program. Nếu trong quá trình biên dịch có lỗi xảy ra thì compiler sẽ dừng lại và thông báo lỗi.
Why Do We Need Compilers?
- Cho phép code những ngôn ngữ lập trình cấp cao có cú pháp gần với ngôn ngữ tự nhiên hơn.
- Cho phép tạo ra file
.exe
mà có thể được thực thi nhiều lần. Điều này giúp chương trình chạy nhanh hơn và hiệu quả hơn so với việc thông dịch chương trình. - File thực thi được tạo ra mang tính portable: có thể chạy ở nhiều loại hệ điều hành và phần cứng khác nhau.
Types Of Compiler
- Cross compiler: chương trình được biên dịch có thể được chạy ở một máy có CPU và hệ điều hành khác với compiler. Ví dụ: GNU Compiler Collection.
- Bootstrap compiler: là trình biên dịch được viết bằng ngôn ngữ mà nó sẽ biên dịch.
- Transcompiler: giúp chuyển đổi một ngôn ngữ lập trình này sang ngôn ngữ lập trình khác.
- Decompiler: biên dịch từ ngôn ngữ lập trình cấp thấp sang ngôn ngữ lập trình cấp cao.
- Just-in-time compiler: là sự kết hợp giữa compiler và interpreter.
Resources
- https://www.geeksforgeeks.org/introduction-to-compilers/
- https://www.toppr.com/guides/computer-science/computer-fundamentals/system-software/compiler/
- https://www.youtube.com/watch?v=OXCWbWh6H6M
- https://www.baeldung.com/cs/how-compilers-work
- https://www.youtube.com/watch?v=RCxE5vdgUTA&list=PLoCMsyE1cvdUZRe1udlyjpzTww1U5olL2&index=2