Tôi có nên sử dụng trình tạo trình phân tích cú pháp hay tôi nên cuộn mã lexer và trình phân tích cú pháp tùy chỉnh của riêng mình?


81

Những lợi thế và bất lợi cụ thể của từng cách để làm việc trên một ngữ pháp ngôn ngữ lập trình?

Tại sao / Khi nào tôi nên tự lăn? Tại sao / Khi nào tôi nên sử dụng máy phát điện?


Cung cấp cho Boost.Sprite Qi một shot.
Ebrahim Mohammadi

Câu trả lời:


78

Có ba lựa chọn thực sự, cả ba đều thích hợp hơn trong các tình huống khác nhau.

Tùy chọn 1: trình tạo trình phân tích cú pháp hoặc 'bạn cần phân tích một số ngôn ngữ và bạn chỉ muốn làm cho nó hoạt động, chết tiệt'

Giả sử, bạn được yêu cầu xây dựng trình phân tích cú pháp cho một số định dạng dữ liệu cổ NGAY BÂY GIỜ. Hoặc bạn cần trình phân tích cú pháp của bạn để được nhanh chóng. Hoặc bạn cần trình phân tích cú pháp của bạn để có thể dễ dàng bảo trì.

Trong những trường hợp này, có lẽ bạn tốt nhất nên sử dụng trình tạo trình phân tích cú pháp. Bạn không cần phải tìm hiểu chi tiết, bạn không cần phải có nhiều mã phức tạp để hoạt động chính xác, bạn chỉ cần viết ra ngữ pháp mà đầu vào sẽ tuân thủ, viết một số mã xử lý và trình phân tích cú pháp tức thì.

Những lợi thế rất rõ ràng:

  • Thật dễ dàng để viết một đặc tả, đặc biệt nếu định dạng đầu vào không quá lạ (tùy chọn 2 sẽ tốt hơn nếu có).
  • Bạn kết thúc với một phần công việc rất dễ bảo trì dễ hiểu: một định nghĩa ngữ pháp thường chảy tự nhiên hơn nhiều so với mã.
  • Các trình phân tích cú pháp được tạo bởi trình tạo trình phân tích cú pháp tốt thường nhanh hơn rất nhiều so với mã viết tay. Mã viết tay thể nhanh hơn, nhưng chỉ khi bạn biết công cụ của mình - đây là lý do tại sao hầu hết các trình biên dịch được sử dụng rộng rãi đều sử dụng trình phân tích cú pháp đệ quy gốc viết tay.

Có một điều bạn phải cẩn thận với trình tạo phân tích cú pháp: đôi khi có thể từ chối ngữ pháp của bạn. Để biết tổng quan về các loại trình phân tích cú pháp khác nhau và cách chúng có thể cắn bạn, bạn có thể muốn bắt đầu ở đây . Ở đây bạn có thể tìm thấy một cái nhìn tổng quan về rất nhiều triển khai và các loại ngữ pháp mà họ chấp nhận.

Tùy chọn 2: trình phân tích cú pháp viết tay hoặc 'bạn muốn xây dựng trình phân tích cú pháp của riêng mình và bạn quan tâm đến việc thân thiện với người dùng'

Trình tạo phân tích cú pháp rất hay, nhưng chúng không thân thiện với người dùng (người dùng cuối chứ không phải bạn). Bạn thường không thể đưa ra thông báo lỗi tốt, cũng như không thể cung cấp phục hồi lỗi. Có lẽ ngôn ngữ của bạn rất kỳ lạ và các trình phân tích cú pháp từ chối ngữ pháp của bạn hoặc bạn cần kiểm soát nhiều hơn trình tạo cho bạn.

Trong những trường hợp này, sử dụng trình phân tích cú pháp đệ quy gốc viết tay có lẽ là tốt nhất. Mặc dù việc xử lý đúng có thể phức tạp, bạn có toàn quyền kiểm soát trình phân tích cú pháp của mình để bạn có thể thực hiện tất cả các loại nội dung hay mà bạn không thể làm với trình tạo trình phân tích cú pháp, như thông báo lỗi và thậm chí khôi phục lỗi (thử xóa tất cả dấu chấm phẩy khỏi tệp C # : trình biên dịch C # sẽ khiếu nại, nhưng dù sao cũng sẽ phát hiện ra hầu hết các lỗi khác bất kể sự hiện diện của dấu chấm phẩy).

Các trình phân tích cú pháp viết tay cũng thường hoạt động tốt hơn các trình phân tích cú pháp được tạo ra, giả sử chất lượng của trình phân tích cú pháp là đủ cao. Mặt khác, nếu bạn không quản lý để viết một trình phân tích cú pháp tốt - thường là do (sự kết hợp) thiếu kinh nghiệm, kiến ​​thức hoặc thiết kế - thì hiệu suất thường chậm hơn. Đối với các từ vựng thì ngược lại là đúng: các từ vựng được tạo ra thường sử dụng tra cứu bảng, làm cho chúng nhanh hơn (hầu hết) các văn bản viết tay.

Giáo dục, viết trình phân tích cú pháp của riêng bạn sẽ dạy cho bạn nhiều hơn là sử dụng một trình tạo. Rốt cuộc, bạn phải viết mã ngày càng phức tạp hơn, cộng với việc bạn phải hiểu chính xác cách bạn phân tích một ngôn ngữ. Mặt khác, nếu bạn muốn học cách tạo ngôn ngữ của riêng mình (vì vậy, hãy có kinh nghiệm về thiết kế ngôn ngữ), thì tùy chọn 1 hoặc tùy chọn 3 là thích hợp hơn: nếu bạn đang phát triển một ngôn ngữ, nó có thể sẽ thay đổi rất nhiều, và tùy chọn 1 và 3 cho bạn thời gian dễ dàng hơn với điều đó.

Tùy chọn 3: trình tạo trình phân tích cú pháp viết tay hoặc 'bạn đang cố gắng học hỏi nhiều từ dự án này và bạn sẽ không phiền khi kết thúc với một đoạn mã tiện lợi mà bạn có thể sử dụng lại nhiều'

Đây là con đường tôi hiện đang đi xuống: bạn viết trình tạo trình phân tích cú pháp của riêng bạn . Mặc dù rất không cần thiết, nhưng làm điều này có thể sẽ dạy cho bạn nhiều nhất.

Để cho bạn biết những gì làm một dự án như thế này liên quan đến tôi sẽ cho bạn biết về tiến trình của riêng tôi.

Trình tạo lexer

Tôi đã tạo trình tạo lexer của riêng mình trước. Tôi thường thiết kế phần mềm bắt đầu bằng cách sử dụng mã, vì vậy tôi đã nghĩ về cách tôi muốn có thể sử dụng mã của mình và viết đoạn mã này (nó ở C #):

Lexer<CalculatorToken> calculatorLexer = new Lexer<CalculatorToken>(
    new List<StringTokenPair>()
    { // This is just like a lex specification:
      //                    regex   token
        new StringTokenPair("\\+",  CalculatorToken.Plus),
        new StringTokenPair("\\*",  CalculatorToken.Times),
        new StringTokenPair("(",    CalculatorToken.LeftParenthesis),
        new StringTokenPair(")",    CalculatorToken.RightParenthesis),
        new StringTokenPair("\\d+", CalculatorToken.Number),
    });

foreach (CalculatorToken token in
             calculatorLexer.GetLexer(new StringReader("15+4*10")))
{ // This will iterate over all tokens in the string.
    Console.WriteLine(token.Value);
}

// Prints:
// 15
// +
// 4
// *
// 10

Các cặp mã thông báo chuỗi đầu vào được chuyển đổi thành cấu trúc đệ quy tương ứng mô tả các biểu thức chính quy mà chúng thể hiện bằng cách sử dụng các ý tưởng của ngăn xếp số học. Điều này sau đó được chuyển đổi thành NFA (automaton hữu hạn hữu hạn), sau đó được chuyển đổi thành DFA (automaton hữu hạn xác định). Sau đó, bạn có thể kết hợp các chuỗi với DFA.

Bằng cách này, bạn sẽ có được một ý tưởng tốt về cách chính xác các từ vựng hoạt động. Ngoài ra, nếu bạn thực hiện đúng cách, kết quả từ trình tạo lexer của bạn có thể nhanh như triển khai chuyên nghiệp. Bạn cũng không mất bất kỳ biểu cảm nào so với tùy chọn 2 và không có nhiều biểu cảm so với tùy chọn 1.

Tôi đã triển khai trình tạo lexer của mình chỉ trong hơn 1600 dòng mã. Mã này làm cho công việc trên, nhưng nó vẫn tạo ra lexer khi bạn khởi động chương trình: Tôi sẽ thêm mã để ghi nó vào đĩa vào một lúc nào đó.

Nếu bạn muốn biết làm thế nào để viết lexer của riêng bạn, đây là một nơi tốt để bắt đầu.

Trình tạo trình phân tích cú pháp

Sau đó, bạn viết trình tạo trình phân tích cú pháp của bạn. Tôi đề cập ở đây một lần nữa để biết tổng quan về các loại trình phân tích cú pháp khác nhau - như một quy tắc chung, họ càng có thể phân tích cú pháp, chúng càng chậm.

Tốc độ không phải là vấn đề đối với tôi, tôi đã chọn triển khai trình phân tích cú pháp Earley. Việc triển khai nâng cao của trình phân tích cú pháp Earley đã được chứng minh là chậm hơn khoảng hai lần so với các loại trình phân tích cú pháp khác.

Đổi lại với tốc độ đó, bạn có khả năng phân tích bất kỳ loại ngữ pháp nào, thậm chí là mơ hồ. Điều này có nghĩa là bạn không bao giờ phải lo lắng về việc trình phân tích cú pháp của bạn có bất kỳ đệ quy trái nào trong đó hay không, hoặc xung đột giảm ca là gì. Bạn cũng có thể xác định ngữ pháp dễ dàng hơn bằng cách sử dụng các ngữ pháp mơ hồ nếu kết quả là cây phân tích cú pháp nào không quan trọng, chẳng hạn như bạn phân tích 1 + 2 + 3 như (1 + 2) +3 hay 1 + (2 + 3).

Đây là những gì một đoạn mã sử dụng trình tạo trình phân tích cú pháp của tôi có thể trông như sau:

Lexer<CalculatorToken> calculatorLexer = new Lexer<CalculatorToken>(
    new List<StringTokenPair>()
    {
        new StringTokenPair("\\+",  CalculatorToken.Plus),
        new StringTokenPair("\\*",  CalculatorToken.Times),
        new StringTokenPair("(",    CalculatorToken.LeftParenthesis),
        new StringTokenPair(")",    CalculatorToken.RightParenthesis),
        new StringTokenPair("\\d+", CalculatorToken.Number),
    });

Grammar<IntWrapper, CalculatorToken> calculator
    = new Grammar<IntWrapper, CalculatorToken>(calculatorLexer);

// Declaring the nonterminals.
INonTerminal<IntWrapper> expr = calculator.AddNonTerminal<IntWrapper>();
INonTerminal<IntWrapper> term = calculator.AddNonTerminal<IntWrapper>();
INonTerminal<IntWrapper> factor = calculator.AddNonTerminal<IntWrapper>();

// expr will be our head nonterminal.
calculator.SetAsMainNonTerminal(expr);

// expr: term | expr Plus term;
calculator.AddProduction(expr, term.GetDefault());
calculator.AddProduction(expr,
                         expr.GetDefault(),
                         CalculatorToken.Plus.GetDefault(),
                         term.AddCode(
                         (x, r) => { x.Result.Value += r.Value; return x; }
                         ));

// term: factor | term Times factor;
calculator.AddProduction(term, factor.GetDefault());
calculator.AddProduction(term,
                         term.GetDefault(),
                         CalculatorToken.Times.GetDefault(),
                         factor.AddCode
                         (
                         (x, r) => { x.Result.Value *= r.Value; return x; }
                         ));

// factor: LeftParenthesis expr RightParenthesis
//         | Number;
calculator.AddProduction(factor,
                         CalculatorToken.LeftParenthesis.GetDefault(),
                         expr.GetDefault(),
                         CalculatorToken.RightParenthesis.GetDefault());
calculator.AddProduction(factor,
                         CalculatorToken.Number.AddCode
                         (
                         (x, s) => { x.Result = new IntWrapper(int.Parse(s));
                                     return x; }
                         ));

IntWrapper result = calculator.Parse("15+4*10");
// result == 55

(Lưu ý rằng IntWrapper chỉ đơn giản là một Int32, ngoại trừ C # yêu cầu nó phải là một lớp, do đó tôi phải giới thiệu một lớp bao bọc)

Tôi hy vọng bạn thấy rằng đoạn mã trên rất mạnh mẽ: bất kỳ ngữ pháp nào bạn có thể đưa ra đều có thể được phân tích cú pháp. Bạn có thể thêm các đoạn mã tùy ý trong ngữ pháp có khả năng thực hiện nhiều nhiệm vụ. Nếu bạn quản lý để làm cho tất cả điều này hoạt động, bạn có thể sử dụng lại mã kết quả để thực hiện rất nhiều nhiệm vụ rất dễ dàng: chỉ cần tưởng tượng xây dựng một trình thông dịch dòng lệnh bằng cách sử dụng đoạn mã này.


3
Tôi nghĩ rằng bạn đánh giá thấp khối lượng công việc cần thiết để tạo một trình phân tích cú pháp và từ vựng hiệu suất cao.

Tôi đã hoàn thành việc xây dựng trình tạo lexer của riêng mình và tôi đã đi khá xa cùng với việc xây dựng trình tạo trình phân tích cú pháp của riêng tôi khi tôi quyết định thực hiện một thuật toán khác thay thế. Tôi không mất nhiều thời gian để làm cho tất cả hoạt động, nhưng một lần nữa tôi đã không nhắm đến 'hiệu suất cao', chỉ là 'hiệu suất tốt' và 'hiệu suất tiệm cận tuyệt vời' - Unicode là một con chó cái có được thời gian chạy tốt cho và sử dụng C # đã áp đặt một chi phí hiệu năng.
Alex ten Brink

Câu trả lời rất hay. Tôi sẽ đồng ý với lựa chọn của bạn Nr. 3 cho tất cả các lý do bạn nêu ở trên. Nhưng tôi có thể nói thêm rằng, như trường hợp của tôi, bạn cũng rất nghiêm túc trong việc thiết kế ngôn ngữ, có lẽ bạn cũng nên sử dụng trình tạo trình phân tích cú pháp cùng lúc với việc cố gắng tạo ngôn ngữ của riêng bạn. Vì vậy, bạn có thể bắt đầu các vấn đề ngôn ngữ và có thể thấy ngôn ngữ của mình hoạt động nhanh hơn
Lefteris

1
Có một lựa chọn thứ tư: bộ kết hợp trình phân tích cú pháp.
YuriAlbu Stew

@AlextenBrink Bạn có tình cờ có tài khoản github không? Tôi thực sự muốn có được bàn tay của tôi trên lexer / trình phân tích cú pháp. Điều ấn tượng bạn đã thực hiện.
Behrooz

22

Nếu bạn chưa bao giờ, đã từng viết một trình phân tích cú pháp, tôi sẽ khuyên bạn nên làm điều đó. Thật thú vị, và bạn học được cách mọi thứ hoạt động, và bạn học cách đánh giá cao nỗ lực mà trình phân tích cú pháp và trình tạo từ vựng tiết kiệm cho bạn khỏi làm lần sau khi bạn cần một trình phân tích cú pháp.

Tôi cũng đề nghị bạn thử đọc http://compilers.iecc.com/crenshaw/ vì nó có thái độ rất thực tế đối với cách thực hiện.


2
Đề nghị tốt và một liên kết rất hữu ích.
Maniero

14

Ưu điểm của việc viết trình phân tích cú pháp gốc đệ quy của riêng bạn là bạn có thể tạo các thông báo lỗi chất lượng cao về các lỗi cú pháp. Sử dụng trình tạo trình phân tích cú pháp, bạn có thể tạo các lỗi sản xuất và thêm thông báo lỗi tùy chỉnh tại một số điểm nhất định, nhưng trình tạo trình phân tích cú pháp không phù hợp với khả năng kiểm soát hoàn toàn việc phân tích cú pháp.

Một lợi thế khác của việc viết của riêng bạn là dễ dàng phân tích thành một cách trình bày đơn giản hơn mà không có sự tương ứng 1-1 với ngữ pháp của bạn.

Nếu ngữ pháp của bạn đã được sửa và các thông báo lỗi rất quan trọng, hãy xem xét việc tự lăn hoặc ít nhất là sử dụng trình tạo trình phân tích cú pháp cung cấp cho bạn các thông báo lỗi bạn cần. Nếu ngữ pháp của bạn liên tục thay đổi, bạn nên xem xét sử dụng trình tạo phân tích cú pháp thay thế.

Bjarne Stroustrup nói về cách anh ta sử dụng YACC cho lần triển khai C ++ đầu tiên (xem Thiết kế và tiến hóa của C ++ ). Trong trường hợp đầu tiên, anh ta muốn anh ta viết trình phân tích cú pháp gốc đệ quy của riêng mình!


Tôi hầu như không tin các thí nghiệm đầu tiên nên có với trình tạo phân tích cú pháp. Bạn đã cho tôi một số lợi thế để trao đổi với một giải pháp tùy chỉnh. Tôi chưa quyết định gì cả, nhưng đó là một câu trả lời hữu ích để giúp tôi.
Maniero

++ Câu trả lời này chính xác là những gì tôi sẽ nói. Tôi đã xây dựng nhiều ngôn ngữ và hầu như luôn sử dụng gốc đệ quy. Tôi chỉ nói thêm rằng đã có lúc ngôn ngữ tôi cần được xây dựng đơn giản nhất bằng cách xếp một số macro trên đầu C hoặc C ++ (hoặc Lisp).
Mike Dunlavey

JavaCC được tuyên bố là có các thông báo lỗi tốt nhất. Ngoài ra, hãy chú ý đến lỗi JavaScript và các thông báo cảnh báo trên V8 và Firefox, tôi nghĩ rằng họ không sử dụng bất kỳ trình tạo trình phân tích cú pháp nào.
Ming-Tang

2
@SHiNKiROU: Thật vậy, có lẽ không phải là một tai nạn mà JavaCC cũng sử dụng phân tích cú pháp gốc đệ quy.
Macneil

10

Tùy chọn 3: Không (Cuộn trình tạo trình phân tích cú pháp của riêng bạn)

Chỉ vì có lý do để không sử dụng ANTLR , bison , Coco / R , Grammatica , JavaCC , Lemon , Parboiled , SableCC , Quex , v.v. - điều đó không có nghĩa là bạn nên ngay lập tức cuộn trình phân tích cú pháp + lexer của riêng bạn.

Xác định lý do tại sao tất cả các công cụ này không đủ tốt - tại sao chúng không cho phép bạn đạt được mục tiêu của mình?

Trừ khi bạn chắc chắn rằng những điểm kỳ lạ trong ngữ pháp mà bạn đang xử lý là duy nhất, bạn không nên tạo một trình phân tích cú pháp tùy chỉnh + từ vựng cho nó. Thay vào đó, hãy tạo một công cụ sẽ tạo ra những gì bạn muốn, nhưng cũng có thể được sử dụng để đáp ứng các nhu cầu trong tương lai, sau đó phát hành dưới dạng Phần mềm miễn phí để ngăn chặn những người khác gặp vấn đề tương tự như bạn.


1
Tôi đồng ý với lần đầu tiên thử trình tạo trình phân tích cú pháp và sau đó thử một giải pháp tùy chỉnh, nhưng lợi thế cụ thể (dis) nào? Đây gần như là một lời khuyên chung.
Maniero

1
Đó là lời khuyên chung - nhưng sau đó bạn hỏi một câu hỏi chung. : P Tôi sẽ mở rộng nó với một số suy nghĩ cụ thể hơn về ưu và nhược điểm vào ngày mai.
Peter Boughton

1
Tôi nghĩ bạn đánh giá thấp khối lượng công việc cần thiết để tạo một trình phân tích cú pháp và từ vựng tùy chỉnh. Đặc biệt là một tái sử dụng.

8

Cuộn trình phân tích cú pháp của riêng bạn buộc bạn phải suy nghĩ trực tiếp về sự phức tạp của ngôn ngữ của bạn. Nếu ngôn ngữ khó phân tích, có lẽ nó sẽ khó hiểu.

Có rất nhiều sự quan tâm đến các trình tạo phân tích cú pháp trong những ngày đầu, được thúc đẩy bởi cú pháp ngôn ngữ rất phức tạp (một số người sẽ nói "bị tra tấn"). JOVIAL là một ví dụ đặc biệt tồi tệ: nó yêu cầu hai biểu tượng nhìn vào thời điểm mà mọi thứ khác yêu cầu nhiều nhất là một biểu tượng. Điều này làm cho việc tạo trình phân tích cú pháp cho trình biên dịch JOVIAL trở nên khó khăn hơn mong đợi (vì General Dynamics / Fort Worth Division đã học được cách khó khăn khi họ mua trình biên dịch JOVIAL cho chương trình F-16).

Ngày nay, gốc đệ quy là phương thức phổ biến, bởi vì nó dễ dàng hơn cho các nhà văn biên dịch. Trình biên dịch gốc đệ quy thưởng mạnh mẽ cho thiết kế ngôn ngữ đơn giản, gọn gàng, trong đó việc viết một trình phân tích cú pháp gốc đệ quy cho một ngôn ngữ đơn giản, gọn gàng hơn là một ngôn ngữ lộn xộn, lộn xộn.

Cuối cùng: Bạn đã cân nhắc việc nhúng ngôn ngữ của mình vào LISP và để một thông dịch viên LISP thực hiện công việc nặng nhọc cho bạn chưa? AutoCAD đã làm điều đó, và thấy nó làm cho cuộc sống của họ dễ dàng hơn rất nhiều. Có khá nhiều trình thông dịch LISP nhẹ ngoài kia, một số có thể nhúng.


Đó là một đối số thú vị để đưa ra một giải pháp tùy chỉnh.
Maniero

1
Rất đẹp. Tôi sẽ chỉ thêm vào như một điểm thông tin mà Fortran yêu cầu nhìn gần như tùy ý (toàn bộ dòng) để phân tích mọi thứ, trước khi JOVIAL. Nhưng tại thời điểm đó, họ không có ý tưởng nào khác để biến (hoặc thực hiện) một ngôn ngữ.
Macneil

Đi bộ là phương tiện giao thông tốt nhất vì nó cho bạn thời gian để suy nghĩ xem việc đi đến nơi bạn đang đi có thực sự xứng đáng hay không. Nó cũng khỏe mạnh.
babou

6

Tôi đã viết một trình phân tích cú pháp cho ứng dụng thương mại một lần và tôi đã sử dụng yacc . Có một nguyên mẫu cạnh tranh trong đó một nhà phát triển đã viết toàn bộ bằng tay trong C ++ và nó hoạt động chậm hơn khoảng năm lần.

Đối với lexer cho trình phân tích cú pháp này, tôi đã viết nó hoàn toàn bằng tay. Phải mất - xin lỗi, nó đã gần như 10 năm trước đây, vì vậy tôi không nhớ nó một cách chính xác - khoảng 1000 dòng trong C .

Lý do tại sao tôi viết lexer bằng tay là ngữ pháp đầu vào của trình phân tích cú pháp. Đó là một yêu cầu, một cái gì đó mà việc triển khai trình phân tích cú pháp của tôi phải tuân thủ, trái ngược với thứ tôi thiết kế. (Tất nhiên tôi sẽ thiết kế nó theo cách khác. Và tốt hơn!) Ngữ pháp phụ thuộc hoàn toàn vào ngữ cảnh và thậm chí từ vựng phụ thuộc vào ngữ nghĩa ở một số nơi. Ví dụ, dấu chấm phẩy có thể là một phần của mã thông báo ở một nơi, nhưng dấu phân cách ở một nơi khác - dựa trên cách giải thích ngữ nghĩa của một số yếu tố đã được phân tích cú pháp trước đó. Vì vậy, tôi đã "chôn vùi" những phụ thuộc ngữ nghĩa như vậy trong từ vựng viết tay và điều đó đã để lại cho tôi một BNF khá đơn giản , dễ thực hiện trong yacc.

THÊM để đáp ứng với Macneil : yacc cung cấp một sự trừu tượng hóa rất mạnh mẽ cho phép lập trình viên suy nghĩ về các thiết bị đầu cuối, không phải thiết bị đầu cuối, sản xuất và những thứ tương tự. Ngoài ra, khi triển khai yylex()chức năng, nó giúp tôi tập trung vào việc trả lại mã thông báo hiện tại và không lo lắng về những gì trước hoặc sau nó. Lập trình viên C ++ làm việc ở cấp độ ký tự, không có lợi ích của sự trừu tượng hóa đó và cuối cùng tạo ra một thuật toán phức tạp hơn và kém hiệu quả hơn. Chúng tôi kết luận rằng tốc độ chậm hơn không liên quan gì đến chính C ++ hoặc bất kỳ thư viện nào. Chúng tôi đã đo tốc độ phân tích cú pháp thuần túy với các tệp được tải trong bộ nhớ; nếu chúng tôi gặp sự cố đệm tệp, yacc sẽ không phải là công cụ được chúng tôi lựa chọn để giải quyết.

CSONG MUỐN THÊM : đây không phải là một công thức để viết các trình phân tích cú pháp nói chung, chỉ là một ví dụ về cách nó hoạt động trong một tình huống cụ thể.


Tôi tò mò về việc triển khai C ++ chậm hơn năm lần bằng tay: Có lẽ đó là bộ đệm tệp kém? Nó có thể tạo ra một sự khác biệt lớn.
Macneil

@Macneil: Tôi sẽ đăng một bổ sung cho câu trả lời của tôi; Nhận xét quá dài.
azheglov

1
++ Kinh nghiệm tốt. Tôi sẽ không đặt quá nhiều vào hiệu suất. Thật dễ dàng để các chương trình tốt bị chậm lại bởi một cái gì đó ngớ ngẩn và không cần thiết. Tôi đã viết đủ các trình phân tích cú pháp gốc đệ quy để biết không nên làm gì, vì vậy tôi nghi ngờ liệu có gì nhanh hơn không. Rốt cuộc, các nhân vật cần phải được đọc. Tôi nghi ngờ các trình phân tích cú pháp chạy khỏi bảng sẽ chậm hơn một chút, nhưng có lẽ không đủ để chú ý.
Mike Dunlavey

3

Điều đó phụ thuộc hoàn toàn vào những gì bạn cần phân tích. Bạn có thể tự lăn nhanh hơn bạn có thể đạt được mục đích học tập của một người từ chối không? Các công cụ được phân tích cú pháp tĩnh có đủ để bạn không hối hận về quyết định này không? Bạn có thấy việc triển khai hiện tại quá phức tạp không? Nếu vậy, hãy vui vẻ tự lăn, nhưng chỉ khi bạn không thực hiện một lộ trình học tập.

Gần đây, tôi thực sự thích trình phân tích cú pháp chanh , được cho là đơn giản và dễ dàng nhất mà tôi từng sử dụng. Vì mục đích làm cho mọi thứ dễ bảo trì, tôi chỉ sử dụng nó cho hầu hết các nhu cầu. SQLite sử dụng nó cũng như một số dự án đáng chú ý khác.

Nhưng, tôi hoàn toàn không hứng thú với các từ vựng, ngoài việc họ không cản trở tôi khi tôi cần sử dụng một (do đó, chanh). Bạn có thể, và nếu vậy, tại sao không làm cho một? Tôi có cảm giác bạn sẽ quay lại sử dụng một thứ tồn tại, nhưng hãy gãi ngứa nếu bạn phải :)


3
+1 cho "Bạn có thể tự lăn nhanh hơn mức bạn có thể đạt được trong quá trình học của một người từ chối không?"
bobah

Vâng, điểm tốt.
Maniero

3

Nó phụ thuộc vào mục tiêu của bạn là gì.

Bạn đang cố gắng học cách trình phân tích cú pháp / trình biên dịch làm việc? Sau đó viết của riêng bạn từ đầu. Đó là cách duy nhất bạn thực sự học để đánh giá cao tất cả những gì họ đang làm. Tôi đã viết một vài tháng qua, và đó là một trải nghiệm thú vị và có giá trị, đặc biệt là 'ah, vì vậy đó là lý do tại sao ngôn ngữ X thực hiện điều này ...'.

Bạn có cần nhanh chóng kết hợp một cái gì đó cho một ứng dụng đúng hạn không? Sau đó, có lẽ sử dụng một công cụ phân tích cú pháp.

Bạn có cần một cái gì đó mà bạn muốn mở rộng trong vòng 10, 20, thậm chí 30 năm tới không? Viết của riêng bạn, và dành thời gian của bạn. Nó sẽ có giá trị nó.


Đây là công việc đầu tiên của tôi về trình biên dịch, tôi học / thử nghiệm và đó là ý định của tôi để duy trì nó trong thời gian dài.
Maniero

3

Bạn đã xem xét cách tiếp cận bàn làm việc ngôn ngữ Martin Fowlers ? Trích dẫn từ bài báo

Sự thay đổi rõ ràng nhất mà bàn làm việc ngôn ngữ tạo ra cho phương trình là sự dễ dàng tạo DSL bên ngoài. Bạn không còn phải viết một trình phân tích cú pháp. Bạn phải xác định cú pháp trừu tượng - nhưng đó thực sự là một bước mô hình hóa dữ liệu khá đơn giản. Ngoài ra, DSL của bạn có được một IDE mạnh mẽ - mặc dù bạn phải dành một chút thời gian để xác định trình soạn thảo đó. Máy phát điện vẫn là thứ bạn phải làm và ý nghĩa của tôi là nó không dễ hơn bao giờ hết. Nhưng sau đó, xây dựng một máy phát cho DSL tốt và đơn giản là một trong những phần dễ nhất của bài tập.

Đọc điều đó, tôi sẽ nói rằng những ngày viết trình phân tích cú pháp của riêng bạn đã kết thúc và tốt hơn là sử dụng một trong những thư viện có sẵn. Khi bạn đã thành thạo thư viện thì tất cả các DSL mà bạn tạo trong tương lai đều được hưởng lợi từ kiến ​​thức đó. Ngoài ra, những người khác không phải học cách tiếp cận của bạn để phân tích cú pháp.

Chỉnh sửa để bao gồm nhận xét (và câu hỏi sửa đổi)

Ưu điểm của việc tự lăn

  1. Bạn sẽ sở hữu trình phân tích cú pháp và có được tất cả những trải nghiệm đáng yêu về suy nghĩ thông qua một loạt các vấn đề phức tạp
  2. Bạn có thể nghĩ ra một điều đặc biệt mà không ai khác nghĩ tới (không chắc nhưng bạn có vẻ là một người thông minh)
  3. Nó sẽ khiến bạn bận rộn với một vấn đề thú vị

Vì vậy, trong ngắn hạn, bạn nên tự lăn lộn khi bạn muốn thực sự hack sâu vào ruột của một vấn đề khó khăn nghiêm trọng mà bạn cảm thấy có động lực mạnh mẽ để làm chủ.

Ưu điểm của việc sử dụng thư viện của người khác

  1. Bạn sẽ tránh phát minh lại bánh xe (một vấn đề phổ biến trong lập trình bạn sẽ đồng ý)
  2. Bạn có thể tập trung vào kết quả cuối cùng (ngôn ngữ mới sáng bóng của bạn) và không phải lo lắng quá nhiều về cách phân tích cú pháp, v.v.
  3. Bạn sẽ thấy ngôn ngữ của mình hoạt động nhanh hơn nhiều (nhưng phần thưởng của bạn sẽ ít hơn vì đó không phải là tất cả của bạn)

Do đó, nếu bạn muốn có kết quả nhanh chóng, hãy sử dụng thư viện của người khác.

Nhìn chung, điều này dẫn đến một sự lựa chọn về mức độ bạn muốn sở hữu vấn đề, và do đó là giải pháp. Nếu bạn muốn tất cả thì hãy cuộn của riêng bạn.


Đó là một sự thay thế tuyệt vời cho suy nghĩ.
Maniero

1
@bigown Đã chỉnh sửa để trả lời tốt hơn câu hỏi của bạn
Gary Rowe

2

Lợi thế lớn để viết của riêng bạn là bạn sẽ biết cách viết của riêng bạn. Lợi thế lớn khi sử dụng một công cụ như yacc là bạn sẽ biết cách sử dụng công cụ này. Tôi là một fan hâm mộ của ngọn cây cho khám phá ban đầu.


Không đặc biệt hữu ích. Có thể bạn cũng đã nói, ưu điểm của việc học lái xe là bạn có thể lái xe. Ưu điểm của việc học lái xe đạp là bạn có thể đi xe đạp.
Zearin

1

Tại sao không rẽ nhánh một trình tạo trình phân tích cú pháp nguồn mở và biến nó thành của riêng bạn? Nếu bạn không sử dụng trình tạo trình phân tích cú pháp, mã của bạn sẽ rất khó duy trì, nếu bạn thực hiện thay đổi lớn cú pháp ngôn ngữ của mình.

Trong các trình phân tích cú pháp của mình, tôi đã sử dụng các biểu thức chính quy (ý tôi là kiểu Perl) để mã hóa và sử dụng một số hàm tiện lợi để tăng khả năng đọc mã. Tuy nhiên, một mã phân tích cú pháp tạo có thể nhanh hơn bằng cách làm cho bảng trạng thái và dài switch- cases, có thể làm tăng kích thước mã nguồn trừ khi bạn .gitignorecho họ.

Đây là hai ví dụ về trình phân tích cú pháp tùy chỉnh bằng văn bản của tôi:

https://github.com/SHiNKiROU/DesignScript - một phương ngữ BASIC, vì tôi quá lười để viết lookahead trong ký hiệu mảng, tôi đã hy sinh chất lượng thông báo lỗi https://github.com/SHiNKiROU/ExprParser - Một máy tính công thức. Lưu ý các thủ thuật siêu lập trình kỳ lạ


0

"Tôi có nên sử dụng 'bánh xe' đã thử và thử lại này không?"


1
"Bánh xe" này bạn nói về cái gì? ;-)
Jason Whitehorn

IMO đây không phải là một ý kiến ​​tốt về câu hỏi này. Đây chỉ là một lời khuyên chung không phù hợp với trường hợp cụ thể. Tôi bắt đầu nghi ngờ rằng đề xuất area51.stackexchange.com/proposeals/7848 đã bị đóng cửa sớm.
Maniero

2
Nếu bánh xe không bao giờ được phát minh lại, chúng ta sẽ không di chuyển với tốc độ 100km / ngày - trừ khi bạn đề xuất các khối đá nặng quay tròn trên trục gỗ tốt hơn nhiều biến thể của lốp xe hiện đại được sử dụng trong có nhiều xe không?
Peter Boughton

Đó là một ý kiến ​​hợp lệ, và đó là trực giác đúng đắn. Tôi nghĩ câu trả lời này có thể hữu ích hơn nếu bạn có thể liệt kê những ưu điểm hoặc nhược điểm cụ thể, bởi vì loại điều này hoàn toàn phụ thuộc vào hoàn cảnh.
Macneil

@Peter: Đó là một điều để phát minh lại một cái gì đó (ngụ ý làm điều đó hoàn toàn khác) nhưng để tinh chỉnh một giải pháp hiện có để đáp ứng các yêu cầu bổ sung thì tốt hơn. Tôi là tất cả để 'cải thiện', nhưng quay trở lại bảng vẽ cho một vấn đề đã được giải quyết có vẻ sai.
JBRWilkinson
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.