Trong quá trình xảy ra lỗi cú pháp? (mã hóa hoặc phân tích cú pháp)


23

Tôi đang cố gắng để hiểu và biên dịch, từng bước tìm ra một hình ảnh tổng thể. Vì vậy, tôi đã đưa ra một câu hỏi trong khi đọc http://www.cs.man.ac.uk/~pjj/farrell/comp3.html bài viết này

Nó nói rằng :

Giai đoạn tiếp theo của trình biên dịch được gọi là Trình phân tích cú pháp. Phần này của trình biên dịch có sự hiểu biết về ngữ pháp của ngôn ngữ. Nó chịu trách nhiệm xác định các lỗi cú pháp và dịch một chương trình không có lỗi thành các cấu trúc dữ liệu nội bộ có thể được diễn giải hoặc viết ra bằng ngôn ngữ khác.

Nhưng tôi không thể tìm ra làm thế nào mã thông báo có thể mã hóa chính xác luồng đã cho có lỗi cú pháp.

Nó nên bị mắc kẹt ở đó hoặc cung cấp một số thông tin sai cho trình phân tích cú pháp. Ý tôi là không phải tokenizing cũng là một loại dịch giả?

Vì vậy, làm thế nào nó chỉ khắc phục các dòng mã bị lỗi từ vựng trong khi token hóa.

Có một ví dụ về mã thông báo bên trong liên kết ở trên trong tiêu đề Tokenizer .

Theo tôi hiểu hình thức của mã thông báo có vẻ như, nếu có gì đó không đúng trong mã thông báo mã cũng sẽ bị hỏng.

Bạn có thể vui lòng làm rõ sự hiểu lầm của tôi?

Câu trả lời:


32

Mã thông báo chỉ là tối ưu hóa trình phân tích cú pháp. Hoàn toàn có thể thực hiện trình phân tích cú pháp mà không cần mã thông báo.

Một mã thông báo (hoặc lexer hoặc máy quét) chọn đầu vào thành một danh sách các mã thông báo. Một số phần của chuỗi (bình luận, khoảng trắng) thường bị bỏ qua. Mỗi mã thông báo có một loại (ý nghĩa của chuỗi này trong ngôn ngữ) và một giá trị (chuỗi tạo nên mã thông báo). Ví dụ: đoạn mã nguồn PHP

$a + $b

có thể được đại diện bởi các mã thông báo

Variable('$a'),
Plus('+'),
Variable('$b')

Mã thông báo không xem xét liệu mã thông báo có khả thi trong ngữ cảnh này hay không. Ví dụ: đầu vào

$a $b + +

sẽ vui vẻ tạo ra luồng mã thông báo

Variable('$a'),
Variable('$b'),
Plus('+'),
Plus('+')

Khi trình phân tích cú pháp tiêu thụ các mã thông báo này, nó sẽ nhận thấy rằng hai biến không thể theo nhau và hai toán tử infix cũng không thể. (Lưu ý rằng các ngôn ngữ khác có các cú pháp khác nhau trong đó luồng mã thông báo như vậy có thể hợp pháp, nhưng không phải trong PHP).

Trình phân tích cú pháp vẫn có thể thất bại ở giai đoạn mã thông báo. Ví dụ: có thể có một nhân vật bất hợp pháp:

$a × ½ — 3

Một mã thông báo PHP sẽ không thể khớp đầu vào này với các quy tắc của nó và sẽ tạo ra lỗi trước khi quá trình phân tích cú pháp chính bắt đầu.

Chính thức hơn, mã thông báo được sử dụng khi mỗi mã thông báo có thể được mô tả như một ngôn ngữ thông thường . Các mã thông báo sau đó có thể được kết hợp cực kỳ hiệu quả, có thể được triển khai dưới dạng DFA. Ngược lại, ngữ pháp chính thường không có ngữ cảnh và đòi hỏi thuật toán phân tích cú pháp phức tạp hơn, ít hiệu quả hơn như LALR.


Vì vậy, chúng tôi có thể nghĩ mã thông báo là một phần của trình phân tích cú pháp và lỗi cú pháp có thể xảy ra hoặc là bước mã hóa hoặc bước phân tích cú pháp theo mẫu lỗi cú pháp. Cảm ơn bạn đã làm rõ.
FZE

4
@FZE: Bạn có thể nghĩ như vậy, nhưng nó sai lệch. Lexing không phải là "chỉ là một tối ưu hóa trình phân tích cú pháp". Thay vào đó, lexing ánh xạ một biểu diễn vật lý (một số chuỗi ký tự) thành một biểu diễn logic (các mã thông báo được đại diện bởi các ký tự đó). Đây phân lập phân tích cú pháp từ chi tiết vụn vặt như thế nào khi kết thúc một dòng được đại diện, hoặc cho dù bạn quyết định để đại diện cho một logic và như andhay &&hay cái gì khác. Nó (hầu hết) tách biệt và khác với phân tích cú pháp. Tối ưu hóa (nếu có) là một tác dụng phụ gần như tình cờ.
Jerry Coffin

@JerryCoffin cảm ơn vì lời giải thích thêm nó có ý nghĩa hơn bây giờ.
FZE

2
@JerryCoffin, amon đúng là sự khác biệt không phải là cơ bản. Bạn có thể tạo một ngữ pháp BNF gắn kết bao gồm cả các phần "lexer" và "Parser". Chúng tôi thường phân loại các quy tắc thành cấp độ thấp (ví dụ: số, toán tử cộng) và cấp độ cao (tổng kết), nhưng chính ngữ pháp không tạo ra sự khác biệt như vậy.
Paul Draper

1
@PaulDraper Không chắc chắn nếu tách ra một ngôn ngữ thông thường như giai đoạn đầu tiên là lựa chọn đúng đắn. Ví dụ, các cặp trùng khớp (không thường xuyên) có thể cần thiết để mô tả chuỗi ký tự trong một số ngôn ngữ, nhưng vẫn có ý nghĩa để xử lý chúng trong giai đoạn đầu tiên. Tránh / giảm thiểu theo dõi ngược có vẻ như là một hướng dẫn tốt hơn.
CodeInChaos

16

Bạn thường mong đợi hầu hết các lỗi cú pháp đến từ trình phân tích cú pháp, không phải từ vựng.

Các lexer sẽ tạo ra một lỗi nếu (và chủ yếu chỉ khi) có một cái gì đó trong đầu vào không thể được token hóa. Tuy nhiên, trong nhiều ngôn ngữ, hầu như bất kỳ chuỗi ký tự nào cũng có thể được biến thành mã thông báo thuộc loại nào đó, vì vậy lỗi ở đây khá bất thường.

Trình phân tích cú pháp sẽ tạo ra lỗi nếu đầu vào chứa mã thông báo hợp lệ, nhưng các mã thông báo đó không được sắp xếp để chúng tạo thành các câu lệnh / biểu thức hợp lệ trong ngôn ngữ đích. Điều này là phổ biến hơn nhiều như là một quy tắc.


11

Tokenizer chỉ phân chia luồng ký tự thành mã thông báo. Từ mã thông báo POV, điều này là hoàn toàn hợp lệ:

1 * * 1

và dịch sang một cái gì đó như: ["1", MULTIPLY, MULTIPLY, "1"] Chỉ trình phân tích cú pháp có thể từ chối các biểu thức như vậy - nó biết toán tử nhân có thể theo một toán tử nhân khác. Ví dụ: trong JavaScript, nó tạo ra:

Uncaught SyntaxError: Unexpected token *(…)

Có lỗi có thể được phát hiện bởi mã thông báo. Ví dụ: chuỗi ký tự chưa hoàn thành: "abchoặc số không hợp lệ : 0x0abcdefg. Chúng vẫn có thể được báo cáo là lỗi cú pháp:

Uncaught SyntaxError: Unexpected token ILLEGAL

Lưu ý tuy nhiên mã thông báo không được nhận dạng và được báo cáo là ILLEGAL.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.