PageRenderTime 90ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 1ms

/cwxeditor_src/cwx/script.d

https://bitbucket.org/k4nagatsuki/cwxeditor
D | 3617 lines | 3511 code | 32 blank | 74 comment | 700 complexity | 444ab6f057a2e301dd6f3fa17ba6be2f MD5 | raw file
Possible License(s): LGPL-2.1
  1. module cwx.script;
  2. import cwx.props;
  3. import cwx.types;
  4. import cwx.summary;
  5. import cwx.utils;
  6. import cwx.event;
  7. import cwx.motion;
  8. import cwx.background;
  9. import cwx.area;
  10. import cwx.card;
  11. import cwx.coupon;
  12. import cwx.structs;
  13. import std.algorithm;
  14. import std.conv;
  15. import std.array;
  16. import std.ascii;
  17. import std.stdio;
  18. import std.string;
  19. import std.regex;
  20. import std.exception;
  21. import std.traits;
  22. import std.range : ElementType;
  23. /// ???????????????????
  24. struct CWXSError {
  25. /// ?????????
  26. string message;
  27. /// ???????
  28. int errLine;
  29. /// ??????
  30. int errPos;
  31. /// ?????????? __FILE__
  32. string file;
  33. /// ?????????? __LINE__
  34. size_t line;
  35. }
  36. /// ditto
  37. class CWXScriptException : Exception {
  38. this (string file, size_t line, string text, const CWXSError[] errors, bool over100) { mixin(S_TRACE);
  39. super ("cwx script error", file, line);
  40. _text = text;
  41. _errors = errors;
  42. _over100 = over100;
  43. }
  44. private string _text;
  45. private const(CWXSError[]) _errors;
  46. private bool _over100;
  47. /// ??????????
  48. @property
  49. const
  50. string text() {return _text;}
  51. /// ???????????
  52. @property
  53. const
  54. const(CWXSError[]) errors() {return _errors;}
  55. /// ????100???????
  56. @property
  57. const
  58. bool over100() {return _over100;}
  59. }
  60. /// ?????????
  61. struct VarSet {
  62. string var; /// ????
  63. string value; /// ????
  64. }
  65. /// ???????????
  66. struct CompileOption {
  67. bool linkId = false; /// ?????????????
  68. int startLine = 0; /// ?????????????(??0)?
  69. int startPos = 0; /// ??????????????(??0)?
  70. int addLines = 0; /// ????????????(??0)?
  71. }
  72. /// ??????????????????????
  73. /// ??????????????CWXScriptException?????
  74. Content[] compile(const(CProps) prop, const(Summary) summ, string script, in CompileOption opt) { mixin(S_TRACE);
  75. auto compiler = new CWXScript(prop, summ, script);
  76. auto tokens = compiler.tokenize(script, opt);
  77. if (compiler.errors.length) throw new CWXScriptException(__FILE__, __LINE__, script, compiler.errors, false);
  78. auto nodes = compiler.analyzeSyntax(tokens);
  79. if (compiler.errors.length) throw new CWXScriptException(__FILE__, __LINE__, script, compiler.errors, false);
  80. auto r = compiler.analyzeSemantics(nodes, opt);
  81. if (compiler.errors.length) throw new CWXScriptException(__FILE__, __LINE__, script, compiler.errors, false);
  82. return r;
  83. }
  84. /// ??????????????????????????????
  85. /// ?????????????-1????
  86. CType firstContentType(const(CProps) prop, const(Summary) summ, string script) { mixin(S_TRACE);
  87. CompileOption opt;
  88. auto compiler = new CWXScript(prop, summ, script);
  89. auto tokens = compiler.tokenize(script, opt);
  90. if (compiler.errors.length) return cast(CType)-1;
  91. foreach (ref token; tokens) { mixin(S_TRACE);
  92. if (token.kind is CWXScript.Kind.START) return CType.START;
  93. if (token.kind is CWXScript.Kind.SYMBOL) { mixin(S_TRACE);
  94. auto p = token.value.toLower() in CWXScript.KEYS.keywords;
  95. if (p) { mixin(S_TRACE);
  96. return *p;
  97. }
  98. }
  99. }
  100. return cast(CType)-1;
  101. }
  102. /// ?????????????????????
  103. class CWXScript {
  104. private const(CProps) _prop;
  105. private const(Summary) _summ;
  106. private string _text;
  107. private size_t _maxError;
  108. private size_t _autoWrap;
  109. /// ???????????
  110. this (const(CProps) prop, const(Summary) summ, string text = "", size_t maxError = 100, size_t autoWrap = 250) { mixin(S_TRACE);
  111. _prop = prop;
  112. _summ = summ;
  113. _text = text;
  114. _maxError = maxError;
  115. _autoWrap = autoWrap;
  116. }
  117. private CWXSError[] _errors;
  118. /// ??????????????????????
  119. @property
  120. const
  121. const(CWXSError[]) errors() {return _errors;}
  122. /// s????????????????????
  123. static string createString(string s) { mixin(S_TRACE);
  124. return "\"" ~ std.array.replace(s, "\"", "\"\"") ~ "\"";
  125. }
  126. private void throwError(string File = __FILE__, size_t Line = __LINE__)
  127. (lazy string message, in Token tok) { mixin(S_TRACE);
  128. throwErrorToken!(File, Line)(message, tok.line, tok.pos, tok.value);
  129. }
  130. private void throwErrorToken(string File = __FILE__, size_t Line = __LINE__)
  131. (lazy string message, int line, int pos, string value) { mixin(S_TRACE);
  132. if (!_maxError) return;
  133. if (_errors.length && _errors[$ - 1].errLine == line && _errors[$ - 1].errPos == pos) { mixin(S_TRACE);
  134. // ?????????????????
  135. return;
  136. }
  137. string msg;
  138. if (!_prop || !_prop.msgs) { mixin(S_TRACE);
  139. msg = "";
  140. } else { mixin(S_TRACE);
  141. msg = message;
  142. }
  143. if (_maxError <= _errors.length) { mixin(S_TRACE);
  144. throw new CWXScriptException(__FILE__, __LINE__, _text, errors, true);
  145. }
  146. _errors ~= CWXSError(msg, line, pos, File, Line);
  147. }
  148. /// Token????
  149. static enum Kind {
  150. START, /// start
  151. IF, /// if
  152. FI, /// fi
  153. ELIF, /// elif
  154. SIF, /// sif
  155. O_BRA, /// [
  156. C_BRA, /// ]
  157. SYMBOL, /// ????????????????
  158. NUMBER, /// ???
  159. VAR_NAME, /// ????
  160. EQ, /// =
  161. COMMA, /// comma
  162. STRING, /// ????
  163. PLU, /// +
  164. MIN, /// -
  165. MUL, /// *
  166. DIV, /// /
  167. RES, /// %
  168. CAT, /// ~
  169. O_PAR, /// (
  170. C_PAR, /// )
  171. COMMENT /// ????
  172. }
  173. /// ???????????
  174. static struct Token {
  175. int line; /// ?????????
  176. int pos; /// ??????
  177. size_t index; /// ????????????
  178. Kind kind; /// ???
  179. string value; /// ??
  180. string comment = ""; /// ????????
  181. /// ??????
  182. const
  183. string toString() { mixin(S_TRACE);
  184. return .format("Token {line %d : %d, %d, %s, %s, %s}", line, pos, index, to!(string)(kind), value, comment);
  185. }
  186. /// o??????
  187. const
  188. bool opEquals(ref const(Token) o) { mixin(S_TRACE);
  189. return line == o.line && pos == o.pos && index == o.index && kind == o.kind && value == o.value && comment == o.comment;
  190. }
  191. }
  192. private static string[] wrap(string line, size_t width) { mixin(S_TRACE);
  193. if (width > 0) { mixin(S_TRACE);
  194. string[] lines;
  195. while (lengthJ(line) > width) { mixin(S_TRACE);
  196. auto l = sliceJ(line, 0, width);
  197. if (!l.length) { mixin(S_TRACE);
  198. l = sliceJ(line, 0, width + 1);
  199. }
  200. lines ~= l;
  201. line = line[l.length .. $];
  202. }
  203. lines ~= line;
  204. return lines;
  205. }
  206. return [line];
  207. }
  208. const
  209. private size_t stringCenter(string[] linesBase, size_t width) { mixin(S_TRACE);
  210. string[] lines;
  211. if (width > 0) { mixin(S_TRACE);
  212. foreach (line; linesBase) { mixin(S_TRACE);
  213. lines ~= wrap(line, width);
  214. }
  215. } else { mixin(S_TRACE);
  216. lines = linesBase;
  217. }
  218. int ln;
  219. int lc = cast(int) lineCount(lines);
  220. if (lc > 0 && lc < _prop.looks.messageLine) { mixin(S_TRACE);
  221. int lnt = cast(int) _prop.looks.messageLine - (lc - 1);
  222. ln = lnt / 2 + 1;
  223. } else { mixin(S_TRACE);
  224. ln = 0;
  225. }
  226. return ln > 0 ? ln : 0;
  227. }
  228. /// Token????????????????
  229. /// ????????????
  230. /// ????????????????????????
  231. private string stringValue(in Token tok, size_t width) { mixin(S_TRACE);
  232. string decode(in char[] s, char esc) { mixin(S_TRACE);
  233. char[] buf = new char[s.length];
  234. size_t len = 0;
  235. bool escape = false;
  236. foreach (char c; s) { mixin(S_TRACE);
  237. if (!escape && c == esc) { mixin(S_TRACE);
  238. escape = true;
  239. } else { mixin(S_TRACE);
  240. buf[len] = c;
  241. len++;
  242. escape = false;
  243. }
  244. }
  245. if (escape) { mixin(S_TRACE);
  246. buf[len] = esc;
  247. len++;
  248. }
  249. buf = buf[0 .. len];
  250. return assumeUnique(buf);
  251. }
  252. if (tok.kind !is Kind.STRING || tok.value.length < 2) { mixin(S_TRACE);
  253. throwError(_prop.msgs.scriptErrorInvalidString, tok);
  254. }
  255. if (tok.value[0] == '@') { mixin(S_TRACE);
  256. char[] buf;
  257. auto linesBase = .splitLines!string(tok.value[0 .. $ - 1].idup);
  258. string firstLine = linesBase[0];
  259. string[] lines;
  260. string[] resultLines;
  261. foreach (i, line; linesBase[1 .. $]) { mixin(S_TRACE);
  262. line = .astripl(line);
  263. line = decode(line, tok.value[0]);
  264. if (line.length >= 1 && line[0] == '\\') { mixin(S_TRACE);
  265. line = line[1 .. $];
  266. }
  267. resultLines ~= line;
  268. lines ~= wrap(line, width);
  269. }
  270. if (firstLine.length > 1) { mixin(S_TRACE);
  271. auto lnStr = std.string.toLower(.astrip(firstLine[1 .. $]));
  272. bool isNum = std.string.isNumeric(lnStr);
  273. if (!isNum && icmp(lnStr, "c") != 0 && icmp(lnStr, "center") != 0) { mixin(S_TRACE);
  274. throwError(_prop.msgs.scriptErrorInvalidStr, tok);
  275. }
  276. int ln;
  277. if (isNum) { mixin(S_TRACE);
  278. ln = .to!(int)(lnStr);
  279. } else { mixin(S_TRACE);
  280. ln = stringCenter(lines, 0);
  281. }
  282. if (ln > 0) { mixin(S_TRACE);
  283. buf.length = ln - 1;
  284. }
  285. buf[] = '\n';
  286. }
  287. foreach (i, line; resultLines) { mixin(S_TRACE);
  288. if (i > 0) buf ~= '\n';
  289. buf ~= line;
  290. }
  291. return assumeUnique(buf);
  292. }
  293. return decode(tok.value[1 .. $ - 1], tok.value[0]);
  294. } unittest { mixin(S_TRACE);
  295. debug mixin(UTPerf);
  296. auto s = new CWXScript(new CProps("", null), null);
  297. assert (s.stringValue(Token(0, 0, 0, Kind.STRING, `"abc"`), 0) == "abc");
  298. assert (s.stringValue(Token(0, 0, 0, Kind.STRING, `"a""bc"`), 0) == "a\"bc");
  299. assert (s.stringValue(Token(0, 0, 0, Kind.STRING, `'ab''c'`), 0) == "ab'c");
  300. assert (s.stringValue(Token(0, 2, 0, Kind.STRING, "@ 3\n\t\tte@@st\n t\\e\\st\n\\ a\n\\\\ a@"), false)
  301. == "\n\nte@st\nt\\e\\st\n a\n\\ a");
  302. assert (s.stringValue(Token(0, 0, 0, Kind.STRING, "@\nabcabc\n@"), 3) == "abcabc", s.stringValue(Token(0, 0, 0, Kind.STRING, "@\nabcabc\n@"), 3));
  303. }
  304. /// text???????????????
  305. /// ?????????????text???????
  306. string[] eatEmptyVars(ref string text, ref CompileOption opt) { mixin(S_TRACE);
  307. auto tokens = tokenizeImpl(text, true, opt);
  308. // ?????????
  309. Token[] tokens2;
  310. foreach (tok; tokens) { mixin(S_TRACE);
  311. if (tok.kind !is Kind.COMMENT) { mixin(S_TRACE);
  312. tokens2 ~= tok;
  313. }
  314. }
  315. string[] r;
  316. size_t index = 0;
  317. size_t len = 0;
  318. opt.startLine = 0;
  319. opt.startPos = 0;
  320. // ??????????????'='?????????????
  321. foreach (i, tok; tokens2) { mixin(S_TRACE);
  322. if (tok.kind is Kind.EQ) { mixin(S_TRACE);
  323. throwError(_prop.msgs.scriptErrorInvalidSyntax, tok);
  324. break;
  325. } else if (tok.kind is Kind.VAR_NAME) { mixin(S_TRACE);
  326. if (i + 1 < tokens2.length && tokens2[i + 1].kind is Kind.EQ) { mixin(S_TRACE);
  327. break;
  328. }
  329. index = tok.index;
  330. len = tok.value.length;
  331. opt.startLine = tok.line;
  332. opt.startPos = tok.pos + len;
  333. r ~= tok.value;
  334. }
  335. }
  336. text = text[index + len .. $];
  337. return r;
  338. } unittest { mixin(S_TRACE);
  339. debug mixin(UTPerf);
  340. CompileOption opt;
  341. auto s = new CWXScript(new CProps("", null), null);
  342. auto str = "/*c1*/ $test1 $test2 //$test3\n$test4// aaa\n$test5 = a $test6 endsc true";
  343. auto vars = s.eatEmptyVars(str, opt);
  344. assert (vars == ["$test1", "$test2", "$test4"], .text(vars));
  345. assert (str == "// aaa\n$test5 = a $test6 endsc true", str);
  346. assert (opt.startLine == 1);
  347. assert (opt.startPos == 6);
  348. }
  349. /// text??? = ?????????????
  350. static string pushVars(string text, in VarSet[] varTable, ref CompileOption opt) { mixin(S_TRACE);
  351. string[] lines;
  352. opt.addLines = 0;
  353. foreach (t; varTable) { mixin(S_TRACE);
  354. string v = t.value;
  355. if (!v.length) v = `""`;
  356. auto l = t.var ~ " = " ~ v;
  357. lines ~= l;
  358. opt.addLines += v.splitLines().length;
  359. }
  360. lines ~= text;
  361. opt.startLine -= opt.addLines;
  362. return lines.join("\n");
  363. } unittest { mixin(S_TRACE);
  364. CompileOption opt;
  365. VarSet[] varSet = [VarSet("$a", "1"), VarSet("$b", "2")];
  366. auto r = pushVars("aaa bbb", varSet, opt);
  367. assert (r == "$a = 1\n$b = 2\naaa bbb", r);
  368. assert (opt.startLine == -2, .text(opt.startLine));
  369. }
  370. /// text?Token??????
  371. Token[] tokenize(string text, in CompileOption opt = CompileOption.init) { mixin(S_TRACE);
  372. return tokenizeImpl(text, false, opt);
  373. } unittest { mixin(S_TRACE);
  374. debug mixin(UTPerf);
  375. auto s = new CWXScript(new CProps("", null), null);
  376. auto tokens = s.tokenize("");
  377. assert (tokens == [], to!string(tokens));
  378. tokens = s.tokenize("/*/*\n*/*/");
  379. assert (tokens == [Token(0, 0, 0, Kind.COMMENT, "/*/*\n*/*/", "")], to!string(tokens));
  380. tokens = s.tokenize("/*c*/start, 12.3 \ntest1 [$void] =\"str\ning//\"\n\r //comment\nELIF if\n1/2+3*4%(5-6)");
  381. assert (tokens
  382. == [
  383. Token(0, 0, 0, Kind.COMMENT, "/*c*/", ""),
  384. Token(0, 5, 5, Kind.START, "start", "c"),
  385. Token(0, 10, 10, Kind.COMMA, ","),
  386. Token(0, 12, 12, Kind.NUMBER, "12.3"),
  387. Token(1, 0, 18, Kind.SYMBOL, "test1"),
  388. Token(1, 6, 24, Kind.O_BRA, "["),
  389. Token(1, 7, 25, Kind.VAR_NAME, "$void"),
  390. Token(1, 12, 30, Kind.C_BRA, "]"),
  391. Token(1, 14, 32, Kind.EQ, "="),
  392. Token(1, 15, 33, Kind.STRING, "\"str\ning//\""),
  393. Token(4, 1, 47, Kind.COMMENT, "//comment\n", ""),
  394. Token(5, 0, 57, Kind.ELIF, "ELIF", "comment\n"),
  395. Token(5, 5, 62, Kind.IF, "if"),
  396. Token(6, 0, 65, Kind.NUMBER, "1"),
  397. Token(6, 1, 66, Kind.DIV, "/"),
  398. Token(6, 2, 67, Kind.NUMBER, "2"),
  399. Token(6, 3, 68, Kind.PLU, "+"),
  400. Token(6, 4, 69, Kind.NUMBER, "3"),
  401. Token(6, 5, 70, Kind.MUL, "*"),
  402. Token(6, 6, 71, Kind.NUMBER, "4"),
  403. Token(6, 7, 72, Kind.RES, "%"),
  404. Token(6, 8, 73, Kind.O_PAR, "("),
  405. Token(6, 9, 74, Kind.NUMBER, "5"),
  406. Token(6, 10, 75, Kind.MIN, "-"),
  407. Token(6, 11, 76, Kind.NUMBER, "6"),
  408. Token(6, 12, 77, Kind.C_PAR, ")")
  409. ], to!string(tokens));
  410. }
  411. private Token[] tokenizeImpl(ref string text, bool eatEmptyVarMode, in CompileOption opt) { mixin(S_TRACE);
  412. if (!text.length) return [];
  413. Token[] r;
  414. text = std.array.replace(text, "\r\n", "\n");
  415. text = std.array.replace(text, "\r", "\n");
  416. string dtext = text;
  417. auto reg = .regex("(" ~ std.string.join(TOKENS.dup, ")|(") ~ ")", "gi");
  418. size_t i = 0;
  419. size_t hits = 0;
  420. size_t pos = 0;
  421. size_t index = 0;
  422. int commentLevel = 0;
  423. size_t lastCommentLine = 0;
  424. size_t lastCommentPos = 0;
  425. size_t lastCommentIndex = 0;
  426. string post;
  427. bool spaceAfter = false;
  428. string docComment = "";
  429. string fullComment = "";
  430. @property int sLine() {return cast(int) i + opt.startLine;}
  431. @property int sPos() {return (cast(int) i == opt.addLines) ? (cast(int) pos + opt.startPos) : pos;}
  432. foreach (token; .match(dtext, reg)) { mixin(S_TRACE);
  433. post = token.post;
  434. string pre = token.pre;
  435. index = pre.length;
  436. auto dstr = token.hit;
  437. void retCount2(string dstr) { mixin(S_TRACE);
  438. size_t count = .count(dstr, "\n");
  439. if (count > 0) { mixin(S_TRACE);
  440. pos = dstr.length - std.string.lastIndexOf(dstr, '\n') - 1;
  441. i += count;
  442. } else { mixin(S_TRACE);
  443. pos += dstr.length;
  444. }
  445. }
  446. void retCount() { mixin(S_TRACE);
  447. retCount2(dstr);
  448. }
  449. auto c = dstr[0];
  450. string str = to!string(dstr);
  451. if (cast(int) pre.length - cast(int) hits > 0) { mixin(S_TRACE);
  452. if (0 < commentLevel) { mixin(S_TRACE);
  453. /// in comment
  454. retCount2(pre[hits .. $]);
  455. hits = pre.length;
  456. } else { mixin(S_TRACE);
  457. string lpre = pre;
  458. if (lpre.length && (lpre[$ - 1] == '@' || lpre[$ - 1] == '"' || lpre[$ - 1] == '\'')) { mixin(S_TRACE);
  459. throwErrorToken(_prop.msgs.scriptErrorUnCloseString, sLine, sPos, "");
  460. return r;
  461. } else { mixin(S_TRACE);
  462. throwErrorToken(_prop.msgs.scriptErrorInvalidToken, sLine, sPos, "");
  463. }
  464. }
  465. }
  466. if (0 < commentLevel) { mixin(S_TRACE);
  467. fullComment ~= .text(pre[hits .. $]) ~ str;
  468. }
  469. bool commentStart = false;
  470. if (str == "/*") { mixin(S_TRACE);
  471. // multi line comment (open)
  472. spaceAfter = true;
  473. if (commentLevel == 0) { mixin(S_TRACE);
  474. commentStart = true;
  475. lastCommentLine = i;
  476. lastCommentPos = pos;
  477. lastCommentIndex = index;
  478. }
  479. pos += dstr.length;
  480. if (0 == commentLevel) fullComment = str;
  481. commentLevel++;
  482. } else if (str == "*/") { mixin(S_TRACE);
  483. // multi line comment (close)
  484. spaceAfter = true;
  485. pos += dstr.length;
  486. if (commentLevel <= 0) { mixin(S_TRACE);
  487. throwErrorToken(_prop.msgs.scriptErrorUnOpenComment, sLine, sPos, str);
  488. }
  489. commentLevel--;
  490. if (0 == commentLevel) { mixin(S_TRACE);
  491. r ~= Token(lastCommentLine, lastCommentPos, lastCommentIndex, Kind.COMMENT, fullComment, "");
  492. }
  493. } else if (0 < commentLevel) { mixin(S_TRACE);
  494. spaceAfter = true;
  495. retCount();
  496. } else if (std.ascii.isAlpha(c) || c == '_') { mixin(S_TRACE);
  497. if (eatEmptyVarMode) return r;
  498. spaceAfter = false;
  499. // symbol
  500. switch (std.string.toLower(dstr)) {
  501. case "start":
  502. r ~= Token(sLine, sPos, index, Kind.START, str, docComment);
  503. break;
  504. case "if":
  505. r ~= Token(sLine, sPos, index, Kind.IF, str, docComment);
  506. break;
  507. case "elif":
  508. r ~= Token(sLine, sPos, index, Kind.ELIF, str, docComment);
  509. break;
  510. case "fi":
  511. r ~= Token(sLine, sPos, index, Kind.FI, str, docComment);
  512. break;
  513. case "sif":
  514. r ~= Token(sLine, sPos, index, Kind.SIF, str, docComment);
  515. break;
  516. default:
  517. r ~= Token(sLine, sPos, index, Kind.SYMBOL, str, docComment);
  518. break;
  519. }
  520. pos += dstr.length;
  521. docComment = "";
  522. } else if (c == '$') { mixin(S_TRACE);
  523. spaceAfter = false;
  524. r ~= Token(sLine, sPos, index, Kind.VAR_NAME, str, docComment);
  525. pos += dstr.length;
  526. docComment = "";
  527. } else if (c == '=') { mixin(S_TRACE);
  528. spaceAfter = false;
  529. r ~= Token(sLine, sPos, index, Kind.EQ, str, docComment);
  530. pos += dstr.length;
  531. docComment = "";
  532. } else if (c == '[') { mixin(S_TRACE);
  533. if (eatEmptyVarMode) return r;
  534. // open bracket
  535. spaceAfter = false;
  536. r ~= Token(sLine, sPos, index, Kind.O_BRA, str, docComment);
  537. pos += dstr.length;
  538. docComment = "";
  539. } else if (c == ']') { mixin(S_TRACE);
  540. if (eatEmptyVarMode) return r;
  541. // close bracket
  542. spaceAfter = false;
  543. r ~= Token(sLine, sPos, index, Kind.C_BRA, str, docComment);
  544. pos += dstr.length;
  545. docComment = "";
  546. } else if (isDigit(c)) { mixin(S_TRACE);
  547. if (eatEmptyVarMode) return r;
  548. // number
  549. spaceAfter = false;
  550. r ~= Token(sLine, sPos, index, Kind.NUMBER, str, docComment);
  551. pos += dstr.length;
  552. docComment = "";
  553. } else if (c == '@' || c == '"' || c == '\'') { mixin(S_TRACE);
  554. if (eatEmptyVarMode) return r;
  555. // string
  556. if (!spaceAfter && r.length && r[$ - 1].kind is Kind.STRING
  557. && r[$ - 1].value[$ - 1] == c) { mixin(S_TRACE);
  558. // ???string???
  559. r[$ - 1].value ~= str;
  560. } else { mixin(S_TRACE);
  561. r ~= Token(sLine, sPos, index, Kind.STRING, str, docComment);
  562. }
  563. spaceAfter = false;
  564. retCount();
  565. docComment = "";
  566. } else if (std.ascii.isWhite(c)) { mixin(S_TRACE);
  567. // whitespace
  568. spaceAfter = true;
  569. retCount();
  570. } else if (c == '+') { mixin(S_TRACE);
  571. if (eatEmptyVarMode) return r;
  572. // plus
  573. spaceAfter = false;
  574. r ~= Token(sLine, sPos, index, Kind.PLU, str, docComment);
  575. pos += dstr.length;
  576. docComment = "";
  577. } else if (c == '-') { mixin(S_TRACE);
  578. if (eatEmptyVarMode) return r;
  579. // minus
  580. spaceAfter = false;
  581. r ~= Token(sLine, sPos, index, Kind.MIN, str, docComment);
  582. pos += dstr.length;
  583. docComment = "";
  584. } else if (c == '*') { mixin(S_TRACE);
  585. if (eatEmptyVarMode) return r;
  586. // multiply
  587. spaceAfter = false;
  588. r ~= Token(sLine, sPos, index, Kind.MUL, str, docComment);
  589. pos += dstr.length;
  590. docComment = "";
  591. } else if (c == '/') { mixin(S_TRACE);
  592. if (dstr.length >= 2 && str[1] == '/') { mixin(S_TRACE);
  593. // line comment
  594. spaceAfter = true;
  595. r ~= Token(sLine, sPos, index, Kind.COMMENT, str, "");
  596. i++;
  597. pos = 0;
  598. if (2 < str.length) docComment ~= str[2 .. $];
  599. } else { mixin(S_TRACE);
  600. if (eatEmptyVarMode) return r;
  601. // divide
  602. spaceAfter = false;
  603. r ~= Token(sLine, sPos, index, Kind.DIV, str, docComment);
  604. pos += dstr.length;
  605. docComment = "";
  606. }
  607. } else if (c == '%') { mixin(S_TRACE);
  608. if (eatEmptyVarMode) return r;
  609. // residue
  610. spaceAfter = false;
  611. r ~= Token(sLine, sPos, index, Kind.RES, str, docComment);
  612. pos += dstr.length;
  613. docComment = "";
  614. } else if (c == '~') { mixin(S_TRACE);
  615. if (eatEmptyVarMode) return r;
  616. // cat
  617. spaceAfter = false;
  618. r ~= Token(sLine, sPos, index, Kind.CAT, str, docComment);
  619. pos += dstr.length;
  620. docComment = "";
  621. } else if (c == '(') { mixin(S_TRACE);
  622. if (eatEmptyVarMode) return r;
  623. // open paren
  624. spaceAfter = false;
  625. r ~= Token(sLine, sPos, index, Kind.O_PAR, str, docComment);
  626. pos += dstr.length;
  627. docComment = "";
  628. } else if (c == ')') { mixin(S_TRACE);
  629. if (eatEmptyVarMode) return r;
  630. // close paren
  631. spaceAfter = false;
  632. r ~= Token(sLine, sPos, index, Kind.C_PAR, str, docComment);
  633. pos += dstr.length;
  634. docComment = "";
  635. } else if (c == ',') { mixin(S_TRACE);
  636. if (eatEmptyVarMode) return r;
  637. // comma
  638. spaceAfter = false;
  639. r ~= Token(sLine, sPos, index, Kind.COMMA, str, docComment);
  640. pos += dstr.length;
  641. docComment = "";
  642. } else { mixin(S_TRACE);
  643. assert (0);
  644. }
  645. if (!commentStart && 0 < commentLevel) { mixin(S_TRACE);
  646. docComment ~= str;
  647. }
  648. hits += dstr.length;
  649. }
  650. if (0 < commentLevel) { mixin(S_TRACE);
  651. throwErrorToken(_prop.msgs.scriptErrorUnCloseComment, lastCommentLine, lastCommentPos, "");
  652. }
  653. if (post.length) throwErrorToken(_prop.msgs.scriptErrorInvalidToken, sLine, sPos, "");
  654. return r;
  655. }
  656. private enum CRKind {STR, INT, REAL}
  657. private class CalcResult {
  658. CRKind kind = CRKind.INT;
  659. union {
  660. string str;
  661. long numInt;
  662. real numReal;
  663. }
  664. this () { mixin(S_TRACE);
  665. numInt = 0;
  666. }
  667. void cat(in CProps prop, in Token tok, in CalcResult rval) { mixin(S_TRACE);
  668. switch (kind) {
  669. case CRKind.STR: break;
  670. case CRKind.INT: str = to!(string)(numInt); break;
  671. case CRKind.REAL: str = to!(string)(numReal); break;
  672. default: assert (0);
  673. }
  674. switch (rval.kind) {
  675. case CRKind.STR:
  676. str ~= rval.str;
  677. break;
  678. case CRKind.INT:
  679. str ~= to!(string)(rval.numInt);
  680. break;
  681. case CRKind.REAL:
  682. str ~= to!(string)(rval.numReal);
  683. break;
  684. default: assert (0);
  685. }
  686. kind = CRKind.STR;
  687. }
  688. private void calc(string Calc, bool ChkDiv)(in CProps prop, in Token tok, in CalcResult rval) { mixin(S_TRACE);
  689. if (kind is CRKind.STR || rval.kind is CRKind.STR) { mixin(S_TRACE);
  690. throwError(prop.msgs.scriptErrorInvalidNumber, tok);
  691. }
  692. static if (ChkDiv) {
  693. if ((rval.kind is CRKind.REAL ? rval.numReal : rval.numInt) == 0) { mixin(S_TRACE);
  694. throwError(prop.msgs.scriptErrorZeroDivision, tok);
  695. }
  696. }
  697. if (kind is CRKind.REAL || rval.kind is CRKind.REAL) { mixin(S_TRACE);
  698. real lvalue = kind is CRKind.REAL ? numReal : numInt;
  699. real rvalue = rval.kind is CRKind.REAL ? rval.numReal : rval.numInt;
  700. mixin ("numReal = lvalue " ~ Calc ~ " rvalue;");
  701. kind = CRKind.REAL;
  702. } else if (kind is CRKind.INT && rval.kind is CRKind.INT) { mixin(S_TRACE);
  703. mixin ("numInt " ~ Calc ~ "= rval.numInt;");
  704. } else { mixin(S_TRACE);
  705. throwError(prop.msgs.scriptErrorInvalidNumber, tok);
  706. }
  707. }
  708. public alias calc!("+", false) add;
  709. public alias calc!("-", false) min;
  710. public alias calc!("*", false) mul;
  711. public alias calc!("/", true) div;
  712. public alias calc!("%", true) res;
  713. const
  714. bool opEquals(ref const(CalcResult) val) { mixin(S_TRACE);
  715. if (kind !is val.kind) return false;
  716. final switch (kind) {
  717. case CRKind.STR: return str == val.str;
  718. case CRKind.INT: return numInt == val.numInt;
  719. case CRKind.REAL: return numReal == val.numReal;
  720. }
  721. }
  722. const
  723. bool opEquals(int val) { mixin(S_TRACE);
  724. return opEquals(cast(long) val);
  725. }
  726. const
  727. bool opEquals(long val) { mixin(S_TRACE);
  728. final switch (kind) {
  729. case CRKind.STR: return false;
  730. case CRKind.INT: return numInt == val;
  731. case CRKind.REAL: return numReal == val;
  732. }
  733. }
  734. const
  735. bool opEquals(real val) { mixin(S_TRACE);
  736. final switch (kind) {
  737. case CRKind.STR: return false;
  738. case CRKind.INT: return numInt == val;
  739. case CRKind.REAL: return numReal == val;
  740. }
  741. }
  742. const
  743. bool opEquals(string val) { mixin(S_TRACE);
  744. return kind is CRKind.STR && str == val;
  745. }
  746. override
  747. const
  748. string toString() { mixin(S_TRACE);
  749. final switch (kind) {
  750. case CRKind.STR: return str;
  751. case CRKind.INT: return to!(string)(numInt);
  752. case CRKind.REAL: return to!(string)(numReal);
  753. }
  754. }
  755. }
  756. private const OPE_LEVEL_MAX = 2;
  757. private CalcResult calcNum(in Token[] tokens, ref size_t i, in const(Node)[][string] varTable, size_t strWidth) { mixin(S_TRACE);
  758. assert (i < tokens.length);
  759. auto tok = tokens[i];
  760. if (tok.kind is Kind.VAR_NAME) { mixin(S_TRACE);
  761. i++;
  762. try { mixin(S_TRACE);
  763. auto vt = var(tok, varTable);
  764. auto r = new CalcResult;
  765. if (vt.kind is Kind.STRING) { mixin(S_TRACE);
  766. r.kind = CRKind.STR;
  767. r.str = stringValue(vt, strWidth);
  768. } else if (std.string.indexOf(vt.value, '.') != -1) { mixin(S_TRACE);
  769. r.kind = CRKind.REAL;
  770. r.numReal = to!(real)(vt.value);
  771. } else { mixin(S_TRACE);
  772. r.kind = CRKind.INT;
  773. r.numInt = to!(long)(vt.value);
  774. }
  775. return r;
  776. } catch (Exception e) {
  777. throwError(_prop.msgs.scriptErrorReqNumber, tok);
  778. auto r = new CalcResult;
  779. r.kind = CRKind.INT;
  780. r.numInt = 0;
  781. return r;
  782. }
  783. }
  784. bool min = false;
  785. if (tok.kind is Kind.PLU) { mixin(S_TRACE);
  786. i++;
  787. if (tokens[i].kind !is Kind.NUMBER) { mixin(S_TRACE);
  788. throwError(_prop.msgs.scriptErrorInvalidNumber, tok);
  789. }
  790. } else if (tok.kind is Kind.MIN) { mixin(S_TRACE);
  791. i++;
  792. if (tokens[i].kind !is Kind.NUMBER) { mixin(S_TRACE);
  793. throwError(_prop.msgs.scriptErrorInvalidNumber, tok);
  794. }
  795. min = true;
  796. }
  797. if (tokens.length <= i || !(tokens[i].kind is Kind.NUMBER || tokens[i].kind is Kind.STRING)) { mixin(S_TRACE);
  798. throwError(_prop.msgs.scriptErrorInvalidNumber, tok);
  799. }
  800. auto r = new CalcResult;
  801. try { mixin(S_TRACE);
  802. if (tokens[i].kind is Kind.NUMBER) { mixin(S_TRACE);
  803. if (std.string.indexOf(tokens[i].value, '.') != -1) { mixin(S_TRACE);
  804. r.kind = CRKind.REAL;
  805. r.numReal = to!(real)(tokens[i].value);
  806. if (min) r.numReal = -r.numReal;
  807. } else { mixin(S_TRACE);
  808. r.kind = CRKind.INT;
  809. r.numInt = to!(long)(tokens[i].value);
  810. if (min) r.numInt = -r.numInt;
  811. }
  812. } else if (tokens[i].kind is Kind.STRING) { mixin(S_TRACE);
  813. r.kind = CRKind.STR;
  814. r.str = stringValue(tokens[i], strWidth);
  815. } else { mixin(S_TRACE);
  816. throwError(_prop.msgs.scriptErrorReqNumber, tokens[i]);
  817. }
  818. i++;
  819. return r;
  820. } catch (Exception e) {
  821. throwError(_prop.msgs.scriptErrorReqNumber, tokens[i]);
  822. }
  823. return r;
  824. }
  825. private CalcResult calcPar(in Token[] tokens, ref size_t i, in const(Node)[][string] varTable, size_t strWidth) { mixin(S_TRACE);
  826. assert (i < tokens.length);
  827. auto tok = tokens[i];
  828. switch (tok.kind) {
  829. case Kind.O_PAR:
  830. i++;
  831. auto r = calcImpl(0, tokens, i, varTable, strWidth);
  832. if (tokens[i].kind !is Kind.C_PAR) { mixin(S_TRACE);
  833. throwError(_prop.msgs.scriptErrorCloseParenNotFound, tok);
  834. }
  835. i++;
  836. return r;
  837. default:
  838. return calcNum(tokens, i, varTable, strWidth);
  839. }
  840. }
  841. private CalcResult calcImpl(size_t opeLevel, in Token[] tokens, ref size_t i, in const(Node)[][string] varTable, size_t strWidth) { mixin(S_TRACE);
  842. assert (i < tokens.length);
  843. CalcResult r;
  844. if (opeLevel >= OPE_LEVEL_MAX) { mixin(S_TRACE);
  845. r = calcPar(tokens, i, varTable, strWidth);
  846. } else { mixin(S_TRACE);
  847. r = calcImpl(opeLevel + 1, tokens, i, varTable, strWidth);
  848. }
  849. while (i < tokens.length) { mixin(S_TRACE);
  850. auto tok = tokens[i];
  851. switch (opeLevel) {
  852. case 0:
  853. switch (tok.kind) {
  854. case Kind.CAT:
  855. i++;
  856. if (tokens.length < i) { mixin(S_TRACE);
  857. throwError(_prop.msgs.scriptErrorInvalidCalc, tok);
  858. return r;
  859. }
  860. r.cat(_prop, tok, calcImpl(1, tokens, i, varTable, strWidth));
  861. break;
  862. default:
  863. return r;
  864. }
  865. break;
  866. case 1:
  867. switch (tok.kind) {
  868. case Kind.PLU:
  869. i++;
  870. if (tokens.length < i) { mixin(S_TRACE);
  871. throwError(_prop.msgs.scriptErrorInvalidCalc, tok);
  872. return r;
  873. }
  874. r.add(_prop, tok, calcImpl(1, tokens, i, varTable, strWidth));
  875. break;
  876. case Kind.MIN:
  877. i++;
  878. if (tokens.length < i) { mixin(S_TRACE);
  879. throwError(_prop.msgs.scriptErrorInvalidCalc, tok);
  880. return r;
  881. }
  882. r.min(_prop, tok, calcImpl(1, tokens, i, varTable, strWidth));
  883. break;
  884. default:
  885. return r;
  886. }
  887. break;
  888. case 2:
  889. switch (tok.kind) {
  890. case Kind.MUL:
  891. i++;
  892. if (tokens.length <= i) { mixin(S_TRACE);
  893. throwError(_prop.msgs.scriptErrorInvalidCalc, tok);
  894. return r;
  895. }
  896. r.mul(_prop, tok, calcPar(tokens, i, varTable, strWidth));
  897. break;
  898. case Kind.DIV:
  899. i++;
  900. if (tokens.length <= i) { mixin(S_TRACE);
  901. throwError(_prop.msgs.scriptErrorInvalidCalc, tok);
  902. return r;
  903. }
  904. r.div(_prop, tok, calcPar(tokens, i, varTable, strWidth));
  905. break;
  906. case Kind.RES:
  907. i++;
  908. if (tokens.length <= i) { mixin(S_TRACE);
  909. throwError(_prop.msgs.scriptErrorInvalidCalc, tok);
  910. return r;
  911. }
  912. r.res(_prop, tok, calcPar(tokens, i, varTable, strWidth));
  913. break;
  914. default:
  915. return r;
  916. }
  917. break;
  918. default: assert (0);
  919. }
  920. }
  921. return r;
  922. }
  923. /// tokens???????????????????
  924. CalcResult calc(in Token[] tokens, ref size_t i, in const(Node)[][string] varTable, size_t strWidth) { mixin(S_TRACE);
  925. assert (i < tokens.length);
  926. return calcImpl(0, tokens, i, varTable, strWidth);
  927. } unittest { mixin(S_TRACE);
  928. debug mixin(UTPerf);
  929. size_t i;
  930. Token[] tokens;
  931. const(Node)[][string] varTable;
  932. auto tok = Token(0, 0, 0, Kind.NUMBER, "15");
  933. varTable["$abc"] = [Node(NodeType.VALUE, tok)];
  934. tok = Token(0, 0, 0, Kind.NUMBER, "0");
  935. varTable["$s"] = [Node(NodeType.VALUE, tok)];
  936. auto s = new CWXScript(new CProps("", null), null);
  937. i = 0;
  938. assert (s.calc(s.tokenize("10 * $abc"), i, varTable, 0) == 150);
  939. i = 0;
  940. assert (s.calc(s.tokenize("(-42)"), i, varTable, 0) == -42);
  941. i = 0;
  942. assert (s.calc(s.tokenize("2*2+3"), i, varTable, 0) == 7);
  943. i = 0;
  944. assert (s.calc(s.tokenize("2*(2+3)"), i, varTable, 0) == 10);
  945. i = 0;
  946. assert (s.calc(s.tokenize("1+2*3"), i, varTable, 0) == 7);
  947. i = 0;
  948. assert (s.calc(s.tokenize("(1+2)*3"), i, varTable, 0) == 9);
  949. i = 0;
  950. assert (s.calc(s.tokenize("-3-3"), i, varTable, 0) == -6);
  951. i = 0;
  952. assert (s.calc(s.tokenize("2 * 3 % 4"), i, varTable, 0) == 2);
  953. i = 0;
  954. assert (s.calc(s.tokenize("3 + -3-3"), i, varTable, 0) == -3);
  955. i = 0;
  956. assert (s.calc(s.tokenize("1+2 * 3 % 4"), i, varTable, 0) == 3);
  957. i = 0;
  958. assert (s.calc(s.tokenize("1+2 ~ 3 % 4"), i, varTable, 0) == "33");
  959. i = 0;
  960. tokens = s.tokenize("1 + $s");
  961. assert (s.calc(tokens, i, varTable, 0) == 1);
  962. i = 0;
  963. tokens = s.tokenize("$s + 1");
  964. assert (s.calc(tokens, i, varTable, 0) == 1);
  965. i = 0;
  966. tokens = s.tokenize("1+2 * 3 % 4 + -3-$abc $abc");
  967. assert (s.calc(tokens, i, varTable, 0) == -15);
  968. assert (tokens[i].value == "$abc");
  969. i = 0;
  970. tokens = s.tokenize("(1+2) * 3 % 4 + (-3-3) if");
  971. assert (s.calc(tokens, i, varTable, 0) == -5);
  972. assert (tokens[i].value == "if");
  973. }
  974. private static struct Keywords {
  975. immutable CType[string] keywords;
  976. immutable string[CType] commands;
  977. }
  978. private static shared immutable Keywords KEYS;
  979. shared static this () { mixin(S_TRACE);
  980. auto keywords = [
  981. cast(string) "start":CType.START,
  982. cast(string) "gobattle":CType.START_BATTLE,
  983. cast(string) "endsc":CType.END,
  984. cast(string) "gameover":CType.END_BAD_END,
  985. cast(string) "goarea":CType.CHANGE_AREA,
  986. cast(string) "chback":CType.CHANGE_BG_IMAGE,
  987. cast(string) "effect":CType.EFFECT,
  988. cast(string) "break":CType.EFFECT_BREAK,
  989. cast(string) "gostart":CType.LINK_START,
  990. cast(string) "gopack":CType.LINK_PACKAGE,
  991. cast(string) "msg":CType.TALK_MESSAGE,
  992. cast(string) "dialog":CType.TALK_DIALOG,
  993. cast(string) "bgm":CType.PLAY_BGM,
  994. cast(string) "se":CType.PLAY_SOUND,
  995. cast(string) "wait":CType.WAIT,
  996. cast(string) "elapse":CType.ELAPSE_TIME,
  997. cast(string) "callstart":CType.CALL_START,
  998. cast(string) "callpack":CType.CALL_PACKAGE,
  999. cast(string) "brflag":CType.BRANCH_FLAG,
  1000. cast(string) "brstepm":CType.BRANCH_MULTI_STEP,
  1001. cast(string) "brstept":CType.BRANCH_STEP,
  1002. cast(string) "selmember":CType.BRANCH_SELECT,
  1003. cast(string) "brability":CType.BRANCH_ABILITY,
  1004. cast(string) "brrandom":CType.BRANCH_RANDOM,
  1005. cast(string) "brlevel":CType.BRANCH_LEVEL,
  1006. cast(string) "brstatus":CType.BRANCH_STATUS,
  1007. cast(string) "brcount":CType.BRANCH_PARTY_NUMBER,
  1008. cast(string) "brarea":CType.BRANCH_AREA,
  1009. cast(string) "brbattle":CType.BRANCH_BATTLE,
  1010. cast(string) "bronbattle":CType.BRANCH_IS_BATTLE,
  1011. cast(string) "brcast":CType.BRANCH_CAST,
  1012. cast(string) "britem":CType.BRANCH_ITEM,
  1013. cast(string) "brskill":CType.BRANCH_SKILL,
  1014. cast(string) "brinfo":CType.BRANCH_INFO,
  1015. cast(string) "brbeast":CType.BRANCH_BEAST,
  1016. cast(string) "brmoney":CType.BRANCH_MONEY,
  1017. cast(string) "brcoupon":CType.BRANCH_COUPON,
  1018. cast(string) "brstamp":CType.BRANCH_COMPLETE_STAMP,
  1019. cast(string) "brgossip":CType.BRANCH_GOSSIP,
  1020. cast(string) "setflag":CType.SET_FLAG,
  1021. cast(string) "setstep":CType.SET_STEP,
  1022. cast(string) "stepup":CType.SET_STEP_UP,
  1023. cast(string) "stepdown":CType.SET_STEP_DOWN,
  1024. cast(string) "revflag":CType.REVERSE_FLAG,
  1025. cast(string) "chkflag":CType.CHECK_FLAG,
  1026. cast(string) "getcast":CType.GET_CAST,
  1027. cast(string) "getitem":CType.GET_ITEM,
  1028. cast(string) "getskill":CType.GET_SKILL,
  1029. cast(string) "getinfo":CType.GET_INFO,
  1030. cast(string) "getbeast":CType.GET_BEAST,
  1031. cast(string) "getmoney":CType.GET_MONEY,
  1032. cast(string) "getcoupon":CType.GET_COUPON,
  1033. cast(string) "getstamp":CType.GET_COMPLETE_STAMP,
  1034. cast(string) "getgossip":CType.GET_GOSSIP,
  1035. cast(string) "losecast":CType.LOSE_CAST,
  1036. cast(string) "loseitem":CType.LOSE_ITEM,
  1037. cast(string) "loseskill":CType.LOSE_SKILL,
  1038. cast(string) "loseinfo":CType.LOSE_INFO,
  1039. cast(string) "losebeast":CType.LOSE_BEAST,
  1040. cast(string) "losemoney":CType.LOSE_MONEY,
  1041. cast(string) "losecoupon":CType.LOSE_COUPON,
  1042. cast(string) "losestamp":CType.LOSE_COMPLETE_STAMP,
  1043. cast(string) "losegossip":CType.LOSE_GOSSIP,
  1044. cast(string) "showparty":CType.SHOW_PARTY,
  1045. cast(string) "hideparty":CType.HIDE_PARTY,
  1046. cast(string) "redraw":CType.REDISPLAY,
  1047. cast(string) "cpstep":CType.SUBSTITUTE_STEP,
  1048. cast(string) "cpflag":CType.SUBSTITUTE_FLAG,
  1049. cast(string) "cmpstep":CType.BRANCH_STEP_CMP,
  1050. cast(string) "cmpflag":CType.BRANCH_FLAG_CMP,
  1051. cast(string) "selrandom":CType.BRANCH_RANDOM_SELECT,
  1052. cast(string) "brkeycode":CType.BRANCH_KEY_CODE,
  1053. cast(string) "chkstep":CType.CHECK_STEP,
  1054. cast(string) "brround":CType.BRANCH_ROUND,
  1055. cast(string) "mvback":CType.MOVE_BG_IMAGE,
  1056. cast(string) "rplback":CType.REPLACE_BG_IMAGE,
  1057. cast(string) "loseback":CType.LOSE_BG_IMAGE,
  1058. ];
  1059. string[CType] commands;
  1060. foreach (name, type; keywords) { mixin(S_TRACE);
  1061. commands[type] = name;
  1062. }
  1063. KEYS = Keywords(.assumeUnique(keywords), .assumeUnique(commands));
  1064. }
  1065. /// ??????
  1066. enum NodeType {
  1067. VAR_SET, /// ??????
  1068. START, /// ???????????
  1069. COMMAND, /// ??????????
  1070. VALUES, /// VALUE????
  1071. VALUE, /// ?????
  1072. }
  1073. /// ??????????????????????
  1074. struct Node {
  1075. NodeType type; /// ??
  1076. Token token; /// ???Token?
  1077. const(Node)[] texts = []; /// ?????
  1078. const(Node)[] attr; /// ???
  1079. const(Node)[] childs; /// ?????
  1080. bool nextIsChild; /// ??????????????
  1081. alias childs values; /// ????VALUES?????????VALUE???????
  1082. const(Token)[] calc; /// ????
  1083. alias calc var; /// ???
  1084. alias texts value; /// ????
  1085. const(Node)[] beforeVars; /// ????????????????
  1086. /// o??????
  1087. const
  1088. bool opEquals(ref const(Node) o) { mixin(S_TRACE);
  1089. return type == o.type && token == o.token && texts == o.texts
  1090. && attr == o.attr && childs == o.childs && calc == o.calc
  1091. && beforeVars == o.beforeVars;
  1092. }
  1093. /// ?????????
  1094. @property
  1095. const
  1096. Node dup() { mixin(S_TRACE);
  1097. return Node(type, token, texts.dup, attr.dup, childs.dup, nextIsChild, calc.dup, beforeVars.dup);
  1098. }
  1099. /// ??????
  1100. const
  1101. string toString() {return token.toString();}
  1102. /// ???????????????????
  1103. static string code(string indent, in Node[] array) {return code(" ", "", "", array, "sif");}
  1104. private static string code(string indent, string bIndentValue, string indentValue, in Node[] array, string ifString) { mixin(S_TRACE);
  1105. string calcCode(in Node[] calc) { mixin(S_TRACE);
  1106. char[] calcBuf;
  1107. enforce(1 == calc.length);
  1108. foreach (i, tok; calc[0].calc) { mixin(S_TRACE);
  1109. if ((tok.kind is Kind.C_PAR)
  1110. || (i > 0 && calc[0].calc[i - 1].kind is Kind.O_PAR)) { mixin(S_TRACE);
  1111. calcBuf ~= tok.value;
  1112. } else { mixin(S_TRACE);
  1113. if (i > 0) calcBuf ~= " ";
  1114. calcBuf ~= tok.value;
  1115. }
  1116. }
  1117. return assumeUnique(calcBuf);
  1118. }
  1119. string buf = "";
  1120. if (!array.length) return buf;
  1121. foreach (node; array) { mixin(S_TRACE);
  1122. if (buf.length) { mixin(S_TRACE);
  1123. buf ~= "\n";
  1124. }
  1125. if (node.type is NodeType.COMMAND && node.texts.length) { mixin(S_TRACE);
  1126. buf ~= .format("%s%s %s\n", ifString == "sif" ? indentValue : bIndentValue, ifString, calcCode(node.texts));
  1127. ifString = "sif";
  1128. }
  1129. if (node.type is NodeType.VAR_SET) { mixin(S_TRACE);
  1130. enforce(node.value.length > 0, new Exception("Invalid node", __FILE__, __LINE__));
  1131. if (node.value[0].token.kind is Kind.STRING) { mixin(S_TRACE);
  1132. buf ~= .format("%s%s = %s", indentValue, node.token.value, node.value[0].token.value);
  1133. } else { mixin(S_TRACE);
  1134. buf ~= .format("%s%s = %s", node.token.value, indentValue, calcCode(node.value));
  1135. }
  1136. continue;
  1137. } else if (node.type is NodeType.VALUE) { mixin(S_TRACE);
  1138. if (node.var.length <= 1) { mixin(S_TRACE);
  1139. buf ~= node.token.value;
  1140. } else { mixin(S_TRACE);
  1141. buf ~= calcCode([node]);
  1142. }
  1143. continue;
  1144. } else if (node.type is NodeType.VALUES) { mixin(S_TRACE);
  1145. string vals = "[";
  1146. foreach (i, c; node.values) { mixin(S_TRACE);
  1147. if (i > 0) vals ~= ", ";
  1148. vals ~= Node.code(indent, [c]);
  1149. }
  1150. vals ~= "]";
  1151. buf ~= vals;
  1152. continue;
  1153. }
  1154. foreach (i, var; node.beforeVars) { mixin(S_TRACE);
  1155. buf ~= .format("%s%s\n", indentValue, Node.code(indent, [var]));
  1156. }
  1157. string attrs = "";
  1158. if (node.token.kind is Kind.START) { mixin(S_TRACE);
  1159. attrs = " " ~ calcCode(node.texts);
  1160. } else { mixin(S_TRACE);
  1161. foreach (i, a; node.attr) { mixin(S_TRACE);
  1162. auto ac = Node.code(indent, [a]);
  1163. if (a.type is NodeType.VALUES && i > 0) { mixin(S_TRACE);
  1164. attrs ~= "\n";
  1165. attrs ~= indentValue;
  1166. attrs ~= .rightJustify("", node.token.value.length + 1);
  1167. attrs ~= ac;
  1168. } else { mixin(S_TRACE);
  1169. attrs ~= i == 0 ? " " : ", ";
  1170. attrs ~= ac;
  1171. }
  1172. }
  1173. }
  1174. buf ~= .format("%s%s%s", indentValue, node.token.value, attrs);
  1175. if (node.nextIsChild) continue;
  1176. bool startBlock = true;
  1177. bool nextIsStartPoint = true;
  1178. const(Node)[][] block;
  1179. foreach (i, c; node.childs) { mixin(S_TRACE);
  1180. if (startBlock && nextIsStartPoint) { mixin(S_TRACE);
  1181. block ~= new const(Node)[0];
  1182. }
  1183. startBlock = false;
  1184. if (c.type !is NodeType.VAR_SET) { mixin(S_TRACE);
  1185. nextIsStartPoint = !c.nextIsChild;
  1186. startBlock = true;
  1187. }
  1188. block[$ - 1] ~= c;
  1189. }
  1190. if (block.length > 1) { mixin(S_TRACE);
  1191. foreach (i, b; block) { mixin(S_TRACE);
  1192. string f = i == 0 ? "if" : "elif";
  1193. buf ~= "\n";
  1194. buf ~= Node.code(indent, indentValue, indentValue ~ indent, b, f);
  1195. }
  1196. buf ~= "\n";
  1197. buf ~= indentValue ~ "fi";
  1198. } else if (block.length == 1) { mixin(S_TRACE);
  1199. buf ~= "\n";
  1200. buf ~= Node.code(indent, indentValue, node.token.kind is Kind.START ? indentValue ~ indent : indentValue, block[0], "sif");
  1201. }
  1202. }
  1203. return buf;
  1204. }
  1205. }
  1206. /// ?????????????
  1207. private string attrValue(in Node node, in const(Node)[][string] varTable, size_t strWidth) { mixin(S_TRACE);
  1208. switch (node.token.kind) {
  1209. case Kind.SYMBOL: return std.string.toLower(node.token.value);
  1210. case Kind.VAR_NAME:
  1211. if (1 < node.calc.length) goto case Kind.STRING;
  1212. auto nodes = var(node, varTable);
  1213. if (!nodes.length) return "";
  1214. auto tok = nodes[0].token;
  1215. if (tok.kind is Kind.STRING) { mixin(S_TRACE);
  1216. return stringValue(tok, strWidth);
  1217. } else if (tok.kind is Kind.SYMBOL) { mixin(S_TRACE);
  1218. return std.string.toLower(tok.value);
  1219. }
  1220. return attrValue(nodes[0], varTable, strWidth);
  1221. case Kind.STRING, Kind.NUMBER, Kind.PLU, Kind.MIN, Kind.O_PAR:
  1222. size_t i = 0;
  1223. auto r = calc(node.calc, i, varTable, strWidth);
  1224. final switch (r.kind) {
  1225. case CRKind.STR: return r.str;
  1226. case CRKind.INT: return to!(string)(r.numInt);
  1227. case CRKind.REAL: return to!(string)(r.numReal);
  1228. }
  1229. case Kind.O_BRA: return "";
  1230. case Kind.COMMA: return "";
  1231. default:
  1232. throwError(_prop.msgs.scriptErrorInvalidAttr, node.token);
  1233. return "";
  1234. }
  1235. assert (0);
  1236. }
  1237. /// ???????
  1238. private const(Node)[] varValue(in Node node, const(Node)[] values, in const(Node)[][string] varTable, size_t strWidth) { mixin(S_TRACE);
  1239. if (!values.length) { mixin(S_TRACE);
  1240. throwError(_prop.msgs.scriptErrorInvalidVar, node.token);
  1241. return values;
  1242. }
  1243. const(Node)[] r;
  1244. foreach (v; values) { mixin(S_TRACE);
  1245. const(Node)[] vs;
  1246. if (v.token.kind is Kind.VAR_NAME) { mixin(S_TRACE);
  1247. vs ~= var(v, varTable);
  1248. } else { mixin(S_TRACE);
  1249. vs = [v];
  1250. }
  1251. foreach (v2; vs) { mixin(S_TRACE);
  1252. switch (v2.token.kind) {
  1253. case Kind.STRING, Kind.NUMBER, Kind.PLU, Kind.MIN, Kind.O_PAR:
  1254. // ??????????
  1255. size_t i = 0;
  1256. Node rNode = v2.dup;
  1257. auto cr = calc(v2.calc, i, varTable, strWidth);
  1258. rNode.token.kind = cr.kind is CRKind.STR ? Kind.STRING : Kind.NUMBER;
  1259. final switch (cr.kind) {
  1260. case CRKind.STR:
  1261. rNode.token.value = createString(cr.str);
  1262. break;
  1263. case CRKind.INT:
  1264. rNode.token.value = to!(string)(cr.numInt);
  1265. break;
  1266. case CRKind.REAL:
  1267. rNode.token.value = to!(string)(cr.numReal);
  1268. break;
  1269. }
  1270. r ~= rNode;
  1271. break;
  1272. default:
  1273. r ~= v2;
  1274. break;
  1275. }
  1276. }
  1277. }
  1278. return r;
  1279. }
  1280. private const(Node)[] var(in Node[] nodes, in const(Node)[][string] varTable) { mixin(S_TRACE);
  1281. const(Node)[] r;
  1282. foreach (node; nodes) { mixin(S_TRACE);
  1283. r ~= var(node, varTable);
  1284. }
  1285. return r;
  1286. }
  1287. private const(Node)[] var(in Node node, in const(Node)[][string] varTable) { mixin(S_TRACE);
  1288. if (node.token.kind is Kind.VAR_NAME) { mixin(S_TRACE);
  1289. auto ptr = std.string.toLower(node.token.value) in varTable;
  1290. if (ptr) { mixin(S_TRACE);
  1291. const(Node)[] r;
  1292. foreach (v; *ptr) { mixin(S_TRACE);
  1293. r ~= var(v, varTable);
  1294. }
  1295. return r;
  1296. }
  1297. throwError(_prop.msgs.scriptErrorUndefinedVar, node.token);
  1298. return [];
  1299. }
  1300. return [node];
  1301. }
  1302. private Token var(in Token tok, in const(Node)[][string] varTable) { mixin(S_TRACE);
  1303. if (tok.kind is Kind.VAR_NAME) { mixin(S_TRACE);
  1304. auto ptr = std.string.toLower(tok.value) in varTable;
  1305. if (ptr && ptr.length) { mixin(S_TRACE);
  1306. return var((*ptr)[0].token, varTable);
  1307. }
  1308. throwError(_prop.msgs.scriptErrorUndefinedVar, tok);
  1309. }
  1310. return tok;
  1311. }
  1312. /// tokens?????Node???????????
  1313. Node[] analyzeSyntax(in Token[] tokens) { mixin(S_TRACE);
  1314. Node[] r;
  1315. size_t i = 0;
  1316. Node[] vars;
  1317. const(Token)[] tokens2;
  1318. foreach (ref tok; tokens) { mixin(S_TRACE);
  1319. if (tok.kind !is Kind.COMMENT) { mixin(S_TRACE);
  1320. tokens2 ~= tok;
  1321. }
  1322. }
  1323. while (i < tokens2.length) { mixin(S_TRACE);
  1324. vars ~= eatVarSet(tokens2, i, KEYS);
  1325. if (tokens2.length <= i) { mixin(S_TRACE);
  1326. break;
  1327. }
  1328. Token tok = tokens2[i];
  1329. if (tok.kind !is Kind.START) { mixin(S_TRACE);
  1330. r ~= analyzeSyntaxBranch(tokens2, i, KEYS, vars);
  1331. continue;
  1332. }
  1333. i++;
  1334. Node node;
  1335. node.type = NodeType.START;
  1336. node.token = tok;
  1337. node.texts = analyzeSyntaxAttr(tokens2, i, KEYS);
  1338. if (!node.texts.length) { mixin(S_TRACE);
  1339. throwError(_prop.msgs.scriptErrorNoStartText, tok);
  1340. }
  1341. node.beforeVars = vars;
  1342. vars = [];
  1343. node.nextIsChild = false;
  1344. c: while (i < tokens2.length) { mixin(S_TRACE);
  1345. vars ~= eatVarSet(tokens2, i, KEYS);
  1346. string cText = "";
  1347. switch (tokens2[i].kind) {
  1348. case Kind.START, Kind.FI: break c;
  1349. case Kind.IF, Kind.ELIF, Kind.SYMBOL, Kind.SIF:
  1350. node.childs ~= analyzeSyntaxBranch(tokens2, i, KEYS, vars);
  1351. continue;
  1352. default:
  1353. throwError(_prop.msgs.scriptErrorInvalidStatement, tokens2[i]);
  1354. i++;
  1355. }
  1356. }
  1357. r ~= node;
  1358. }
  1359. return r;
  1360. } unittest { mixin(S_TRACE);
  1361. debug mixin(UTPerf);
  1362. auto s = new CWXScript(new CProps("", null), null);
  1363. string statement
  1364. = `
  1365. $var1 = 'oops'
  1366. Start "First start"
  1367. if 'abc'
  1368. chback ['mapofwirth.bmp', '', 0, 0, 632, 420]
  1369. ['definn.bmp', '', 50, 50, 200*2, 260]
  1370. ['card.bmp', 'card\mate1', 230, 50, 74, 94, mask],
  1371. 1
  1372. hideparty
  1373. $var2 = 3.5
  1374. wait ($var2 + 1.5)
  1375. msg M
  1376. @ 3
  1377. Talk!
  1378. Talk!
  1379. Talk!
  1380. @
  1381. sif "Single IF"
  1382. brflag 'card\mate1'
  1383. if true
  1384. $var3 = 'what?'
  1385. showparty
  1386. endsc true
  1387. $var_dummy = nothing
  1388. elif false
  1389. gameover // comment1
  1390. fi
  1391. elif 'def' effect 1, 2, 3 + 2
  1392. fi
  1393. // comment2
  1394. $var4 = true
  1395. start "second start"
  1396. showparty
  1397. getskill 1`;
  1398. auto tokens = s.tokenize(statement);
  1399. auto starts = s.analyzeSyntax(tokens);
  1400. assert (Node.code(" ", starts)
  1401. == "$var1 = 'oops'\n"
  1402. ~ "Start \"First start\"\n"
  1403. ~ "if 'abc'\n"
  1404. ~ " chback ['mapofwirth.bmp', '', 0, 0, 632, 420]\n"
  1405. ~ " ['definn.bmp', '', 50, 50, 200 * 2, 260]\n"
  1406. ~ " ['card.bmp', 'card\\mate1', 230, 50, 74, 94, mask], 1\n"
  1407. ~ " hideparty\n"
  1408. ~ " $var2 = 3.5\n"
  1409. ~ " wait ($var2 + 1.5)\n"
  1410. ~ " msg M, @ 3\n"
  1411. ~ " Talk!\n"
  1412. ~ " Talk!\n"
  1413. ~ " Talk!\n"
  1414. ~ " @\n"
  1415. ~ " sif \"Single IF\"\n"
  1416. ~ " brflag 'card\\mate1'\n"
  1417. ~ " if true\n"
  1418. ~ " $var3 = 'what?'\n"
  1419. ~ " showparty\n"
  1420. ~ " endsc true\n"
  1421. ~ " elif false\n"
  1422. ~ " $var_dummy = nothing\n" /* ???????????????? */
  1423. ~ " gameover\n"
  1424. ~ " fi\n"
  1425. ~ "elif 'def'\n"
  1426. ~ " effect 1, 2, 3 + 2\n"
  1427. ~ "fi\n"
  1428. ~ "$var4 = true\n"
  1429. ~ "start \"second start\"\n"
  1430. ~ " showparty\n"
  1431. ~ " getskill 1", Node.code(" ", starts));
  1432. string statement2
  1433. = `
  1434. brflag 'card\mate1'
  1435. if true
  1436. $var3 = 'what?'
  1437. showparty
  1438. endsc true
  1439. $var_dummy = nothing
  1440. elif false
  1441. gameover // comment1
  1442. fi`;
  1443. auto tokens2 = s.tokenize(statement2);
  1444. auto contents = s.analyzeSyntax(tokens2);
  1445. assert (Node.code(" ", contents)
  1446. == "brflag 'card\\mate1'\n"
  1447. ~ "if true\n"
  1448. ~ " $var3 = 'what?'\n"
  1449. ~ " showparty\n"
  1450. ~ " endsc true\n"
  1451. ~ "elif false\n"
  1452. ~ " $var_dummy = nothing\n"
  1453. ~ " gameover\n"
  1454. ~ "fi");
  1455. // ????????????????
  1456. s.analyzeSyntax(s.tokenize("dialog M, @c\n...\n@]"));
  1457. string statement3 = `chback [] goarea 1`;
  1458. auto tokens3 = s.tokenize(statement3);
  1459. auto contents2 = s.analyzeSyntax(tokens3);
  1460. assert (contents2.length == 2);
  1461. assert (contents2[0].nextIsChild);
  1462. assert (!contents2[1].nextIsChild);
  1463. }
  1464. private Node[] analyzeSyntaxBranch(in Token[] tokens, ref size_t i, in Keywords keys, ref Node[] vars) { mixin(S_TRACE);
  1465. Node[] r;
  1466. auto tok = tokens[i];
  1467. switch (tok.kind) {
  1468. case Kind.START: return r;
  1469. case Kind.IF, Kind.VAR_NAME:
  1470. while (i < tokens.length) { mixin(S_TRACE);
  1471. Node[] texts;
  1472. if (tokens[i].kind is Kind.IF || tokens[i].kind is Kind.ELIF) { mixin(S_TRACE);
  1473. i++;
  1474. texts = analyzeSyntaxAttr(tokens, i, keys);
  1475. if (!texts.length) { mixin(S_TRACE);
  1476. throwError(_prop.msgs.scriptErrorNoIfText, tok);
  1477. }
  1478. if (tokens.length <= i) { mixin(S_TRACE);
  1479. throwError(_prop.msgs.scriptErrorNoIfContents, tok);
  1480. }
  1481. }
  1482. auto node = analyzeSyntaxStatement(tokens, i, keys, vars);
  1483. if (node.length) { mixin(S_TRACE);
  1484. node[0].texts = texts;
  1485. }
  1486. r ~= node;
  1487. if (tokens.length <= i) return r;
  1488. switch (tokens[i].kind) {
  1489. case Kind.START: return r;
  1490. case Kind.FI:
  1491. i++;
  1492. return r;
  1493. case Kind.ELIF: continue;
  1494. case Kind.VAR_NAME: continue;
  1495. default:
  1496. throwError(_prop.msgs.scriptErrorInvalidStatement, tokens[i]);
  1497. i++;
  1498. }
  1499. }
  1500. break;
  1501. case Kind.SYMBOL, Kind.SIF:
  1502. r ~= analyzeSyntaxStatement(tokens, i, keys, vars);
  1503. break;
  1504. default:
  1505. throwError(_prop.msgs.scriptErrorInvalidBranch, tok);
  1506. i++;
  1507. break;
  1508. }
  1509. return r;
  1510. }
  1511. private Node[] eatVarSet(in Token[] tokens, ref size_t i, in Keywords keys) { mixin(S_TRACE);
  1512. Node[] r;
  1513. while (i < tokens.length && tokens[i].kind is Kind.VAR_NAME) { mixin(S_TRACE);
  1514. r ~= analyzeSyntaxVar(tokens, i, keys);
  1515. }
  1516. return r;
  1517. }
  1518. private Node[] analyzeSyntaxStatement(in Token[] tokens, ref size_t i, in Keywords keys, ref Node[] vars) { mixin(S_TRACE);
  1519. Node[] r;
  1520. while (true) { mixin(S_TRACE);
  1521. assert (i < tokens.length);
  1522. vars ~= eatVarSet(tokens, i, keys);
  1523. Token tok = tokens[i];
  1524. Node[] sifTexts;
  1525. if (tok.kind is Kind.SIF) { mixin(S_TRACE);
  1526. i++;
  1527. if (tokens.length <= i) { mixin(S_TRACE);
  1528. throwError(_prop.msgs.scriptErrorNoSifText, tok);
  1529. return r;
  1530. }
  1531. sifTexts = analyzeSyntaxAttr(tokens, i, keys);
  1532. if (tokens.length <= i) { mixin(S_TRACE);
  1533. throwError(_prop.msgs.scriptErrorInvalidSif, tok);
  1534. }
  1535. tok = tokens[i];
  1536. } else if (tok.kind !is Kind.SYMBOL) { mixin(S_TRACE);
  1537. throwError(_prop.msgs.scriptErrorInvalidStatement, tok);
  1538. }
  1539. Node node;
  1540. node.type = NodeType.COMMAND;
  1541. node.token = tok;
  1542. node.texts = sifTexts;
  1543. node.nextIsChild = false;
  1544. auto symbol = std.string.toLower(tok.value);
  1545. if (!(symbol in keys.keywords)) { mixin(S_TRACE);
  1546. throwError(_prop.msgs.scriptErrorInvalidKeyword, tok);
  1547. }
  1548. node.beforeVars = vars;
  1549. vars = [];
  1550. i++;
  1551. if (tokens.length <= i) return r ~ node;
  1552. node.attr = analyzeSyntaxAttr(tokens, i, keys);
  1553. vars ~= eatVarSet(tokens, i, keys);
  1554. if (tokens.length <= i) return r ~ node;
  1555. switch (tokens[i].kind) {
  1556. case Kind.START, Kind.ELIF, Kind.FI:
  1557. return r ~ node;
  1558. case Kind.IF:
  1559. node.childs ~= analyzeSyntaxBranch(tokens, i, keys, vars);
  1560. return r ~ node;
  1561. case Kind.SIF, Kind.SYMBOL:
  1562. node.nextIsChild = true;
  1563. r ~= node;
  1564. continue;
  1565. default:
  1566. throwError(_prop.msgs.scriptErrorInvalidStatement, tok);
  1567. return r ~ node;
  1568. }
  1569. }
  1570. return r;
  1571. }
  1572. private Node[] analyzeSyntaxAttr(in Token[] tokens, ref size_t i, in Keywords keys) { mixin(S_TRACE);
  1573. Node[] r;
  1574. while (i < tokens.length) { mixin(S_TRACE);
  1575. Token tok = tokens[i];
  1576. if (r.length > 0 && tok.kind is Kind.COMMA) { mixin(S_TRACE);
  1577. i++;
  1578. tok = tokens[i];
  1579. }
  1580. switch (tok.kind) {
  1581. case Kind.O_BRA:
  1582. r ~= analyzeSyntaxBrackets(tokens, i, keys);
  1583. i++;
  1584. break;
  1585. case Kind.START, Kind.IF, Kind.ELIF, Kind.FI, Kind.SIF:
  1586. return r;
  1587. case Kind.SYMBOL, Kind.NUMBER, Kind.STRING, Kind.PLU, Kind.MIN, Kind.O_PAR:
  1588. if (std.string.toLower(tok.value) in keys.keywords) return r;
  1589. Node node;
  1590. node.type = NodeType.VALUE;
  1591. node.token = tok;
  1592. node.var = analyzeSyntaxValue(tokens, i, keys);
  1593. r ~= node;
  1594. break;
  1595. case Kind.VAR_NAME:
  1596. if (i + 1 < tokens.length && tokens[i + 1].kind is Kind.EQ) { mixin(S_TRACE);
  1597. return r;
  1598. }
  1599. goto case Kind.SYMBOL;
  1600. case Kind.COMMA:
  1601. // ????','?????????0??????
  1602. Node node;
  1603. node.type = NodeType.VALUES;
  1604. node.token = tok;
  1605. node.var = [];
  1606. r ~= node;
  1607. break;
  1608. default:
  1609. throwError(_prop.msgs.scriptErrorInvalidAttr, tok);
  1610. i++;
  1611. }
  1612. }
  1613. return r;
  1614. } unittest { mixin(S_TRACE);
  1615. debug mixin(UTPerf);
  1616. auto s = new CWXScript(new CProps("", null), null);
  1617. Token[] tokens;
  1618. size_t i;
  1619. tokens = s.tokenize(`goarea`);
  1620. i = 0;
  1621. assert (s.analyzeSyntaxAttr(tokens, i, KEYS).length == 0);
  1622. tokens = s.tokenize(`area, 1, "="if "next"`);
  1623. i = 0;
  1624. auto arr = [
  1625. Node(NodeType.VALUE, Token(0, 0, 0, Kind.SYMBOL, "area")),
  1626. Node(NodeType.VALUE, Token(0, 6, 6, Kind.NUMBER, "1")),
  1627. Node(NodeType.VALUE, Token(0, 9, 9, Kind.STRING, `"="`))
  1628. ];
  1629. arr[0].var ~= arr[0].token;
  1630. arr[1].var ~= arr[1].token;
  1631. arr[2].var ~= arr[2].token;
  1632. assert (s.analyzeSyntaxAttr(tokens, i, KEYS) == arr);
  1633. assert (tokens[i].kind is Kind.IF);
  1634. tokens = s.tokenize(`area, 1, "="Start`);
  1635. i = 0;
  1636. assert (s.analyzeSyntaxAttr(tokens, i, KEYS) == arr);
  1637. assert (tokens[i].value == "Start");
  1638. }
  1639. private Node analyzeSyntaxBrackets(in Token[] tokens, ref size_t i, in Keywords keys) { mixin(S_TRACE);
  1640. assert (i < tokens.length);
  1641. auto o = tokens[i];
  1642. if (o.kind !is Kind.O_BRA) { mixin(S_TRACE);
  1643. throwError(_prop.msgs.scriptErrorInvalidValuesOpen, o);
  1644. }
  1645. Node r;
  1646. r.type = NodeType.VALUES;
  1647. r.token = o;
  1648. i++;
  1649. while (i < tokens.length) { mixin(S_TRACE);
  1650. Token tok = tokens[i];
  1651. if (tok.kind is Kind.C_BRA) { mixin(S_TRACE);
  1652. return r;
  1653. }
  1654. if (r.values.length > 0) { mixin(S_TRACE);
  1655. if (tok.kind is Kind.COMMA) { mixin(S_TRACE);
  1656. i++;
  1657. }
  1658. tok = tokens[i];
  1659. }
  1660. switch (tok.kind) {
  1661. case Kind.O_BRA:
  1662. r.values ~= analyzeSyntaxBrackets(tokens, i, keys);
  1663. i++;
  1664. break;
  1665. case Kind.SYMBOL, Kind.NUMBER, Kind.STRING, Kind.VAR_NAME, Kind.O_PAR, Kind.PLU, Kind.MIN:
  1666. auto node = Node(NodeType.VALUE, tok);
  1667. node.var = analyzeSyntaxValue(tokens, i, keys);
  1668. r.values ~= node;
  1669. break;
  1670. default:
  1671. throwError(_prop.msgs.scriptErrorInvalidValuesClose, tok);
  1672. i++;
  1673. }
  1674. }
  1675. throwError(_prop.msgs.scriptErrorCloseBracketNotFound, o);
  1676. return r;
  1677. }
  1678. private Node analyzeSyntaxVar(in Token[] tokens, ref size_t i, in Keywords keys) { mixin(S_TRACE);
  1679. assert (i < tokens.length);
  1680. auto tok = tokens[i];
  1681. if (tok.kind !is Kind.VAR_NAME) { mixin(S_TRACE);
  1682. throwError(_prop.msgs.scriptErrorInvalidVar, tok);
  1683. }
  1684. i++;
  1685. if (tokens.length <= i || tokens[i].kind !is Kind.EQ) { mixin(S_TRACE);
  1686. throwError(_prop.msgs.scriptErrorNoVarSet, tokens[i]);
  1687. }
  1688. Node node;
  1689. node.type = NodeType.VAR_SET;
  1690. node.token = tok;
  1691. i++;
  1692. if (tokens.length <= i) { mixin(S_TRACE);
  1693. throwError(_prop.msgs.scriptErrorNoVarVal, tok);
  1694. }
  1695. node.value = analyzeSyntaxAttr(tokens, i, keys);
  1696. return node;
  1697. }
  1698. private Token[] analyzeSyntaxValue(in Token[] tokens, ref size_t i, in Keywords keys) { mixin(S_TRACE);
  1699. Token[] r;
  1700. bool calcin = false;
  1701. bool num = true;
  1702. while (i < tokens.length) { mixin(S_TRACE);
  1703. auto tok = tokens[i];
  1704. switch (tok.kind) {
  1705. case Kind.VAR_NAME:
  1706. if (!num) return r;
  1707. goto case Kind.NUMBER;
  1708. case Kind.NUMBER, Kind.STRING:
  1709. if (!num) return r;
  1710. r ~= tok;
  1711. i++;
  1712. calcin = true;
  1713. num = false;
  1714. break;
  1715. case Kind.PLU, Kind.MIN:
  1716. if (num) { mixin(S_TRACE);
  1717. r ~= tok;
  1718. i++;
  1719. r ~= tokens[i];
  1720. i++;
  1721. calcin = true;
  1722. num = false;
  1723. break;
  1724. } else { mixin(S_TRACE);
  1725. goto case Kind.MUL;
  1726. }
  1727. case Kind.O_PAR:
  1728. if (!num && calcin) return r;
  1729. r ~= tok;
  1730. i++;
  1731. calcin = true;
  1732. break;
  1733. case Kind.C_PAR:
  1734. if (num) return r;
  1735. r ~= tok;
  1736. i++;
  1737. break;
  1738. case Kind.MUL, Kind.DIV, Kind.RES, Kind.CAT:
  1739. if (num) return r;
  1740. r ~= tok;
  1741. num = true;
  1742. i++;
  1743. break;
  1744. case Kind.COMMA:
  1745. if (!r.length) throwError(_prop.msgs.scriptErrorInvalidValue, tok);
  1746. return r;
  1747. case Kind.SYMBOL:
  1748. if (!calcin) { mixin(S_TRACE);
  1749. r ~= tok;
  1750. i++;
  1751. }
  1752. return r;
  1753. case Kind.O_BRA, Kind.C_BRA:
  1754. case Kind.START, Kind.IF, Kind.FI, Kind.ELIF, Kind.SIF, Kind.EQ:
  1755. return r;
  1756. default:
  1757. throwError(_prop.msgs.scriptErrorInvalidCalc, tok);
  1758. i++;
  1759. }
  1760. }
  1761. return r;
  1762. }
  1763. private T parseAttr(T, bool Within = false)(in CompileOption opt, in Node[] attr, ref size_t i, lazy T defValue, in const(Node)[][string] varTable, size_t msgWidth) { mixin(S_TRACE);
  1764. if (attr.length <= i) return defValue;
  1765. static if (is(T == string)) {
  1766. auto value = attrValue(attr[i], varTable, msgWidth);
  1767. if (attr[i].token.kind is Kind.SYMBOL && value == "stop") { mixin(S_TRACE);
  1768. /// BGM???
  1769. i++;
  1770. return "";
  1771. } else if (attr[i].token.kind is Kind.SYMBOL && value == "random") { mixin(S_TRACE);
  1772. /// ????????????????
  1773. i++;
  1774. return _prop.sys.randomValue;
  1775. }
  1776. i++;
  1777. return value;
  1778. } else static if (is(T == CastRange[])) {
  1779. T r;
  1780. crw: while (i < attr.length) { mixin(S_TRACE);
  1781. auto value = attrValue(attr[i], varTable, msgWidth);
  1782. switch (value) {
  1783. case "field": r ~= [CastRange.PARTY, CastRange.ENEMY, CastRange.NPC]; break;
  1784. case "party", "t", "team": r ~= CastRange.PARTY; break;
  1785. case "enemy": r ~= CastRange.ENEMY; break;
  1786. case "npc": r ~= CastRange.NPC; break;
  1787. default: break crw;
  1788. }
  1789. i++;
  1790. }
  1791. r = r.sort().uniq().array();
  1792. return r;
  1793. } else static if (isVArray!(T)) {
  1794. T r;
  1795. while (i < attr.length) { mixin(S_TRACE);
  1796. auto values = var(attr[i], varTable);
  1797. size_t i2 = 0;
  1798. if (values.length <= i2 || values[0].type !is NodeType.VALUES) break;
  1799. while (i2 < values.length) { mixin(S_TRACE);
  1800. if (!values.length) break;
  1801. if (values[0].token.kind is Kind.COMMA) { mixin(S_TRACE);
  1802. // ????
  1803. break;
  1804. }
  1805. r ~= parseAttr!(ElementType!(T), Within)(opt, values, i2, ElementType!(T).init, varTable, msgWidth);
  1806. if (0 == i2) break;
  1807. }
  1808. i++;
  1809. }
  1810. return r;
  1811. } else static if (is(T == bool)) {
  1812. auto value = attrValue(attr[i], varTable, msgWidth);
  1813. switch (value) {
  1814. case "true", "yes", "on", "all", "random", "average", "complete":
  1815. i++;
  1816. return true;
  1817. case "false", "no", "off", "active", "manual", "max", "nocomplete":
  1818. i++;
  1819. return false;
  1820. default: throwError(_prop.msgs.scriptErrorInvalidBoolVal, attr[i].token);
  1821. }
  1822. } else static if (is(T == Transition)) {
  1823. auto value = attrValue(attr[i], varTable, msgWidth);
  1824. switch (value) {
  1825. case "default": i++; return Transition.DEFAULT;
  1826. case "none": i++; return Transition.NONE;
  1827. case "fade": i++; return Transition.FADE;
  1828. case "dissolve": i++; return Transition.PIXEL_DISSOLVE;
  1829. case "blinds": i++; return Transition.BLINDS;
  1830. default: throwError(_prop.msgs.scriptErrorInvalidTransition, attr[i].token);
  1831. }
  1832. } else static if (is(T == Range)) {
  1833. auto value = attrValue(attr[i], varTable, msgWidth);
  1834. switch (value) {
  1835. case "m", "selected": i++; return Range.SELECTED;
  1836. case "r", "random", "one": i++; return Range.RANDOM;
  1837. case "t", "team": i++; return Range.PARTY;
  1838. case "backpack":
  1839. static if (Within) goto default;
  1840. i++;
  1841. return Range.BACKPACK;
  1842. case "party":
  1843. static if (Within) goto case "team";
  1844. i++;
  1845. return Range.PARTY_AND_BACKPACK;
  1846. case "field":
  1847. static if (Within) goto default;
  1848. i++;
  1849. return Range.FIELD;
  1850. default: throwError(_prop.msgs.scriptErrorInvalidRange, attr[i].token);
  1851. }
  1852. } else static if (is(T == Status)) {
  1853. auto value = attrValue(attr[i], varTable, msgWidth);
  1854. switch (value) {
  1855. case "active": i++; return Status.ACTIVE;
  1856. case "inactive": i++; return Status.INACTIVE;
  1857. case "alive": i++; return Status.ALIVE;
  1858. case "dead": i++; return Status.DEAD;
  1859. case "fine": i++; return Status.FINE;
  1860. case "injured": i++; return Status.INJURED;
  1861. case "heavyinjured": i++; return Status.HEAVY_INJURED;
  1862. case "unconscious": i++; return Status.UNCONSCIOUS;
  1863. case "poison": i++; return Status.POISON;
  1864. case "sleep": i++; return Status.SLEEP;
  1865. case "bind": i++; return Status.BIND;
  1866. case "paralyze": i++; return Status.PARALYZE;
  1867. case "confuse": i++; return Status.CONFUSE;
  1868. case "overheat": i++; return Status.OVERHEAT;
  1869. case "brave": i++; return Status.BRAVE;
  1870. case "panic": i++; return Status.PANIC;
  1871. case "silence": i++; return Status.SILENCE;
  1872. case "faceup": i++; return Status.FACE_UP;
  1873. case "antimagic": i++; return Status.ANTI_MAGIC;
  1874. case "upaction": i++; return Status.UP_ACTION;
  1875. case "upavoid": i++; return Status.UP_AVOID;
  1876. case "upresist": i++; return Status.UP_RESIST;
  1877. case "updefense": i++; return Status.UP_DEFENSE;
  1878. case "downaction": i++; return Status.DOWN_ACTION;
  1879. case "downavoid": i++; return Status.DOWN_AVOID;
  1880. case "downresist": i++; return Status.DOWN_RESIST;
  1881. case "downdefense": i++; return Status.DOWN_DEFENSE;
  1882. case "none": i++; return Status.NONE;
  1883. default: throwError(_prop.msgs.scriptErrorInvalidStatus, attr[i].token);
  1884. }
  1885. } else static if (is(T == Target)) {
  1886. auto value = attrValue(attr[i], varTable, msgWidth);
  1887. bool sleep = false;
  1888. static if (!Within) {
  1889. // ?????
  1890. size_t j = i + 1;
  1891. sleep = parseAttr!(bool)(opt, attr, j, false, varTable, msgWidth);
  1892. }
  1893. switch (value) {
  1894. case "m", "selected":
  1895. i++;
  1896. static if (!Within) i++;
  1897. return Target(Target.M.SELECTED, sleep);
  1898. case "r", "random", "one":
  1899. i++;
  1900. static if (!Within) i++;
  1901. return Target(Target.M.RANDOM, sleep);
  1902. case "u", "unselected":
  1903. i++;
  1904. static if (!Within) i++;
  1905. return Target(Target.M.UNSELECTED, sleep);
  1906. case "t", "team":
  1907. i++;
  1908. static if (!Within) i++;
  1909. return Target(Target.M.PARTY, sleep);
  1910. default: throwError(_prop.msgs.scriptErrorInvalidTarget, attr[i].token);
  1911. }
  1912. } else static if (is(T == EffectType)) {
  1913. auto value = attrValue(attr[i], varTable, msgWidth);
  1914. switch (value) {
  1915. case "physic": i++; return EffectType.PHYSIC;
  1916. case "magic": i++; return EffectType.MAGIC;
  1917. case "mphysic": i++; return EffectType.MAGICAL_PHYSIC;
  1918. case "pmagic": i++; return EffectType.PHYSICAL_MAGIC;
  1919. case "none": i++; return EffectType.NONE;
  1920. default: throwError(_prop.msgs.scriptErrorInvalidEffectType, attr[i].token);
  1921. }
  1922. } else static if (is(T == Resist)) {
  1923. auto value = attrValue(attr[i], varTable, msgWidth);
  1924. switch (value) {
  1925. case "avoid": i++; return Resist.AVOID;
  1926. case "resist": i++; return Resist.RESIST;
  1927. case "unfail": i++; return Resist.UNFAIL;
  1928. default: throwError(_prop.msgs.scriptErrorInvalidResist, attr[i].token);
  1929. }
  1930. } else static if (is(T == CardVisual)) {
  1931. auto value = attrValue(attr[i], varTable, msgWidth);
  1932. switch (value) {
  1933. case "none": i++; return CardVisual.NONE;
  1934. case "reverse": i++; return CardVisual.REVERSE;
  1935. case "hswing": i++; return CardVisual.HORIZONTAL;
  1936. case "vswing": i++; return CardVisual.VERTICAL;
  1937. default: throwError(_prop.msgs.scriptErrorInvalidCardVisual, attr[i].token);
  1938. }
  1939. } else static if (is(T == Mental)) {
  1940. auto value = attrValue(attr[i], varTable, msgWidth);
  1941. switch (value) {
  1942. case "agg": i++; return Mental.AGGRESSIVE;
  1943. case "unagg": i++; return Mental.UNAGGRESSIVE;
  1944. case "cheerf": i++; return Mental.CHEERFUL;
  1945. case "uncheerf": i++; return Mental.UNCHEERFUL;
  1946. case "brave": i++; return Mental.BRAVE;
  1947. case "unbrave": i++; return Mental.UNBRAVE;
  1948. case "caut": i++; return Mental.CAUTIOUS;
  1949. case "uncaut": i++; return Mental.UNCAUTIOUS;
  1950. case "trick": i++; return Mental.TRICKISH;
  1951. case "untrick": i++; return Mental.UNTRICKISH;
  1952. default: throwError(_prop.msgs.scriptErrorInvalidMental, attr[i].token);
  1953. }
  1954. } else static if (is(T == Physical)) {
  1955. auto value = attrValue(attr[i], varTable, msgWidth);
  1956. switch (value) {
  1957. case "dex": i++; return Physical.DEX;
  1958. case "agl": i++; return Physical.AGL;
  1959. case "int": i++; return Physical.INT;
  1960. case "str": i++; return Physical.STR;
  1961. case "vit": i++; return Physical.VIT;
  1962. case "min": i++; return Physical.MIN;
  1963. default: throwError(_prop.msgs.scriptErrorInvalidPhysical, attr[i].token);
  1964. }
  1965. } else static if (is(T == Talker)) {
  1966. return parseTalker!(Within)(attr, i, varTable);
  1967. } else static if (is(T == MType)) {
  1968. auto value = attrValue(attr[i], varTable, msgWidth);
  1969. switch (value) {
  1970. case "heal": i++; return MType.HEAL;
  1971. case "damage": i++; return MType.DAMAGE;
  1972. case "absorb": i++; return MType.ABSORB;
  1973. case "paralyze": i++; return MType.PARALYZE;
  1974. case "disparalyze": i++; return MType.DIS_PARALYZE;
  1975. case "poison": i++; return MType.POISON;
  1976. case "dispoison": i++; return MType.DIS_POISON;
  1977. case "getspilit": i++; return MType.GET_SKILL_POWER;
  1978. case "losespilit": i++; return MType.LOSE_SKILL_POWER;
  1979. case "sleep": i++; return MType.SLEEP;
  1980. case "confuse": i++; return MType.CONFUSE;
  1981. case "overheat": i++; return MType.OVERHEAT;
  1982. case "brave": i++; return MType.BRAVE;
  1983. case "panic": i++; return MType.PANIC;
  1984. case "resetmind": i++; return MType.NORMAL;
  1985. case "bind": i++; return MType.BIND;
  1986. case "disbind": i++; return MType.DIS_BIND;
  1987. case "silence": i++; return MType.SILENCE;
  1988. case "dissilence": i++; return MType.DIS_SILENCE;
  1989. case "faceup": i++; return MType.FACE_UP;
  1990. case "facedown": i++; return MType.FACE_DOWN;
  1991. case "antimagic": i++; return MType.ANTI_MAGIC;
  1992. case "disantimagic": i++; return MType.DIS_ANTI_MAGIC;
  1993. case "enhaction": i++; return MType.ENHANCE_ACTION;
  1994. case "enhavoid": i++; return MType.ENHANCE_AVOID;
  1995. case "enhresist": i++; return MType.ENHANCE_RESIST;
  1996. case "enhdefense": i++; return MType.ENHANCE_DEFENSE;
  1997. case "vantarget": i++; return MType.VANISH_TARGET;
  1998. case "vancard": i++; return MType.VANISH_CARD;
  1999. case "vanbeast": i++; return MType.VANISH_BEAST;
  2000. case "dealattack": i++; return MType.DEAL_ATTACK_CARD;
  2001. case "dealpowerful": i++; return MType.DEAL_POWERFUL_ATTACK_CARD;
  2002. case "dealcritical": i++; return MType.DEAL_CRITICAL_ATTACK_CARD;
  2003. case "dealfeint": i++; return MType.DEAL_FEINT_CARD;
  2004. case "dealdefense": i++; return MType.DEAL_DEFENSE_CARD;
  2005. case "dealdistance": i++; return MType.DEAL_DISTANCE_CARD;
  2006. case "dealconfuse": i++; return MType.DEAL_CONFUSE_CARD;
  2007. case "dealskill": i++; return MType.DEAL_SKILL_CARD;
  2008. case "summon": i++; return MType.SUMMON_BEAST;
  2009. case "cancelaction": i++; return MType.CANCEL_ACTION;
  2010. default: throwError(_prop.msgs.scriptErrorInvalidMotionType, attr[i].token);
  2011. }
  2012. } else static if (is(T == Element)) {
  2013. auto value = attrValue(attr[i], varTable, msgWidth);
  2014. switch (value) {
  2015. case "all": i++; return Element.ALL;
  2016. case "phy": i++; return Element.HEALTH;
  2017. case "mind": i++; return Element.MIND;
  2018. case "holy": i++; return Element.MIRACLE;
  2019. case "magic": i++; return Element.MAGIC;
  2020. case "fire": i++; return Element.FIRE;
  2021. case "ice": i++; return Element.ICE;
  2022. default: throwError(_prop.msgs.scriptErrorInvalidElement, attr[i].token);
  2023. }
  2024. } else static if (is(T == DamageType)) {
  2025. auto value = attrValue(attr[i], varTable, msgWidth);
  2026. switch (value) {
  2027. case "level": i++; return DamageType.LEVEL_RATIO;
  2028. case "value": i++; return DamageType.NORMAL;
  2029. case "max": i++; return DamageType.MAX;
  2030. default: throwError(_prop.msgs.scriptErrorInvalidDamageType, attr[i].token);
  2031. }
  2032. } else static if (is(T == EffectCardType)) {
  2033. auto value = attrValue(attr[i], varTable, msgWidth);
  2034. switch (value) {
  2035. case "all": i++; return EffectCardType.ALL;
  2036. case "skill": i++; return EffectCardType.SKILL;
  2037. case "item": i++; return EffectCardType.ITEM;
  2038. case "beast": i++; return EffectCardType.BEAST;
  2039. default: throwError(_prop.msgs.scriptErrorInvalidEffectCardType, attr[i].token);
  2040. }
  2041. } else static if (is(T == Comparison4)) {
  2042. auto value = attrValue(attr[i], varTable, msgWidth);
  2043. switch (value) {
  2044. case "=": i++; return Comparison4.Eq;
  2045. case "<>", "!=": i++; return Comparison4.Ne;
  2046. case ">": i++; return Comparison4.Lt;
  2047. case "<": i++; return Comparison4.Gt;
  2048. default: throwError(_prop.msgs.scriptErrorInvalidComparison4, attr[i].token);
  2049. }
  2050. } else static if (is(T == Comparison3)) {
  2051. auto value = attrValue(attr[i], varTable, msgWidth);
  2052. switch (value) {
  2053. case "=": i++; return Comparison3.Eq;
  2054. case ">": i++; return Comparison3.Lt;
  2055. case "<": i++; return Comparison3.Gt;
  2056. default: throwError(_prop.msgs.scriptErrorInvalidComparison3, attr[i].token);
  2057. }
  2058. } else static if (is(T == BlendMode)) {
  2059. auto value = attrValue(attr[i], varTable, msgWidth);
  2060. switch (value) {
  2061. case "normal": i++; return BlendMode.Normal;
  2062. case "add": i++; return BlendMode.Add;
  2063. case "sub": i++; return BlendMode.Subtract;
  2064. case "mul": i++; return BlendMode.Multiply;
  2065. default: throwError(_prop.msgs.scriptErrorInvalidBlendMode, attr[i].token);
  2066. }
  2067. } else static if (is(T == GradientDir)) {
  2068. auto value = attrValue(attr[i], varTable, msgWidth);
  2069. switch (value) {
  2070. case "none": i++; return GradientDir.None;
  2071. case "h", "horizontal": i++; return GradientDir.LeftToRight;
  2072. case "v", "vertical": i++; return GradientDir.TopToBottom;
  2073. default: throwError(_prop.msgs.scriptErrorInvalidGradientDir, attr[i].token);
  2074. }
  2075. } else static if (is(T == CRGB)) {
  2076. if (attr[i].type is NodeType.VALUES) { mixin(S_TRACE);
  2077. auto vals = attr[i].values;
  2078. size_t j = 0;
  2079. int r = parseAttr!(int)(opt, vals, j, defValue.r, varTable, msgWidth);
  2080. int g = parseAttr!(int)(opt, vals, j, defValue.g, varTable, msgWidth);
  2081. int b = parseAttr!(int)(opt, vals, j, defValue.b, varTable, msgWidth);
  2082. int a = parseAttr!(int)(opt, vals, j, defValue.a, varTable, msgWidth);
  2083. i++;
  2084. return CRGB(r, g, b, a);
  2085. } else { mixin(S_TRACE);
  2086. string value = parseAttr!(string)(opt, attr, i, "#000000", varTable, msgWidth);
  2087. value = value.toLower();
  2088. if (value.startsWith("#") && (7 == value.length || 9 == value.length)) { mixin(S_TRACE);
  2089. foreach (c; value[1..$]) { mixin(S_TRACE);
  2090. if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f'))) { mixin(S_TRACE);
  2091. throwError(_prop.msgs.scriptErrorInvalidColor, attr[i].token);
  2092. return defValue;
  2093. }
  2094. }
  2095. string val = value[1..3];
  2096. int r = parse!int(val, 16);
  2097. val = value[3..5];
  2098. int g = parse!int(val, 16);
  2099. val = value[5..7];
  2100. int b = parse!int(val, 16);
  2101. int a = 255;
  2102. if (9 == value.length) { mixin(S_TRACE);
  2103. val = value[7..9];
  2104. a = parse!int(val, 16);
  2105. }
  2106. return CRGB(r, g, b, a);
  2107. } else { mixin(S_TRACE);
  2108. throwError(_prop.msgs.scriptErrorInvalidColor, attr[i].token);
  2109. return defValue;
  2110. }
  2111. }
  2112. } else static if (is(T == BgImage)) {
  2113. if (attr[i].type !is NodeType.VALUES) { mixin(S_TRACE);
  2114. throwError(_prop.msgs.scriptErrorInvalidBgImage, attr[i].token);
  2115. return defValue;
  2116. }
  2117. auto vals = attr[i].values.dup;
  2118. string type = "image";
  2119. if (vals && vals[0].token.kind is Kind.SYMBOL) { mixin(S_TRACE);
  2120. type = attrValue(vals[0], varTable, msgWidth);
  2121. vals = vals[1..$];
  2122. }
  2123. BgImage r;
  2124. size_t j = 0;
  2125. switch (type) {
  2126. case "image":
  2127. string path = decodePath(parseAttr!(string)(opt, vals, j, "", varTable, msgWidth));
  2128. r = new ImageCell(path, "", 0, 0, 0, 0, false);
  2129. break;
  2130. case "text":
  2131. string text = parseAttr!(string)(opt, vals, j, "", varTable, msgWidth);
  2132. string fontName = parseAttr!(string)(opt, vals, j, "", varTable, msgWidth);
  2133. int size = parseAttr!(int)(opt, vals, j, 18, varTable, msgWidth);
  2134. CRGB color = parseAttr!(CRGB)(opt, vals, j, CRGB(0, 0, 0, 255), varTable, msgWidth);
  2135. bool bold = false, italic = false, underline = false, strike = false, vertical = false;
  2136. BorderingType borderingType = BorderingType.None;
  2137. bgtw: while (j < vals.length) { mixin(S_TRACE);
  2138. if (vals[j].token.kind !is Kind.SYMBOL) break;
  2139. switch (attrValue(vals[j], varTable, msgWidth)) {
  2140. case "bold": j++; bold = true; break;
  2141. case "italic": j++; italic = true; break;
  2142. case "underline", "uline": j++; underline = true; break;
  2143. case "strike": j++; strike = true; break;
  2144. case "vertical": j++; vertical = true; break;
  2145. case "border1": j++; borderingType = BorderingType.Outline; break;
  2146. case "border2": j++; borderingType = BorderingType.Inline; break;
  2147. default: break bgtw;
  2148. }
  2149. }
  2150. CRGB borderingColor = CRGB(255, 255, 255, 255);
  2151. int borderingWidth = 1;
  2152. final switch (borderingType) {
  2153. case BorderingType.None: break;
  2154. case BorderingType.Outline:
  2155. borderingColor = parseAttr!(CRGB)(opt, vals, j, borderingColor, varTable, msgWidth);
  2156. break;
  2157. case BorderingType.Inline:
  2158. borderingColor = parseAttr!(CRGB)(opt, vals, j, borderingColor, varTable, msgWidth);
  2159. borderingWidth = parseAttr!(int)(opt, vals, j, borderingWidth, varTable, msgWidth);
  2160. break;
  2161. }
  2162. r = new TextCell(text, fontName, size, color, bold, italic, underline, strike, vertical,
  2163. borderingType, borderingColor, borderingWidth, "", 0, 0, 0, 0, false);
  2164. break;
  2165. case "color":
  2166. BlendMode blendMode = parseAttr!(BlendMode)(opt, vals, j, BlendMode.Normal, varTable, msgWidth);
  2167. CRGB color1 = parseAttr!(CRGB)(opt, vals, j, CRGB(255, 255, 255, 255), varTable, msgWidth);
  2168. GradientDir gradientDir = GradientDir.None;
  2169. CRGB color2 = CRGB(0, 0, 0, 255);
  2170. if (j < vals.length && vals[j].token.kind is Kind.SYMBOL) { mixin(S_TRACE);
  2171. gradientDir = parseAttr!(GradientDir)(opt, vals, j, gradientDir, varTable, msgWidth);
  2172. color2 = parseAttr!(CRGB)(opt, vals, j, color2, varTable, msgWidth);
  2173. }
  2174. r = new ColorCell(blendMode, gradientDir, color1, color2, "", 0, 0, 0, 0, false);
  2175. break;
  2176. case "pc":
  2177. auto pcNumber = parseAttr!(int)(opt, vals, j, 1, varTable, msgWidth);
  2178. r = new PCCell(pcNumber, "", 0, 0, 0, 0, false);
  2179. default:
  2180. throwError(_prop.msgs.scriptErrorInvalidBgImage, attr[i].token);
  2181. return defValue;
  2182. }
  2183. r.flag = parseAttr!(string)(opt, vals, j, "", varTable, msgWidth);
  2184. r.x = parseAttr!(int)(opt, vals, j, 0, varTable, msgWidth);
  2185. r.y = parseAttr!(int)(opt, vals, j, 0, varTable, msgWidth);
  2186. auto size = _prop.looks.viewSize;
  2187. r.width = parseAttr!(int)(opt, vals, j, cast(int) size.width, varTable, msgWidth);
  2188. r.height = parseAttr!(int)(opt, vals, j, cast(int) size.height, varTable, msgWidth);
  2189. if (cast(ImageCell)r) {
  2190. r.mask = parseAttr!(bool)(opt, vals, j, r.mask, varTable, msgWidth);
  2191. }
  2192. r.cellName = parseAttr!(string)(opt, vals, j, r.cellName, varTable, msgWidth);
  2193. r.foreground = parseAttr!(bool)(opt, vals, j, r.foreground, varTable, msgWidth);
  2194. i++;
  2195. return r;
  2196. } else static if (is(T == Motion)) {
  2197. if (attr[i].type !is NodeType.VALUES) { mixin(S_TRACE);
  2198. throwError(_prop.msgs.scriptErrorInvalidMotion, attr[i].token);
  2199. }
  2200. size_t j = 0;
  2201. auto vals = attr[i].values;
  2202. MType type = parseAttr!(MType)(opt, vals, j, MType.HEAL, varTable, msgWidth);
  2203. auto r = new Motion(type, Element.ALL);
  2204. auto detail = r.detail;
  2205. if (detail.use(MArg.VALUE_TYPE)) { mixin(S_TRACE);
  2206. r.damageType = parseAttr!(DamageType)(opt, vals, j, r.damageType, varTable, msgWidth);
  2207. }
  2208. if (detail.use(MArg.U_VALUE)) { mixin(S_TRACE);
  2209. r.uValue = parseAttr!(int)(opt, vals, j, cast(int) r.uValue, varTable, msgWidth);
  2210. }
  2211. if (detail.use(MArg.A_VALUE)) { mixin(S_TRACE);
  2212. r.aValue = parseAttr!(int)(opt, vals, j, r.aValue, varTable, msgWidth);
  2213. }
  2214. if (detail.use(MArg.ROUND)) { mixin(S_TRACE);
  2215. r.round = parseAttr!(int)(opt, vals, j, r.round, varTable, msgWidth);
  2216. }
  2217. if (detail.use(MArg.BEAST)) { mixin(S_TRACE);
  2218. ulong beast = parseAttr!(ulong)(opt, vals, j, 0UL, varTable, msgWidth);
  2219. if (beast != 0 && _summ) { mixin(S_TRACE);
  2220. if (opt.linkId) { mixin(S_TRACE);
  2221. r.beast = new BeastCard(1UL, "", "", "");
  2222. r.beast.linkId = beast;
  2223. } else { mixin(S_TRACE);
  2224. r.beast = _summ.beast(beast);
  2225. }
  2226. }
  2227. }
  2228. r.element = parseAttr!(Element)(opt, vals, j, Element.ALL, varTable, msgWidth);
  2229. i++;
  2230. return r;
  2231. } else static if (is(T == SDialog)) {
  2232. if (attr[i].type !is NodeType.VALUES) { mixin(S_TRACE);
  2233. throwError(_prop.msgs.scriptErrorInvalidDialog, attr[i].token);
  2234. }
  2235. size_t j = 0;
  2236. auto vals = attr[i].values;
  2237. auto r = new SDialog;
  2238. if (vals.length > 2) { mixin(S_TRACE);
  2239. // ?????????
  2240. string[] cs;
  2241. foreach (v; vals[0 .. $ - 1]) { mixin(S_TRACE);
  2242. cs ~= parseAttr!(string)(opt, vals, j, "", varTable, msgWidth);
  2243. }
  2244. r.rCoupons = cs;
  2245. } else if (vals.length > 1) { mixin(S_TRACE);
  2246. // ?????????
  2247. if (vals[j].type is NodeType.VALUES) { mixin(S_TRACE);
  2248. // ???????????????????
  2249. string[] cs;
  2250. foreach (v; vals[j].values) { mixin(S_TRACE);
  2251. cs ~= attrValue(v, varTable, 0);
  2252. }
  2253. r.rCoupons = cs;
  2254. j++;
  2255. } else { mixin(S_TRACE);
  2256. // ';'??????????
  2257. r.rCoupons = std.string.split(parseAttr!(string)(opt, vals, j, "", varTable, msgWidth), ";");
  2258. }
  2259. }
  2260. r.text = parseAttr!(string)(opt, vals, j, r.text, varTable, msgWidth);
  2261. i++;
  2262. return r;
  2263. } else static if (is(T == Coupon)) {
  2264. if (attr[i].type !is NodeType.VALUES) { mixin(S_TRACE);
  2265. string name = attrValue(attr[i], varTable, msgWidth);
  2266. i++;
  2267. return new Coupon(name, 1);
  2268. }
  2269. size_t j = 0;
  2270. auto vals = attr[i].values;
  2271. string name = parseAttr!(string)(opt, vals, j, "", varTable, msgWidth);
  2272. int value = parseAttr!(int)(opt, vals, j, 0, varTable, msgWidth);
  2273. auto r = new Coupon(name, value);
  2274. i++;
  2275. return r;
  2276. } else static if (is(T:CoordinateType)) {
  2277. auto value = attrValue(attr[i], varTable, msgWidth);
  2278. switch (value) {
  2279. case "none": i++; return CoordinateType.None;
  2280. case "abs", "absolute": i++; return CoordinateType.Absolute;
  2281. case "rel", "relative": i++; return CoordinateType.Relative;
  2282. case "per", "percent", "percentage": i++; return CoordinateType.Percentage;
  2283. default: throwError(_prop.msgs.scriptErrorInvalidCoordinateType, attr[i].token);
  2284. }
  2285. } else static if (is(T == int)) {
  2286. auto value = attrValue(attr[i], varTable, msgWidth);
  2287. if (attr[i].token.kind is Kind.SYMBOL && value == "all") { mixin(S_TRACE);
  2288. return 0; // ??????
  2289. }
  2290. try { mixin(S_TRACE);
  2291. auto r = to!(int)(value);
  2292. i++;
  2293. return r;
  2294. } catch (Exception e) {
  2295. throwError(_prop.msgs.scriptErrorReqNumber, attr[i].token);
  2296. }
  2297. } else static if (is(T == ulong)) {
  2298. auto value = attrValue(attr[i], varTable, msgWidth);
  2299. try { mixin(S_TRACE);
  2300. auto r = to!(ulong)(value);
  2301. i++;
  2302. return r;
  2303. } catch (Exception e) {
  2304. throwError(_prop.msgs.scriptErrorReqID, attr[i].token);
  2305. }
  2306. } else static assert (0, T.stringof);
  2307. return T.init;
  2308. }
  2309. private void parseAttrTalker(in Node[] attr, ref size_t i, ref Talker t, ref string cardPath, in const(Node)[][string] varTable) { mixin(S_TRACE);
  2310. if (attr.length <= i) return;
  2311. auto nodes = var(attr[i], varTable);
  2312. auto value = attrValue(attr[i], varTable, 0);
  2313. if (nodes.length && nodes[0].token.kind is Kind.STRING) { mixin(S_TRACE);
  2314. t = Talker.IMAGE;
  2315. cardPath = value;
  2316. i++;
  2317. return;
  2318. }
  2319. cardPath = "";
  2320. t = parseTalker!(false)(attr, i, varTable);
  2321. }
  2322. private Talker parseTalker(bool Within)(in Node[] attr, ref size_t i, in const(Node)[][string] varTable) { mixin(S_TRACE);
  2323. auto node = attr[i];
  2324. auto value = attrValue(node, varTable, 0);
  2325. switch (value) {
  2326. case "n", "none":
  2327. static if (Within) goto default;
  2328. i++;
  2329. return Talker.NARRATION;
  2330. case "m", "selected": i++; return Talker.SELECTED;
  2331. case "u", "unselected": i++; return Talker.UNSELECTED;
  2332. case "r", "random": i++; return Talker.RANDOM;
  2333. case "c", "card":
  2334. static if (Within) goto default;
  2335. i++;
  2336. return Talker.CARD;
  2337. case "v", "valued": i++; return Talker.VALUED;
  2338. default:
  2339. throwError(_prop.msgs.scriptErrorInvalidTalker, node.token);
  2340. i++;
  2341. }
  2342. return Talker.NARRATION;
  2343. }
  2344. private string parseNextValue(in Node node, in Keywords keys, in const(Node)[][string] varTable) { mixin(S_TRACE);
  2345. if (!node.texts.length) return "";
  2346. auto nodes = varValue(node, node.texts, varTable, 0);
  2347. if (!nodes.length) return "";
  2348. auto value = nodes[0].token;
  2349. string r;
  2350. if (value.kind is Kind.SYMBOL) { mixin(S_TRACE);
  2351. auto valStr = std.string.toLower(value.value);
  2352. if (valStr in keys.keywords) { mixin(S_TRACE);
  2353. throwError(_prop.msgs.scriptErrorUndefinedSymbol, node.token);
  2354. }
  2355. switch (valStr) {
  2356. case "default":
  2357. r = _prop.sys.evtChildDefault;
  2358. break;
  2359. case "select", "over", "true", "success", "yes", "has", "on":
  2360. r = _prop.sys.evtChildTrue;
  2361. break;
  2362. case "cancel", "under", "false", "failure", "no", "hasnot", "off":
  2363. r = _prop.sys.evtChildFalse;
  2364. break;
  2365. default:
  2366. throwError(_prop.msgs.scriptErrorUndefinedSymbol, node.token);
  2367. }
  2368. } else { mixin(S_TRACE);
  2369. switch (value.kind) {
  2370. case Kind.STRING: r = stringValue(value, 0); break;
  2371. case Kind.NUMBER: r = to!(string)(value.value); break;
  2372. default: throwError(_prop.msgs.scriptErrorInvalidValue, value);
  2373. }
  2374. }
  2375. return r;
  2376. }
  2377. /// Node????????????????
  2378. Content[] analyzeSemantics(in Node[] nodes, in CompileOption opt) { mixin(S_TRACE);
  2379. const(Node)[][string] varTable;
  2380. size_t autoWrapCount = 0;
  2381. string[] startNames;
  2382. Content[] dummy;
  2383. return analyzeSemanticsImpl(opt, nodes, KEYS, varTable, 0, autoWrapCount, startNames, dummy, true);
  2384. }
  2385. private Content[] analyzeSemanticsImpl(in CompileOption opt, in Node[] nodes, in Keywords keys, ref const(Node)[][string] varTable, size_t stack, ref size_t autoWrapCount, ref string[] startNames, ref Content[] topGroup, bool isTop) { mixin(S_TRACE);
  2386. Content[] r;
  2387. auto commentReg = .regex(`^[\s|\*|\/]*(.*)[\s|\*|\/]*$`);
  2388. string parseComment(string comment) { mixin(S_TRACE);
  2389. string r = "";
  2390. foreach (i, line; splitLines!string(comment)) { mixin(S_TRACE);
  2391. auto m = .match(line, commentReg);
  2392. if (!m.empty) { mixin(S_TRACE);
  2393. line = m.captures[1];
  2394. }
  2395. if (r.length || line.length) { mixin(S_TRACE);
  2396. r ~= line;
  2397. r ~= "\n";
  2398. }
  2399. }
  2400. return lastRet(r);
  2401. }
  2402. Content lastParent = null;
  2403. bool nextIsChild = false;
  2404. foreach (node; nodes) { mixin(S_TRACE);
  2405. foreach (var; node.beforeVars) { mixin(S_TRACE);
  2406. if (var.type !is NodeType.VAR_SET) { mixin(S_TRACE);
  2407. throwError(_prop.msgs.scriptErrorInvalidVar, var.token);
  2408. }
  2409. if (var.value.length) { mixin(S_TRACE);
  2410. varTable[std.string.toLower(var.token.value)] = this.var(var.value, varTable);
  2411. } else { mixin(S_TRACE);
  2412. throwError(_prop.msgs.scriptErrorNoVarVal, var.token);
  2413. }
  2414. }
  2415. if (node.type is NodeType.VAR_SET) { mixin(S_TRACE);
  2416. if (node.value.length) { mixin(S_TRACE);
  2417. varTable[std.string.toLower(node.token.value)] = var(node.value, varTable);
  2418. } else { mixin(S_TRACE);
  2419. throwError(_prop.msgs.scriptErrorNoVarVal, node.token);
  2420. }
  2421. continue;
  2422. }
  2423. if (node.type !is NodeType.COMMAND && node.type !is NodeType.START) { mixin(S_TRACE);
  2424. throwError(_prop.msgs.scriptErrorInvalidCommand, node.token);
  2425. }
  2426. if (node.type is NodeType.START) { mixin(S_TRACE);
  2427. stack = 0;
  2428. }
  2429. string val = std.string.toLower(node.token.value);
  2430. auto cmdPtr = val in KEYS.keywords;
  2431. if (!cmdPtr) { mixin(S_TRACE);
  2432. throwError(_prop.msgs.scriptErrorInvalidCommand, node.token);
  2433. }
  2434. string comment = node.token.comment;
  2435. auto c = new Content(*cmdPtr, parseNextValue(node, keys, varTable));
  2436. if (node.type is NodeType.START) { mixin(S_TRACE);
  2437. c.setName(_prop, createNewName(c.name, (string name) { mixin(S_TRACE);
  2438. foreach (sn; startNames) { mixin(S_TRACE);
  2439. if (0 == icmp(sn, name)) { mixin(S_TRACE);
  2440. return false;
  2441. }
  2442. }
  2443. return true;
  2444. }));
  2445. startNames ~= c.name;
  2446. }
  2447. c.comment = parseComment(comment);
  2448. size_t i = 0;
  2449. auto detail = c.detail;
  2450. if (detail.use(CArg.TALKER_C)) { mixin(S_TRACE);
  2451. Talker t = c.talkerC;
  2452. string path = encodePath(c.cardPath);
  2453. parseAttrTalker(node.attr, i, t, path, varTable);
  2454. c.talkerC = t;
  2455. c.cardPath = decodePath(path);
  2456. }
  2457. if (detail.use(CArg.TEXT)) { mixin(S_TRACE);
  2458. c.text = parseAttr!(string)(opt, node.attr, i, c.text, varTable,
  2459. c.talkerC is Talker.NARRATION ? _prop.looks.messageLen : _prop.looks.messageImageLen);
  2460. }
  2461. if (detail.use(CArg.TALKER_NC)) { mixin(S_TRACE);
  2462. c.talkerNC = parseAttr!(Talker, true)(opt, node.attr, i, c.talkerNC, varTable, 0);
  2463. }
  2464. if (detail.use(CArg.DIALOGS)) { mixin(S_TRACE);
  2465. c.dialogs = parseAttr!(SDialog[])(opt, node.attr, i, c.dialogs, varTable, _prop.looks.messageImageLen);
  2466. if (!c.dialogs.length) { mixin(S_TRACE);
  2467. c.dialogs = [new SDialog];
  2468. }
  2469. }
  2470. if (detail.use(CArg.CELL_NAME)) { mixin(S_TRACE);
  2471. c.cellName = parseAttr!(string)(opt, node.attr, i, c.cellName, varTable, 0);
  2472. }
  2473. if (detail.use(CArg.POSITION_TYPE)) { mixin(S_TRACE);
  2474. c.positionType = parseAttr!(CoordinateType)(opt, node.attr, i, c.positionType, varTable, 0);
  2475. }
  2476. if (detail.use(CArg.X)) { mixin(S_TRACE);
  2477. c.x = parseAttr!(int)(opt, node.attr, i, c.x, varTable, 0);
  2478. }
  2479. if (detail.use(CArg.Y)) { mixin(S_TRACE);
  2480. c.y = parseAttr!(int)(opt, node.attr, i, c.y, varTable, 0);
  2481. }
  2482. if (detail.use(CArg.SIZE_TYPE)) { mixin(S_TRACE);
  2483. c.sizeType = parseAttr!(CoordinateType)(opt, node.attr, i, c.sizeType, varTable, 0);
  2484. }
  2485. if (detail.use(CArg.WIDTH)) { mixin(S_TRACE);
  2486. c.width = parseAttr!(int)(opt, node.attr, i, c.width, varTable, 0);
  2487. }
  2488. if (detail.use(CArg.HEIGHT)) { mixin(S_TRACE);
  2489. c.height = parseAttr!(int)(opt, node.attr, i, c.height, varTable, 0);
  2490. }
  2491. if (detail.use(CArg.BG_IMAGES)) { mixin(S_TRACE);
  2492. c.backs = parseAttr!(BgImage[])(opt, node.attr, i, c.backs, varTable, 0);
  2493. }
  2494. if (detail.use(CArg.TARGET_NS)) { mixin(S_TRACE);
  2495. c.targetNS = parseAttr!(Target, true)(opt, node.attr, i, c.targetNS, varTable, 0);
  2496. }
  2497. if (detail.use(CArg.TARGET_S)) { mixin(S_TRACE);
  2498. c.targetS = parseAttr!(Target)(opt, node.attr, i, c.targetS, varTable, 0);
  2499. }
  2500. if (detail.use(CArg.RANGE)) { mixin(S_TRACE);
  2501. c.range = parseAttr!(Range)(opt, node.attr, i, c.range, varTable, 0);
  2502. }
  2503. if (detail.use(CArg.CAST_RANGE)) { mixin(S_TRACE);
  2504. c.castRange = parseAttr!(CastRange[])(opt, node.attr, i, c.castRange, varTable, 0);
  2505. }
  2506. if (detail.use(CArg.KEY_CODE_RANGE)) { mixin(S_TRACE);
  2507. c.keyCodeRange = parseAttr!(Range)(opt, node.attr, i, c.keyCodeRange, varTable, 0);
  2508. }
  2509. if (detail.use(CArg.EFFECT_CARD_TYPE)) { mixin(S_TRACE);
  2510. c.effectCardType = parseAttr!(EffectCardType)(opt, node.attr, i, c.effectCardType, varTable, 0);
  2511. }
  2512. if (detail.use(CArg.AREA)) { mixin(S_TRACE);
  2513. c.area = parseAttr!(ulong)(opt, node.attr, i, c.area, varTable, 0);
  2514. }
  2515. if (detail.use(CArg.BATTLE)) { mixin(S_TRACE);
  2516. c.battle = parseAttr!(ulong)(opt, node.attr, i, c.battle, varTable, 0);
  2517. }
  2518. if (detail.use(CArg.PACKAGE)) { mixin(S_TRACE);
  2519. c.packages = parseAttr!(ulong)(opt, node.attr, i, c.packages, varTable, 0);
  2520. }
  2521. if (detail.use(CArg.CAST)) { mixin(S_TRACE);
  2522. c.casts = parseAttr!(ulong)(opt, node.attr, i, c.casts, varTable, 0);
  2523. }
  2524. if (detail.use(CArg.ITEM)) { mixin(S_TRACE);
  2525. c.item = parseAttr!(ulong)(opt, node.attr, i, c.item, varTable, 0);
  2526. }
  2527. if (detail.use(CArg.SKILL)) { mixin(S_TRACE);
  2528. c.skill = parseAttr!(ulong)(opt, node.attr, i, c.skill, varTable, 0);
  2529. }
  2530. if (detail.use(CArg.INFO)) { mixin(S_TRACE);
  2531. c.info = parseAttr!(ulong)(opt, node.attr, i, c.info, varTable, 0);
  2532. }
  2533. if (detail.use(CArg.BEAST)) { mixin(S_TRACE);
  2534. c.beast = parseAttr!(ulong)(opt, node.attr, i, c.beast, varTable, 0);
  2535. }
  2536. if (detail.use(CArg.START)) { mixin(S_TRACE);
  2537. c.start = parseAttr!(string)(opt, node.attr, i, c.start, varTable, 0);
  2538. }
  2539. if (detail.use(CArg.COMPLETE)) { mixin(S_TRACE);
  2540. c.complete = parseAttr!(bool)(opt, node.attr, i, c.complete, varTable, 0);
  2541. }
  2542. if (detail.use(CArg.MONEY)) { mixin(S_TRACE);
  2543. c.money = parseAttr!(int)(opt, node.attr, i, c.money, varTable, 0);
  2544. }
  2545. if (detail.use(CArg.COUPON)) { mixin(S_TRACE);
  2546. c.coupon = parseAttr!(string)(opt, node.attr, i, c.coupon, varTable, 0);
  2547. }
  2548. if (detail.use(CArg.COUPON_VALUE)) { mixin(S_TRACE);
  2549. c.couponValue = parseAttr!(int)(opt, node.attr, i, c.couponValue, varTable, 0);
  2550. }
  2551. if (detail.use(CArg.COMPLETE_STAMP)) { mixin(S_TRACE);
  2552. c.completeStamp = parseAttr!(string)(opt, node.attr, i, c.completeStamp, varTable, 0);
  2553. }
  2554. if (detail.use(CArg.GOSSIP)) { mixin(S_TRACE);
  2555. c.gossip = parseAttr!(string)(opt, node.attr, i, c.gossip, varTable, 0);
  2556. }
  2557. if (detail.use(CArg.KEY_CODE)) { mixin(S_TRACE);
  2558. c.keyCode = parseAttr!(string)(opt, node.attr, i, c.keyCode, varTable, 0);
  2559. }
  2560. if (detail.use(CArg.FLAG)) { mixin(S_TRACE);
  2561. c.flag = parseAttr!(string)(opt, node.attr, i, c.flag, varTable, 0);
  2562. }
  2563. if (detail.use(CArg.FLAG_2)) { mixin(S_TRACE);
  2564. c.flag2 = parseAttr!(string)(opt, node.attr, i, c.flag2, varTable, 0);
  2565. }
  2566. if (detail.use(CArg.STEP)) { mixin(S_TRACE);
  2567. c.step = parseAttr!(string)(opt, node.attr, i, c.step, varTable, 0);
  2568. }
  2569. if (detail.use(CArg.STEP_2)) { mixin(S_TRACE);
  2570. c.step2 = parseAttr!(string)(opt, node.attr, i, c.step2, varTable, 0);
  2571. }
  2572. if (detail.use(CArg.COMPARISON_4)) { mixin(S_TRACE);
  2573. c.comparison4 = parseAttr!(Comparison4)(opt, node.attr, i, c.comparison4, varTable, 0);
  2574. }
  2575. if (detail.use(CArg.COMPARISON_3)) { mixin(S_TRACE);
  2576. c.comparison3 = parseAttr!(Comparison3)(opt, node.attr, i, c.comparison3, varTable, 0);
  2577. }
  2578. if (detail.use(CArg.FLAG_VALUE)) { mixin(S_TRACE);
  2579. c.flagValue = parseAttr!(bool)(opt, node.attr, i, c.flagValue, varTable, 0);
  2580. }
  2581. if (detail.use(CArg.STEP_VALUE)) { mixin(S_TRACE);
  2582. c.stepValue = parseAttr!(int)(opt, node.attr, i, c.stepValue, varTable, 0);
  2583. }
  2584. if (detail.use(CArg.CARD_NUMBER)) { mixin(S_TRACE);
  2585. c.cardNumber = parseAttr!(int)(opt, node.attr, i, c.cardNumber, varTable, 0);
  2586. }
  2587. if (detail.use(CArg.MOTIONS)) { mixin(S_TRACE);
  2588. c.motions = parseAttr!(Motion[])(opt, node.attr, i, c.motions, varTable, 0);
  2589. }
  2590. if (detail.use(CArg.CARD_VISUAL)) { mixin(S_TRACE);
  2591. c.cardVisual = parseAttr!(CardVisual)(opt, node.attr, i, c.cardVisual, varTable, 0);
  2592. }
  2593. if (detail.use(CArg.UNSIGNED_LEVEL)) { mixin(S_TRACE);
  2594. c.unsignedLevel = parseAttr!(int)(opt, node.attr, i, c.unsignedLevel, varTable, 0);
  2595. }
  2596. if (detail.use(CArg.SIGNED_LEVEL)) { mixin(S_TRACE);
  2597. c.signedLevel = parseAttr!(int)(opt, node.attr, i, c.signedLevel, varTable, 0);
  2598. }
  2599. if (detail.use(CArg.LEVEL_MIN)) { mixin(S_TRACE);
  2600. c.levelMin = parseAttr!(int)(opt, node.attr, i, c.levelMin, varTable, 0);
  2601. }
  2602. if (detail.use(CArg.LEVEL_MAX)) { mixin(S_TRACE);
  2603. c.levelMax = parseAttr!(int)(opt, node.attr, i, c.levelMax, varTable, 0);
  2604. }
  2605. if (detail.use(CArg.PHYSICAL)) { mixin(S_TRACE);
  2606. c.physical = parseAttr!(Physical)(opt, node.attr, i, c.physical, varTable, 0);
  2607. }
  2608. if (detail.use(CArg.MENTAL)) { mixin(S_TRACE);
  2609. c.mental = parseAttr!(Mental)(opt, node.attr, i, c.mental, varTable, 0);
  2610. }
  2611. if (detail.use(CArg.WAIT)) { mixin(S_TRACE);
  2612. c.wait = parseAttr!(int)(opt, node.attr, i, c.wait, varTable, 0);
  2613. }
  2614. if (detail.use(CArg.PERCENT)) { mixin(S_TRACE);
  2615. c.percent = parseAttr!(int)(opt, node.attr, i, c.percent, varTable, 0);
  2616. }
  2617. if (detail.use(CArg.TARGET_ALL)) { mixin(S_TRACE);
  2618. c.targetAll = parseAttr!(bool)(opt, node.attr, i, c.targetAll, varTable, 0);
  2619. }
  2620. if (detail.use(CArg.RANDOM)) { mixin(S_TRACE);
  2621. c.random = parseAttr!(bool)(opt, node.attr, i, c.random, varTable, 0);
  2622. }
  2623. if (detail.use(CArg.AVERAGE)) { mixin(S_TRACE);
  2624. c.average = parseAttr!(bool)(opt, node.attr, i, c.average, varTable, 0);
  2625. }
  2626. if (detail.use(CArg.PARTY_NUMBER)) { mixin(S_TRACE);
  2627. c.partyNumber = parseAttr!(int)(opt, node.attr, i, c.partyNumber, varTable, 0);
  2628. }
  2629. if (detail.use(CArg.SUCCESS_RATE)) { mixin(S_TRACE);
  2630. c.successRate = parseAttr!(int)(opt, node.attr, i, c.successRate, varTable, 0);
  2631. }
  2632. if (detail.use(CArg.EFFECT_TYPE)) { mixin(S_TRACE);
  2633. c.effectType = parseAttr!(EffectType)(opt, node.attr, i, c.effectType, varTable, 0);
  2634. }
  2635. if (detail.use(CArg.RESIST)) { mixin(S_TRACE);
  2636. c.resist = parseAttr!(Resist)(opt, node.attr, i, c.resist, varTable, 0);
  2637. }
  2638. if (detail.use(CArg.STATUS)) { mixin(S_TRACE);
  2639. c.status = parseAttr!(Status)(opt, node.attr, i, c.status, varTable, 0);
  2640. }
  2641. if (detail.use(CArg.BGM_PATH)) { mixin(S_TRACE);
  2642. c.bgmPath = encodePath(parseAttr!(string)(opt, node.attr, i, decodePath(c.bgmPath), varTable, 0));
  2643. }
  2644. if (detail.use(CArg.SOUND_PATH)) { mixin(S_TRACE);
  2645. c.soundPath = encodePath(parseAttr!(string)(opt, node.attr, i, decodePath(c.soundPath), varTable, 0));
  2646. }
  2647. if (detail.use(CArg.TRANSITION_SPEED)) { mixin(S_TRACE);
  2648. c.transitionSpeed = parseAttr!(int)(opt, node.attr, i, c.transitionSpeed, varTable, 0);
  2649. }
  2650. if (detail.use(CArg.TRANSITION)) { mixin(S_TRACE);
  2651. c.transition = parseAttr!(Transition)(opt, node.attr, i, c.transition, varTable, 0);
  2652. }
  2653. if (detail.use(CArg.INIT_VALUE)) { mixin(S_TRACE);
  2654. c.initValue = parseAttr!(int)(opt, node.attr, i, c.initValue, varTable, 0);
  2655. }
  2656. if (detail.use(CArg.COUPONS)) { mixin(S_TRACE);
  2657. c.coupons = parseAttr!(Coupon[])(opt, node.attr, i, c.coupons, varTable, 0);
  2658. }
  2659. if (detail.use(CArg.ROUND)) { mixin(S_TRACE);
  2660. c.round = parseAttr!(int)(opt, node.attr, i, c.round, varTable, 0);
  2661. }
  2662. Content autoWrap(Content c) { mixin(S_TRACE);
  2663. if (!c.detail.owner) { mixin(S_TRACE);
  2664. throwError(_prop.msgs.scriptErrorCanNotHaveContent, node.token);
  2665. }
  2666. if (_autoWrap <= stack) { mixin(S_TRACE);
  2667. autoWrapCount++;
  2668. stack = 0;
  2669. auto s = new Content(CType.START, .createNewName(.format("Auto wrap (%d)", autoWrapCount), (string name) { mixin(S_TRACE);
  2670. foreach (sn; startNames) { mixin(S_TRACE);
  2671. if (0 == icmp(sn, name)) { mixin(S_TRACE);
  2672. return false;
  2673. }
  2674. }
  2675. return true;
  2676. }));
  2677. startNames ~= s.name;
  2678. auto link = new Content(CType.LINK_START, c.name);
  2679. link.start = s.name;
  2680. c.add(_prop, link);
  2681. if (isTop) { mixin(S_TRACE);
  2682. r ~= s;
  2683. } else { mixin(S_TRACE);
  2684. topGroup ~= s;
  2685. }
  2686. return s;
  2687. }
  2688. return c;
  2689. }
  2690. if (nextIsChild) { mixin(S_TRACE);
  2691. /// ????????? nextIsChild is true ?
  2692. auto parent = autoWrap(lastParent);
  2693. parent.add(_prop, c);
  2694. stack++;
  2695. } else { mixin(S_TRACE);
  2696. r ~= c;
  2697. }
  2698. if (node.childs.length) { mixin(S_TRACE);
  2699. auto parent = autoWrap(c);
  2700. foreach (chld; analyzeSemanticsImpl(opt, node.childs, keys, varTable, stack + 1, autoWrapCount, startNames, isTop ? r : topGroup, false)) { mixin(S_TRACE);
  2701. parent.add(_prop, chld);
  2702. }
  2703. }
  2704. nextIsChild = node.nextIsChild;
  2705. if (nextIsChild) { mixin(S_TRACE);
  2706. lastParent = c;
  2707. }
  2708. }
  2709. return r;
  2710. }
  2711. const
  2712. string toScript(in Content[] cs, string evtChildOK, bool legacy, string indent = "\t") { mixin(S_TRACE);
  2713. char[] buf;
  2714. auto table = new VarTable;
  2715. toScriptImpl(evtChildOK, buf, cs, indent, "", KEYS, table, legacy);
  2716. auto vars = table.vars();
  2717. if (vars.length) { mixin(S_TRACE);
  2718. buf = std.string.join(vars, "\n") ~ "\n\n" ~ buf;
  2719. }
  2720. return assumeUnique(buf);
  2721. }
  2722. const
  2723. private string[] toAttr(bool Within = false, T)(T value, string command, string indentValue, VarTable vars, size_t strWidth = 0) { mixin(S_TRACE);
  2724. string[] attrs;
  2725. static if (is(Unqual!(T) == Symbol)) {
  2726. attrs ~= value;
  2727. } else static if (is(Unqual!(T) == string)) {
  2728. string attr;
  2729. auto lines = splitLines!string(value.idup);
  2730. if (lines.length == 0) { mixin(S_TRACE);
  2731. attr ~= `""`;
  2732. } else if (lines.length == 1) { mixin(S_TRACE);
  2733. attr ~= createString(lines[0]);
  2734. } else { mixin(S_TRACE);
  2735. size_t lns = 0;
  2736. foreach (i, line; lines) { mixin(S_TRACE);
  2737. if (line.length) { mixin(S_TRACE);
  2738. lns = i;
  2739. break;
  2740. }
  2741. }
  2742. attr ~= "@";
  2743. if (lns > 0) { mixin(S_TRACE);
  2744. if (vars.useCenter && lns + 1 == stringCenter(lines, strWidth)) { mixin(S_TRACE);
  2745. attr ~= " center";
  2746. } else { mixin(S_TRACE);
  2747. attr ~= " " ~ to!(string)(lns + 1);
  2748. }
  2749. lines = lines[lns .. $];
  2750. }
  2751. attr ~= "\n";
  2752. bool spaceLine = false;
  2753. foreach (line; lines) { mixin(S_TRACE);
  2754. line = std.array.replace(line, "@", "@@");
  2755. if (line.length && (line[0] == ' ' || line[0] == '\t' || line[0] == '\\')) { mixin(S_TRACE);
  2756. line = "\\" ~ line;
  2757. }
  2758. attr ~= indentValue ~ line ~ "\n";
  2759. spaceLine = line.length == 0;
  2760. }
  2761. if (spaceLine) { mixin(S_TRACE);
  2762. attr ~= "@";
  2763. } else { mixin(S_TRACE);
  2764. attr ~= indentValue ~ "@";
  2765. }
  2766. }
  2767. attrs ~= attr;
  2768. } else static if (isVArray!(T)) {
  2769. foreach (i, v; value) { mixin(S_TRACE);
  2770. attrs ~= toAttr(v, command, indentValue, vars, strWidth);
  2771. }
  2772. attrs = [attrs.join(" ")];
  2773. } else static if (is(T : bool)) {
  2774. attrs ~= value ? "true": "false";
  2775. } else static if (is(T == Transition)) {
  2776. switch (value) {
  2777. case Transition.DEFAULT: attrs ~= "default"; break;
  2778. case Transition.NONE: attrs ~= "none"; break;
  2779. case Transition.FADE: attrs ~= "fade"; break;
  2780. case Transition.PIXEL_DISSOLVE: attrs ~= "dissolve"; break;
  2781. case Transition.BLINDS: attrs ~= "blinds"; break;
  2782. default: assert (0);
  2783. }
  2784. } else static if (is(T : Range)) {
  2785. switch (value) {
  2786. case Range.SELECTED: attrs ~= "M"; break;
  2787. case Range.RANDOM: attrs ~= "R"; break;
  2788. case Range.PARTY: attrs ~= "T"; break;
  2789. case Range.BACKPACK: attrs ~= "backpack"; break;
  2790. case Range.PARTY_AND_BACKPACK: attrs ~= "party"; break;
  2791. case Range.FIELD: attrs ~= "field"; break;
  2792. default: assert (0);
  2793. }
  2794. } else static if (is(T : CastRange)) {
  2795. switch (value) {
  2796. case CastRange.PARTY: attrs ~= "party"; break;
  2797. case CastRange.ENEMY: attrs ~= "enemy"; break;
  2798. case CastRange.NPC: attrs ~= "npc"; break;
  2799. default: assert (0);
  2800. }
  2801. } else static if (is(T : Status)) {
  2802. switch (value) {
  2803. case Status.ACTIVE: attrs ~= "active"; break;
  2804. case Status.INACTIVE: attrs ~= "inactive"; break;
  2805. case Status.ALIVE: attrs ~= "alive"; break;
  2806. case Status.DEAD: attrs ~= "dead"; break;
  2807. case Status.FINE: attrs ~= "fine"; break;
  2808. case Status.INJURED: attrs ~= "injured"; break;
  2809. case Status.HEAVY_INJURED: attrs ~= "heavyinjured"; break;
  2810. case Status.UNCONSCIOUS: attrs ~= "unconscious"; break;
  2811. case Status.POISON: attrs ~= "poison"; break;
  2812. case Status.SLEEP: attrs ~= "sleep"; break;
  2813. case Status.BIND: attrs ~= "bind"; break;
  2814. case Status.PARALYZE: attrs ~= "paralyze"; break;
  2815. case Status.CONFUSE: attrs ~= "confuse"; break;
  2816. case Status.OVERHEAT: attrs ~= "overheat"; break;
  2817. case Status.BRAVE: attrs ~= "brave"; break;
  2818. case Status.PANIC: attrs ~= "panic"; break;
  2819. case Status.SILENCE: attrs ~= "silence"; break;
  2820. case Status.FACE_UP: attrs ~= "faceup"; break;
  2821. case Status.ANTI_MAGIC: attrs ~= "antimagic"; break;
  2822. case Status.UP_ACTION: attrs ~= "upaction"; break;
  2823. case Status.UP_AVOID: attrs ~= "upavoid"; break;
  2824. case Status.UP_RESIST: attrs ~= "upresist"; break;
  2825. case Status.UP_DEFENSE: attrs ~= "updefense"; break;
  2826. case Status.DOWN_ACTION: attrs ~= "downaction"; break;
  2827. case Status.DOWN_AVOID: attrs ~= "downavoid"; break;
  2828. case Status.DOWN_RESIST: attrs ~= "downresist"; break;
  2829. case Status.DOWN_DEFENSE: attrs ~= "downdefense"; break;
  2830. case Status.NONE: attrs ~= "none"; break;
  2831. default: assert (0);
  2832. }
  2833. } else static if (is(T : Target)) {
  2834. switch (value.m) {
  2835. case Target.M.SELECTED: attrs ~= "M"; break;
  2836. case Target.M.RANDOM: attrs ~= "R"; break;
  2837. case Target.M.UNSELECTED: attrs ~= "U"; break;
  2838. case Target.M.PARTY: attrs ~= "T"; break;
  2839. default: assert (0);
  2840. }
  2841. static if (!Within) {
  2842. attrs ~= toAttr(value.sleep, command, indentValue, vars);
  2843. }
  2844. } else static if (is(T : EffectType)) {
  2845. switch (value) {
  2846. case EffectType.PHYSIC: attrs ~= "physic"; break;
  2847. case EffectType.MAGIC: attrs ~= "magic"; break;
  2848. case EffectType.MAGICAL_PHYSIC: attrs ~= "mphysic"; break;
  2849. case EffectType.PHYSICAL_MAGIC: attrs ~= "pmagic"; break;
  2850. case EffectType.NONE: attrs ~= "none"; break;
  2851. default: assert (0);
  2852. }
  2853. } else static if (is(T : Resist)) {
  2854. switch (value) {
  2855. case Resist.AVOID: attrs ~= "avoid"; break;
  2856. case Resist.RESIST: attrs ~= "resist"; break;
  2857. case Resist.UNFAIL: attrs ~= "unfail"; break;
  2858. default: assert (0);
  2859. }
  2860. } else static if (is(T : CardVisual)) {
  2861. switch (value) {
  2862. case CardVisual.NONE: attrs ~= "none"; break;
  2863. case CardVisual.REVERSE: attrs ~= "reverse"; break;
  2864. case CardVisual.HORIZONTAL: attrs ~= "hswing"; break;
  2865. case CardVisual.VERTICAL: attrs ~= "vswing"; break;
  2866. default: assert (0);
  2867. }
  2868. } else static if (is(T : Mental)) {
  2869. switch (value) {
  2870. case Mental.AGGRESSIVE: attrs ~= "agg"; break;
  2871. case Mental.UNAGGRESSIVE: attrs ~= "unagg"; break;
  2872. case Mental.CHEERFUL: attrs ~= "cheerf"; break;
  2873. case Mental.UNCHEERFUL: attrs ~= "uncheerf"; break;
  2874. case Mental.BRAVE: attrs ~= "brave"; break;
  2875. case Mental.UNBRAVE: attrs ~= "unbrave"; break;
  2876. case Mental.CAUTIOUS: attrs ~= "caut"; break;
  2877. case Mental.UNCAUTIOUS: attrs ~= "uncaut"; break;
  2878. case Mental.TRICKISH: attrs ~= "trick"; break;
  2879. case Mental.UNTRICKISH: attrs ~= "untrick"; break;
  2880. default: assert (0);
  2881. }
  2882. } else static if (is(T : Physical)) {
  2883. switch (value) {
  2884. case Physical.DEX: attrs ~= "dex"; break;
  2885. case Physical.AGL: attrs ~= "agl"; break;
  2886. case Physical.INT: attrs ~= "int"; break;
  2887. case Physical.STR: attrs ~= "str"; break;
  2888. case Physical.VIT: attrs ~= "vit"; break;
  2889. case Physical.MIN: attrs ~= "min"; break;
  2890. default: assert (0);
  2891. }
  2892. } else static if (is(T : Talker)) {
  2893. attrs ~= toAttrTalker(value, "");
  2894. } else static if (is(T : MType)) {
  2895. switch (value) {
  2896. case MType.HEAL: attrs ~= "heal"; break;
  2897. case MType.DAMAGE: attrs ~= "damage"; break;
  2898. case MType.ABSORB: attrs ~= "absorb"; break;
  2899. case MType.PARALYZE: attrs ~= "paralyze"; break;
  2900. case MType.DIS_PARALYZE: attrs ~= "disparalyze"; break;
  2901. case MType.POISON: attrs ~= "poison"; break;
  2902. case MType.DIS_POISON: attrs ~= "dispoison"; break;
  2903. case MType.GET_SKILL_POWER: attrs ~= "getspilit"; break;
  2904. case MType.LOSE_SKILL_POWER: attrs ~= "losespilit"; break;
  2905. case MType.SLEEP: attrs ~= "sleep"; break;
  2906. case MType.CONFUSE: attrs ~= "confuse"; break;
  2907. case MType.OVERHEAT: attrs ~= "overheat"; break;
  2908. case MType.BRAVE: attrs ~= "brave"; break;
  2909. case MType.PANIC: attrs ~= "panic"; break;
  2910. case MType.NORMAL: attrs ~= "resetmind"; break;
  2911. case MType.BIND: attrs ~= "bind"; break;
  2912. case MType.DIS_BIND: attrs ~= "disbind"; break;
  2913. case MType.SILENCE: attrs ~= "silence"; break;
  2914. case MType.DIS_SILENCE: attrs ~= "dissilence"; break;
  2915. case MType.FACE_UP: attrs ~= "faceup"; break;
  2916. case MType.FACE_DOWN: attrs ~= "facedown"; break;
  2917. case MType.ANTI_MAGIC: attrs ~= "antimagic"; break;
  2918. case MType.DIS_ANTI_MAGIC: attrs ~= "disantimagic"; break;
  2919. case MType.ENHANCE_ACTION: attrs ~= "enhaction"; break;
  2920. case MType.ENHANCE_AVOID: attrs ~= "enhavoid"; break;
  2921. case MType.ENHANCE_RESIST: attrs ~= "enhresist"; break;
  2922. case MType.ENHANCE_DEFENSE: attrs ~= "enhdefense"; break;
  2923. case MType.VANISH_TARGET: attrs ~= "vantarget"; break;
  2924. case MType.VANISH_CARD: attrs ~= "vancard"; break;
  2925. case MType.VANISH_BEAST: attrs ~= "vanbeast"; break;
  2926. case MType.DEAL_ATTACK_CARD: attrs ~= "dealattack"; break;
  2927. case MType.DEAL_POWERFUL_ATTACK_CARD: attrs ~= "dealpowerful"; break;
  2928. case MType.DEAL_CRITICAL_ATTACK_CARD: attrs ~= "dealcritical"; break;
  2929. case MType.DEAL_FEINT_CARD: attrs ~= "dealfeint"; break;
  2930. case MType.DEAL_DEFENSE_CARD: attrs ~= "dealdefense"; break;
  2931. case MType.DEAL_DISTANCE_CARD: attrs ~= "dealdistance"; break;
  2932. case MType.DEAL_CONFUSE_CARD: attrs ~= "dealconfuse"; break;
  2933. case MType.DEAL_SKILL_CARD: attrs ~= "dealskill"; break;
  2934. case MType.SUMMON_BEAST: attrs ~= "summon"; break;
  2935. case MType.CANCEL_ACTION: attrs ~= "cancelaction"; break;
  2936. default: assert (0);
  2937. }
  2938. } else static if (is(T : Element)) {
  2939. switch (value) {
  2940. case Element.ALL: attrs ~= "all"; break;
  2941. case Element.HEALTH: attrs ~= "phy"; break;
  2942. case Element.MIND: attrs ~= "mind"; break;
  2943. case Element.MIRACLE: attrs ~= "holy"; break;
  2944. case Element.MAGIC: attrs ~= "magic"; break;
  2945. case Element.FIRE: attrs ~= "fire"; break;
  2946. case Element.ICE: attrs ~= "ice"; break;
  2947. default: assert (0);
  2948. }
  2949. } else static if (is(T : DamageType)) {
  2950. switch (value) {
  2951. case DamageType.LEVEL_RATIO: attrs ~= "level"; break;
  2952. case DamageType.NORMAL: attrs ~= "value"; break;
  2953. case DamageType.MAX: attrs ~= "max"; break;
  2954. default: assert (0);
  2955. }
  2956. } else static if (is(T : EffectCardType)) {
  2957. switch (value) {
  2958. case EffectCardType.ALL: attrs ~= "all"; break;
  2959. case EffectCardType.SKILL: attrs ~= "skill"; break;
  2960. case EffectCardType.ITEM: attrs ~= "item"; break;
  2961. case EffectCardType.BEAST: attrs ~= "beast"; break;
  2962. default: assert (0);
  2963. }
  2964. } else static if (is(T : Comparison4)) {
  2965. switch (value) {
  2966. case Comparison4.Eq: attrs ~= createString("="); break;
  2967. case Comparison4.Ne: attrs ~= createString("<>"); break;
  2968. case Comparison4.Lt: attrs ~= createString(">"); break;
  2969. case Comparison4.Gt: attrs ~= createString("<"); break;
  2970. default: assert (0);
  2971. }
  2972. } else static if (is(T : Comparison3)) {
  2973. switch (value) {
  2974. case Comparison3.Eq: attrs ~= createString("="); break;
  2975. case Comparison3.Lt: attrs ~= createString(">"); break;
  2976. case Comparison3.Gt: attrs ~= createString("<"); break;
  2977. default: assert (0);
  2978. }
  2979. } else static if (is(T : BlendMode)) {
  2980. final switch (value) {
  2981. case BlendMode.Normal: attrs ~= "normal"; break;
  2982. case BlendMode.Mask: attrs ~= "normal"; break; // Mask?Normal???
  2983. case BlendMode.Add: attrs ~= "add"; break;
  2984. case BlendMode.Subtract: attrs ~= "sub"; break;
  2985. case BlendMode.Multiply: attrs ~= "mul"; break;
  2986. }
  2987. } else static if (is(T : GradientDir)) {
  2988. final switch (value) {
  2989. case GradientDir.None: attrs ~= "none"; break;
  2990. case GradientDir.LeftToRight: attrs ~= "horizontal"; break;
  2991. case GradientDir.TopToBottom: attrs ~= "vertical"; break;
  2992. }
  2993. } else static if (is(T : CRGB)) {
  2994. if (value.a == 255) { mixin(S_TRACE);
  2995. attrs ~= createString(.tryFormat("#%02X%02X%02X", value.r, value.g, value.b));
  2996. } else { mixin(S_TRACE);
  2997. attrs ~= createString(.tryFormat("#%02X%02X%02X%02X", value.r, value.g, value.b, value.a));
  2998. }
  2999. } else static if (is(Unqual!(T) : BgImage)) {
  3000. string[] attrs2;
  3001. auto ic = cast(ImageCell) value;
  3002. if (ic) { mixin(S_TRACE);
  3003. attrs2 ~= toAttr(encodePath(ic.path), command, indentValue, vars);
  3004. }
  3005. auto tc = cast(TextCell) value;
  3006. if (tc) { mixin(S_TRACE);
  3007. attrs2 ~= "text";
  3008. attrs2 ~= toAttr(tc.text, command, indentValue, vars);
  3009. attrs2 ~= toAttr(tc.fontName, command, indentValue, vars);
  3010. attrs2 ~= toAttr(tc.size, command, indentValue, vars);
  3011. attrs2 ~= toAttr(tc.color, command, indentValue, vars);
  3012. string[] style = [];
  3013. if (tc.bold) style ~= "bold";
  3014. if (tc.italic) style ~= "italic";
  3015. if (tc.underline) style ~= "underline";
  3016. if (tc.strike) style ~= "strike";
  3017. if (tc.vertical) style ~= "vertical";
  3018. final switch (tc.borderingType) {
  3019. case BorderingType.None:
  3020. attrs2 ~= std.string.join(style, " ");
  3021. break;
  3022. case BorderingType.Outline:
  3023. style ~= "border1";
  3024. attrs2 ~= std.string.join(style, " ");
  3025. attrs2 ~= toAttr(tc.borderingColor, command, indentValue, vars);
  3026. break;
  3027. case BorderingType.Inline:
  3028. style ~= "border2";
  3029. attrs2 ~= std.string.join(style, " ");
  3030. attrs2 ~= toAttr(tc.borderingColor, command, indentValue, vars);
  3031. attrs2 ~= toAttr(tc.borderingWidth, command, indentValue, vars);
  3032. break;
  3033. }
  3034. }
  3035. auto cc = cast(ColorCell) value;
  3036. if (cc) { mixin(S_TRACE);
  3037. attrs2 ~= "color";
  3038. attrs2 ~= toAttr(cc.blendMode, command, indentValue, vars);
  3039. attrs2 ~= toAttr(cc.color1, command, indentValue, vars);
  3040. final switch (cc.gradientDir) {
  3041. case GradientDir.None:
  3042. break;
  3043. case GradientDir.LeftToRight:
  3044. case GradientDir.TopToBottom:
  3045. attrs2 ~= toAttr(cc.gradientDir, command, indentValue, vars);
  3046. attrs2 ~= toAttr(cc.color2, command, indentValue, vars);
  3047. break;
  3048. }
  3049. }
  3050. auto pc = cast(PCCell)value;
  3051. if (pc) { mixin(S_TRACE);
  3052. attrs2 ~= "pc";
  3053. attrs2 ~= toAttr(pc.pcNumber, command, indentValue, vars);
  3054. }
  3055. attrs2 ~= toAttr(value.flag, command, indentValue, vars);
  3056. attrs2 ~= toAttr(value.x, command, indentValue, vars);
  3057. attrs2 ~= toAttr(value.y, command, indentValue, vars);
  3058. attrs2 ~= toAttr(value.width, command, indentValue, vars);
  3059. attrs2 ~= toAttr(value.height, command, indentValue, vars);
  3060. if (ic) { mixin(S_TRACE);
  3061. attrs2 ~= toAttr(ic.mask, command, indentValue, vars);
  3062. }
  3063. if (value.cellName != "") { mixin(S_TRACE);
  3064. attrs2 ~= toAttr(value.cellName, command, indentValue, vars);
  3065. }
  3066. if (value.foreground) { mixin(S_TRACE);
  3067. attrs2 ~= toAttr(value.foreground, command, indentValue, vars);
  3068. }
  3069. attrs ~= "[" ~ std.string.join(attrs2, ", ") ~ "]";
  3070. } else static if (is(Unqual!(T) : Motion)) {
  3071. auto detail = value.detail;
  3072. string[] attrs2;
  3073. attrs2 ~= toAttr(value.type, command, indentValue, vars);
  3074. if (detail.use(MArg.VALUE_TYPE)) { mixin(S_TRACE);
  3075. attrs2 ~= toAttr(value.damageType, command, indentValue, vars);
  3076. }
  3077. if (detail.use(MArg.U_VALUE)) { mixin(S_TRACE);
  3078. attrs2 ~= toAttr(value.uValue, command, indentValue, vars);
  3079. }
  3080. if (detail.use(MArg.A_VALUE)) { mixin(S_TRACE);
  3081. attrs2 ~= toAttr(value.aValue, command, indentValue, vars);
  3082. }
  3083. if (detail.use(MArg.ROUND)) { mixin(S_TRACE);
  3084. attrs2 ~= toAttr(value.round, command, indentValue, vars);
  3085. }
  3086. if (detail.use(MArg.BEAST)) { mixin(S_TRACE);
  3087. if (value.beast) { mixin(S_TRACE);
  3088. attrs2 ~= toAttr(vars.id(_summ.findSameBeast(value.beast), value.beast.linkId), command, indentValue, vars);
  3089. } else { mixin(S_TRACE);
  3090. attrs2 ~= toAttr(cast(Symbol) "0", command, indentValue, vars);
  3091. }
  3092. }
  3093. attrs2 ~= toAttr(value.element, command, indentValue, vars);
  3094. attrs ~= "[" ~ std.string.join(attrs2, ", ") ~ "]";
  3095. } else static if (is(Unqual!(T) : SDialog)) {
  3096. string[] attrs2;
  3097. bool semic = false;
  3098. foreach (c; value.rCoupons) { mixin(S_TRACE);
  3099. if (std.string.indexOf(c, ";") >= 0) { mixin(S_TRACE);
  3100. semic = true;
  3101. break;
  3102. }
  3103. }
  3104. if (semic) { mixin(S_TRACE);
  3105. attrs2 ~= "[" ~ std.string.join(toAttr(value.rCoupons, command, indentValue, vars), ", ") ~ "]";
  3106. } else { mixin(S_TRACE);
  3107. attrs2 ~= createString(std.string.join(value.rCoupons.dup, ";"));
  3108. }
  3109. attrs2 ~= toAttr(value.text, command, indentValue, vars, strWidth);
  3110. attrs ~= "[" ~ std.string.join(attrs2, ", ") ~ "]";
  3111. } else static if (is(Unqual!(T) : Coupon)) {
  3112. string[] attrs2;
  3113. attrs2 ~= createString(value.name);
  3114. attrs2 ~= to!(string)(value.value);
  3115. attrs ~= "[" ~ std.string.join(attrs2, ", ") ~ "]";
  3116. } else static if (is(T:CoordinateType)) {
  3117. final switch (value) {
  3118. case CoordinateType.None: attrs ~= "none"; break;
  3119. case CoordinateType.Absolute: attrs ~= "abs"; break;
  3120. case CoordinateType.Relative: attrs ~= "rel"; break;
  3121. case CoordinateType.Percentage: attrs ~= "per"; break;
  3122. }
  3123. } else static if (is(T : int)) {
  3124. attrs ~= to!(string)(value);
  3125. } else static if (is(T : uint)) {
  3126. attrs ~= to!(string)(value);
  3127. } else static if (is(T : ulong)) {
  3128. attrs ~= to!(string)(value);
  3129. } else static assert (0);
  3130. return attrs;
  3131. }
  3132. const
  3133. private string toAttrTalker(Talker t, string cardPath) { mixin(S_TRACE);
  3134. switch (t) {
  3135. case Talker.NARRATION: return "none";
  3136. case Talker.SELECTED: return "M";
  3137. case Talker.UNSELECTED: return "U";
  3138. case Talker.RANDOM: return "R";
  3139. case Talker.CARD: return "C";
  3140. case Talker.IMAGE: return createString(encodePath(cardPath));
  3141. case Talker.VALUED: return "V";
  3142. default: assert (0);
  3143. }
  3144. }
  3145. private struct Symbol {
  3146. string symbol;
  3147. alias symbol this;
  3148. }
  3149. private static class VarTable {
  3150. bool useVar = true;
  3151. bool useCenter = true;
  3152. private Symbol idVar(string Name, A)(in A a, ulong id, ref string[ulong] tbl, ref ulong[string] tblR) { mixin(S_TRACE);
  3153. if (!useVar || !a) return Symbol(to!(string)(id));
  3154. auto p = a.id in tbl;
  3155. if (p) return Symbol(*p);
  3156. string base = "$" ~ Name ~ "_" ~ validVarName(a.name);
  3157. string name = base;
  3158. size_t i = 1;
  3159. while (name in tblR) { mixin(S_TRACE);
  3160. i++;
  3161. name = base ~ "_" ~ to!(string)(i);
  3162. }
  3163. tbl[a.id] = name;
  3164. tblR[name] = a.id;
  3165. return Symbol(name);
  3166. }
  3167. private string[ulong] _areas;
  3168. private ulong[string] _areasR;
  3169. private string[ulong] _battles;
  3170. private ulong[string] _battlesR;
  3171. private string[ulong] _packages;
  3172. private ulong[string] _packagesR;
  3173. private string[ulong] _casts;
  3174. private ulong[string] _castsR;
  3175. private string[ulong] _skills;
  3176. private ulong[string] _skillsR;
  3177. private string[ulong] _items;
  3178. private ulong[string] _itemsR;
  3179. private string[ulong] _beasts;
  3180. private ulong[string] _beastsR;
  3181. private string[ulong] _infos;
  3182. private ulong[string] _infosR;
  3183. Symbol id(in Area a, ulong id) {return idVar!("area")(a, id, _areas, _areasR);}
  3184. Symbol id(in Battle a, ulong id) {return idVar!("battle")(a, id, _battles, _battlesR);}
  3185. Symbol id(in Package a, ulong id) {return idVar!("pack")(a, id, _packages, _packagesR);}
  3186. Symbol id(in CastCard a, ulong id) {return idVar!("cast")(a, id, _casts, _castsR);}
  3187. Symbol id(in SkillCard a, ulong id) {return idVar!("skill")(a, id, _skills, _skillsR);}
  3188. Symbol id(in ItemCard a, ulong id) {return idVar!("item")(a, id, _items, _itemsR);}
  3189. Symbol id(in BeastCard a, ulong id) {return idVar!("beast")(a, id, _beasts, _beastsR);}
  3190. Symbol id(in InfoCard a, ulong id) {return idVar!("info")(a, id, _infos, _infosR);}
  3191. private static string[] vars(in string[ulong] arr) { mixin(S_TRACE);
  3192. string[] r;
  3193. foreach (id; arr.keys.sort) { mixin(S_TRACE);
  3194. r ~= arr[id] ~ " = " ~ to!(string)(id);
  3195. }
  3196. return r;
  3197. }
  3198. const
  3199. string[] vars() { mixin(S_TRACE);
  3200. string[] r;
  3201. r ~= vars(_areas);
  3202. r ~= vars(_battles);
  3203. r ~= vars(_packages);
  3204. r ~= vars(_casts);
  3205. r ~= vars(_skills);
  3206. r ~= vars(_items);
  3207. r ~= vars(_beasts);
  3208. r ~= vars(_infos);
  3209. return r;
  3210. }
  3211. }
  3212. const
  3213. private void toScriptImpl(string evtChildOK, ref char[] buf, in Content[] cs, string indent, string indentValue, in Keywords keys, VarTable vars, bool legacy) { mixin(S_TRACE);
  3214. foreach (i, c; cs) { mixin(S_TRACE);
  3215. if (i > 0) buf ~= "\n\n";
  3216. buf ~= indentValue;
  3217. auto detail = c.detail;
  3218. if (c.comment.length) { mixin(S_TRACE);
  3219. foreach (line; splitLines!string(lastRet(c.comment))) { mixin(S_TRACE);
  3220. buf ~= "// " ~ line;
  3221. buf ~= "\n";
  3222. buf ~= indentValue;
  3223. }
  3224. }
  3225. string command = keys.commands[c.type];
  3226. buf ~= command;
  3227. string[] attrs;
  3228. if (c.type is CType.START) { mixin(S_TRACE);
  3229. attrs ~= createString(c.name);
  3230. }
  3231. size_t msgLen = 0;
  3232. if (detail.use(CArg.TALKER_C)) { mixin(S_TRACE);
  3233. attrs ~= toAttrTalker(c.talkerC, c.cardPath);
  3234. if (c.talkerC is Talker.NARRATION) { mixin(S_TRACE);
  3235. msgLen = _prop.looks.messageLen;
  3236. } else { mixin(S_TRACE);
  3237. msgLen = _prop.looks.messageImageLen;
  3238. }
  3239. }
  3240. if (detail.use(CArg.TEXT)) { mixin(S_TRACE);
  3241. attrs ~= toAttr(c.text, command, indentValue, vars, msgLen);
  3242. }
  3243. if (detail.use(CArg.TALKER_NC)) { mixin(S_TRACE);
  3244. attrs ~= toAttr!(true)(c.talkerNC, command, indentValue, vars);
  3245. msgLen = _prop.looks.messageImageLen;
  3246. }
  3247. if (detail.use(CArg.DIALOGS)) { mixin(S_TRACE);
  3248. attrs ~= toAttr(c.dialogs, command, indentValue, vars, msgLen);
  3249. }
  3250. if (detail.use(CArg.CELL_NAME)) { mixin(S_TRACE);
  3251. attrs ~= toAttr(c.cellName, command, indentValue, vars);
  3252. }
  3253. if (detail.use(CArg.POSITION_TYPE)) { mixin(S_TRACE);
  3254. attrs ~= toAttr(c.positionType, command, indentValue, vars);
  3255. }
  3256. if (detail.use(CArg.X)) { mixin(S_TRACE);
  3257. attrs ~= toAttr(c.x, command, indentValue, vars);
  3258. }
  3259. if (detail.use(CArg.Y)) { mixin(S_TRACE);
  3260. attrs ~= toAttr(c.y, command, indentValue, vars);
  3261. }
  3262. if (detail.use(CArg.SIZE_TYPE)) { mixin(S_TRACE);
  3263. attrs ~= toAttr(c.sizeType, command, indentValue, vars);
  3264. }
  3265. if (detail.use(CArg.WIDTH)) { mixin(S_TRACE);
  3266. attrs ~= toAttr(c.width, command, indentValue, vars);
  3267. }
  3268. if (detail.use(CArg.HEIGHT)) { mixin(S_TRACE);
  3269. attrs ~= toAttr(c.height, command, indentValue, vars);
  3270. }
  3271. if (detail.use(CArg.BG_IMAGES)) { mixin(S_TRACE);
  3272. attrs ~= toAttr(c.backs, command, indentValue, vars);
  3273. }
  3274. if (detail.use(CArg.TARGET_NS)) { mixin(S_TRACE);
  3275. attrs ~= toAttr!(true)(c.targetNS, command, indentValue, vars);
  3276. }
  3277. if (detail.use(CArg.TARGET_S)) { mixin(S_TRACE);
  3278. attrs ~= toAttr(c.targetS, command, indentValue, vars);
  3279. }
  3280. if (detail.use(CArg.RANGE)) { mixin(S_TRACE);
  3281. attrs ~= toAttr(c.range, command, indentValue, vars);
  3282. }
  3283. if (detail.use(CArg.CAST_RANGE)) { mixin(S_TRACE);
  3284. attrs ~= toAttr(c.castRange, command, indentValue, vars);
  3285. }
  3286. if (detail.use(CArg.KEY_CODE_RANGE)) { mixin(S_TRACE);
  3287. attrs ~= toAttr(c.keyCodeRange, command, indentValue, vars);
  3288. }
  3289. if (detail.use(CArg.EFFECT_CARD_TYPE)) { mixin(S_TRACE);
  3290. attrs ~= toAttr(c.effectCardType, command, indentValue, vars);
  3291. }
  3292. if (detail.use(CArg.AREA)) { mixin(S_TRACE);
  3293. auto a = _summ.area(c.area);
  3294. attrs ~= toAttr(vars.id(a, c.area), command, indentValue, vars);
  3295. }
  3296. if (detail.use(CArg.BATTLE)) { mixin(S_TRACE);
  3297. auto a = _summ.battle(c.battle);
  3298. attrs ~= toAttr(vars.id(a, c.battle), command, indentValue, vars);
  3299. }
  3300. if (detail.use(CArg.PACKAGE)) { mixin(S_TRACE);
  3301. auto a = _summ.cwPackage(c.packages);
  3302. attrs ~= toAttr(vars.id(a, c.packages), command, indentValue, vars);
  3303. }
  3304. if (detail.use(CArg.CAST)) { mixin(S_TRACE);
  3305. auto a = _summ.cwCast(c.casts);
  3306. attrs ~= toAttr(vars.id(a, c.casts), command, indentValue, vars);
  3307. }
  3308. if (detail.use(CArg.ITEM)) { mixin(S_TRACE);
  3309. auto a = _summ.item(c.item);
  3310. attrs ~= toAttr(vars.id(a, c.item), command, indentValue, vars);
  3311. }
  3312. if (detail.use(CArg.SKILL)) { mixin(S_TRACE);
  3313. auto a = _summ.skill(c.skill);
  3314. attrs ~= toAttr(vars.id(a, c.skill), command, indentValue, vars);
  3315. }
  3316. if (detail.use(CArg.INFO)) { mixin(S_TRACE);
  3317. auto a = _summ.info(c.info);
  3318. attrs ~= toAttr(vars.id(a, c.info), command, indentValue, vars);
  3319. }
  3320. if (detail.use(CArg.BEAST)) { mixin(S_TRACE);
  3321. auto a = _summ.beast(c.beast);
  3322. attrs ~= toAttr(vars.id(a, c.beast), command, indentValue, vars);
  3323. }
  3324. if (detail.use(CArg.START)) { mixin(S_TRACE);
  3325. attrs ~= toAttr(c.start, command, indentValue, vars);
  3326. }
  3327. if (detail.use(CArg.COMPLETE)) { mixin(S_TRACE);
  3328. attrs ~= toAttr(c.complete, command, indentValue, vars);
  3329. }
  3330. if (detail.use(CArg.MONEY)) { mixin(S_TRACE);
  3331. attrs ~= toAttr(c.money, command, indentValue, vars);
  3332. }
  3333. if (detail.use(CArg.COUPON)) { mixin(S_TRACE);
  3334. attrs ~= toAttr(c.coupon, command, indentValue, vars);
  3335. }
  3336. if (detail.use(CArg.COUPON_VALUE)) { mixin(S_TRACE);
  3337. attrs ~= toAttr(c.couponValue, command, indentValue, vars);
  3338. }
  3339. if (detail.use(CArg.COMPLETE_STAMP)) { mixin(S_TRACE);
  3340. attrs ~= toAttr(c.completeStamp, command, indentValue, vars);
  3341. }
  3342. if (detail.use(CArg.KEY_CODE)) { mixin(S_TRACE);
  3343. attrs ~= toAttr(c.keyCode, command, indentValue, vars);
  3344. }
  3345. if (detail.use(CArg.GOSSIP)) { mixin(S_TRACE);
  3346. attrs ~= toAttr(c.gossip, command, indentValue, vars);
  3347. }
  3348. if (detail.use(CArg.FLAG)) { mixin(S_TRACE);
  3349. if (_prop && _prop.sys.randomValue == c.flag) { mixin(S_TRACE);
  3350. attrs ~= toAttr(Symbol("random"), command, indentValue, vars);
  3351. } else { mixin(S_TRACE);
  3352. attrs ~= toAttr(c.flag, command, indentValue, vars);
  3353. }
  3354. }
  3355. if (detail.use(CArg.FLAG_2)) { mixin(S_TRACE);
  3356. if (_prop && _prop.sys.randomValue == c.flag2) { mixin(S_TRACE);
  3357. attrs ~= toAttr(Symbol("random"), command, indentValue, vars);
  3358. } else { mixin(S_TRACE);
  3359. attrs ~= toAttr(c.flag2, command, indentValue, vars);
  3360. }
  3361. }
  3362. if (detail.use(CArg.STEP)) { mixin(S_TRACE);
  3363. if (_prop && _prop.sys.randomValue == c.step) { mixin(S_TRACE);
  3364. attrs ~= toAttr(Symbol("random"), command, indentValue, vars);
  3365. } else { mixin(S_TRACE);
  3366. attrs ~= toAttr(c.step, command, indentValue, vars);
  3367. }
  3368. }
  3369. if (detail.use(CArg.STEP_2)) { mixin(S_TRACE);
  3370. if (_prop && _prop.sys.randomValue == c.step2) { mixin(S_TRACE);
  3371. attrs ~= toAttr(Symbol("random"), command, indentValue, vars);
  3372. } else { mixin(S_TRACE);
  3373. attrs ~= toAttr(c.step2, command, indentValue, vars);
  3374. }
  3375. }
  3376. if (detail.use(CArg.COMPARISON_4)) { mixin(S_TRACE);
  3377. attrs ~= toAttr(c.comparison4, command, indentValue, vars);
  3378. }
  3379. if (detail.use(CArg.COMPARISON_3)) { mixin(S_TRACE);
  3380. attrs ~= toAttr(c.comparison3, command, indentValue, vars);
  3381. }
  3382. if (detail.use(CArg.FLAG_VALUE)) { mixin(S_TRACE);
  3383. attrs ~= toAttr(c.flagValue, command, indentValue, vars);
  3384. }
  3385. if (detail.use(CArg.STEP_VALUE)) { mixin(S_TRACE);
  3386. attrs ~= toAttr(c.stepValue, command, indentValue, vars);
  3387. }
  3388. if (detail.use(CArg.CARD_NUMBER)) { mixin(S_TRACE);
  3389. if (c.cardNumber != 0) { mixin(S_TRACE);
  3390. attrs ~= toAttr(c.cardNumber, command, indentValue, vars);
  3391. } else { mixin(S_TRACE);
  3392. attrs ~= toAttr(Symbol("all"), command, indentValue, vars);
  3393. }
  3394. }
  3395. if (detail.use(CArg.MOTIONS)) { mixin(S_TRACE);
  3396. attrs ~= toAttr(c.motions, command, indentValue, vars);
  3397. }
  3398. if (detail.use(CArg.CARD_VISUAL)) { mixin(S_TRACE);
  3399. attrs ~= toAttr(c.cardVisual, command, indentValue, vars);
  3400. }
  3401. if (detail.use(CArg.UNSIGNED_LEVEL)) { mixin(S_TRACE);
  3402. attrs ~= toAttr(c.unsignedLevel, command, indentValue, vars);
  3403. }
  3404. if (detail.use(CArg.SIGNED_LEVEL)) { mixin(S_TRACE);
  3405. attrs ~= toAttr(c.signedLevel, command, indentValue, vars);
  3406. }
  3407. if (detail.use(CArg.LEVEL_MIN)) { mixin(S_TRACE);
  3408. attrs ~= toAttr(c.levelMin, command, indentValue, vars);
  3409. }
  3410. if (detail.use(CArg.LEVEL_MAX)) { mixin(S_TRACE);
  3411. attrs ~= toAttr(c.levelMax, command, indentValue, vars);
  3412. }
  3413. if (detail.use(CArg.PHYSICAL)) { mixin(S_TRACE);
  3414. attrs ~= toAttr(c.physical, command, indentValue, vars);
  3415. }
  3416. if (detail.use(CArg.MENTAL)) { mixin(S_TRACE);
  3417. attrs ~= toAttr(c.mental, command, indentValue, vars);
  3418. }
  3419. if (detail.use(CArg.WAIT)) { mixin(S_TRACE);
  3420. attrs ~= toAttr(c.wait, command, indentValue, vars);
  3421. }
  3422. if (detail.use(CArg.PERCENT)) { mixin(S_TRACE);
  3423. attrs ~= toAttr(c.percent, command, indentValue, vars);
  3424. }
  3425. if (detail.use(CArg.TARGET_ALL)) { mixin(S_TRACE);
  3426. attrs ~= toAttr(c.targetAll, command, indentValue, vars);
  3427. }
  3428. if (detail.use(CArg.RANDOM)) { mixin(S_TRACE);
  3429. attrs ~= toAttr(c.random, command, indentValue, vars);
  3430. }
  3431. if (detail.use(CArg.AVERAGE)) { mixin(S_TRACE);
  3432. attrs ~= toAttr(c.average, command, indentValue, vars);
  3433. }
  3434. if (detail.use(CArg.PARTY_NUMBER)) { mixin(S_TRACE);
  3435. attrs ~= toAttr(c.partyNumber, command, indentValue, vars);
  3436. }
  3437. if (detail.use(CArg.SUCCESS_RATE)) { mixin(S_TRACE);
  3438. attrs ~= toAttr(c.successRate, command, indentValue, vars);
  3439. }
  3440. if (detail.use(CArg.EFFECT_TYPE)) { mixin(S_TRACE);
  3441. attrs ~= toAttr(c.effectType, command, indentValue, vars);
  3442. }
  3443. if (detail.use(CArg.RESIST)) { mixin(S_TRACE);
  3444. attrs ~= toAttr(c.resist, command, indentValue, vars);
  3445. }
  3446. if (detail.use(CArg.STATUS)) { mixin(S_TRACE);
  3447. attrs ~= toAttr(c.status, command, indentValue, vars);
  3448. }
  3449. if (detail.use(CArg.BGM_PATH)) { mixin(S_TRACE);
  3450. if (c.bgmPath.length) { mixin(S_TRACE);
  3451. attrs ~= toAttr(encodePath(c.bgmPath), command, indentValue, vars);
  3452. } else { mixin(S_TRACE);
  3453. attrs ~= toAttr(Symbol("stop"), command, indentValue, vars);
  3454. }
  3455. }
  3456. if (detail.use(CArg.SOUND_PATH)) { mixin(S_TRACE);
  3457. attrs ~= toAttr(encodePath(c.soundPath), command, indentValue, vars);
  3458. }
  3459. if (!legacy) { mixin(S_TRACE);
  3460. if (detail.use(CArg.TRANSITION_SPEED)) { mixin(S_TRACE);
  3461. attrs ~= toAttr(c.transitionSpeed, command, indentValue, vars);
  3462. }
  3463. if (detail.use(CArg.TRANSITION)) { mixin(S_TRACE);
  3464. attrs ~= toAttr(c.transition, command, indentValue, vars);
  3465. }
  3466. }
  3467. if (c.talkerNC is Talker.VALUED) { mixin(S_TRACE);
  3468. // ?????
  3469. if (detail.use(CArg.INIT_VALUE)) { mixin(S_TRACE);
  3470. attrs ~= toAttr(c.initValue, command, indentValue, vars);
  3471. }
  3472. if (detail.use(CArg.COUPONS) && c.coupons.length) { mixin(S_TRACE);
  3473. attrs ~= toAttr(c.coupons, command, indentValue, vars);
  3474. }
  3475. }
  3476. if (detail.use(CArg.ROUND)) { mixin(S_TRACE);
  3477. attrs ~= toAttr(c.round, command, indentValue, vars);
  3478. }
  3479. bool useIf = c.next.length > 1;
  3480. bool useSif = c.next.length == 1 && c.next[0].name.length;
  3481. if (!useIf) { mixin(S_TRACE);
  3482. foreach (chld; c.next) { mixin(S_TRACE);
  3483. if (chld.name.length) { mixin(S_TRACE);
  3484. if (detail.nextType is CNextType.TEXT && chld.name == evtChildOK) { mixin(S_TRACE);
  3485. continue;
  3486. }
  3487. useIf = true;
  3488. break;
  3489. }
  3490. }
  3491. }
  3492. if (attrs.length) { mixin(S_TRACE);
  3493. buf ~= " " ~ std.string.join(attrs, ", ");
  3494. }
  3495. foreach (idx, chld; c.next) { mixin(S_TRACE);
  3496. if (useIf) { mixin(S_TRACE);
  3497. buf ~= "\n" ~ indentValue;
  3498. if (useSif) { mixin(S_TRACE);
  3499. buf ~= "sif ";
  3500. } else { mixin(S_TRACE);
  3501. buf ~= idx == 0 ? "if " : "elif ";
  3502. }
  3503. final switch (detail.nextType) {
  3504. case CNextType.NONE:
  3505. buf ~= `""`;
  3506. break;
  3507. case CNextType.TEXT:
  3508. buf ~= createString(chld.name);
  3509. break;
  3510. case CNextType.BOOL:
  3511. buf ~= icmp(chld.name, _prop.sys.evtChildTrue) == 0 ? "true" : "false";
  3512. break;
  3513. case CNextType.STEP:
  3514. case CNextType.ID_AREA:
  3515. case CNextType.ID_BATTLE:
  3516. if (icmp(chld.name, _prop.sys.evtChildDefault) == 0) { mixin(S_TRACE);
  3517. buf ~= "default";
  3518. } else { mixin(S_TRACE);
  3519. buf ~= chld.name;
  3520. }
  3521. break;
  3522. case CNextType.TRIO:
  3523. buf ~= createString(chld.name);
  3524. break;
  3525. }
  3526. buf ~= "\n";
  3527. auto nextIndent = useSif ? indentValue : indentValue ~ indent;
  3528. toScriptImpl(evtChildOK, buf, [chld], indent, nextIndent, keys, vars, legacy);
  3529. } else { mixin(S_TRACE);
  3530. buf ~= "\n";
  3531. if (c.type is CType.START) { mixin(S_TRACE);
  3532. toScriptImpl(evtChildOK, buf, [chld], indent, indentValue ~ indent, keys, vars, legacy);
  3533. } else { mixin(S_TRACE);
  3534. toScriptImpl(evtChildOK, buf, [chld], indent, indentValue, keys, vars, legacy);
  3535. }
  3536. }
  3537. }
  3538. if (useIf && !useSif) { mixin(S_TRACE);
  3539. buf ~= "\n" ~ indentValue ~ "fi";
  3540. }
  3541. }
  3542. }
  3543. }
  3544. private string validVarName(string name) { mixin(S_TRACE);
  3545. char[] buf;
  3546. buf.length = name.length;
  3547. foreach (i, char c; name) { mixin(S_TRACE);
  3548. switch (c) {
  3549. case '\0', '\b', '\t', '\n', '\v', '\f', '\r', ' ', '!',
  3550. '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',',
  3551. '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[',
  3552. '\\', ']', '^', '`', '{', '}', '|', '~':
  3553. buf[i] = '_';
  3554. break;
  3555. default:
  3556. buf[i] = c;
  3557. break;
  3558. }
  3559. }
  3560. return assumeUnique(buf);
  3561. }
  3562. private const string[] TOKENS = [
  3563. `[a-z_][a-z_0-9]*`, // symbol or keyword
  3564. "\\$[^\b\t\n\v\f\r !\"#$%&\'\\(\\)*+,\\-./:;<=>?@\\[\\\\\\]^`{}|~]+", // variable
  3565. `=`, // equql
  3566. `[0-9]+(\.[0-9]+)?`, // number
  3567. `\[`, // open bracket
  3568. `\]`, // close bracket
  3569. `,`, // comma
  3570. "\"(\"\"|[^\"])*?\"", // string
  3571. "'(''|[^'])*?'", // string
  3572. `@[ \t]*([0-9]+|c|center)?[ \t]*\n(([^@]|@@|\n)*\n)?[ \t]*@`, // string
  3573. `[ \t\r\n]+`, // whitespace
  3574. `\+`, // plus
  3575. `-`, // minus
  3576. `\*\/?`, // multiply or comment end
  3577. `\/(\/.*(\n|$)|\*)?`, // divide or comment start
  3578. `%`, // residue
  3579. `~`, // cat
  3580. `\(`, // open paren
  3581. `\)` // close paren
  3582. ];