- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
这件事在我的脑海里已经有一段时间了。我对递归下降解析器很感兴趣,并且想知道如何实现它。我想要的是一个简单的解析器,它可以理解简单的算术,例如“5+5”或“(5+5)*3”。
我认为第一步是编写一个“tokenizer”,它将整个输入字符串分解为许多子字符串。这部分我已经完成(我什至不得不询问它 here 。如果你不想的话,你不必点击链接,因为我也在此处发布相关代码。)我的这个标记器,我最终得到一个 string
或标记的 vector
。现在,困难的部分:我想解析这些标记。
我已阅读 Wikipedia article on recursive descent parsers .我确实了解整体概念,但与往常一样,实现有点令人困惑。在那篇文章中,有一个用于非常简单的编程语言的递归下降解析器的 C 实现,文章中也进行了讨论。我尽我所能研究了该代码,并尝试基本上编写相同的东西,但对于我的程序。下面是那个代码。
我真正困惑的是这个解析器做了什么。它似乎通过程序并“预期”语法的某些部分。但是一旦它到达那里,它会做什么?例如,这里有一个来自维基百科代码的函数,它应该解析一个“术语”:
void term(void) {
factor();
while (sym == times || sym == slash) {
getsym();
factor();
}
}
这是为了解析这个语法:
term = factor {("*"|"/") factor} .
这是有道理的。但它与实际术语有什么关系?假设该术语只是“6”,或者是“3 * 2”并且结果为6。它将如何将其合并到输入的其余部分中? term()
不应该返回 double
而不是 void
(返回 6)吗?还是以其他方式完成的?
另外,让这样的解析器输出代码和立即对输入采取行动(即编译器与解释器)之间有什么区别?这两者(至少在这个例子中)在理论上是相同的实现方式,还是根本不同?
欢迎任何意见。到目前为止,这是我的代码:
#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;
}
编辑:我应该提到我的代码总是打印:ERROR: syntax error
,来自factor()
函数。
最佳答案
维基百科文章包含一个看起来非常完整的解析器(但没有词法分析器!),它什么都不做。
对于实际解释结果,一般的想法是,每个解析函数将部分解释的结果传回给它的父/调用者。对于树中的每个规则,结果可能是不同的类型。如果您正在为完整的语言编写解析器,部分解释的结果可能是一个简单的常量(可以是任何类型),也可能是整个函数(您可能需要稍后编译)。在方程解析器的情况下,每个规则将简单地对调用其他函数获得的元素执行所需的操作,并将结果传递回调用它的函数。
想到了两种方法:
让每个函数接受一个 something* result
参数。对于简单的方程解析器,这可能是所有元素的 float* result
。
只需将所有函数从 void rule_x()...
更改为 float rule_x()...
即可返回结果。
在任何一种情况下,您都需要一些方法来处理错误。如果您在 C 中,则没有异常(exception),因此您最好使用选项 1 并使用返回值来指示成功。然后会有很多
if(!expression(&result)) return 0;
但在 C++ 中,您可以将解析包装在异常处理程序中,并在错误中抛出异常,从而中止解析的其余部分。
如果您愿意,事情会变得更加有趣,比如使用优化或 JIT 编译整个语言,并尝试从语法错误中优雅地恢复并继续解析。
了解这个主题的书是 dragon book .
关于c++ - 如何在 C++ 中使用简单的递归下降解析器解析基本算术(例如 "5+5")?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10378903/
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!