Điều này đã được ghi nhớ trong một thời gian. Tôi bị hấp dẫn bởi các trình phân tích cú pháp gốc đệ quy, và muốn biết cách thực hiện một. Những gì tôi muốn là một trình phân tích cú pháp đơn giản sẽ hiểu số học đơn giản như "5 + 5" hoặc "(5 + 5) * 3".Làm cách nào để phân tích cú pháp số học cơ bản (ví dụ: "5 + 5") bằng cách sử dụng trình phân tích cú pháp gốc đệ quy đơn giản trong C++?
Tôi tìm bước đầu tiên là viết 'tokenizer', lấy toàn bộ chuỗi đầu vào và chia thành nhiều chuỗi. Phần này tôi đã làm (thậm chí tôi đã phải hỏi về nó here. Bạn không cần phải theo liên kết nếu bạn không muốn, vì tôi đang đăng mã relavent ở đây là tốt.) Với tokenizer này của tôi, tôi kết thúc bằng một số vector
trong số string
s hoặc mã thông báo. Bây giờ, phần khó khăn: Tôi muốn phân tích những thẻ đó.
Tôi đã đọc Wikipedia article on recursive descent parsers. Tôi hiểu khái niệm tổng thể, nhưng như mọi khi, việc thực hiện là một chút bối rối. Trong bài viết đó, có một triển khai C của một trình phân tích cú pháp gốc đệ quy cho một ngôn ngữ lập trình rất đơn giản, cũng được thảo luận trong bài viết. Tôi đã nghiên cứu mã đó tốt nhất có thể, và cố gắng viết một cách cơ bản, nhưng đối với chương trình của tôi. Dưới đây là mã đó.
Điều tôi thực sự nhầm lẫn là trình phân tích cú pháp này làm gì. Dường như đi qua chương trình và 'mong đợi' một số phần nhất định của ngữ pháp. Nhưng một khi nó đến đó, nó làm gì? Ví dụ, đây là một trong những chức năng từ mã Wikipedia đó là nghĩa vụ để phân tích một 'hạn':
void term(void) {
factor();
while (sym == times || sym == slash) {
getsym();
factor();
}
}
này có nghĩa là để phân tích ngữ pháp này:
term = factor {("*"|"/") factor} .
có ý nghĩa. Nhưng nó làm gì với thuật ngữ thực tế? Giả sử thuật ngữ này chỉ đơn giản là "6" hoặc là "3 * 2" và có giá trị 6. Làm cách nào để kết hợp nó vào phần còn lại của đầu vào? Không nên term()
trả lại double
thay vì void
(để trả lại 6)? Hoặc là nó được thực hiện một số cách khác?
Ngoài ra, sự khác biệt giữa việc phân tích cú pháp như thế này với mã đầu ra là gì và ngay lập tức hành động dựa trên đầu vào (tức là trình biên dịch so với trình thông dịch)? Có phải hai (trong ví dụ này ít nhất) về mặt lý thuyết được thực hiện theo cùng một cách, hoặc về cơ bản chúng có khác nhau không?
Mọi đầu vào đều được chào đón. Đây là mã của tôi vậy, đến nay:
#include <iostream>
#include <string>
#include <vector>
#include <ctype.h>
#include <sstream>
using namespace std;
vector<string> symbolize(string);
bool accept(string);
void getSymbol();
void error(string s);
bool expect(string);
void expression();
void term();
void factor();
int currentPosition = -1;
string symbol;
vector<string> symbols;
int main(int argc, const char * argv[])
{
string input;
getline(cin,input);
symbols = symbolize(input);
getSymbol();
expression();
return 0;
}
void factor(){
if(isdigit(symbol.c_str()[0])){}
else if(accept("(")){
expression();
expect(")");
}
else {
error("Syntax error");
}
}
void term(){
factor();
while(symbol=="*"||symbol=="/"){
getSymbol();
factor();
}
}
void expression(){
if(symbol == "+" || symbol == "-") getSymbol();
term();
while(symbol == "+" || symbol == "-"){
getSymbol();
term();
}
}
void error(string s){
cout << endl << "ERROR: " << s << endl;
}
void getSymbol(){
currentPosition++;
if(currentPosition>=symbols.size())error("Unexpectedly reached end of input");
}
bool expect(string s){
if(accept(s))return true;
else error("Expected '" + s + "'");
return false;
}
bool accept(string s){
if(s==symbol){getSymbol();return true;}
return false;
}
// Takes a string and breaks it into substrings
vector<string> symbolize(string input){
int position = 0;
char c;
//stringstream s;
vector<string> symbols;
enum symbolType {TEXT,OPERATOR}symbolType,charType;
while(position < input.size()){
stringstream s;
c = input.at(position);
if(isalnum(c))symbolType = TEXT;
else symbolType = OPERATOR;
charType = symbolType;
while(symbolType == charType){
s << c;
position++;
if(position>=input.length())break;
c = input.at(position);
if(isspace(c)||c=='\n'){position++; break;}
if(isalnum(c)) charType = TEXT;
else charType = OPERATOR;
}
symbols.push_back(s.str());
}
return symbols;
}
Edit: tôi nên đề cập rằng mã của tôi luôn in: ERROR: syntax error
, từ factor()
chức năng.
mã demo không làm bất kỳ điều gì với đầu vào mà nó đọc. Bạn phải tự thêm phần đó. –
thường, một trình thông dịch sẽ thực hiện phép nhân ngay sau khi nó đọc số thứ hai và trả về kết quả. Một trình biên dịch sẽ tạo ra một đối tượng "nhân", và cung cấp cho nó các đối tượng được trả về bởi các cạnh bên trái và bên phải, và trả về (hoặc lưu trữ) đối tượng đó. –
@MooingDuck Nhưng làm thế nào nó biết rằng nó trên kết quả thứ hai, khi nó là cùng một chức năng chạy đệ quy? – Hassan