Viết Trình biên dịch Trình biên dịch - Thông tin chi tiết về Sử dụng và Tính năng


10

Đây là một phần của một loạt các câu hỏi tập trung vào dự án chị em với Dự án Trừu tượng, nhằm mục đích trừu tượng hóa các khái niệm được sử dụng trong thiết kế ngôn ngữ dưới dạng khung. Dự án chị em được gọi là OILexer, nhằm mục đích xây dựng một trình phân tích cú pháp từ các tệp ngữ pháp, mà không sử dụng mã tiêm vào các kết quả khớp.

Một số trang khác liên quan đến những câu hỏi này, liên quan đến gõ cấu trúc, có thể được xem tại đây và dễ sử dụng, được tìm thấy ở đây . Chủ đề meta liên quan đến một cuộc điều tra về khung và vị trí thích hợp để đăng có thể được tìm thấy ở đây .

Tôi đang đi đến điểm mà tôi sắp bắt đầu trích xuất cây phân tích cú pháp từ một ngữ pháp nhất định, theo sau là trình phân tích cú pháp đệ quy sử dụng DFA để phân biệt các đường dẫn chuyển tiếp (tương tự như LL (*) của ANTLR 4, vì vậy tôi Hình tôi sẽ mở nó ra để hiểu rõ hơn.

Trong một trình biên dịch trình phân tích cú pháp, loại tính năng nào là lý tưởng?

Cho đến nay ở đây là một tổng quan ngắn gọn về những gì được thực hiện:

  1. Mẫu
  2. Nhìn về phía trước dự đoán, biết những gì hợp lệ tại một điểm nhất định.
  3. Quy tắc 'Tiêu chuẩn hóa' lấy chữ theo quy tắc và giải quyết mã thông báo họ đến từ đâu.
  4. Tự động không điều kiện
  5. Automata quyết định
  6. Máy trạng thái từ vựng đơn giản để nhận dạng mã thông báo
  7. Phương pháp tự động hóa mã thông báo:
    • Quét - hữu ích cho nhận xét: Nhận xét: = "/ *" Quét ("* /");
    • Phép trừ - Hữu ích cho Mã định danh: Mã định danh: = Phép trừ (Mã định danh, Từ khóa);
      • Đảm bảo định danh không chấp nhận từ khóa.
    • Mã hóa - Mã hóa tự động hóa dưới dạng chuỗi X chuyển tiếp N cơ sở.
      • UnicodeEscape: = "\\ u" BaseEncode (Mã định danhCharNoEscape, 16, 4);
        • Làm cho một lối thoát unicode trong thập lục phân, với các chuyển tiếp hex 4. Sự khác biệt giữa điều này và: [0-9A-Fa-f] {4} là tự động hóa kết quả với Mã hóa giới hạn tập hợp các giá trị thập lục phân được phép trong phạm vi của Định danhCharNoEscape. Vì vậy, nếu bạn cung cấp cho nó \ u005c, phiên bản mã hóa sẽ không chấp nhận giá trị. Những thứ như thế này có một cảnh báo nghiêm trọng: Sử dụng một cách tiết kiệm. Việc tự động hóa kết quả có thể khá phức tạp.

Những gì không được triển khai là thế hệ CST, tôi cần điều chỉnh các tự động xác định để thực hiện bối cảnh thích hợp để làm việc này.

Đối với bất kỳ ai quan tâm, tôi đã tải lên một bản in đẹp của mẫu ban đầu của dự án T * y♯ . Mỗi tệp nên liên kết với mọi tệp khác, tôi bắt đầu liên kết theo các quy tắc riêng để tuân theo chúng, nhưng sẽ mất quá nhiều thời gian (sẽ tự động hóa đơn giản hơn!)

Nếu cần thêm ngữ cảnh, xin vui lòng gửi cho phù hợp.

Chỉnh sửa 5-14-2013 : Tôi đã viết mã để tạo đồ thị GraphViz cho các máy trạng thái trong một ngôn ngữ nhất định. Dưới đây là một sơ đồ đồ thị của GraphViz . Các thành viên được liên kết trong mô tả ngôn ngữ nên có một rulename.txt trong thư mục tương đối của họ với sơ đồ cho quy tắc đó. Một số mô tả ngôn ngữ đã thay đổi kể từ khi tôi đăng ví dụ, điều này là do đơn giản hóa mọi thứ về ngữ pháp. Đây là một hình ảnh graphviz thú vị .


8
Tường o 'văn bản. Đừng hiểu sai về điều này, tôi đánh giá cao một vấn đề được giải thích kỹ lưỡng. Trong trường hợp này, nó chỉ đơn giản là quá dài dòng. Từ những gì tôi đã thu thập được, bạn đang hỏi những tính năng nào cần được đưa vào trình phân tích cú pháp ngữ pháp hoặc cách tạo một tính năng mà không bắt đầu từ đầu? Vui lòng chỉnh sửa để trả lời các câu hỏi sau (bạn không phải viết lại, chỉ cần nối vào cuối tóm tắt): Vấn đề của bạn là gì? Những ràng buộc nào bạn bị ràng buộc bởi các giải pháp khả thi cho vấn đề của bạn (nó phải nhanh, phải là LL *, v.v.)?
Neil

1
Tôi đang yêu cầu cái nhìn sâu sắc về bộ tính năng. Trọng tâm là dễ sử dụng. Khó khăn nằm ở việc ai đó không biết dự án, hiểu rõ về dự án để họ được thông báo hướng tới trọng tâm của nó. Tôi không hỏi 'làm thế nào để làm', tôi đang hỏi liên quan đến khả năng sử dụng. Gợi ý về cách cắt câu hỏi được đánh giá cao.
Alexander Morou

1
Đối với tôi, không rõ ràng về dự án là gì. Ví dụ, kể từ thời của yacc, chúng tôi đã thấy rất nhiều trình tạo phân tích cú pháp. Điều gì khác biệt trong OILexer của bạn? Có gì mới không?
Ingo

1
Mục tiêu của dự án này là đơn giản hóa việc tạo trình phân tích cú pháp. Tương tự có, với YACC / Bison và FLEX / LEX. Sự khác biệt chính là để tránh sự phức tạp liên quan đến các chương trình đó. Giữ mọi thứ đơn giản và đi vào vấn đề là mục tiêu chính. Đây là lý do tại sao tôi tạo ra một định dạng không có phân đoạn kỳ lạ, nhưng mục tiêu là làm cho nó giống với lập trình thông thường: chỉ dành riêng cho phát triển ngôn ngữ. Mã thông báo được xác định bằng cách sử dụng ': =' sau tên của chúng, quy tắc được xác định bằng cách sử dụng :: = sau tên của chúng. Các mẫu sử dụng '<' và '>' cho các đối số của họ theo sau là ":: =" vì chúng chia sẻ cú pháp quy tắc.
Alexander Morou

3
Sự tập trung khủng khiếp này vào việc phân tích cú pháp dường như bị đặt sai chỗ; đây là một vấn đề được giải quyết tốt và nó hầu như không làm hỏng những gì bạn cần để xử lý ngôn ngữ lập trình. Google cho bài tiểu luận của tôi về "cuộc sống sau khi phân tích cú pháp".
Ira Baxter

Câu trả lời:


5

Đây là một câu hỏi tuyệt vời.

Gần đây tôi đã làm việc với rất nhiều phân tích cú pháp và IMHO một số tính năng chính là:

  • API lập trình - vì vậy nó có thể được sử dụng từ bên trong ngôn ngữ lập trình, lý tưởng nhất là chỉ cần nhập thư viện. Nó cũng có thể có giao diện giống GUI hoặc BNF, nhưng giao diện lập trình là chìa khóa, bởi vì bạn có thể sử dụng lại công cụ, IDE, phân tích tĩnh, kiểm tra, tiện ích trừu tượng ngôn ngữ, lập trình viên quen thuộc, trình tạo tài liệu, quá trình xây dựng, v.v. Ngoài ra, bạn có thể tương tác chơi với các trình phân tích cú pháp nhỏ, giúp giảm đáng kể thời gian học tập. Những lý do này đặt nó ở đầu danh sách "các tính năng quan trọng" của tôi.

  • báo cáo lỗi, như @guysherman đã đề cập. Khi tìm thấy lỗi, tôi muốn biết lỗi đó ở đâu và chuyện gì đang xảy ra khi nó xảy ra. Thật không may, tôi đã không thể tìm thấy các tài nguyên tốt để giải thích cách tạo ra các lỗi tốt khi quay lại để chơi. (Mặc dù ghi chú @ Sk-logic của bình luận bên dưới).

  • kết quả một phần. Khi phân tích cú pháp thất bại, tôi muốn có thể xem những gì đã được phân tích thành công từ phần đầu vào trước vị trí xảy ra lỗi.

  • trừu tượng hóa. Bạn không bao giờ có thể xây dựng đủ các chức năng và người dùng sẽ luôn cần nhiều hơn, vì vậy cố gắng tìm ra tất cả các chức năng có thể ở phía trước sẽ thất bại. Đây có phải là những gì bạn có nghĩa là bởi các mẫu?

  • Tôi đồng ý với dự đoán số 2 của bạn (dự đoán trước). Tôi nghĩ rằng nó giúp tạo ra các báo cáo lỗi tốt. Nó có hữu ích cho bất cứ điều gì khác?

  • hỗ trợ xây dựng cây phân tích khi phân tích cú pháp xảy ra, có lẽ:

    • một cây cú pháp cụ thể, trong đó cấu trúc của cây tương ứng trực tiếp với ngữ pháp và bao gồm thông tin bố trí để báo cáo lỗi của các giai đoạn sau. Trong trường hợp này, khách hàng không cần phải làm bất cứ điều gì để có được cấu trúc cây phù hợp - nó phải phụ thuộc trực tiếp vào ngữ pháp.
    • một cây cú pháp trừu tượng. Trong trường hợp này, người dùng có thể sử dụng bất kỳ và tất cả các cây phân tích cú pháp
  • một số loại đăng nhập. Tôi không chắc chắn về điều này; có thể để hiển thị dấu vết của các quy tắc mà trình phân tích cú pháp đã thử hoặc để theo dõi các mã thông báo rác như khoảng trắng hoặc nhận xét, trong trường hợp (ví dụ) bạn muốn sử dụng mã thông báo để tạo tài liệu HTML.

  • khả năng đối phó với các ngôn ngữ nhạy cảm ngữ cảnh. Không chắc chắn điều này quan trọng như thế nào - trong thực tế, có vẻ như sẽ phân tích một siêu ngôn ngữ với ngữ pháp không ngữ cảnh, sau đó kiểm tra các ràng buộc nhạy cảm theo ngữ cảnh trong các lần vượt qua sau.

  • thông báo lỗi tùy chỉnh, để tôi có thể điều chỉnh các báo cáo lỗi trong các tình huống cụ thể và có thể nhanh chóng hiểu và khắc phục sự cố hơn.

Mặt khác, tôi không thấy việc sửa lỗi đặc biệt quan trọng - mặc dù tôi không cập nhật về tiến trình hiện tại. Các vấn đề tôi nhận thấy là các sửa chữa tiềm năng mà các công cụ cung cấp là: 1) quá nhiều và 2) không tương ứng với các lỗi thực tế đã gây ra và vì vậy không hữu ích lắm. Hy vọng tình trạng này sẽ được cải thiện (hoặc có lẽ đã làm như vậy).


Tôi đã chỉnh sửa nội dung câu hỏi để bao gồm một liên kết đến PrecedenceHelper trong dấu đầu dòng có nội dung 'Mẫu'. Nó cho phép các bộ dữ liệu mảng tham số, vì vậy nếu bạn có bốn tham số, mỗi mảng tham số, mẫu phải được sử dụng trong các bộ đối số gồm bốn tham số.
Alexander Morou

Lý do chính mà bạn sẽ xây dựng CST là bố cục tổng thể của tệp được phân tích cú pháp. Nếu bạn muốn in đẹp tài liệu, đặt cược tốt nhất của bạn là sử dụng một CST vì tên AST của chúng ngụ ý thiếu thông tin để xử lý khoảng cách lẻ mà một CST sẽ nắm bắt. Chuyển đổi một CST thường khá dễ dàng nếu đó là một CST tốt.
Alexander Morou

Tôi đã chỉnh sửa lại câu hỏi về chủ đề tích hợp sẵn để sử dụng.
Alexander Morou

Tôi nghĩ rằng tôi đã không làm tốt công việc bày tỏ quan điểm của mình về các mẫu / chức năng: Ý tôi là vì bạn không bao giờ có đủ, nên một hệ thống không nên cố gắng tìm ra chúng trước thời hạn: người dùng cần có khả năng tạo của riêng mình.

1
Tôi đã tìm thấy một cách tiếp cận đặc biệt hữu ích cho báo cáo lỗi với quay lui vô hạn (Packrat): mỗi quy tắc sản xuất được chú thích với các thông báo lỗi (được gọi là "blah-blah-blah dự kiến") và khi thất bại, thông báo đó được lưu trữ trong luồng theo cùng một cách như các thẻ ghi nhớ. Nếu tất cả các lỗi không thể phục hồi (phân tích cú pháp chấm dứt trước khi đến cuối luồng), thông báo lỗi ngoài cùng bên phải (hoặc một tập hợp các thông báo như vậy) là phù hợp nhất. Đó là điều dễ nhất để làm, tất nhiên có nhiều cách để tinh chỉnh nó hơn nữa với nhiều chú thích hơn.
SK-logic

5

Tôi không có kinh nghiệm về thiết kế ngôn ngữ, nhưng tôi đã từng viết một trình phân tích cú pháp một lần, khi tôi đang tạo và IDE cho một công cụ trò chơi.

Một cái gì đó quan trọng đối với người dùng cuối cùng của bạn, theo tôi, là thông báo lỗi có ý nghĩa. Không phải là một điểm đặc biệt rung chuyển trái đất, tôi biết, nhưng theo nó ngược lại, một trong những ý nghĩa chính của điều này là bạn cần có thể tránh được những thông tin sai lệch. Trường hợp dương tính giả đến từ đâu? Họ đến từ trình phân tích cú pháp rơi ở lỗi đầu tiên và không bao giờ hoàn toàn hồi phục. C / C ++ nổi tiếng về điều này (mặc dù các trình biên dịch mới hơn thông minh hơn một chút).

Vì vậy, những gì bạn cần thay thế? Tôi nghĩ rằng thay vì chỉ biết những gì / không hợp lệ tại một thời điểm, bạn cần biết cách lấy những gì không hợp lệ và thực hiện một thay đổi tối thiểu để làm cho nó hợp lệ - để bạn có thể tiếp tục phân tích cú pháp mà không tạo ra các lỗi sai liên quan để gốc đệ quy của bạn nhận được nhầm lẫn. Việc có thể xây dựng một trình phân tích cú pháp có thể tạo ra thông tin này không chỉ cung cấp cho bạn một trình phân tích cú pháp rất mạnh mẽ mà còn mở ra một số tính năng tuyệt vời cho phần mềm sử dụng trình phân tích cú pháp.

Tôi nhận ra rằng tôi có thể đề xuất một cái gì đó thực sự khó khăn, hoặc rõ ràng ngu ngốc, xin lỗi nếu đây là trường hợp. Nếu đây không phải là thứ bạn đang tìm kiếm, tôi sẽ vui vẻ xóa câu trả lời của tôi.


Đây là một trong những điều tôi dự định làm. Để hỗ trợ kiến ​​thức về tên miền của tôi, một người bạn của tôi đã đề nghị viết một trình phân tích cú pháp thực tế bằng tay để có được sự tự động hóa nó. Một điều tôi nhận ra khá nhanh: trình phân tích cú pháp rất phức tạp và có những thứ chúng tôi làm bằng tay giúp đơn giản hóa quy trình. Các quy tắc và mẫu chia sẻ cùng một cú pháp; tuy nhiên, có các yếu tố ngôn ngữ hợp lệ trong Mẫu nhưng không phải là quy tắc, có các trạng thái bên trong xử lý tác vụ này. Điều này mang đến một ý tưởng: các quy tắc sẽ có thể chỉ định các công cụ hỗ trợ đường dẫn để chia sẻ quy tắc phụ dễ dàng hơn.
Alexander Morou

... Điều này khá đơn giản để thực hiện tự động hóa, nhưng sẽ yêu cầu tự động hóa phải có các điều kiện dựa trên trạng thái. Tôi sẽ làm việc với một số điều này và lấy lại cho bạn. ANTLR sử dụng tự động hóa trạng thái hữu hạn để xử lý các chu kỳ nói: "T" *, trong đó tôi sẽ sử dụng nó để xử lý hầu hết quá trình phân tích cú pháp vì các mức giảm sẽ sạch hơn như các trạng thái khi có hơn 800 biến thể trong quy tắc (điều này sẽ phình to nhanh chóng như mã spaghetti ở dạng if / khác.)
Alexander Morou

0

Ngữ pháp không được có các hạn chế như "không để lại các quy tắc đệ quy". Thật nực cười khi các công cụ được sử dụng rộng rãi ngày nay có điều này và chỉ có thể hiểu được việc sử dụng ngữ pháp LL - gần 50 năm sau khi yacc làm đúng.

Một ví dụ cho đệ quy đúng (sử dụng cú pháp yacc):

list: 
      elem                  { $$ = singleton($1); }
    | elem ',' list         { $$ = cons($1, $2);  }
    ;

Một ví dụ cho đệ quy trái (sử dụng yacc synatx):

funapp:
    term                    { $$ = $1; }
    | funapp term           { $$ = application($1, $2); }
    ;

Bây giờ, điều này có thể "tái cấu trúc" thành thứ khác, nhưng trong cả hai trường hợp, loại đệ quy cụ thể chỉ là cách "đúng" để viết này, vì (trong ngôn ngữ ví dụ) danh sách là đệ quy phải trong khi ứng dụng hàm được đệ quy trái .

Người ta có thể mong đợi từ các công cụ tiên tiến mà họ hỗ trợ theo cách tự nhiên để viết nội dung, thay vì yêu cầu một công cụ "tái cấu trúc" mọi thứ phải ở dạng đệ quy trái / phải mà công cụ áp đặt lên một.


Vâng, thật tuyệt khi không có những hạn chế tùy tiện như thế. Tuy nhiên, ví dụ về đệ quy trái không thể thay thế bằng toán tử lặp lại (chẳng hạn như biểu thức chính quy *hoặc +định lượng) là gì? Tôi thoải mái thừa nhận rằng kiến ​​thức của tôi trong lĩnh vực này còn hạn chế, nhưng tôi chưa bao giờ sử dụng đệ quy trái mà không thể tái cấu trúc thành sự lặp lại. Và tôi cũng thấy phiên bản lặp lại rõ ràng hơn (mặc dù đó chỉ là sở thích cá nhân).

1
@MattFenwick Xem chỉnh sửa của tôi. Lưu ý cách chỉ thị cú pháp dẫn đến các hành động ngữ nghĩa đơn giản và tự nhiên (ví dụ) để tạo cây cú pháp. Trong khi với sự lặp lại, (không có sẵn trong yacc, btw), tôi đoán bạn thường cần kiểm tra xem bạn có một danh sách trống, một đơn, v.v.
Ingo

Cảm ơn bạn đã phản hồi. Tôi nghĩ rằng tôi hiểu rõ hơn bây giờ - Tôi muốn viết những ví dụ như list = sepBy1(',', elem)funapp = term{+}(và tất nhiên sepBy1+sẽ được thực hiện về mặt trái / phải đệ quy, và sản xuất cây cú pháp chuẩn). Vì vậy, không phải tôi nghĩ rằng đệ quy trái và phải là xấu, chỉ là tôi cảm thấy họ ở mức độ thấp và muốn sử dụng một sự trừu tượng hóa ở cấp độ cao hơn để có thể làm cho mọi thứ rõ ràng hơn. Cảm ơn một lần nữa!

1
Bạn được chào đón @MattFenwick. Nhưng sau đó có thể là một vấn đề của hương vị. Đối với tôi, đệ quy là (ít nhất là trong bối cảnh của các ngôn ngữ, tất cả vốn là đệ quy hoặc hoàn toàn không thú vị) theo cách tự nhiên hơn để nghĩ về nó. Ngoài ra, một cây là một cấu trúc dữ liệu đệ quy, vì vậy tôi thấy không cần phải quay lại phép lặp để mô phỏng đệ quy. Nhưng, tất nhiên, sở thích là khác nhau.
Ingo
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.