(Đây dài hơn tôi dự định; xin vui lòng chịu với tôi.)
Hầu hết các ngôn ngữ được tạo thành từ một cái gì đó gọi là "cú pháp": ngôn ngữ bao gồm một số từ khóa được xác định rõ, và đầy đủ các biểu thức mà bạn có thể xây dựng trong ngôn ngữ đó được xây dựng từ cú pháp đó. Ví dụ, giả sử bạn có một số học đơn giản là "số" ngôn ngữ chỉ lấy số nguyên đơn là đầu vào và hoàn toàn bỏ qua thứ tự các phép toán (tôi đã nói với bạn đó là một ngôn ngữ đơn giản). Ngôn ngữ đó có thể được xác định theo cú pháp:
// The | means "or" and the := represents definition
$expression := $number | $expression $operator $expression
$number := 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
$operator := + | - | * | /
Từ ba quy tắc này, bạn có thể tạo bất kỳ số biểu thức số học đầu vào đơn số. Sau đó bạn có thể viết một trình phân tích cú pháp cho cú pháp này phân tích bất kỳ đầu vào hợp lệ nào thành các loại thành phần của nó ($expression
, $number
hoặc $operator
) và xử lý kết quả. Ví dụ, khái niệm 3 + 4 * 5
có thể được chia như sau:
// Parentheses used for ease of explanation; they have no true syntactical meaning
$expression = 3 + 4 * 5
= $expression $operator (4 * 5) // Expand into $exp $op $exp
= $number $operator $expression // Rewrite: $exp -> $num
= $number $operator $expression $operator $expression // Expand again
= $number $operator $number $operator $number // Rewrite again
Bây giờ chúng ta có một cú pháp đầy đủ phân tích cú pháp, trong ngôn ngữ định nghĩa của chúng tôi, đối với những biểu hiện ban đầu. Một khi chúng ta có điều này, chúng ta có thể đi qua và viết một trình phân tích cú pháp để tìm ra kết quả của tất cả các kết hợp của $number $operator $number
và nhả ra một kết quả khi chúng ta chỉ còn lại một $number
.
Lưu ý rằng không còn các cấu trúc $expression
trong phiên bản được phân tích cú pháp cuối cùng của biểu thức gốc của chúng tôi. Đó là bởi vì $expression
luôn có thể được giảm xuống thành một sự kết hợp của những thứ khác trong ngôn ngữ của chúng tôi.
PHP cũng giống nhau: cấu trúc ngôn ngữ được nhận dạng tương đương với $number
hoặc $operator
của chúng tôi. Chúng không thể giảm bớt vào các cấu trúc ngôn ngữ khác; thay vào đó, chúng là các đơn vị cơ sở mà từ đó ngôn ngữ được xây dựng. Sự khác biệt chính giữa các hàm và cấu trúc ngôn ngữ là: trình phân tích cú pháp giao dịch trực tiếp với các cấu trúc ngôn ngữ. Nó đơn giản hóa các hàm thành các cấu trúc ngôn ngữ.
Lý do cấu trúc ngôn ngữ có thể hoặc không yêu cầu dấu ngoặc đơn và lý do một số có giá trị trả về trong khi một số khác không phụ thuộc hoàn toàn vào chi tiết kỹ thuật cụ thể của việc thực thi trình phân tích cú pháp PHP. Tôi không phải là thành thạo trong cách phân tích cú pháp hoạt động, vì vậy tôi không thể giải quyết những câu hỏi cụ thể, nhưng tưởng tượng trong một giây một ngôn ngữ mà bắt đầu với điều này:
$expression := ($expression) | ...
hiệu quả, ngôn ngữ này là miễn phí để lấy bất kỳ biểu thức nào nó tìm thấy và loại bỏ các dấu ngoặc đơn xung quanh.PHP (và ở đây tôi sử dụng phỏng đoán thuần túy) có thể sử dụng cấu trúc ngôn ngữ tương tự: print("Hello")
có thể bị giảm xuống print "Hello"
trước khi được phân tích cú pháp hoặc ngược lại (định nghĩa ngôn ngữ có thể thêm dấu ngoặc đơn cũng như loại bỏ chúng).
Đây là gốc của lý do bạn không thể xác định lại cấu trúc ngôn ngữ như echo
hoặc print
: chúng được mã hóa một cách hiệu quả vào trình phân tích cú pháp, trong khi các hàm được ánh xạ tới bộ cấu trúc ngôn ngữ và trình phân tích cú pháp cho phép bạn thay đổi ánh xạ đó tại thời gian biên dịch hoặc thời gian chạy để thay thế bộ cấu trúc hoặc biểu thức ngôn ngữ của riêng bạn.
Vào cuối ngày, sự khác biệt nội bộ giữa các cấu trúc và biểu thức là: cấu trúc ngôn ngữ được hiểu và xử lý bởi trình phân tích cú pháp. Các hàm dựng sẵn, được cung cấp bởi ngôn ngữ, được ánh xạ và đơn giản hóa thành một bộ cấu trúc ngôn ngữ trước khi phân tích cú pháp. Thông tin
thêm:
- Backus-Naur form, cú pháp sử dụng để xác định ngôn ngữ chính thức (yacc sử dụng hình thức này)
Edit: Đọc qua một số các câu trả lời khác, người ta làm điểm tốt . Trong số đó:
- Trình xây dựng ngôn ngữ được gọi nhanh hơn hàm. Điều này đúng, nếu chỉ có một chút, bởi vì trình thông dịch PHP không cần phải ánh xạ hàm đó tới các hàm tương đương ngôn ngữ của nó trước khi phân tích cú pháp. Tuy nhiên, trên một máy móc hiện đại, sự khác biệt là khá không đáng kể.
- Kiểm tra lỗi bỏ qua nội tuyến dựng sẵn ngôn ngữ. Điều này có thể hoặc có thể không đúng, tùy thuộc vào việc thực hiện nội bộ PHP cho mỗi nội trang dựng sẵn. Nó chắc chắn đúng là thường xuyên hơn không, chức năng sẽ có kiểm tra lỗi nâng cao hơn và các chức năng khác mà nội trang không có.
- Không thể sử dụng cấu trúc ngôn ngữ làm hàm gọi lại. Điều này đúng bởi vì cấu trúc là không phải là hàm. Họ là những thực thể riêng biệt. Khi bạn mã một nội trang dựng sẵn, bạn không mã hóa một hàm nhận các đối số - cú pháp của nội trang dựng sẵn được xử lý trực tiếp bởi trình phân tích cú pháp và được nhận dạng dưới dạng hàm dựng sẵn, chứ không phải là hàm. (Điều này có thể dễ hiểu hơn nếu bạn xem xét các ngôn ngữ có chức năng hạng nhất: hiệu quả, bạn có thể truyền các chức năng xung quanh làm đối tượng. Bạn không thể làm điều đó với nội trang dựng sẵn.)
wow! Cảm ơn câu trả lời tuyệt vời này! –
Câu trả lời tuyệt vời là đủ mở để áp dụng cho nhiều ngôn ngữ, không chỉ PHP. Cảm ơn! –
Chắc chắn rất dài, nhưng wow chắc chắn đáng giá. –