Con đường gian nan
Bạn muốn một trình phân tích cú pháp gốc đệ quy .
Để được ưu tiên, bạn cần phải suy nghĩ đệ quy, ví dụ: sử dụng chuỗi mẫu của bạn,
1+11*5
để thực hiện việc này theo cách thủ công, bạn sẽ phải đọc 1
, sau đó xem dấu cộng và bắt đầu một "phiên" phân tích cú pháp đệ quy hoàn toàn mới bắt đầu bằng 11
... và đảm bảo phân tích cú pháp 11 * 5
thành nhân tố của chính nó, tạo ra một cây phân tích cú pháp với 1 + (11 * 5)
.
Tất cả điều này đều cảm thấy rất đau đớn ngay cả khi cố gắng giải thích, đặc biệt là với sự bất lực cộng thêm của C. Hãy xem, sau khi phân tích cú pháp 11, nếu * thực sự là dấu +, bạn sẽ phải bỏ nỗ lực tạo một thuật ngữ và thay vào đó phân tích cú pháp 11
chính nó như một yếu tố. Đầu tôi đang nổ tung. Nó có thể với chiến lược đệ quy tốt, nhưng có một cách tốt hơn ...
Cách dễ dàng (đúng)
Nếu bạn sử dụng công cụ GPL như Bison, bạn có thể không cần lo lắng về các vấn đề cấp phép vì mã C do bison tạo ra không được GPL đề cập (IANAL nhưng tôi khá chắc chắn rằng các công cụ GPL không buộc GPL bật mã / mã nhị phân được tạo; ví dụ: Apple biên dịch mã như giả sử, Aperture với GCC và họ bán nó mà không cần phải mã GPL).
Tải xuống Bison (hoặc thứ gì đó tương đương, ANTLR, v.v.).
Thường có một số mã mẫu mà bạn có thể chỉ cần chạy bison và nhận mã C mong muốn của bạn để minh họa cho máy tính bốn chức năng này:
http://www.gnu.org/software/bison/manual/html_node/Infix-Calc.html
Nhìn vào đoạn mã đã tạo và thấy rằng điều này không dễ dàng như nó vẫn tưởng. Ngoài ra, lợi thế của việc sử dụng một công cụ như Bison là 1) bạn học được điều gì đó (đặc biệt nếu bạn đọc cuốn sách Dragon và tìm hiểu về ngữ pháp), 2) bạn tránh được NIH cố gắng phát minh lại bánh xe. Với một công cụ tạo trình phân tích cú pháp thực sự, bạn thực sự có hy vọng mở rộng quy mô sau này, cho những người khác biết rằng trình phân tích cú pháp là lĩnh vực của các công cụ phân tích cú pháp.
Cập nhật:
Mọi người ở đây đã đưa ra nhiều lời khuyên hữu ích. Cảnh báo duy nhất của tôi về việc bỏ qua các công cụ phân tích cú pháp hoặc chỉ sử dụng thuật toán Shunting Yard hoặc trình phân tích cú pháp đệ quy đệ quy cuộn bằng tay là một ngày nào đó các ngôn ngữ đồ chơi nhỏ 1 có thể biến thành các ngôn ngữ thực tế lớn với các hàm (sin, cos, log) và các biến, điều kiện và cho các vòng lặp.
Flex / Bison rất có thể là quá mức cần thiết đối với một trình thông dịch nhỏ, đơn giản, nhưng trình phân tích cú pháp + đánh giá một lần có thể gây ra sự cố khi cần thực hiện thay đổi hoặc cần thêm các tính năng. Tình huống của bạn sẽ khác nhau và bạn sẽ cần sử dụng phán đoán của mình; chỉ cần không trừng phạt người khác vì tội lỗi của bạn [2] và xây dựng một công cụ không đầy đủ.
Công cụ yêu thích của tôi để phân tích cú pháp
Công cụ tốt nhất trên thế giới cho công việc này là thư viện Parsec (dành cho trình phân tích cú pháp đệ quy tốt) đi kèm với ngôn ngữ lập trình Haskell. Nó trông rất giống BNF , hoặc giống một số công cụ chuyên dụng hoặc ngôn ngữ miền cụ thể để phân tích cú pháp (mã mẫu [3]), nhưng thực tế nó chỉ là một thư viện thông thường trong Haskell, có nghĩa là nó biên dịch trong cùng một bước xây dựng như phần còn lại. mã Haskell của bạn và bạn có thể viết mã Haskell tùy ý và gọi mã đó trong trình phân tích cú pháp của bạn, đồng thời bạn có thể trộn và kết hợp các thư viện khác trong cùng một mã . (Nhân tiện, việc nhúng một ngôn ngữ phân tích cú pháp như thế này bằng một ngôn ngữ không phải Haskell dẫn đến vô số lỗi cú pháp. Tôi đã làm điều này trong C # và nó hoạt động khá tốt nhưng nó không đẹp và ngắn gọn.)
Ghi chú:
1 Richard Stallman nói, trong Tại sao bạn không nên sử dụng Tcl
Bài học chính của Emacs là một ngôn ngữ cho phần mở rộng không nên là một "ngôn ngữ mở rộng". Nó phải là một ngôn ngữ lập trình thực, được thiết kế để viết và duy trì các chương trình quan trọng. Bởi vì mọi người sẽ muốn làm điều đó!
[2] Vâng, tôi vĩnh viễn bị sẹo khi sử dụng "ngôn ngữ" đó.
Cũng xin lưu ý rằng khi tôi gửi mục nhập này, bản xem trước là chính xác, nhưng trình phân tích cú pháp của SO chưa đầy đủ đã ăn thẻ neo đóng của tôi trên đoạn đầu tiên , chứng tỏ rằng trình phân tích cú pháp không phải là thứ gì đó đáng lo ngại vì nếu bạn sử dụng regexes và một lần nữa sẽ tấn công bạn có thể sẽ nhận được một cái gì đó nhỏ và sai nhỏ .
[3] Đoạn mã của trình phân tích cú pháp Haskell sử dụng Parsec: một máy tính bốn hàm được mở rộng với số mũ, dấu ngoặc đơn, khoảng trắng cho phép nhân và hằng số (như pi và e).
aexpr = expr `chainl1` toOp
expr = optChainl1 term addop (toScalar 0)
term = factor `chainl1` mulop
factor = sexpr `chainr1` powop
sexpr = parens aexpr
<|> scalar
<|> ident
powop = sym "^" >>= return . (B Pow)
<|> sym "^-" >>= return . (\x y -> B Pow x (B Sub (toScalar 0) y))
toOp = sym "->" >>= return . (B To)
mulop = sym "*" >>= return . (B Mul)
<|> sym "/" >>= return . (B Div)
<|> sym "%" >>= return . (B Mod)
<|> return . (B Mul)
addop = sym "+" >>= return . (B Add)
<|> sym "-" >>= return . (B Sub)
scalar = number >>= return . toScalar
ident = literal >>= return . Lit
parens p = do
lparen
result <- p
rparen
return result