载入中...
搜索中...
未找到
DnutParser.cpp
浏览该文件的文档.
2
3#include <cctype>
4#include <cstdio>
5#include <cstdlib>
6#include <stdexcept>
7#include <string>
8#include <utility>
9#include <vector>
10
11namespace eve::dialogue {
12namespace {
13
14enum class Tok { Ident, Str, Num, Punct, Eof };
15
16struct Token {
17 Tok kind = Tok::Eof;
18 std::string value;
19 int line = 1;
20};
21
22std::string floatToString(double v) {
23 char buf[64];
24 std::snprintf(buf, sizeof(buf), "%g", v);
25 return buf;
26}
27
28std::string scalarToString(const DataValue &v) {
29 switch (v.kind) {
31 return v.s;
33 return std::to_string(v.i);
35 return floatToString(v.f);
37 return v.b ? "true" : "false";
38 default:
39 return {};
40 }
41}
42
43DataValue numValue(const std::string &raw) {
44 if (raw.find_first_of(".eE") != std::string::npos)
45 return DataValue::number(std::strtod(raw.c_str(), nullptr));
46 return DataValue::integer(std::strtoll(raw.c_str(), nullptr, 10));
47}
48
49DataValue negNumValue(const std::string &raw) {
50 DataValue v = numValue(raw);
51 if (v.kind == DataValue::Kind::Int) v.i = -v.i;
52 else v.f = -v.f;
53 return v;
54}
55
56class Parser {
57public:
58 Parser(std::string source, std::string path) : source_(std::move(source)), path_(std::move(path)) {}
59
60 bool parse(DataValue &out, std::string &error) {
61 try {
62 tokenize();
63 out = parsePools();
64 return true;
65 } catch (const ParseError &e) {
66 error = e.what();
67 return false;
68 }
69 }
70
71private:
72 struct ParseError : std::runtime_error {
73 explicit ParseError(const std::string &msg) : std::runtime_error(msg) {}
74 };
75
76 std::string source_;
77 std::string path_;
78 std::vector<Token> toks_;
79 size_t pos_ = 0;
80
81 const Token &cur() const { return toks_[pos_]; }
82 Token adv() {
83 Token t = toks_[pos_];
84 if (pos_ + 1 < toks_.size()) ++pos_;
85 return t;
86 }
87 bool isPunct(const std::string &p) const {
88 return cur().kind == Tok::Punct && cur().value == p;
89 }
90 bool isIdent() const { return cur().kind == Tok::Ident; }
91
92 [[noreturn]] void fail(const std::string &msg) const {
93 const std::string at = cur().kind == Tok::Eof ? "<结束>" : cur().value;
94 throw ParseError(path_ + ":" + std::to_string(cur().line) + ": " + msg +
95 "(实际是 '" + at + "')");
96 }
97 void expectPunct(const std::string &p) {
98 if (!isPunct(p)) fail("期望 '" + p + "'");
99 adv();
100 }
101 std::string expectIdent(const std::string &what) {
102 if (!isIdent()) fail("期望标识符 " + what);
103 return adv().value;
104 }
105
106 void tokenize() {
107 toks_.clear();
108 const std::string &src = source_;
109 const size_t n = src.size();
110 size_t i = 0;
111 int line = 1;
112 const auto add = [&](Tok k, std::string v) {
113 toks_.push_back(Token{k, std::move(v), line});
114 };
115
116 while (i < n) {
117 const char c = src[i];
118 if (c == '\n') {
119 ++line;
120 ++i;
121 continue;
122 }
123 if (c == ' ' || c == '\t' || c == '\r') {
124 ++i;
125 continue;
126 }
127 if (c == '/' && i + 1 < n && src[i + 1] == '/') {
128 while (i < n && src[i] != '\n') ++i;
129 continue;
130 }
131 if (c == '/' && i + 1 < n && src[i + 1] == '*') {
132 const int startLine = line;
133 i += 2;
134 while (i < n && !(src[i] == '*' && i + 1 < n && src[i + 1] == '/')) {
135 if (src[i] == '\n') ++line;
136 ++i;
137 }
138 if (i >= n)
139 throw ParseError(path_ + ":" + std::to_string(startLine) + ": 未闭合的块注释");
140 i += 2;
141 continue;
142 }
143 if (c == '"' || c == '\'') {
144 const char quote = c;
145 const int startLine = line;
146 ++i;
147 std::string s;
148 while (i < n && src[i] != quote) {
149 if (src[i] == '\\' && i + 1 < n) {
150 ++i;
151 const char e = src[i];
152 switch (e) {
153 case 'n': s += '\n'; break;
154 case 't': s += '\t'; break;
155 case 'r': s += '\r'; break;
156 case '"': s += '"'; break;
157 case '\'': s += '\''; break;
158 case '\\': s += '\\'; break;
159 case '{': s += '{'; break;
160 case '}': s += '}'; break;
161 default: s += e; break;
162 }
163 ++i;
164 } else {
165 s += src[i++];
166 }
167 }
168 if (i >= n)
169 throw ParseError(path_ + ":" + std::to_string(startLine) + ": 未闭合的字符串");
170 ++i;
171 add(Tok::Str, std::move(s));
172 continue;
173 }
174 if (std::isdigit(static_cast<unsigned char>(c)) ||
175 (c == '.' && i + 1 < n && std::isdigit(static_cast<unsigned char>(src[i + 1])))) {
176 const size_t start = i;
177 while (i < n) {
178 const char d = src[i];
179 if (d >= '0' && d <= '9') {
180 ++i;
181 } else if (d == '.') {
182 ++i;
183 } else if ((d == 'e' || d == 'E') && i + 1 < n &&
184 ((src[i + 1] >= '0' && src[i + 1] <= '9') ||
185 src[i + 1] == '+' || src[i + 1] == '-')) {
186 i += 2;
187 } else {
188 break;
189 }
190 }
191 add(Tok::Num, src.substr(start, i - start));
192 continue;
193 }
194 if (std::isalpha(static_cast<unsigned char>(c)) || c == '_') {
195 const size_t start = i;
196 while (i < n) {
197 const char d = src[i];
198 if (std::isalnum(static_cast<unsigned char>(d)) || d == '_' || d == '.') ++i;
199 else break;
200 }
201 add(Tok::Ident, src.substr(start, i - start));
202 continue;
203 }
204 const std::string two = (i + 1 < n) ? src.substr(i, 2) : "";
205 if (two == "==" || two == "!=" || two == ">=" || two == "<=" ||
206 two == "&&" || two == "||") {
207 add(Tok::Punct, two);
208 i += 2;
209 continue;
210 }
211 if (c == '{' || c == '}' || c == '(' || c == ')' || c == '[' || c == ']' ||
212 c == ':' || c == ',' || c == '=' || c == '>' || c == '<' || c == '!' ||
213 c == '-') {
214 add(Tok::Punct, std::string(1, c));
215 ++i;
216 continue;
217 }
218 throw ParseError(path_ + ":" + std::to_string(line) + ": 无法识别的字符 '" +
219 std::string(1, c) + "'");
220 }
221 toks_.push_back(Token{Tok::Eof, "", line});
222 }
223
224 DataValue parseLiteralValue() {
225 const Token &t = cur();
226 if (t.kind == Tok::Str) {
227 adv();
228 return DataValue::string(t.value);
229 }
230 if (t.kind == Tok::Num) {
231 adv();
232 return numValue(t.value);
233 }
234 if (t.kind == Tok::Ident) {
235 if (t.value == "true") {
236 adv();
237 return DataValue::boolean(true);
238 }
239 if (t.value == "false") {
240 adv();
241 return DataValue::boolean(false);
242 }
243 fail("条件字面量只支持字符串/数字/true/false");
244 }
245 if (isPunct("-")) {
246 adv();
247 const Token &tt = cur();
248 if (tt.kind == Tok::Num) {
249 adv();
250 return negNumValue(tt.value);
251 }
252 fail("'-' 后应跟数字");
253 }
254 fail("期望字面量");
255 }
256
257 DataValue parseComparison() {
258 if (!isIdent()) fail("条件左侧应为变量名");
259 const std::string varName = adv().value;
260 const Token &op = cur();
261 if (op.kind != Tok::Punct ||
262 (op.value != "==" && op.value != "!=" && op.value != ">" && op.value != "<" &&
263 op.value != ">=" && op.value != "<="))
264 fail("期望比较运算符(== != > < >= <=)");
265 adv();
266 DataValue value = parseLiteralValue();
267 std::string mapped;
268 if (op.value == "==") mapped = "eq";
269 else if (op.value == "!=") mapped = "ne";
270 else if (op.value == ">") mapped = "gt";
271 else if (op.value == "<") mapped = "lt";
272 else if (op.value == ">=") mapped = "ge";
273 else mapped = "le";
274 return DataValue::object({
275 {"var", DataValue::string(varName)},
276 {"op", DataValue::string(mapped)},
277 {"value", std::move(value)},
278 });
279 }
280
281 DataValue parseNot() {
282 if (isPunct("!")) {
283 adv();
284 return DataValue::object({{"not", parseNot()}});
285 }
286 if (isPunct("(")) {
287 adv();
288 DataValue e = parseOr();
289 expectPunct(")");
290 return e;
291 }
292 return parseComparison();
293 }
294
295 DataValue parseAnd() {
296 DataValue left = parseNot();
297 while (isPunct("&&")) {
298 adv();
299 DataValue right = parseNot();
300 bool appended = false;
301 if (left.kind == DataValue::Kind::Object) {
302 for (auto &kv : left.obj) {
303 if (kv.first == "all" && kv.second.kind == DataValue::Kind::Array) {
304 kv.second.arr.push_back(std::move(right));
305 appended = true;
306 break;
307 }
308 }
309 }
310 if (!appended) {
311 left = DataValue::object(
312 {{"all", DataValue::array({std::move(left), std::move(right)})}});
313 }
314 }
315 return left;
316 }
317
318 DataValue parseOr() {
319 DataValue left = parseAnd();
320 while (isPunct("||")) {
321 adv();
322 DataValue right = parseAnd();
323 bool appended = false;
324 if (left.kind == DataValue::Kind::Object) {
325 for (auto &kv : left.obj) {
326 if (kv.first == "any" && kv.second.kind == DataValue::Kind::Array) {
327 kv.second.arr.push_back(std::move(right));
328 appended = true;
329 break;
330 }
331 }
332 }
333 if (!appended) {
334 left = DataValue::object(
335 {{"any", DataValue::array({std::move(left), std::move(right)})}});
336 }
337 }
338 return left;
339 }
340
341 void parseAttrs(const int lineNum, std::vector<std::pair<std::string, DataValue>> &out) {
342 while (cur().line == lineNum && !isPunct("}") && cur().kind != Tok::Eof) {
343 if (!isIdent()) fail("期望属性名");
344 const std::string name = adv().value;
345 if (name == "meta" && isPunct("(")) {
346 adv();
347 std::vector<std::pair<std::string, DataValue>> metaFields;
348 while (!isPunct(")")) {
349 if (!isIdent()) fail("meta 键应为标识符");
350 const std::string k = adv().value;
351 expectPunct("=");
352 DataValue v = parseLiteralValue();
353 if (v.kind != DataValue::Kind::String && v.kind != DataValue::Kind::Int &&
355 fail("meta 值只支持标量");
356 metaFields.emplace_back(k, DataValue::string(scalarToString(v)));
357 if (isPunct(",")) adv();
358 else if (!isPunct(")")) fail("meta 内期望 ',' 或 ')'");
359 }
360 adv(); // )
361 out.emplace_back("meta", DataValue::object(std::move(metaFields)));
362 continue;
363 }
364 if (name == "tags") {
365 expectPunct("=");
366 if (!isPunct("[")) fail("tags 后应为 [");
367 adv();
368 std::vector<DataValue> arr;
369 while (!isPunct("]")) {
370 if (cur().kind != Tok::Str) fail("tags 元素应为字符串");
371 arr.emplace_back(DataValue::string(adv().value));
372 if (isPunct(",")) adv();
373 else if (!isPunct("]")) fail("tags 内期望 ',' 或 ']'");
374 }
375 adv(); // ]
376 out.emplace_back("tags", DataValue::array(std::move(arr)));
377 continue;
378 }
379 expectPunct("=");
380 DataValue v = parseLiteralValue();
381 if (name == "weight" || name == "i18n" || name == "id") {
382 out.emplace_back(name, std::move(v));
383 } else {
384 fail("未知属性 '" + name + "'");
385 }
386 }
387 }
388
389 DataValue parseLine(const std::string &poolId, int idx, const DataValue *inheritWhen) {
390 const int lineNum = cur().line;
391 std::string speaker;
392 if (isPunct("-")) {
393 adv();
394 } else if (isIdent()) {
395 if (cur().value == "when") fail("when 分组不允许嵌套");
396 speaker = adv().value;
397 expectPunct(":");
398 } else {
399 fail("期望说话人或 '-'");
400 }
401 if (cur().kind != Tok::Str) fail("期望台词字符串");
402 const std::string text = adv().value;
403
404 std::vector<std::pair<std::string, DataValue>> fields;
405 fields.emplace_back("speaker", DataValue::string(speaker));
406 fields.emplace_back("text", DataValue::string(text));
407 if (inheritWhen) fields.emplace_back("when", *inheritWhen);
408 std::vector<std::pair<std::string, DataValue>> attrs;
409 parseAttrs(lineNum, attrs);
410 bool hasId = false;
411 for (auto &kv : attrs) {
412 if (kv.first == "id") hasId = true;
413 fields.push_back(std::move(kv));
414 }
415 if (!hasId) fields.emplace_back("id", DataValue::string(poolId + "." + std::to_string(idx)));
416 return DataValue::object(std::move(fields));
417 }
418
419 void parsePool(std::vector<std::pair<std::string, DataValue>> &pools) {
420 expectIdent("pool");
421 const std::string poolId = expectIdent("pool 名称");
422 long long noRepeat = -1;
423 while (isIdent() && cur().value == "noRepeat") {
424 adv();
425 expectPunct("=");
426 const Token &t = cur();
427 if (t.kind != Tok::Num) fail("noRepeat 应为数字");
428 noRepeat = std::strtoll(adv().value.c_str(), nullptr, 10);
429 }
430 expectPunct("{");
431
432 std::vector<DataValue> lines;
433 int idx = 1;
434 while (!isPunct("}")) {
435 if (cur().kind == Tok::Eof) fail("未闭合的 pool 块");
436 if (isIdent() && cur().value == "pool") fail("pool 块不允许嵌套");
437 if (isIdent() && cur().value == "when") {
438 adv();
439 DataValue cond = parseOr();
440 expectPunct("{");
441 while (!isPunct("}")) {
442 if (cur().kind == Tok::Eof) fail("未闭合的 when 块");
443 lines.push_back(parseLine(poolId, idx, &cond));
444 ++idx;
445 }
446 adv();
447 continue;
448 }
449 lines.push_back(parseLine(poolId, idx, nullptr));
450 ++idx;
451 }
452 adv(); // }
453
454 std::vector<std::pair<std::string, DataValue>> poolFields;
455 poolFields.emplace_back("lines", DataValue::array(std::move(lines)));
456 if (noRepeat >= 0) poolFields.emplace_back("noRepeat", DataValue::integer(noRepeat));
457 pools.emplace_back(poolId, DataValue::object(std::move(poolFields)));
458 }
459
460 DataValue parsePools() {
461 std::vector<std::pair<std::string, DataValue>> pools;
462 while (cur().kind != Tok::Eof) parsePool(pools);
463 return DataValue::object({{"pools", DataValue::object(std::move(pools))}});
464 }
465};
466
467} // namespace
468
469bool parseDnut(const std::string &source, const std::string &path, DataValue &outRoot,
470 std::string &error) {
471 Parser parser(source, path);
472 return parser.parse(outRoot, error);
473}
474
475} // namespace eve::dialogue
int line
Tok kind
std::string value
glm::vec3 n
Definition Grass.cpp:64
std::string error
uint32_t c
int idx
glm::vec4 p[6]
const char * name
Definition RockMesh.cpp:21
int d
int v
uint32_t s
Definition Weather.cpp:28
bool parseDnut(const std::string &source, const std::string &path, DataValue &outRoot, std::string &error)
WidgetDesc text(std::string content, std::string id)
Static text label.
Definition Widget.cpp:235
Generic JSON-like value tree used by the Squirrel bridge: dialogue pools and conditions arrive as Squ...
Definition Dialogue.h:29
static DataValue string(std::string v)
Definition Dialogue.h:59
static DataValue integer(long long v)
Definition Dialogue.h:41
static DataValue boolean(bool v)
Definition Dialogue.h:53
static DataValue array(std::vector< DataValue > v)
Definition Dialogue.h:65
static DataValue object(std::vector< std::pair< std::string, DataValue > > v)
Definition Dialogue.h:71
static DataValue number(double v)
Definition Dialogue.h:47