lexers vs trình phân tích cú pháp


308

Có phải lexers và Parsers thực sự khác nhau về lý thuyết?

Có vẻ thời trang để ghét các biểu thức thông thường: mã hóa kinh dị , một bài đăng blog khác .

Tuy nhiên, các công cụ dựa trên lexing phổ biến: pygments , geshi hoặc prettify , tất cả đều sử dụng các biểu thức thông thường. Họ dường như lex bất cứ điều gì ...

Khi nào đủ từ vựng, khi nào bạn cần EBNF?

Có ai đã sử dụng các mã thông báo được tạo ra bởi các nhà ngoại giao này với trình tạo trình phân tích cú pháp bison hoặc antlr chưa?


2
Đúng. Tôi đang cố gắng phân tích autohotkey. Tôi đã có thể xây dựng một cú pháp tô sáng bằng cách sử dụng pygments rất nhanh. Nhưng antlr mất nhiều thời gian hơn ... Tôi chưa thấy nhiều sự thụ phấn chéo giữa hai công cụ.
Naveen

67
Nó chỉ hợp thời trang để ghét những biểu hiện thông thường khi chúng bị lạm dụng. Nhiều người cố gắng sử dụng các biểu thức thông thường khi cần phân tích cú pháp không ngữ cảnh. Họ luôn thất bại. Và họ đổ lỗi cho công nghệ biểu hiện thường xuyên. Điều đó giống như phàn nàn rằng búa của bạn là một cái cưa nhàu nát. Đúng, nhưng bạn sẽ không nhận được nhiều thiện cảm.
Ira Baxter

2
Tôi đang bắt đầu tăng tốc với antlr, rất may. Rất nhiều từ vựng là không có ngữ cảnh và đôi khi thậm chí phụ thuộc vào ngữ cảnh.
Naveen

1
Một khía cạnh cơ bản của vấn đề lexer vs trình phân tích cú pháp là các từ vựng dựa trên automata hữu hạn (FSA), hoặc chính xác hơn là các bộ chuyển đổi hữu hạn (FST). Hầu hết các hình thức phân tích cú pháp (không chỉ là Bối cảnh miễn phí) được đóng lại dưới giao điểm với FSA hoặc ứng dụng FST. Do đó, việc sử dụng biểu mẫu dựa trên biểu thức chính quy đơn giản hơn cho lexer không làm tăng sự phức tạp của các cấu trúc cú pháp của các hình thức phân tích cú pháp phức tạp hơn. Đây là một vấn đề mô đun hoàn toàn chính khi xác định cấu trúc và ngữ nghĩa của các ngôn ngữ, vui vẻ bỏ qua các câu trả lời được bình chọn cao.
babou

Cần lưu ý rằng các trình phân tích cú pháp và trình phân tích cú pháp không phải khác nhau, ví dụ LLLPG và các phiên bản trước đó của ANTLR sử dụng cùng một hệ thống phân tích cú pháp LL (k) cho cả trình phân tích cú pháp và trình phân tích cú pháp. Sự khác biệt chính là regexes thường là đủ cho các từ vựng nhưng không phải là trình phân tích cú pháp.
Qwertie

Câu trả lời:


475

Những gì trình phân tích cú pháp và từ vựng có điểm chung:

  1. Họ đọc các ký hiệu của một số bảng chữ cái từ đầu vào của họ.

    • Gợi ý: Bảng chữ cái không nhất thiết phải là chữ cái. Nhưng nó phải là biểu tượng nguyên tử cho ngôn ngữ được hiểu bởi trình phân tích cú pháp / lexer.
    • Biểu tượng cho lexer: ký tự ASCII.
    • Các biểu tượng cho trình phân tích cú pháp: các mã thông báo cụ thể, là các ký hiệu đầu cuối của ngữ pháp của chúng.
  2. Họ phân tích các ký hiệu này và cố gắng khớp chúng với ngữ pháp của ngôn ngữ mà chúng hiểu.

    • Đây là nơi khác biệt thực sự thường nằm. Xem bên dưới để biết thêm.
    • Ngữ pháp được hiểu bởi các từ vựng: ngữ pháp thông thường (cấp 3 của Chomsky).
    • Ngữ pháp được hiểu bởi các trình phân tích cú pháp: ngữ pháp không ngữ cảnh (cấp độ 2 của Chomsky).
  3. Họ gắn ngữ nghĩa (ý nghĩa) với các phần ngôn ngữ họ tìm thấy.

    • Lexers đính kèm có nghĩa là bằng cách phân loại lexemes (chuỗi ký tự từ đầu vào) là đặc biệt thẻ . Ví dụ: Tất cả những lexemes: *, ==, <=, ^sẽ được phân loại là "điều hành" token bằng C / C ++ lexer.
    • Parsers đính kèm có nghĩa là bằng cách phân loại chuỗi các thẻ từ đầu vào (câu) là đặc biệt thuộc đầu cuối và xây dựng cây phân tích cú pháp . Ví dụ như tất cả các chuỗi mã thông báo: [number][operator][number], [id][operator][id], [id][operator][number][operator][number]sẽ được phân loại là "khái niệm" không thuộc đầu cuối bằng C / C ++ phân tích cú pháp.
  4. Họ có thể đính kèm một số ý nghĩa bổ sung (dữ liệu) cho các yếu tố được công nhận.

    • Khi một lexer nhận ra một chuỗi ký tự cấu thành một số thích hợp, nó có thể chuyển đổi nó thành giá trị nhị phân của nó và lưu trữ bằng mã thông báo "số".
    • Tương tự, khi trình phân tích cú pháp nhận ra một biểu thức, nó có thể tính giá trị của nó và lưu trữ với nút "biểu thức" của cây cú pháp.
  5. Tất cả họ sản xuất trên đầu ra của họ một câu thích hợp của ngôn ngữ họ nhận ra.

    • Lexers sản xuất mã thông báo , đó là những câu của ngôn ngữ thông thường mà họ nhận ra. Mỗi mã thông báo có thể có một cú pháp bên trong (mặc dù cấp 3, không phải cấp 2), nhưng điều đó không quan trọng đối với dữ liệu đầu ra và đối với dữ liệu đọc chúng.
    • Các trình phân tích cú pháp tạo ra các cây cú pháp , là các biểu diễn các câu của ngôn ngữ không ngữ cảnh mà chúng nhận ra. Thông thường, nó chỉ là một cây lớn cho toàn bộ tệp tài liệu / nguồn, bởi vì toàn bộ tệp tài liệu / nguồn là một câu thích hợp cho chúng. Nhưng không có bất kỳ lý do nào khiến trình phân tích cú pháp không thể tạo ra một loạt các cây cú pháp trên đầu ra của nó. Ví dụ, nó có thể là một trình phân tích cú pháp nhận ra các thẻ SGML được dán vào văn bản thuần túy. Vì vậy, nó sẽ hóa tài liệu SGML thành một loạt các mã thông báo : [TXT][TAG][TAG][TXT][TAG][TXT]....

Như bạn có thể thấy, trình phân tích cú pháp và mã thông báo có nhiều điểm chung. Một trình phân tích cú pháp có thể là mã thông báo cho trình phân tích cú pháp khác, đọc mã thông báo đầu vào của nó dưới dạng các ký hiệu từ bảng chữ cái riêng của nó (mã thông báo đơn giản là ký hiệu của một số bảng chữ cái) theo cách tương tự như các câu từ một ngôn ngữ có thể là ký hiệu chữ cái của một ngôn ngữ khác, ở cấp độ cao hơn ngôn ngữ. Ví dụ: nếu *-là các ký hiệu của bảng chữ cái M(dưới dạng "ký hiệu mã Morse"), thì bạn có thể xây dựng một trình phân tích cú pháp nhận ra các chuỗi của các dấu chấm và dòng này là các chữ cái được mã hóa trong mã Morse. Các câu trong ngôn ngữ "Mã Morse" có thể là mã thông báo cho một số trình phân tích cú pháp khác, mà các mã thông báo nàylà các ký hiệu nguyên tử của ngôn ngữ của nó (ví dụ: ngôn ngữ "Từ tiếng Anh"). Và những "Từ tiếng Anh" này có thể là mã thông báo (ký hiệu của bảng chữ cái) cho một số trình phân tích cú pháp cấp cao hơn hiểu ngôn ngữ "Câu tiếng Anh". Và tất cả các ngôn ngữ này chỉ khác nhau về sự phức tạp của ngữ pháp . Chỉ có bấy nhiêu thôi.

Vậy tất cả những gì về "cấp độ ngữ pháp của Chomsky"? Chà, Noam Chomsky đã phân loại ngữ pháp thành bốn cấp độ tùy thuộc vào độ phức tạp của chúng:

  • Cấp độ 3: Ngữ pháp thông thường

    Họ sử dụng biểu thức thông thường, có nghĩa là, họ có thể chỉ gồm những biểu tượng của bảng chữ cái ( a, b), concatenations của họ ( ab, aba, bbbETD.), Hoặc lựa chọn thay thế (ví dụ a|b).
    Chúng có thể được thực hiện dưới dạng automata trạng thái hữu hạn (FSA), như NFA (Automond Finin Automite Finite Automaton) hoặc DFA tốt hơn (Xác định hữu hạn tự động).
    Các ngữ pháp thông thường không thể xử lý bằng cú pháp lồng nhau , ví dụ các dấu ngoặc đơn được lồng / khớp đúng (()()(()())), các thẻ HTML / BBcode lồng nhau, các khối lồng nhau, v.v.
  • Cấp độ 2: Ngữ pháp không ngữ cảnh

    Chúng có thể có các nhánh lồng nhau, đệ quy, tự tương tự trong các cây cú pháp của chúng, vì vậy chúng có thể xử lý tốt với các cấu trúc lồng nhau.
    Chúng có thể được thực hiện như máy tự động nhà nước với ngăn xếp. Ngăn xếp này được sử dụng để thể hiện mức độ lồng của cú pháp. Trong thực tế, chúng thường được triển khai như một trình phân tích cú pháp gốc đệ quy, từ trên xuống, sử dụng ngăn xếp cuộc gọi thủ tục của máy để theo dõi mức lồng nhau và sử dụng các thủ tục / hàm được gọi đệ quy cho mọi ký hiệu không đầu cuối trong cú pháp của chúng.
    Nhưng họ không thể xử lý với một cú pháp nhạy cảm theo ngữ cảnh . Ví dụ: khi bạn có một biểu thức x+3và trong một ngữ cảnh, đây xcó thể là tên của một biến và trong ngữ cảnh khác, nó có thể là tên của một hàm, v.v.
  • Cấp độ 1: Ngữ pháp nhạy cảm theo ngữ cảnh

  • Cấp độ 0: Các ngữ pháp không giới hạn
    Cũng được gọi là các ngữ pháp đệ quy.


70
Ồ vâng? Vậy những "từ hoặc mã thông báo" đó là gì? Chúng chỉ là những câu trong ngôn ngữ thông thường, bao gồm các chữ cái trong bảng chữ cái. Và những "cấu trúc" hoặc "cây" trong trình phân tích cú pháp là gì? Chúng cũng là các câu , nhưng trong một ngôn ngữ cấp cao khác, trong đó các mã thông báo cụ thể là các ký hiệu chữ cái. Sự khác biệt không phải là những gì bạn đã nói, mà là ở tính linh hoạt của ngôn ngữ được sử dụng . Đối đầu với -1 của bạn với bất kỳ cẩm nang nào về lý thuyết phân tích cú pháp.
SasQ

3
@SasQ Sẽ công bằng khi nói rằng cả Trình phân tích cú pháp và Trình phân tích cú pháp đều lấy một số ngữ pháp và một loạt mã thông báo làm đầu vào?
Parag

4
Khá là vậy. Cả hai đều lấy một loạt các biểu tượng từ bảng chữ cái mà họ nhận ra. Đối với lexer, bảng chữ cái này chỉ bao gồm các ký tự đơn giản. Đối với trình phân tích cú pháp, bảng chữ cái bao gồm các ký hiệu đầu cuối, bất cứ thứ gì chúng được xác định. Chúng cũng có thể là các ký tự, nếu bạn không sử dụng từ vựng và sử dụng mã định danh một ký tự và số một chữ số, v.v. (khá hữu ích ở giai đoạn phát triển đầu tiên). Nhưng chúng thường là mã thông báo (các lớp từ vựng) vì mã thông báo là một sự trừu tượng hóa tốt: bạn có thể thay đổi các từ vựng thực tế (chuỗi) mà chúng đại diện và trình phân tích cú pháp không thấy sự thay đổi.
SasQ

6
Ví dụ: bạn có thể sử dụng ký hiệu đầu cuối STMT_ENDtrong cú pháp của mình (đối với trình phân tích cú pháp) để biểu thị phần cuối của hướng dẫn. Bây giờ bạn có thể có một mã thông báo có cùng tên được liên kết với nó, được tạo bởi lexer. Nhưng bạn có thể thay đổi từ vựng thực tế mà nó đại diện. Ví dụ. bạn có thể định nghĩa STMT_END;có mã nguồn giống như C / C ++. Hoặc bạn có thể định nghĩa nó theo endcách nào đó tương tự như kiểu Pascal. Hoặc bạn có thể định nghĩa nó chỉ là '\n'kết thúc hướng dẫn với cuối dòng, như trong Python. Nhưng cú pháp của hướng dẫn (và trình phân tích cú pháp) không thay đổi :-) Chỉ cần thay đổi từ vựng.
SasQ

24
Giờ trên wikipedia và google không giúp được gì, nhưng bạn đã giải thích ngữ pháp của Chomsky trong 3 phút. Cảm ơn bạn.
enrey

107

Vâng, họ rất khác nhau về lý thuyết, và trong thực hiện.

Các từ vựng được sử dụng để nhận ra "các từ" tạo nên các yếu tố ngôn ngữ, bởi vì cấu trúc của các từ đó thường đơn giản. Các biểu thức chính quy cực kỳ tốt trong việc xử lý cấu trúc đơn giản này và có các công cụ khớp biểu thức chính quy hiệu suất rất cao được sử dụng để thực hiện các từ vựng.

Trình phân tích cú pháp được sử dụng để nhận ra "cấu trúc" của cụm từ ngôn ngữ. Cấu trúc như vậy nói chung vượt xa những gì "biểu thức chính quy" có thể nhận ra, do đó, người ta cần các trình phân tích cú pháp "nhạy cảm ngữ cảnh" để trích xuất cấu trúc đó. Các trình phân tích cú pháp nhạy với ngữ cảnh rất khó xây dựng, do đó, thỏa hiệp kỹ thuật là sử dụng các ngữ pháp "không ngữ cảnh" và thêm các bản hack vào các trình phân tích cú pháp ("bảng biểu tượng", v.v.) để xử lý phần nhạy cảm theo ngữ cảnh.

Không phải công nghệ lexing hay phân tích cú pháp sẽ sớm biến mất.

Chúng có thể được thống nhất bằng cách quyết định sử dụng công nghệ "phân tích cú pháp" để nhận ra "từ", như hiện đang được khám phá bởi cái gọi là trình phân tích cú pháp GLR không quét. Điều đó có chi phí thời gian chạy, vì bạn đang áp dụng máy móc chung hơn cho những vấn đề thường không cần đến nó và thường bạn phải trả cho việc đó. Khi bạn có nhiều chu kỳ miễn phí, chi phí đó có thể không quan trọng. Nếu bạn xử lý nhiều văn bản, thì chi phí không thành vấn đề và các trình phân tích cú pháp biểu thức chính quy cổ điển sẽ tiếp tục được sử dụng.


40
Lời giải thích tuyệt vời, Ira. Thêm vào sự tương tự của bạn: Trong khi các nhà từ vựng nói về việc làm cho các từ đúng, các trình phân tích cú pháp là về việc làm cho các câu đúng. "Xem chạy tại chỗ" và "chạy tại chỗ Xem" đều hợp lệ khi có liên quan đến từ vựng. Phải có một trình phân tích cú pháp để xác định rằng cấu trúc cụm từ là sai (trong ngữ pháp tiếng Anh).
Alan

Tôi đoán một trình phân tích cú pháp là một trình phân tích cú pháp như một trình phân tích cây là một trình phân tích cú pháp. Tôi không tin rằng lý thuyết này khác nhau: antlr.org/wiki/display/~admin/ANTLR+v4+lexers nhưng tôi bắt đầu hiểu sự khác biệt trong quy ước giữa chúng ...
Naveen

4
Lý thuyết rất khác nhau. Hầu hết các công nghệ phân tích cú pháp đang cố gắng xử lý các ngôn ngữ không ngữ cảnh ở một mức độ nào đó (một số chỉ làm một phần, ví dụ: LALR, một số làm tất cả, ví dụ GLR). Hầu hết các công nghệ lexer chỉ cố gắng thực hiện các biểu thức thông thường.
Ira Baxter

3
Lý thuyết là khác nhau, bởi vì nó đã được đề xuất bởi nhiều người khác nhau và sử dụng các thuật ngữ và thuật toán khác nhau. Nhưng nếu bạn nhìn chúng kỹ hơn, bạn có thể phát hiện ra những điểm tương đồng. Ví dụ, vấn đề đệ quy trái rất giống với vấn đề không xác định trong NFA và loại bỏ đệ quy trái tương tự như loại bỏ tính không xác định và chuyển NFA thành DFA. Mã thông báo là câu cho mã thông báo (đầu ra), nhưng ký hiệu chữ cái cho trình phân tích cú pháp (đầu vào). Tôi không phủ nhận sự khác biệt (cấp độ Chomsky), nhưng sự tương đồng giúp ích rất nhiều cho thiết kế.
SasQ

1
Myememate là vào lý thuyết thể loại. Ông đã chỉ ra làm thế nào khái niệm lý thuyết phân loại của các sheaves bao trùm tất cả các loại khớp mẫu và có thể rút ra phân tích cú pháp LR từ một đặc tả phân loại trừu tượng. Vì vậy, trong thực tế, nếu bạn đi đủ trừu tượng, bạn có thể tìm thấy những điểm tương đồng như vậy. Điểm của lý thuyết thể loại là bạn thường có thể trừu tượng hóa "tất cả các cách lên"; Tôi chắc chắn rằng bạn có thể xây dựng một trình phân tích lý thuyết thể loại đã xóa đi sự khác biệt. Nhưng bất kỳ việc sử dụng thực tế nào của nó đều phải đưa ra vấn đề cụ thể, và sau đó sự khác biệt hiển thị là có thật.
Ira Baxter

32

Khi nào đủ từ vựng, khi nào bạn cần EBNF?

EBNF thực sự không bổ sung nhiều vào sức mạnh của ngữ pháp. Đó chỉ là một ký hiệu tiện lợi / phím tắt / "đường cú pháp" so với các quy tắc ngữ pháp chuẩn của Chomsky's Form Form (CNF). Ví dụ: thay thế EBNF:

S --> A | B

bạn có thể đạt được trong CNF bằng cách liệt kê riêng từng sản phẩm thay thế:

S --> A      // `S` can be `A`,
S --> B      // or it can be `B`.

Phần tử tùy chọn từ EBNF:

S --> X?

bạn có thể đạt được trong CNF bằng cách sử dụng một nullable sản xuất, có nghĩa là, một trong những có thể được thay thế bằng một chuỗi rỗng (biểu thị bằng chỉ sản xuất có sản phẩm nào ở đây, một số khác sử dụng epsilon hoặc lambda hoặc hình tròn vượt qua):

S --> B       // `S` can be `B`,
B --> X       // and `B` can be just `X`,
B -->         // or it can be empty.

Một sản phẩm ở dạng như cái cuối cùng Bở trên được gọi là "erasure", bởi vì nó có thể xóa bất cứ thứ gì nó đại diện cho các sản phẩm khác (sản phẩm là một chuỗi rỗng thay vì một thứ khác).

Không lặp lại hoặc nhiều hơn từ EBNF:

S --> A*

bạn có thể obtan bằng cách sử dụng sản xuất đệ quy , nghĩa là, một thứ tự nhúng vào đâu đó trong đó. Nó có thể được thực hiện theo hai cách. Đầu tiên là đệ quy trái (thường nên tránh, bởi vì trình phân tích cú pháp gốc đệ quy từ trên xuống không thể phân tích cú pháp):

S --> S A    // `S` is just itself ended with `A` (which can be done many times),
S -->        // or it can begin with empty-string, which stops the recursion.

Biết rằng nó chỉ tạo ra một chuỗi rỗng (cuối cùng) theo sau là 0 hoặc nhiều As, cùng một chuỗi ( nhưng không phải cùng ngôn ngữ! ) Có thể được biểu thị bằng cách sử dụng đệ quy đúng :

S --> A S    // `S` can be `A` followed by itself (which can be done many times),
S -->        // or it can be just empty-string end, which stops the recursion.

Và khi nói đến +một hoặc nhiều lần lặp lại từ EBNF:

S --> A+

nó có thể được thực hiện bằng cách bao thanh toán một Avà sử dụng *như trước:

S --> A A*

mà bạn có thể diễn đạt trong CNF như vậy (tôi sử dụng đệ quy đúng ở đây; cố gắng tự mình tìm ra một bài tập khác như một bài tập):

S --> A S   // `S` can be one `A` followed by `S` (which stands for more `A`s),
S --> A     // or it could be just one single `A`.

Biết rằng, bây giờ bạn có thể nhận ra một ngữ pháp cho một biểu thức chính quy (nghĩa là ngữ pháp thông thường ) là một ngữ pháp có thể được thể hiện trong một sản phẩm EBNF duy nhất chỉ bao gồm các ký hiệu đầu cuối. Tổng quát hơn, bạn có thể nhận ra các ngữ pháp thông thường khi bạn thấy các sản phẩm tương tự như sau:

A -->        // Empty (nullable) production (AKA erasure).
B --> x      // Single terminal symbol.
C --> y D    // Simple state change from `C` to `D` when seeing input `y`.
E --> F z    // Simple state change from `E` to `F` when seeing input `z`.
G --> G u    // Left recursion.
H --> v H    // Right recursion.

Đó là, chỉ sử dụng các chuỗi rỗng, các ký hiệu đầu cuối, các đầu cuối đơn giản để thay thế và thay đổi trạng thái và chỉ sử dụng đệ quy để đạt được sự lặp lại (lặp, chỉ là đệ quy tuyến tính - một đệ quy không giống như nhánh cây). Không có gì cao cấp hơn những thứ này, sau đó bạn chắc chắn đó là một cú pháp thông thường và bạn có thể đi với chỉ từ vựng cho điều đó.

Nhưng khi cú pháp của bạn sử dụng đệ quy theo cách không tầm thường, để tạo ra các cấu trúc lồng vào nhau, giống như cây, giống như sau:

S --> a S b    // `S` can be itself "parenthesized" by `a` and `b` on both sides.
S -->          // or it could be (ultimately) empty, which ends recursion.

sau đó bạn có thể dễ dàng thấy rằng điều này không thể được thực hiện bằng biểu thức chính quy, bởi vì bạn không thể giải quyết nó thành một sản phẩm EBNF theo bất kỳ cách nào; bạn sẽ kết thúc với việc thay thế Svô thời hạn, điều này sẽ luôn thêm một as và bs khác ở cả hai bên. Các phần tử (cụ thể hơn: Automata State Finata được sử dụng bởi các nhà từ vựng) không thể được tính vào số tùy ý (chúng là hữu hạn, nhớ không?), Vì vậy chúng không biết có bao nhiêu as ở đó để khớp chúng với rất nhiều bs. Các ngữ pháp như thế này được gọi là ngữ pháp không ngữ cảnh (ít nhất là) và chúng yêu cầu một trình phân tích cú pháp.

Ngữ pháp không ngữ cảnh được biết đến để phân tích cú pháp, vì vậy chúng được sử dụng rộng rãi để mô tả cú pháp ngôn ngữ lập trình. Nhưng còn nhiều hơn thế. Đôi khi cần một ngữ pháp tổng quát hơn - khi bạn có nhiều thứ để đếm cùng một lúc, một cách độc lập. Ví dụ, khi bạn muốn mô tả một ngôn ngữ trong đó người ta có thể sử dụng dấu ngoặc tròn và dấu ngoặc vuông xen kẽ, nhưng chúng phải được ghép nối chính xác với nhau (niềng răng có niềng răng, tròn với vòng tròn). Loại ngữ pháp này được gọi là nhạy cảm ngữ cảnh . Bạn có thể nhận ra nó bằng cách nó có nhiều hơn một biểu tượng bên trái (trước mũi tên). Ví dụ:

A R B --> A S B

Bạn có thể nghĩ về các biểu tượng bổ sung này ở bên trái như một "bối cảnh" để áp dụng quy tắc. Có thể có một số điều kiện tiên quyết, postconditions vv Ví dụ, các quy tắc trên sẽ thay thế Rvào S, nhưng chỉ khi nó ở giữa AB, để lại những ABbản thân không thay đổi. Loại cú pháp này thực sự khó phân tích, bởi vì nó cần một máy Turing đầy đủ. Đó là một câu chuyện hoàn toàn khác, vì vậy tôi sẽ kết thúc ở đây.


1
Bạn nói rằng EBNF "chỉ là một ký hiệu tiện lợi / phím tắt /" đường cú pháp "so với các quy tắc ngữ pháp chuẩn của Chomsky's Form Form (CNF)". Nhưng CNF hầu như không có gì để làm với chủ đề trong tay. EBNF có thể dễ dàng được chuyển đổi thành BNF tiêu chuẩn. Giai đoạn = Stage. Đó là đường cú pháp cho BNF tiêu chuẩn.
babou

11

Để trả lời câu hỏi như đã hỏi (không lặp lại quá mức những gì xuất hiện trong các câu trả lời khác)

Trình phân tích cú pháp và trình phân tích cú pháp không khác nhau lắm, như được đề xuất bởi câu trả lời được chấp nhận. Cả hai đều dựa trên các hình thức ngôn ngữ đơn giản: ngôn ngữ thông thường cho các từ vựng và, hầu như luôn luôn, các ngôn ngữ không có ngữ cảnh (CF) cho các trình phân tích cú pháp. Cả hai đều được liên kết với các mô hình tính toán khá đơn giản, máy tự động trạng thái hữu hạn và máy tự động ngăn xếp đẩy xuống. Ngôn ngữ thông thường là trường hợp đặc biệt của ngôn ngữ không ngữ cảnh, do đó, ngôn ngữ có thể được sản xuất với công nghệ CF phức tạp hơn một chút. Nhưng nó không phải là một ý tưởng tốt cho ít nhất hai lý do.

Một điểm cơ bản trong lập trình là một thành phần hệ thống phải được xây dựng với công nghệ phù hợp nhất, để dễ sản xuất, hiểu và bảo trì. Công nghệ không nên quá mức (sử dụng các kỹ thuật phức tạp và tốn kém hơn nhiều so với mức cần thiết), cũng không nên ở giới hạn sức mạnh của nó, do đó đòi hỏi phải có các kỹ thuật để đạt được mục tiêu mong muốn.

Đó là lý do tại sao "Có vẻ thời trang để ghét các biểu thức thông thường". Mặc dù họ có thể làm rất nhiều, đôi khi họ yêu cầu mã hóa rất khó đọc để đạt được nó, chưa kể đến việc các phần mở rộng và hạn chế khác nhau trong việc thực hiện phần nào làm giảm tính đơn giản về mặt lý thuyết của họ. Những người làm công việc thường không làm điều đó và thường là một công nghệ đơn giản, hiệu quả và phù hợp để phân tích mã thông báo. Sử dụng trình phân tích cú pháp CF cho mã thông báo sẽ là quá mức cần thiết, mặc dù điều đó là có thể.

Một lý do khác để không sử dụng chủ nghĩa hình thức CF cho các nhà ngoại giao là vì sau đó có thể sẽ rất hấp dẫn khi sử dụng toàn bộ sức mạnh CF. Nhưng điều đó có thể làm tăng các vấn đề cấu trúc liên quan đến việc đọc các chương trình.

Về cơ bản, hầu hết cấu trúc của văn bản chương trình, từ đó ý nghĩa được trích xuất, là một cấu trúc cây. Nó diễn tả cách câu parse (chương trình) được tạo từ các quy tắc cú pháp. Ngữ nghĩa được bắt nguồn bởi các kỹ thuật thành phần (đồng cấu cho định hướng toán học) từ cách các quy tắc cú pháp được sáng tác để xây dựng cây phân tích cú pháp. Do đó cấu trúc cây là cần thiết. Thực tế là các mã thông báo được xác định bằng một từ vựng dựa trên tập hợp thông thường không làm thay đổi tình hình, bởi vì CF được tạo bằng thông thường vẫn cung cấp CF (Tôi đang nói rất lỏng lẻo về các bộ chuyển đổi thông thường, chuyển đổi một dòng ký tự thành một luồng mã thông báo).

Tuy nhiên, CF được tạo bằng CF (thông qua các bộ chuyển đổi CF ... xin lỗi vì toán học), không nhất thiết phải cung cấp CF và có thể làm cho mọi thứ trở nên tổng quát hơn, nhưng ít biến đổi hơn trong thực tế. Vì vậy, CF không phải là công cụ thích hợp cho các từ vựng, mặc dù nó có thể được sử dụng.

Một trong những khác biệt chính giữa ngôn ngữ thông thường và CF là ngôn ngữ thông thường (và bộ chuyển đổi) kết hợp rất tốt với hầu hết mọi hình thức theo nhiều cách khác nhau, trong khi ngôn ngữ CF (và bộ chuyển đổi) thì không, ngay cả với chính họ (với một vài ngoại lệ).

(Lưu ý rằng các bộ chuyển đổi thông thường có thể có những cách sử dụng khác, chẳng hạn như chính thức hóa một số kỹ thuật xử lý lỗi cú pháp.)

BNF chỉ là một cú pháp cụ thể để trình bày ngữ pháp CF.

EBNF là một loại đường cú pháp cho BNF , sử dụng các phương tiện của ký hiệu thông thường để đưa ra phiên bản ngữ pháp của BNF. Nó luôn có thể được chuyển đổi thành một BNF thuần tương đương.

Tuy nhiên, ký hiệu thông thường thường được sử dụng trong EBNF chỉ để nhấn mạnh các phần này của cú pháp tương ứng với cấu trúc của các yếu tố từ vựng, và nên được nhận ra bằng từ vựng, trong khi phần còn lại được trình bày bằng BNF thẳng. Nhưng nó không phải là một quy tắc tuyệt đối.

Tóm lại, cấu trúc mã thông báo đơn giản hơn được phân tích tốt hơn với công nghệ đơn giản hơn của ngôn ngữ thông thường, trong khi cấu trúc hướng cây của ngôn ngữ (cú pháp chương trình) được xử lý tốt hơn bởi ngữ pháp CF.

Tôi cũng đề nghị xem xét câu trả lời của AHR .

Nhưng điều này để lại một câu hỏi mở: Tại sao cây?

Cây xanh là một cơ sở tốt để chỉ định cú pháp bởi vì

  • họ đưa ra một cấu trúc đơn giản cho văn bản

  • rất thuận tiện cho việc liên kết ngữ nghĩa với văn bản trên cơ sở cấu trúc đó, với một công nghệ được hiểu rõ về mặt toán học (thành phần thông qua phép đồng hình), như đã chỉ ra ở trên. Nó là một công cụ đại số cơ bản để xác định ngữ nghĩa của các hình thức toán học.

Do đó, nó là một đại diện trung gian tốt, được thể hiện bằng sự thành công của Cây cú pháp trừu tượng (AST). Lưu ý rằng AST thường khác với cây phân tích cú pháp vì công nghệ phân tích cú pháp được sử dụng bởi nhiều chuyên gia (chẳng hạn như LL hoặc LR) chỉ áp dụng cho một tập hợp các ngữ pháp CF, do đó buộc các sai lệch ngữ pháp mà sau này được sửa trong AST. Điều này có thể tránh được với công nghệ phân tích cú pháp chung hơn (dựa trên lập trình động) chấp nhận bất kỳ ngữ pháp CF nào.

Tuyên bố về thực tế rằng các ngôn ngữ lập trình nhạy cảm với ngữ cảnh (CS) thay vì CF là tùy ý và gây tranh cãi.

Vấn đề là sự phân tách cú pháp và ngữ nghĩa là tùy ý. Kiểm tra khai báo hoặc thỏa thuận loại có thể được coi là một phần của cú pháp hoặc một phần của ngữ nghĩa. Điều tương tự cũng sẽ đúng với giới tính và thỏa thuận số trong các ngôn ngữ tự nhiên. Nhưng có những ngôn ngữ tự nhiên trong đó thỏa thuận số nhiều phụ thuộc vào ý nghĩa ngữ nghĩa thực sự của các từ, do đó nó không phù hợp với cú pháp.

Nhiều định nghĩa về ngôn ngữ lập trình trong ngữ nghĩa biểu thị vị trí khai báo và kiểm tra kiểu trong ngữ nghĩa. Vì vậy, tuyên bố như được thực hiện bởi Ira Baxter rằng các trình phân tích cú pháp CF đang bị hack để có được độ nhạy ngữ cảnh theo yêu cầu của cú pháp là tốt nhất là một cái nhìn tùy tiện về tình huống. Nó có thể được tổ chức dưới dạng hack trong một số trình biên dịch, nhưng nó không phải như vậy.

Ngoài ra, không chỉ các trình phân tích cú pháp CS (theo nghĩa được sử dụng trong các câu trả lời khác ở đây) khó xây dựng và kém hiệu quả. Chúng cũng không đủ để thể hiện rõ ràng sự nhạy cảm của bối cảnh có thể cần thiết. Và họ không tự nhiên tạo ra một cấu trúc cú pháp (chẳng hạn như cây phân tích cú pháp) thuận tiện để lấy được ngữ nghĩa của chương trình, tức là để tạo mã được biên dịch.


Vâng, cây phân tích và AST là khác nhau, nhưng khá nhiều không phải là một cách thực sự hữu ích. Xem cuộc thảo luận của tôi về điều này: stackoverflow.com/a/1916687/120163
Ira Baxter

@IraBaxter Tôi không đồng ý với bạn, nhưng tôi thực sự không có thời gian để soạn thảo một câu trả lời rõ ràng cho bài đăng của bạn. Về cơ bản, bạn đang có một quan điểm thực dụng (và cũng bảo vệ hệ thống của riêng bạn, tôi nghĩ vậy). Điều này thậm chí còn dễ dàng hơn vì bạn đang sử dụng các trình phân tích cú pháp CF chung (tuy nhiên GLR có thể không hiệu quả nhất), thay vì các yếu tố quyết định như trong một số hệ thống. Tôi coi AST là đại diện tham chiếu, vốn tự cho mình điều trị được xác định chính thức, biến đổi có thể chứng minh chính xác, bằng chứng toán học, không xuất hiện thành nhiều biểu diễn cụ thể, v.v.
babou

Quan điểm "thực dụng" là lý do tôi cho rằng chúng không khác nhau theo cách hữu ích. Và tôi chỉ đơn giản là không tin rằng việc sử dụng (ad hoc AST) mang lại cho bạn "các phép biến đổi chính xác"; AST ad hoc của bạn không có mối quan hệ rõ ràng với ngữ pháp thực tế của ngôn ngữ đang được xử lý (và ở đây, vâng, hệ thống của tôi có thể phòng thủ được rằng "AST" của chúng tôi có thể là một dạng đẳng cấu tương đương với BNF). Ad hoc ASTs dont cung cấp cho bạn bất kỳ khả năng bổ sung cho unparse để "nhiều cơ quan đại diện bê tông) Bạn phản đối GLR (không hiệu quả nhất) dường như vô nghĩa khá Họ cũng không xác định...
Ira Baxter

Vì vậy, trên thực tế tôi không hiểu bất kỳ phần nào trong phản đối của bạn đối với nhận xét của tôi. Bạn sẽ phải viết rằng "câu trả lời sạch".
Ira Baxter

@IraBaxter Nhận xét quá hạn chế cho một câu trả lời thích hợp (gợi ý?). "Ad hoc" không phải là một vòng loại thích hợp cho AST tôi ủng hộ, mà đôi khi (đôi khi là) cú pháp tham chiếu. Điều này đúng về mặt lịch sử, nhìn cả vào lịch sử của khái niệm AST trong khoa học máy tính và lịch sử của các hệ thống chính thức như các thuật ngữ (cây) trong một đại số được sắp xếp, cùng với giải thích. AST là hình thức tham chiếu, không phải là một dẫn xuất. Xem thêm hệ thống bằng chứng hiện đại và tạo chương trình tự động. Bạn có thể bị thiên vị bởi thực tế là bạn phải làm việc từ một cú pháp cụ thể được thiết kế bởi người khác.
babou

7

Có một số lý do tại sao phần phân tích của trình biên dịch thường được tách thành các giai đoạn phân tích từ vựng và phân tích cú pháp (phân tích cú pháp).

  1. Đơn giản của thiết kế là xem xét quan trọng nhất. Việc phân tách từ vựng và phân tích cú pháp thường cho phép chúng ta đơn giản hóa ít nhất một trong những nhiệm vụ này. Ví dụ, một trình phân tích cú pháp phải xử lý các bình luận và khoảng trắng dưới dạng đơn vị cú pháp sẽ là. Phức tạp hơn đáng kể so với một bình luận có thể giả định và khoảng trắng đã bị loại bỏ bởi bộ phân tích từ vựng. Nếu chúng ta đang thiết kế một ngôn ngữ mới, việc tách biệt các mối quan tâm từ vựng và cú pháp có thể dẫn đến một thiết kế ngôn ngữ tổng thể sạch hơn.
  2. Hiệu quả biên dịch được cải thiện. Một bộ phân tích từ vựng riêng biệt cho phép chúng tôi áp dụng các kỹ thuật chuyên ngành chỉ phục vụ cho nhiệm vụ từ vựng, không phải công việc phân tích cú pháp. Ngoài ra, các kỹ thuật đệm chuyên dụng để đọc các ký tự đầu vào có thể tăng tốc đáng kể trình biên dịch.
  3. Tính di động của trình biên dịch được tăng cường. Đặc thù của thiết bị đầu vào có thể được giới hạn trong bộ phân tích từ vựng.

resource___ Trình biên dịch (Ấn bản 2) được viết bởi- Đại học Alfred V. Abo Columbia Monica S. Lam Đại học Stanford Ravi Sethi Avaya Jeffrey D. Ullman Đại học Stanford

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.