-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparser.cpp
More file actions
122 lines (102 loc) · 2.62 KB
/
parser.cpp
File metadata and controls
122 lines (102 loc) · 2.62 KB
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
115
116
117
118
119
120
121
122
#include "parser.hpp"
#include <stdexcept>
void skipws(std::string_view& source, Location& loc) {
while(!source.empty() && (std::isspace(source[0]) || source[0] == ';')) {
if(source[0] == ';') { // comment
auto n = source.find('\n');
if(n == source.npos) {
// empty the string
auto rem = source.length();
source = source.substr(rem);
loc.col += rem;
return;
}
++loc.row;
loc.col = 0;
source = source.substr(n + 1);
continue;
} else if(source[0] == '\n') {
++loc.row;
loc.col = 0;
} else {
++loc.col;
}
source = source.substr(1);
}
}
void skipws(Parser& parser) {
skipws(parser.source, parser.loc);
}
std::string_view consume(std::string_view& source, unsigned n, Location& loc) {
auto token = source.substr(0, n);
source = source.substr(n);
loc.col += n;
return token;
}
void throwError(std::string msg, const Location& loc) {
msg = std::to_string(loc.row) + ":" + std::to_string(loc.col) + ": " + msg;
throw std::runtime_error(msg);
}
Expression nextExpression(std::string_view& view, Location& loc) {
if(view.empty()) {
throwError("Empty expression (unexpected source end)", loc);
}
skipws(view, loc);
auto oloc = loc;
// 1: test whether it's a number
const char* end = &*view.end();
double value = std::strtod(view.data(), const_cast<char**>(&end));
if(end != view.data()) {
auto count = end - view.data();
view = view.substr(count);
loc.col += count;
return {value, oloc};
}
// 2: test whether it's a string
if(view[0] == '"') {
consume(view, 1, loc); // skip initial "
auto end = view.find('"');
if(end == view.npos) {
throwError("Unterminated '\"'", oloc);
}
auto str = consume(view, end, loc);
consume(view, 1, loc); // skip final "
return {str, oloc};
}
// 3: test if it's application
if(view[0] == '(') {
consume(view, 1, loc);
++loc.depth;
skipws(view, loc);
List list;
while(!view.empty() && view[0] != ')') {
auto e = nextExpression(view, loc);
list.values.push_back(e);
skipws(view, loc);
}
--loc.depth;
if(view.empty()) {
throwError("Unterminted '('", oloc);
}
if(view[0] != ')') {
throwError("Invalid termination of expression", loc);
}
consume(view, 1, loc);
return {list, loc};
}
// otherwise it's an identifier
auto term = view.find_first_of("\n\t\r\v\f ()");
if(term == view.npos) {
throwError("Invalid expression", oloc);
}
auto name = consume(view, term, loc);
if(name == "true") {
return {true, oloc};
} else if(name == "false") {
return {false, oloc};
}
return {Identifier{name}, oloc};
}
Expression nextExpression(Parser& p) {
return nextExpression(p.source, p.loc);
}