简单逻辑解析器的嵌套循环
Nested looping for simple logic parser
我正在为简单的编程语言编写解析器,该语言可能由轴号,两个字母命令以及可能的输入值组成。所有命令均由逗号分开。我有一个解析器,可以将DelineAtor分配给输入,并一次运行每个有效的命令。我正在遇到编程循环功能RP。
我可以拥有这样的命令
MD1,TP,RP5,TT,RP10
我希望它以
运行for (int i = 0; i < 10; i++) {
TT();
for (int j = 0; j < 5; j++) {
TP();
}
}
到目前为止,我将看到第一个RP命令并运行,然后查看第二个RP命令并运行它。RP命令设置为从最后一个RP命令的末尾循环循环。
for (int j = 0; j < 5; j++) {
TP();
}
for (int i = 0; i < 10; i++) {
TT();
}
我尝试了几种不同的方法,但到目前为止还没有运气。所有的帮助都将受到赞赏。
实际上,我认为这个问题太广泛了。另一方面,我无法抗拒"尝试"。
前言
首先,我想批评(一点)问题标题。简单的逻辑解析器对我来说听起来像 boolean表达式的解释器。但是,我记得我的工程同事经常在谈论"程序逻辑"(我还没有实现他们摆脱这一点)。因此,我的建议:如果您(发问者)正在与计算机科学家交谈,请使用"逻辑"一词明智(或者有时看起来很困惑...)
)样本代码MD1,TP,RP5,TT,RP10
看起来我很熟悉。一项简短的Google/Wikipedia研究清楚了我的想法:Wikipedia文章数字控制与CNC机器有关。靠近文章的结尾,提到了编程。(德语"兄弟姐妹"文章提供了更多。)恕我直言,该代码看起来确实有点相似,但似乎更简单。(没有冒犯&ndash;我认为尽可能简单的事情是很好的。)
似乎打算使用的程序表示法是某种程度上像反向抛光符号一样。我至少想提到该术语作为搜索" RPN解释器"的谷歌搜索,以提供许多充分的命中,包括GitHub站点。实际上,对预期语言的描述太短了,无法确定哪个现有的S/W项目可能是合适的。
这么说,我想展示我得到的...
解析器
我首先是用解析器开始的(因为发问者不敢揭露他)。这是mci1.cc
的代码:
#include <iostream>
#include <sstream>
using namespace std;
typedef unsigned char uchar;
enum Token {
TkMD = 'M' | 'D' << 8,
TkRP = 'R' | 'P' << 8,
TkTP = 'T' | 'P' << 8,
TkTT = 'T' | 'T' << 8
};
inline Token tokenize(uchar c0, uchar c1) { return (Token)(c0 | c1 << 8); }
bool parse(istream &in)
{
for (;;) {
// read command (2 chars)
char cmd[2];
if (in >> cmd[0] >> cmd[1]) {
//cout << "DEBUG: token: " << hex << tokenize(cmd[0], cmd[1]) << endl;
switch (tokenize(cmd[0], cmd[1])) {
case TkMD: { // MD<num>
int num;
if (in >> num) {
cout << "Received 'MD" << dec << num << "'." << endl;
} else {
cerr << "ERROR: Number expected after 'MD'!" << endl;
return false;
}
} break;
case TkRP: { // RP<num>
int num;
if (in >> num) {
cout << "Received 'RP" << dec << num << "'." << endl;
} else {
cerr << "ERROR: Number expected after 'RP'!" << endl;
return false;
}
} break;
case TkTP: // TP
cout << "Received 'TP'." << endl;
break;
case TkTT: // TT
cout << "Received 'TT'." << endl;
break;
default:
cerr << "ERROR: Wrong command '" << cmd[0] << cmd[1] << "'!" << endl;
return false;
}
} else {
cerr << "ERROR: Command expected!" << endl;
return false;
}
// try to read separator
char sep;
if (!(in >> sep)) break; // probably EOF (further checks possible)
if (sep != ',') {
cerr << "ERROR: ',' expected!" << endl;
return false;
}
}
return true;
}
int main()
{
// test string
string sample("MD1,TP,RP5,TT,RP10");
// read test string
istringstream in(sample);
if (parse(in)) cout << "Done." << endl;
else cerr << "Interpreting aborted!" << endl;
// done
return 0;
}
我在Windows 10上用g++
和bash
进行了编译和测试:
$ g++ --version
g++ (GCC) 6.4.0
$ g++ -std=c++11 -o mci mci1.cc
$ ./mci
Received 'MD1'.
Received 'TP'.
Received 'RP5'.
Received 'TT'.
Received 'RP10'.
Done.
$
在 iDEONE 上上传了生命演示。
我引入了该功能tokenize()
作为更新的一部分。(当我刷牙和por牙时,我明白了如何摆脱上一个版本的丑陋嵌套的switch
ES。)Tokenization是解析的一种常见技术。但是,实现通常有些不同。
因此,解析器似乎有效。还不是下一个大事,但足以完成下一步...
解释器
要解释解析的命令,我开始做出一个响应。后端&ndash;一组可能存储并执行所需操作的类。
第一步的parse()
功能成为compile()
功能,其中简单的标准输出被代码构建和嵌套操作替换。mci2.cc
:
#include <cassert>
#include <iostream>
#include <stack>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
// super class of all operations
class Op {
protected:
Op() = default;
public:
virtual ~Op() = default;
virtual void exec() const = 0;
// disabled: (to prevent accidental usage)
Op(const Op&) = delete;
Op& operator=(const Op&) = delete;
};
// super class of grouping operations
class Grp: public Op {
protected:
vector<Op*> _pOps; // nested operations
protected:
Grp() = default;
virtual ~Grp()
{
for (Op *pOp : _pOps) delete pOp;
}
public:
void add(Op *pOp) { _pOps.push_back(pOp); }
// disabled: (to prevent accidental usage)
Grp(const Grp&) = delete;
Grp& operator=(const Grp&) = delete;
};
// class for repeat op.
class RP: public Grp {
private:
unsigned _n; // repeat count
public:
RP(unsigned n): Grp(), _n(n) { }
virtual ~RP() = default;
virtual void exec() const
{
cout << "Exec. RP" << _n << endl;
for (unsigned i = 0; i < _n; ++i) {
for (const Op *pOp : _pOps) pOp->exec();
}
}
// disabled: (to prevent accidental usage)
RP(const RP&) = delete;
RP& operator=(const RP&) = delete;
};
// class for TP op.
class TP: public Op {
public:
TP() = default;
virtual ~TP() = default;
virtual void exec() const
{
cout << "Exec. TP" << endl;
}
};
// class for TT op.
class TT: public Op {
public:
TT() = default;
virtual ~TT() = default;
virtual void exec() const
{
cout << "Exec. TT" << endl;
}
};
// class for MD sequence
class MD: public Grp {
private:
unsigned _axis;
public:
MD(unsigned axis): Grp(), _axis(axis) { }
virtual ~MD() = default;
virtual void exec() const
{
cout << "Exec. MD" << _axis << endl;
for (const Op *pOp : _pOps) pOp->exec();
}
};
typedef unsigned char uchar;
enum Token {
TkMD = 'M' | 'D' << 8,
TkRP = 'R' | 'P' << 8,
TkTP = 'T' | 'P' << 8,
TkTT = 'T' | 'T' << 8
};
inline Token tokenize(uchar c0, uchar c1) { return (Token)(c0 | c1 << 8); }
MD* compile(istream &in)
{
MD *pMD = nullptr;
stack<Op*> pOpsNested;
#define ERROR
delete pMD;
while (pOpsNested.size()) { delete pOpsNested.top(); pOpsNested.pop(); }
return nullptr
for (;;) {
// read command (2 chars)
char cmd[2];
if (in >> cmd[0] >> cmd[1]) {
//cout << "DEBUG: token: " << hex << tokenize(cmd[0], cmd[1]) << dec << endl;
switch (tokenize(cmd[0], cmd[1])) {
case TkMD: { // MD<num>
int num;
if (in >> num) {
if (pMD) {
cerr << "ERROR: Unexpected command 'MD" << num << "'!" << endl;
ERROR;
}
pMD = new MD(num);
} else {
cerr << "ERROR: Number expected after 'MD'!" << endl;
ERROR;
}
} break;
case TkRP: { // RP<num>
int num;
if (in >> num) {
if (!pMD) {
cerr << "ERROR: Unexpected command 'RP" << num << "'!" << endl;
ERROR;
}
RP *pRP = new RP(num);
while (pOpsNested.size()) {
pRP->add(pOpsNested.top());
pOpsNested.pop();
}
pOpsNested.push(pRP);
} else {
cerr << "ERROR: Number expected after 'RP'!" << endl;
ERROR;
}
} break;
case TkTP: { // TP
if (!pMD) {
cerr << "ERROR: Unexpected command 'TP'!" << endl;
ERROR;
}
pOpsNested.push(new TP());
} break;
case TkTT: { // TT
if (pOpsNested.empty()) {
cerr << "ERROR: Unexpected command 'TT'!" << endl;
ERROR;
}
pOpsNested.push(new TT());
} break;
default:
cerr << "ERROR: Wrong command '" << cmd[0] << cmd[1] << "'!" << endl;
ERROR;
}
} else {
cerr << "ERROR: Command expected!" << endl;
ERROR;
}
// try to read separator
char sep;
if (!(in >> sep)) break; // probably EOF (further checks possible)
if (sep != ',') {
cerr << "ERROR: ',' expected!" << endl;
ERROR;
}
}
#undef ERROR
assert(pMD != nullptr);
while (pOpsNested.size()) {
pMD->add(pOpsNested.top());
pOpsNested.pop();
}
return pMD;
}
int main()
{
// test string
string sample("MD1,TP,RP3,TT,RP2");
// read test string
istringstream in(sample);
MD *pMD = compile(in);
if (!pMD) {
cerr << "Interpreting aborted!" << endl;
return 1;
}
// execute sequence
pMD->exec();
delete pMD;
// done
return 0;
}
再次,我在Windows 10上使用g++
和bash
进行了编译和测试:
$ g++ -std=c++11 -o mci mci2.cc
$ ./mci
Exec. MD1
Exec. RP2
Exec. TT
Exec. RP3
Exec. TP
Exec. TP
Exec. TP
Exec. TT
Exec. RP3
Exec. TP
Exec. TP
Exec. TP
$
在 iDEONE 上上传了生命演示。
嵌套的技巧在compile()
函数中相当简单:
命令
TP
和TT
添加到临时堆栈pOpsNested
对于命令
RP
,将所有收集的操作添加到RP
实例中弹出pOpsNested
堆栈(从而逆转其订单),
之后,将RP
实例本身推入pOpsNested
堆栈最后,添加了缓冲区
pOpsNested
的内容到序列MD
(因为这些是顶级操作)。
- 如何声明特征矩阵,然后通过嵌套循环初始化它
- 了解嵌套循环打印星号图案
- 无法掌握嵌套循环的写作技巧
- 在 c++ 中实现嵌套循环的更短方法吗?
- 从嵌套循环中的 std::list 中删除将返回访问冲突
- 毕达哥拉斯三重嵌套循环误解
- T(n) 表示嵌套循环
- 2 个嵌套循环的时间复杂度
- 嵌套循环背后的逻辑
- 使用 %s C++嵌套循环
- 嵌套循环和重复迭代器
- 如何在 c++ 下使用嵌套循环和正则表达式降低时间复杂度?
- C++在乘法图中放置随机值(嵌套循环)
- 如何使用 OpenMP 减少嵌套循环?
- 为什么使用 2 个嵌套循环 O(n^2) 复杂度来解决二和问题,当只改变循环计数器逻辑时运行得更快?
- 学习嵌套循环C++与示例混淆
- 如何在CUDA中嵌套循环
- std::vector上的嵌套循环
- 简单逻辑解析器的嵌套循环
- 简单的嵌套循环问题.*形状*