-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathpython_indentation.cpp
114 lines (95 loc) · 3.3 KB
/
python_indentation.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/**
* This is a proof-of-concept example that parses python-like indentation
* blocks.
*/
#include <peg_parser/generator.h>
#include <algorithm>
#include <iostream>
#include <unordered_set>
#include <vector>
int main() {
using namespace std;
struct Block {
size_t begin, length;
};
using Blocks = vector<Block>;
peg_parser::ParserGenerator<void, Blocks &> blockParser;
blockParser["Indentation"] << "' '*";
/** storage for indentation depths */
std::vector<unsigned> indentations;
/** initializer is necessarry to reset the state after syntax errors */
blockParser["InitBlocks"] << "''" << [&](auto &) -> bool {
indentations.resize(0);
return true;
};
/**
* matches the current block intendation
* note that this rule is not cacheable as results are context-dependent
*/
blockParser["SameIndentation"] << "Indentation" <<
[&](auto &s) -> bool { return s->length() == indentations.back(); };
blockParser["SameIndentation"]->cacheable = false;
/** matches a deeper block intendation */
blockParser["DeeperIndentation"]
<< "Indentation" << [&](auto &s) -> bool { return s->length() > indentations.back(); };
blockParser["DeeperIndentation"]->cacheable = false;
// enters a new block and stores the indentation
blockParser["EnterBlock"] << "Indentation" << [&](auto &s) -> bool {
if (indentations.size() == 0 || s->length() > indentations.back()) {
indentations.push_back(s->length());
return true;
} else {
return false;
}
};
blockParser["EnterBlock"]->cacheable = false;
/** matches a line in the current block */
blockParser["Line"] << "SameIndentation (!'\n' .)+ '\n'";
blockParser.getRule("Line")->cacheable = false;
/** matches an empty line */
blockParser["EmptyLine"] << "Indentation '\n'";
/** exits a block and pops the current indentation */
blockParser["ExitBlock"] << "''" << [&](auto &) -> bool {
indentations.pop_back();
return true;
};
blockParser.getRule("ExitBlock")->cacheable = false;
/** store all successfully parsed blocks */
blockParser["Block"] << "&EnterBlock Line (EmptyLine | Block | Line)* &ExitBlock" >>
[](auto e, Blocks &blocks) {
for (auto a : e) a.evaluate(blocks);
blocks.push_back(Block{e.position(), e.length()});
};
blockParser.setStart(blockParser["Start"] << "InitBlocks Block");
while (true) {
string str, input;
cout << "Enter a python-like indented string. Push enter twice to parse." << endl;
cout << "> ";
getline(cin, str);
if (str == "q" || str == "quit") {
break;
}
do {
input += str + '\n';
cout << "- ";
getline(cin, str);
} while (str != "");
try {
Blocks blocks;
blockParser.run(input, blocks);
cout << "matched " << blocks.size() << " blocks." << endl;
for (auto b : blocks) {
cout << "- from line " << std::count(input.begin(), input.begin() + b.begin, '\n') + 1;
cout << " to " << std::count(input.begin(), input.begin() + b.begin + b.length, '\n')
<< endl;
}
} catch (peg_parser::SyntaxError &error) {
auto syntax = error.syntax;
cout << " ";
cout << " "
<< "Syntax error at character " << syntax->end << " while parsing " << syntax->rule->name
<< endl;
}
}
return 0;
}