/cwxeditor_src/cwx/script.d
D | 3617 lines | 3511 code | 32 blank | 74 comment | 700 complexity | 444ab6f057a2e301dd6f3fa17ba6be2f MD5 | raw file
Possible License(s): LGPL-2.1
Large files files are truncated, but you can click here to view the full file
- module cwx.script;
- import cwx.props;
- import cwx.types;
- import cwx.summary;
- import cwx.utils;
- import cwx.event;
- import cwx.motion;
- import cwx.background;
- import cwx.area;
- import cwx.card;
- import cwx.coupon;
- import cwx.structs;
- import std.algorithm;
- import std.conv;
- import std.array;
- import std.ascii;
- import std.stdio;
- import std.string;
- import std.regex;
- import std.exception;
- import std.traits;
- import std.range : ElementType;
- /// ???????????????????
- struct CWXSError {
- /// ?????????
- string message;
- /// ???????
- int errLine;
- /// ??????
- int errPos;
- /// ?????????? __FILE__
- string file;
- /// ?????????? __LINE__
- size_t line;
- }
- /// ditto
- class CWXScriptException : Exception {
- this (string file, size_t line, string text, const CWXSError[] errors, bool over100) { mixin(S_TRACE);
- super ("cwx script error", file, line);
- _text = text;
- _errors = errors;
- _over100 = over100;
- }
- private string _text;
- private const(CWXSError[]) _errors;
- private bool _over100;
- /// ??????????
- @property
- const
- string text() {return _text;}
- /// ???????????
- @property
- const
- const(CWXSError[]) errors() {return _errors;}
- /// ????100???????
- @property
- const
- bool over100() {return _over100;}
- }
- /// ?????????
- struct VarSet {
- string var; /// ????
- string value; /// ????
- }
- /// ???????????
- struct CompileOption {
- bool linkId = false; /// ?????????????
- int startLine = 0; /// ?????????????(??0)?
- int startPos = 0; /// ??????????????(??0)?
- int addLines = 0; /// ????????????(??0)?
- }
- /// ??????????????????????
- /// ??????????????CWXScriptException?????
- Content[] compile(const(CProps) prop, const(Summary) summ, string script, in CompileOption opt) { mixin(S_TRACE);
- auto compiler = new CWXScript(prop, summ, script);
- auto tokens = compiler.tokenize(script, opt);
- if (compiler.errors.length) throw new CWXScriptException(__FILE__, __LINE__, script, compiler.errors, false);
- auto nodes = compiler.analyzeSyntax(tokens);
- if (compiler.errors.length) throw new CWXScriptException(__FILE__, __LINE__, script, compiler.errors, false);
- auto r = compiler.analyzeSemantics(nodes, opt);
- if (compiler.errors.length) throw new CWXScriptException(__FILE__, __LINE__, script, compiler.errors, false);
- return r;
- }
- /// ??????????????????????????????
- /// ?????????????-1????
- CType firstContentType(const(CProps) prop, const(Summary) summ, string script) { mixin(S_TRACE);
- CompileOption opt;
- auto compiler = new CWXScript(prop, summ, script);
- auto tokens = compiler.tokenize(script, opt);
- if (compiler.errors.length) return cast(CType)-1;
- foreach (ref token; tokens) { mixin(S_TRACE);
- if (token.kind is CWXScript.Kind.START) return CType.START;
- if (token.kind is CWXScript.Kind.SYMBOL) { mixin(S_TRACE);
- auto p = token.value.toLower() in CWXScript.KEYS.keywords;
- if (p) { mixin(S_TRACE);
- return *p;
- }
- }
- }
- return cast(CType)-1;
- }
- /// ?????????????????????
- class CWXScript {
- private const(CProps) _prop;
- private const(Summary) _summ;
- private string _text;
- private size_t _maxError;
- private size_t _autoWrap;
- /// ???????????
- this (const(CProps) prop, const(Summary) summ, string text = "", size_t maxError = 100, size_t autoWrap = 250) { mixin(S_TRACE);
- _prop = prop;
- _summ = summ;
- _text = text;
- _maxError = maxError;
- _autoWrap = autoWrap;
- }
- private CWXSError[] _errors;
- /// ??????????????????????
- @property
- const
- const(CWXSError[]) errors() {return _errors;}
- /// s????????????????????
- static string createString(string s) { mixin(S_TRACE);
- return "\"" ~ std.array.replace(s, "\"", "\"\"") ~ "\"";
- }
- private void throwError(string File = __FILE__, size_t Line = __LINE__)
- (lazy string message, in Token tok) { mixin(S_TRACE);
- throwErrorToken!(File, Line)(message, tok.line, tok.pos, tok.value);
- }
- private void throwErrorToken(string File = __FILE__, size_t Line = __LINE__)
- (lazy string message, int line, int pos, string value) { mixin(S_TRACE);
- if (!_maxError) return;
- if (_errors.length && _errors[$ - 1].errLine == line && _errors[$ - 1].errPos == pos) { mixin(S_TRACE);
- // ?????????????????
- return;
- }
- string msg;
- if (!_prop || !_prop.msgs) { mixin(S_TRACE);
- msg = "";
- } else { mixin(S_TRACE);
- msg = message;
- }
- if (_maxError <= _errors.length) { mixin(S_TRACE);
- throw new CWXScriptException(__FILE__, __LINE__, _text, errors, true);
- }
- _errors ~= CWXSError(msg, line, pos, File, Line);
- }
- /// Token????
- static enum Kind {
- START, /// start
- IF, /// if
- FI, /// fi
- ELIF, /// elif
- SIF, /// sif
- O_BRA, /// [
- C_BRA, /// ]
- SYMBOL, /// ????????????????
- NUMBER, /// ???
- VAR_NAME, /// ????
- EQ, /// =
- COMMA, /// comma
- STRING, /// ????
- PLU, /// +
- MIN, /// -
- MUL, /// *
- DIV, /// /
- RES, /// %
- CAT, /// ~
- O_PAR, /// (
- C_PAR, /// )
- COMMENT /// ????
- }
- /// ???????????
- static struct Token {
- int line; /// ?????????
- int pos; /// ??????
- size_t index; /// ????????????
- Kind kind; /// ???
- string value; /// ??
- string comment = ""; /// ????????
- /// ??????
- const
- string toString() { mixin(S_TRACE);
- return .format("Token {line %d : %d, %d, %s, %s, %s}", line, pos, index, to!(string)(kind), value, comment);
- }
- /// o??????
- const
- bool opEquals(ref const(Token) o) { mixin(S_TRACE);
- return line == o.line && pos == o.pos && index == o.index && kind == o.kind && value == o.value && comment == o.comment;
- }
- }
- private static string[] wrap(string line, size_t width) { mixin(S_TRACE);
- if (width > 0) { mixin(S_TRACE);
- string[] lines;
- while (lengthJ(line) > width) { mixin(S_TRACE);
- auto l = sliceJ(line, 0, width);
- if (!l.length) { mixin(S_TRACE);
- l = sliceJ(line, 0, width + 1);
- }
- lines ~= l;
- line = line[l.length .. $];
- }
- lines ~= line;
- return lines;
- }
- return [line];
- }
- const
- private size_t stringCenter(string[] linesBase, size_t width) { mixin(S_TRACE);
- string[] lines;
- if (width > 0) { mixin(S_TRACE);
- foreach (line; linesBase) { mixin(S_TRACE);
- lines ~= wrap(line, width);
- }
- } else { mixin(S_TRACE);
- lines = linesBase;
- }
- int ln;
- int lc = cast(int) lineCount(lines);
- if (lc > 0 && lc < _prop.looks.messageLine) { mixin(S_TRACE);
- int lnt = cast(int) _prop.looks.messageLine - (lc - 1);
- ln = lnt / 2 + 1;
- } else { mixin(S_TRACE);
- ln = 0;
- }
- return ln > 0 ? ln : 0;
- }
- /// Token????????????????
- /// ????????????
- /// ????????????????????????
- private string stringValue(in Token tok, size_t width) { mixin(S_TRACE);
- string decode(in char[] s, char esc) { mixin(S_TRACE);
- char[] buf = new char[s.length];
- size_t len = 0;
- bool escape = false;
- foreach (char c; s) { mixin(S_TRACE);
- if (!escape && c == esc) { mixin(S_TRACE);
- escape = true;
- } else { mixin(S_TRACE);
- buf[len] = c;
- len++;
- escape = false;
- }
- }
- if (escape) { mixin(S_TRACE);
- buf[len] = esc;
- len++;
- }
- buf = buf[0 .. len];
- return assumeUnique(buf);
- }
- if (tok.kind !is Kind.STRING || tok.value.length < 2) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorInvalidString, tok);
- }
- if (tok.value[0] == '@') { mixin(S_TRACE);
- char[] buf;
- auto linesBase = .splitLines!string(tok.value[0 .. $ - 1].idup);
- string firstLine = linesBase[0];
- string[] lines;
- string[] resultLines;
- foreach (i, line; linesBase[1 .. $]) { mixin(S_TRACE);
- line = .astripl(line);
- line = decode(line, tok.value[0]);
- if (line.length >= 1 && line[0] == '\\') { mixin(S_TRACE);
- line = line[1 .. $];
- }
- resultLines ~= line;
- lines ~= wrap(line, width);
- }
- if (firstLine.length > 1) { mixin(S_TRACE);
- auto lnStr = std.string.toLower(.astrip(firstLine[1 .. $]));
- bool isNum = std.string.isNumeric(lnStr);
- if (!isNum && icmp(lnStr, "c") != 0 && icmp(lnStr, "center") != 0) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorInvalidStr, tok);
- }
- int ln;
- if (isNum) { mixin(S_TRACE);
- ln = .to!(int)(lnStr);
- } else { mixin(S_TRACE);
- ln = stringCenter(lines, 0);
- }
- if (ln > 0) { mixin(S_TRACE);
- buf.length = ln - 1;
- }
- buf[] = '\n';
- }
- foreach (i, line; resultLines) { mixin(S_TRACE);
- if (i > 0) buf ~= '\n';
- buf ~= line;
- }
- return assumeUnique(buf);
- }
- return decode(tok.value[1 .. $ - 1], tok.value[0]);
- } unittest { mixin(S_TRACE);
- debug mixin(UTPerf);
- auto s = new CWXScript(new CProps("", null), null);
- assert (s.stringValue(Token(0, 0, 0, Kind.STRING, `"abc"`), 0) == "abc");
- assert (s.stringValue(Token(0, 0, 0, Kind.STRING, `"a""bc"`), 0) == "a\"bc");
- assert (s.stringValue(Token(0, 0, 0, Kind.STRING, `'ab''c'`), 0) == "ab'c");
- assert (s.stringValue(Token(0, 2, 0, Kind.STRING, "@ 3\n\t\tte@@st\n t\\e\\st\n\\ a\n\\\\ a@"), false)
- == "\n\nte@st\nt\\e\\st\n a\n\\ a");
- assert (s.stringValue(Token(0, 0, 0, Kind.STRING, "@\nabcabc\n@"), 3) == "abcabc", s.stringValue(Token(0, 0, 0, Kind.STRING, "@\nabcabc\n@"), 3));
- }
- /// text???????????????
- /// ?????????????text???????
- string[] eatEmptyVars(ref string text, ref CompileOption opt) { mixin(S_TRACE);
- auto tokens = tokenizeImpl(text, true, opt);
- // ?????????
- Token[] tokens2;
- foreach (tok; tokens) { mixin(S_TRACE);
- if (tok.kind !is Kind.COMMENT) { mixin(S_TRACE);
- tokens2 ~= tok;
- }
- }
- string[] r;
- size_t index = 0;
- size_t len = 0;
- opt.startLine = 0;
- opt.startPos = 0;
- // ??????????????'='?????????????
- foreach (i, tok; tokens2) { mixin(S_TRACE);
- if (tok.kind is Kind.EQ) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorInvalidSyntax, tok);
- break;
- } else if (tok.kind is Kind.VAR_NAME) { mixin(S_TRACE);
- if (i + 1 < tokens2.length && tokens2[i + 1].kind is Kind.EQ) { mixin(S_TRACE);
- break;
- }
- index = tok.index;
- len = tok.value.length;
- opt.startLine = tok.line;
- opt.startPos = tok.pos + len;
- r ~= tok.value;
- }
- }
- text = text[index + len .. $];
- return r;
- } unittest { mixin(S_TRACE);
- debug mixin(UTPerf);
- CompileOption opt;
- auto s = new CWXScript(new CProps("", null), null);
- auto str = "/*c1*/ $test1 $test2 //$test3\n$test4// aaa\n$test5 = a $test6 endsc true";
- auto vars = s.eatEmptyVars(str, opt);
- assert (vars == ["$test1", "$test2", "$test4"], .text(vars));
- assert (str == "// aaa\n$test5 = a $test6 endsc true", str);
- assert (opt.startLine == 1);
- assert (opt.startPos == 6);
- }
- /// text??? = ?????????????
- static string pushVars(string text, in VarSet[] varTable, ref CompileOption opt) { mixin(S_TRACE);
- string[] lines;
- opt.addLines = 0;
- foreach (t; varTable) { mixin(S_TRACE);
- string v = t.value;
- if (!v.length) v = `""`;
- auto l = t.var ~ " = " ~ v;
- lines ~= l;
- opt.addLines += v.splitLines().length;
- }
- lines ~= text;
- opt.startLine -= opt.addLines;
- return lines.join("\n");
- } unittest { mixin(S_TRACE);
- CompileOption opt;
- VarSet[] varSet = [VarSet("$a", "1"), VarSet("$b", "2")];
- auto r = pushVars("aaa bbb", varSet, opt);
- assert (r == "$a = 1\n$b = 2\naaa bbb", r);
- assert (opt.startLine == -2, .text(opt.startLine));
- }
- /// text?Token??????
- Token[] tokenize(string text, in CompileOption opt = CompileOption.init) { mixin(S_TRACE);
- return tokenizeImpl(text, false, opt);
- } unittest { mixin(S_TRACE);
- debug mixin(UTPerf);
- auto s = new CWXScript(new CProps("", null), null);
- auto tokens = s.tokenize("");
- assert (tokens == [], to!string(tokens));
- tokens = s.tokenize("/*/*\n*/*/");
- assert (tokens == [Token(0, 0, 0, Kind.COMMENT, "/*/*\n*/*/", "")], to!string(tokens));
- tokens = s.tokenize("/*c*/start, 12.3 \ntest1 [$void] =\"str\ning//\"\n\r //comment\nELIF if\n1/2+3*4%(5-6)");
- assert (tokens
- == [
- Token(0, 0, 0, Kind.COMMENT, "/*c*/", ""),
- Token(0, 5, 5, Kind.START, "start", "c"),
- Token(0, 10, 10, Kind.COMMA, ","),
- Token(0, 12, 12, Kind.NUMBER, "12.3"),
- Token(1, 0, 18, Kind.SYMBOL, "test1"),
- Token(1, 6, 24, Kind.O_BRA, "["),
- Token(1, 7, 25, Kind.VAR_NAME, "$void"),
- Token(1, 12, 30, Kind.C_BRA, "]"),
- Token(1, 14, 32, Kind.EQ, "="),
- Token(1, 15, 33, Kind.STRING, "\"str\ning//\""),
- Token(4, 1, 47, Kind.COMMENT, "//comment\n", ""),
- Token(5, 0, 57, Kind.ELIF, "ELIF", "comment\n"),
- Token(5, 5, 62, Kind.IF, "if"),
- Token(6, 0, 65, Kind.NUMBER, "1"),
- Token(6, 1, 66, Kind.DIV, "/"),
- Token(6, 2, 67, Kind.NUMBER, "2"),
- Token(6, 3, 68, Kind.PLU, "+"),
- Token(6, 4, 69, Kind.NUMBER, "3"),
- Token(6, 5, 70, Kind.MUL, "*"),
- Token(6, 6, 71, Kind.NUMBER, "4"),
- Token(6, 7, 72, Kind.RES, "%"),
- Token(6, 8, 73, Kind.O_PAR, "("),
- Token(6, 9, 74, Kind.NUMBER, "5"),
- Token(6, 10, 75, Kind.MIN, "-"),
- Token(6, 11, 76, Kind.NUMBER, "6"),
- Token(6, 12, 77, Kind.C_PAR, ")")
- ], to!string(tokens));
- }
- private Token[] tokenizeImpl(ref string text, bool eatEmptyVarMode, in CompileOption opt) { mixin(S_TRACE);
- if (!text.length) return [];
- Token[] r;
- text = std.array.replace(text, "\r\n", "\n");
- text = std.array.replace(text, "\r", "\n");
- string dtext = text;
- auto reg = .regex("(" ~ std.string.join(TOKENS.dup, ")|(") ~ ")", "gi");
- size_t i = 0;
- size_t hits = 0;
- size_t pos = 0;
- size_t index = 0;
- int commentLevel = 0;
- size_t lastCommentLine = 0;
- size_t lastCommentPos = 0;
- size_t lastCommentIndex = 0;
- string post;
- bool spaceAfter = false;
- string docComment = "";
- string fullComment = "";
- @property int sLine() {return cast(int) i + opt.startLine;}
- @property int sPos() {return (cast(int) i == opt.addLines) ? (cast(int) pos + opt.startPos) : pos;}
- foreach (token; .match(dtext, reg)) { mixin(S_TRACE);
- post = token.post;
- string pre = token.pre;
- index = pre.length;
- auto dstr = token.hit;
- void retCount2(string dstr) { mixin(S_TRACE);
- size_t count = .count(dstr, "\n");
- if (count > 0) { mixin(S_TRACE);
- pos = dstr.length - std.string.lastIndexOf(dstr, '\n') - 1;
- i += count;
- } else { mixin(S_TRACE);
- pos += dstr.length;
- }
- }
- void retCount() { mixin(S_TRACE);
- retCount2(dstr);
- }
- auto c = dstr[0];
- string str = to!string(dstr);
- if (cast(int) pre.length - cast(int) hits > 0) { mixin(S_TRACE);
- if (0 < commentLevel) { mixin(S_TRACE);
- /// in comment
- retCount2(pre[hits .. $]);
- hits = pre.length;
- } else { mixin(S_TRACE);
- string lpre = pre;
- if (lpre.length && (lpre[$ - 1] == '@' || lpre[$ - 1] == '"' || lpre[$ - 1] == '\'')) { mixin(S_TRACE);
- throwErrorToken(_prop.msgs.scriptErrorUnCloseString, sLine, sPos, "");
- return r;
- } else { mixin(S_TRACE);
- throwErrorToken(_prop.msgs.scriptErrorInvalidToken, sLine, sPos, "");
- }
- }
- }
- if (0 < commentLevel) { mixin(S_TRACE);
- fullComment ~= .text(pre[hits .. $]) ~ str;
- }
- bool commentStart = false;
- if (str == "/*") { mixin(S_TRACE);
- // multi line comment (open)
- spaceAfter = true;
- if (commentLevel == 0) { mixin(S_TRACE);
- commentStart = true;
- lastCommentLine = i;
- lastCommentPos = pos;
- lastCommentIndex = index;
- }
- pos += dstr.length;
- if (0 == commentLevel) fullComment = str;
- commentLevel++;
- } else if (str == "*/") { mixin(S_TRACE);
- // multi line comment (close)
- spaceAfter = true;
- pos += dstr.length;
- if (commentLevel <= 0) { mixin(S_TRACE);
- throwErrorToken(_prop.msgs.scriptErrorUnOpenComment, sLine, sPos, str);
- }
- commentLevel--;
- if (0 == commentLevel) { mixin(S_TRACE);
- r ~= Token(lastCommentLine, lastCommentPos, lastCommentIndex, Kind.COMMENT, fullComment, "");
- }
- } else if (0 < commentLevel) { mixin(S_TRACE);
- spaceAfter = true;
- retCount();
- } else if (std.ascii.isAlpha(c) || c == '_') { mixin(S_TRACE);
- if (eatEmptyVarMode) return r;
- spaceAfter = false;
- // symbol
- switch (std.string.toLower(dstr)) {
- case "start":
- r ~= Token(sLine, sPos, index, Kind.START, str, docComment);
- break;
- case "if":
- r ~= Token(sLine, sPos, index, Kind.IF, str, docComment);
- break;
- case "elif":
- r ~= Token(sLine, sPos, index, Kind.ELIF, str, docComment);
- break;
- case "fi":
- r ~= Token(sLine, sPos, index, Kind.FI, str, docComment);
- break;
- case "sif":
- r ~= Token(sLine, sPos, index, Kind.SIF, str, docComment);
- break;
- default:
- r ~= Token(sLine, sPos, index, Kind.SYMBOL, str, docComment);
- break;
- }
- pos += dstr.length;
- docComment = "";
- } else if (c == '$') { mixin(S_TRACE);
- spaceAfter = false;
- r ~= Token(sLine, sPos, index, Kind.VAR_NAME, str, docComment);
- pos += dstr.length;
- docComment = "";
- } else if (c == '=') { mixin(S_TRACE);
- spaceAfter = false;
- r ~= Token(sLine, sPos, index, Kind.EQ, str, docComment);
- pos += dstr.length;
- docComment = "";
- } else if (c == '[') { mixin(S_TRACE);
- if (eatEmptyVarMode) return r;
- // open bracket
- spaceAfter = false;
- r ~= Token(sLine, sPos, index, Kind.O_BRA, str, docComment);
- pos += dstr.length;
- docComment = "";
- } else if (c == ']') { mixin(S_TRACE);
- if (eatEmptyVarMode) return r;
- // close bracket
- spaceAfter = false;
- r ~= Token(sLine, sPos, index, Kind.C_BRA, str, docComment);
- pos += dstr.length;
- docComment = "";
- } else if (isDigit(c)) { mixin(S_TRACE);
- if (eatEmptyVarMode) return r;
- // number
- spaceAfter = false;
- r ~= Token(sLine, sPos, index, Kind.NUMBER, str, docComment);
- pos += dstr.length;
- docComment = "";
- } else if (c == '@' || c == '"' || c == '\'') { mixin(S_TRACE);
- if (eatEmptyVarMode) return r;
- // string
- if (!spaceAfter && r.length && r[$ - 1].kind is Kind.STRING
- && r[$ - 1].value[$ - 1] == c) { mixin(S_TRACE);
- // ???string???
- r[$ - 1].value ~= str;
- } else { mixin(S_TRACE);
- r ~= Token(sLine, sPos, index, Kind.STRING, str, docComment);
- }
- spaceAfter = false;
- retCount();
- docComment = "";
- } else if (std.ascii.isWhite(c)) { mixin(S_TRACE);
- // whitespace
- spaceAfter = true;
- retCount();
- } else if (c == '+') { mixin(S_TRACE);
- if (eatEmptyVarMode) return r;
- // plus
- spaceAfter = false;
- r ~= Token(sLine, sPos, index, Kind.PLU, str, docComment);
- pos += dstr.length;
- docComment = "";
- } else if (c == '-') { mixin(S_TRACE);
- if (eatEmptyVarMode) return r;
- // minus
- spaceAfter = false;
- r ~= Token(sLine, sPos, index, Kind.MIN, str, docComment);
- pos += dstr.length;
- docComment = "";
- } else if (c == '*') { mixin(S_TRACE);
- if (eatEmptyVarMode) return r;
- // multiply
- spaceAfter = false;
- r ~= Token(sLine, sPos, index, Kind.MUL, str, docComment);
- pos += dstr.length;
- docComment = "";
- } else if (c == '/') { mixin(S_TRACE);
- if (dstr.length >= 2 && str[1] == '/') { mixin(S_TRACE);
- // line comment
- spaceAfter = true;
- r ~= Token(sLine, sPos, index, Kind.COMMENT, str, "");
- i++;
- pos = 0;
- if (2 < str.length) docComment ~= str[2 .. $];
- } else { mixin(S_TRACE);
- if (eatEmptyVarMode) return r;
- // divide
- spaceAfter = false;
- r ~= Token(sLine, sPos, index, Kind.DIV, str, docComment);
- pos += dstr.length;
- docComment = "";
- }
- } else if (c == '%') { mixin(S_TRACE);
- if (eatEmptyVarMode) return r;
- // residue
- spaceAfter = false;
- r ~= Token(sLine, sPos, index, Kind.RES, str, docComment);
- pos += dstr.length;
- docComment = "";
- } else if (c == '~') { mixin(S_TRACE);
- if (eatEmptyVarMode) return r;
- // cat
- spaceAfter = false;
- r ~= Token(sLine, sPos, index, Kind.CAT, str, docComment);
- pos += dstr.length;
- docComment = "";
- } else if (c == '(') { mixin(S_TRACE);
- if (eatEmptyVarMode) return r;
- // open paren
- spaceAfter = false;
- r ~= Token(sLine, sPos, index, Kind.O_PAR, str, docComment);
- pos += dstr.length;
- docComment = "";
- } else if (c == ')') { mixin(S_TRACE);
- if (eatEmptyVarMode) return r;
- // close paren
- spaceAfter = false;
- r ~= Token(sLine, sPos, index, Kind.C_PAR, str, docComment);
- pos += dstr.length;
- docComment = "";
- } else if (c == ',') { mixin(S_TRACE);
- if (eatEmptyVarMode) return r;
- // comma
- spaceAfter = false;
- r ~= Token(sLine, sPos, index, Kind.COMMA, str, docComment);
- pos += dstr.length;
- docComment = "";
- } else { mixin(S_TRACE);
- assert (0);
- }
- if (!commentStart && 0 < commentLevel) { mixin(S_TRACE);
- docComment ~= str;
- }
- hits += dstr.length;
- }
- if (0 < commentLevel) { mixin(S_TRACE);
- throwErrorToken(_prop.msgs.scriptErrorUnCloseComment, lastCommentLine, lastCommentPos, "");
- }
- if (post.length) throwErrorToken(_prop.msgs.scriptErrorInvalidToken, sLine, sPos, "");
- return r;
- }
- private enum CRKind {STR, INT, REAL}
- private class CalcResult {
- CRKind kind = CRKind.INT;
- union {
- string str;
- long numInt;
- real numReal;
- }
- this () { mixin(S_TRACE);
- numInt = 0;
- }
- void cat(in CProps prop, in Token tok, in CalcResult rval) { mixin(S_TRACE);
- switch (kind) {
- case CRKind.STR: break;
- case CRKind.INT: str = to!(string)(numInt); break;
- case CRKind.REAL: str = to!(string)(numReal); break;
- default: assert (0);
- }
- switch (rval.kind) {
- case CRKind.STR:
- str ~= rval.str;
- break;
- case CRKind.INT:
- str ~= to!(string)(rval.numInt);
- break;
- case CRKind.REAL:
- str ~= to!(string)(rval.numReal);
- break;
- default: assert (0);
- }
- kind = CRKind.STR;
- }
- private void calc(string Calc, bool ChkDiv)(in CProps prop, in Token tok, in CalcResult rval) { mixin(S_TRACE);
- if (kind is CRKind.STR || rval.kind is CRKind.STR) { mixin(S_TRACE);
- throwError(prop.msgs.scriptErrorInvalidNumber, tok);
- }
- static if (ChkDiv) {
- if ((rval.kind is CRKind.REAL ? rval.numReal : rval.numInt) == 0) { mixin(S_TRACE);
- throwError(prop.msgs.scriptErrorZeroDivision, tok);
- }
- }
- if (kind is CRKind.REAL || rval.kind is CRKind.REAL) { mixin(S_TRACE);
- real lvalue = kind is CRKind.REAL ? numReal : numInt;
- real rvalue = rval.kind is CRKind.REAL ? rval.numReal : rval.numInt;
- mixin ("numReal = lvalue " ~ Calc ~ " rvalue;");
- kind = CRKind.REAL;
- } else if (kind is CRKind.INT && rval.kind is CRKind.INT) { mixin(S_TRACE);
- mixin ("numInt " ~ Calc ~ "= rval.numInt;");
- } else { mixin(S_TRACE);
- throwError(prop.msgs.scriptErrorInvalidNumber, tok);
- }
- }
- public alias calc!("+", false) add;
- public alias calc!("-", false) min;
- public alias calc!("*", false) mul;
- public alias calc!("/", true) div;
- public alias calc!("%", true) res;
- const
- bool opEquals(ref const(CalcResult) val) { mixin(S_TRACE);
- if (kind !is val.kind) return false;
- final switch (kind) {
- case CRKind.STR: return str == val.str;
- case CRKind.INT: return numInt == val.numInt;
- case CRKind.REAL: return numReal == val.numReal;
- }
- }
- const
- bool opEquals(int val) { mixin(S_TRACE);
- return opEquals(cast(long) val);
- }
- const
- bool opEquals(long val) { mixin(S_TRACE);
- final switch (kind) {
- case CRKind.STR: return false;
- case CRKind.INT: return numInt == val;
- case CRKind.REAL: return numReal == val;
- }
- }
- const
- bool opEquals(real val) { mixin(S_TRACE);
- final switch (kind) {
- case CRKind.STR: return false;
- case CRKind.INT: return numInt == val;
- case CRKind.REAL: return numReal == val;
- }
- }
- const
- bool opEquals(string val) { mixin(S_TRACE);
- return kind is CRKind.STR && str == val;
- }
- override
- const
- string toString() { mixin(S_TRACE);
- final switch (kind) {
- case CRKind.STR: return str;
- case CRKind.INT: return to!(string)(numInt);
- case CRKind.REAL: return to!(string)(numReal);
- }
- }
- }
- private const OPE_LEVEL_MAX = 2;
- private CalcResult calcNum(in Token[] tokens, ref size_t i, in const(Node)[][string] varTable, size_t strWidth) { mixin(S_TRACE);
- assert (i < tokens.length);
- auto tok = tokens[i];
- if (tok.kind is Kind.VAR_NAME) { mixin(S_TRACE);
- i++;
- try { mixin(S_TRACE);
- auto vt = var(tok, varTable);
- auto r = new CalcResult;
- if (vt.kind is Kind.STRING) { mixin(S_TRACE);
- r.kind = CRKind.STR;
- r.str = stringValue(vt, strWidth);
- } else if (std.string.indexOf(vt.value, '.') != -1) { mixin(S_TRACE);
- r.kind = CRKind.REAL;
- r.numReal = to!(real)(vt.value);
- } else { mixin(S_TRACE);
- r.kind = CRKind.INT;
- r.numInt = to!(long)(vt.value);
- }
- return r;
- } catch (Exception e) {
- throwError(_prop.msgs.scriptErrorReqNumber, tok);
- auto r = new CalcResult;
- r.kind = CRKind.INT;
- r.numInt = 0;
- return r;
- }
- }
- bool min = false;
- if (tok.kind is Kind.PLU) { mixin(S_TRACE);
- i++;
- if (tokens[i].kind !is Kind.NUMBER) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorInvalidNumber, tok);
- }
- } else if (tok.kind is Kind.MIN) { mixin(S_TRACE);
- i++;
- if (tokens[i].kind !is Kind.NUMBER) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorInvalidNumber, tok);
- }
- min = true;
- }
- if (tokens.length <= i || !(tokens[i].kind is Kind.NUMBER || tokens[i].kind is Kind.STRING)) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorInvalidNumber, tok);
- }
- auto r = new CalcResult;
- try { mixin(S_TRACE);
- if (tokens[i].kind is Kind.NUMBER) { mixin(S_TRACE);
- if (std.string.indexOf(tokens[i].value, '.') != -1) { mixin(S_TRACE);
- r.kind = CRKind.REAL;
- r.numReal = to!(real)(tokens[i].value);
- if (min) r.numReal = -r.numReal;
- } else { mixin(S_TRACE);
- r.kind = CRKind.INT;
- r.numInt = to!(long)(tokens[i].value);
- if (min) r.numInt = -r.numInt;
- }
- } else if (tokens[i].kind is Kind.STRING) { mixin(S_TRACE);
- r.kind = CRKind.STR;
- r.str = stringValue(tokens[i], strWidth);
- } else { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorReqNumber, tokens[i]);
- }
- i++;
- return r;
- } catch (Exception e) {
- throwError(_prop.msgs.scriptErrorReqNumber, tokens[i]);
- }
- return r;
- }
- private CalcResult calcPar(in Token[] tokens, ref size_t i, in const(Node)[][string] varTable, size_t strWidth) { mixin(S_TRACE);
- assert (i < tokens.length);
- auto tok = tokens[i];
- switch (tok.kind) {
- case Kind.O_PAR:
- i++;
- auto r = calcImpl(0, tokens, i, varTable, strWidth);
- if (tokens[i].kind !is Kind.C_PAR) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorCloseParenNotFound, tok);
- }
- i++;
- return r;
- default:
- return calcNum(tokens, i, varTable, strWidth);
- }
- }
- private CalcResult calcImpl(size_t opeLevel, in Token[] tokens, ref size_t i, in const(Node)[][string] varTable, size_t strWidth) { mixin(S_TRACE);
- assert (i < tokens.length);
- CalcResult r;
- if (opeLevel >= OPE_LEVEL_MAX) { mixin(S_TRACE);
- r = calcPar(tokens, i, varTable, strWidth);
- } else { mixin(S_TRACE);
- r = calcImpl(opeLevel + 1, tokens, i, varTable, strWidth);
- }
- while (i < tokens.length) { mixin(S_TRACE);
- auto tok = tokens[i];
- switch (opeLevel) {
- case 0:
- switch (tok.kind) {
- case Kind.CAT:
- i++;
- if (tokens.length < i) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorInvalidCalc, tok);
- return r;
- }
- r.cat(_prop, tok, calcImpl(1, tokens, i, varTable, strWidth));
- break;
- default:
- return r;
- }
- break;
- case 1:
- switch (tok.kind) {
- case Kind.PLU:
- i++;
- if (tokens.length < i) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorInvalidCalc, tok);
- return r;
- }
- r.add(_prop, tok, calcImpl(1, tokens, i, varTable, strWidth));
- break;
- case Kind.MIN:
- i++;
- if (tokens.length < i) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorInvalidCalc, tok);
- return r;
- }
- r.min(_prop, tok, calcImpl(1, tokens, i, varTable, strWidth));
- break;
- default:
- return r;
- }
- break;
- case 2:
- switch (tok.kind) {
- case Kind.MUL:
- i++;
- if (tokens.length <= i) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorInvalidCalc, tok);
- return r;
- }
- r.mul(_prop, tok, calcPar(tokens, i, varTable, strWidth));
- break;
- case Kind.DIV:
- i++;
- if (tokens.length <= i) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorInvalidCalc, tok);
- return r;
- }
- r.div(_prop, tok, calcPar(tokens, i, varTable, strWidth));
- break;
- case Kind.RES:
- i++;
- if (tokens.length <= i) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorInvalidCalc, tok);
- return r;
- }
- r.res(_prop, tok, calcPar(tokens, i, varTable, strWidth));
- break;
- default:
- return r;
- }
- break;
- default: assert (0);
- }
- }
- return r;
- }
- /// tokens???????????????????
- CalcResult calc(in Token[] tokens, ref size_t i, in const(Node)[][string] varTable, size_t strWidth) { mixin(S_TRACE);
- assert (i < tokens.length);
- return calcImpl(0, tokens, i, varTable, strWidth);
- } unittest { mixin(S_TRACE);
- debug mixin(UTPerf);
- size_t i;
- Token[] tokens;
- const(Node)[][string] varTable;
- auto tok = Token(0, 0, 0, Kind.NUMBER, "15");
- varTable["$abc"] = [Node(NodeType.VALUE, tok)];
- tok = Token(0, 0, 0, Kind.NUMBER, "0");
- varTable["$s"] = [Node(NodeType.VALUE, tok)];
- auto s = new CWXScript(new CProps("", null), null);
- i = 0;
- assert (s.calc(s.tokenize("10 * $abc"), i, varTable, 0) == 150);
- i = 0;
- assert (s.calc(s.tokenize("(-42)"), i, varTable, 0) == -42);
- i = 0;
- assert (s.calc(s.tokenize("2*2+3"), i, varTable, 0) == 7);
- i = 0;
- assert (s.calc(s.tokenize("2*(2+3)"), i, varTable, 0) == 10);
- i = 0;
- assert (s.calc(s.tokenize("1+2*3"), i, varTable, 0) == 7);
- i = 0;
- assert (s.calc(s.tokenize("(1+2)*3"), i, varTable, 0) == 9);
- i = 0;
- assert (s.calc(s.tokenize("-3-3"), i, varTable, 0) == -6);
- i = 0;
- assert (s.calc(s.tokenize("2 * 3 % 4"), i, varTable, 0) == 2);
- i = 0;
- assert (s.calc(s.tokenize("3 + -3-3"), i, varTable, 0) == -3);
- i = 0;
- assert (s.calc(s.tokenize("1+2 * 3 % 4"), i, varTable, 0) == 3);
- i = 0;
- assert (s.calc(s.tokenize("1+2 ~ 3 % 4"), i, varTable, 0) == "33");
- i = 0;
- tokens = s.tokenize("1 + $s");
- assert (s.calc(tokens, i, varTable, 0) == 1);
- i = 0;
- tokens = s.tokenize("$s + 1");
- assert (s.calc(tokens, i, varTable, 0) == 1);
- i = 0;
- tokens = s.tokenize("1+2 * 3 % 4 + -3-$abc $abc");
- assert (s.calc(tokens, i, varTable, 0) == -15);
- assert (tokens[i].value == "$abc");
- i = 0;
- tokens = s.tokenize("(1+2) * 3 % 4 + (-3-3) if");
- assert (s.calc(tokens, i, varTable, 0) == -5);
- assert (tokens[i].value == "if");
- }
- private static struct Keywords {
- immutable CType[string] keywords;
- immutable string[CType] commands;
- }
- private static shared immutable Keywords KEYS;
- shared static this () { mixin(S_TRACE);
- auto keywords = [
- cast(string) "start":CType.START,
- cast(string) "gobattle":CType.START_BATTLE,
- cast(string) "endsc":CType.END,
- cast(string) "gameover":CType.END_BAD_END,
- cast(string) "goarea":CType.CHANGE_AREA,
- cast(string) "chback":CType.CHANGE_BG_IMAGE,
- cast(string) "effect":CType.EFFECT,
- cast(string) "break":CType.EFFECT_BREAK,
- cast(string) "gostart":CType.LINK_START,
- cast(string) "gopack":CType.LINK_PACKAGE,
- cast(string) "msg":CType.TALK_MESSAGE,
- cast(string) "dialog":CType.TALK_DIALOG,
- cast(string) "bgm":CType.PLAY_BGM,
- cast(string) "se":CType.PLAY_SOUND,
- cast(string) "wait":CType.WAIT,
- cast(string) "elapse":CType.ELAPSE_TIME,
- cast(string) "callstart":CType.CALL_START,
- cast(string) "callpack":CType.CALL_PACKAGE,
- cast(string) "brflag":CType.BRANCH_FLAG,
- cast(string) "brstepm":CType.BRANCH_MULTI_STEP,
- cast(string) "brstept":CType.BRANCH_STEP,
- cast(string) "selmember":CType.BRANCH_SELECT,
- cast(string) "brability":CType.BRANCH_ABILITY,
- cast(string) "brrandom":CType.BRANCH_RANDOM,
- cast(string) "brlevel":CType.BRANCH_LEVEL,
- cast(string) "brstatus":CType.BRANCH_STATUS,
- cast(string) "brcount":CType.BRANCH_PARTY_NUMBER,
- cast(string) "brarea":CType.BRANCH_AREA,
- cast(string) "brbattle":CType.BRANCH_BATTLE,
- cast(string) "bronbattle":CType.BRANCH_IS_BATTLE,
- cast(string) "brcast":CType.BRANCH_CAST,
- cast(string) "britem":CType.BRANCH_ITEM,
- cast(string) "brskill":CType.BRANCH_SKILL,
- cast(string) "brinfo":CType.BRANCH_INFO,
- cast(string) "brbeast":CType.BRANCH_BEAST,
- cast(string) "brmoney":CType.BRANCH_MONEY,
- cast(string) "brcoupon":CType.BRANCH_COUPON,
- cast(string) "brstamp":CType.BRANCH_COMPLETE_STAMP,
- cast(string) "brgossip":CType.BRANCH_GOSSIP,
- cast(string) "setflag":CType.SET_FLAG,
- cast(string) "setstep":CType.SET_STEP,
- cast(string) "stepup":CType.SET_STEP_UP,
- cast(string) "stepdown":CType.SET_STEP_DOWN,
- cast(string) "revflag":CType.REVERSE_FLAG,
- cast(string) "chkflag":CType.CHECK_FLAG,
- cast(string) "getcast":CType.GET_CAST,
- cast(string) "getitem":CType.GET_ITEM,
- cast(string) "getskill":CType.GET_SKILL,
- cast(string) "getinfo":CType.GET_INFO,
- cast(string) "getbeast":CType.GET_BEAST,
- cast(string) "getmoney":CType.GET_MONEY,
- cast(string) "getcoupon":CType.GET_COUPON,
- cast(string) "getstamp":CType.GET_COMPLETE_STAMP,
- cast(string) "getgossip":CType.GET_GOSSIP,
- cast(string) "losecast":CType.LOSE_CAST,
- cast(string) "loseitem":CType.LOSE_ITEM,
- cast(string) "loseskill":CType.LOSE_SKILL,
- cast(string) "loseinfo":CType.LOSE_INFO,
- cast(string) "losebeast":CType.LOSE_BEAST,
- cast(string) "losemoney":CType.LOSE_MONEY,
- cast(string) "losecoupon":CType.LOSE_COUPON,
- cast(string) "losestamp":CType.LOSE_COMPLETE_STAMP,
- cast(string) "losegossip":CType.LOSE_GOSSIP,
- cast(string) "showparty":CType.SHOW_PARTY,
- cast(string) "hideparty":CType.HIDE_PARTY,
- cast(string) "redraw":CType.REDISPLAY,
- cast(string) "cpstep":CType.SUBSTITUTE_STEP,
- cast(string) "cpflag":CType.SUBSTITUTE_FLAG,
- cast(string) "cmpstep":CType.BRANCH_STEP_CMP,
- cast(string) "cmpflag":CType.BRANCH_FLAG_CMP,
- cast(string) "selrandom":CType.BRANCH_RANDOM_SELECT,
- cast(string) "brkeycode":CType.BRANCH_KEY_CODE,
- cast(string) "chkstep":CType.CHECK_STEP,
- cast(string) "brround":CType.BRANCH_ROUND,
- cast(string) "mvback":CType.MOVE_BG_IMAGE,
- cast(string) "rplback":CType.REPLACE_BG_IMAGE,
- cast(string) "loseback":CType.LOSE_BG_IMAGE,
- ];
- string[CType] commands;
- foreach (name, type; keywords) { mixin(S_TRACE);
- commands[type] = name;
- }
- KEYS = Keywords(.assumeUnique(keywords), .assumeUnique(commands));
- }
- /// ??????
- enum NodeType {
- VAR_SET, /// ??????
- START, /// ???????????
- COMMAND, /// ??????????
- VALUES, /// VALUE????
- VALUE, /// ?????
- }
- /// ??????????????????????
- struct Node {
- NodeType type; /// ??
- Token token; /// ???Token?
- const(Node)[] texts = []; /// ?????
- const(Node)[] attr; /// ???
- const(Node)[] childs; /// ?????
- bool nextIsChild; /// ??????????????
- alias childs values; /// ????VALUES?????????VALUE???????
- const(Token)[] calc; /// ????
- alias calc var; /// ???
- alias texts value; /// ????
- const(Node)[] beforeVars; /// ????????????????
- /// o??????
- const
- bool opEquals(ref const(Node) o) { mixin(S_TRACE);
- return type == o.type && token == o.token && texts == o.texts
- && attr == o.attr && childs == o.childs && calc == o.calc
- && beforeVars == o.beforeVars;
- }
- /// ?????????
- @property
- const
- Node dup() { mixin(S_TRACE);
- return Node(type, token, texts.dup, attr.dup, childs.dup, nextIsChild, calc.dup, beforeVars.dup);
- }
- /// ??????
- const
- string toString() {return token.toString();}
- /// ???????????????????
- static string code(string indent, in Node[] array) {return code(" ", "", "", array, "sif");}
- private static string code(string indent, string bIndentValue, string indentValue, in Node[] array, string ifString) { mixin(S_TRACE);
- string calcCode(in Node[] calc) { mixin(S_TRACE);
- char[] calcBuf;
- enforce(1 == calc.length);
- foreach (i, tok; calc[0].calc) { mixin(S_TRACE);
- if ((tok.kind is Kind.C_PAR)
- || (i > 0 && calc[0].calc[i - 1].kind is Kind.O_PAR)) { mixin(S_TRACE);
- calcBuf ~= tok.value;
- } else { mixin(S_TRACE);
- if (i > 0) calcBuf ~= " ";
- calcBuf ~= tok.value;
- }
- }
- return assumeUnique(calcBuf);
- }
- string buf = "";
- if (!array.length) return buf;
- foreach (node; array) { mixin(S_TRACE);
- if (buf.length) { mixin(S_TRACE);
- buf ~= "\n";
- }
- if (node.type is NodeType.COMMAND && node.texts.length) { mixin(S_TRACE);
- buf ~= .format("%s%s %s\n", ifString == "sif" ? indentValue : bIndentValue, ifString, calcCode(node.texts));
- ifString = "sif";
- }
- if (node.type is NodeType.VAR_SET) { mixin(S_TRACE);
- enforce(node.value.length > 0, new Exception("Invalid node", __FILE__, __LINE__));
- if (node.value[0].token.kind is Kind.STRING) { mixin(S_TRACE);
- buf ~= .format("%s%s = %s", indentValue, node.token.value, node.value[0].token.value);
- } else { mixin(S_TRACE);
- buf ~= .format("%s%s = %s", node.token.value, indentValue, calcCode(node.value));
- }
- continue;
- } else if (node.type is NodeType.VALUE) { mixin(S_TRACE);
- if (node.var.length <= 1) { mixin(S_TRACE);
- buf ~= node.token.value;
- } else { mixin(S_TRACE);
- buf ~= calcCode([node]);
- }
- continue;
- } else if (node.type is NodeType.VALUES) { mixin(S_TRACE);
- string vals = "[";
- foreach (i, c; node.values) { mixin(S_TRACE);
- if (i > 0) vals ~= ", ";
- vals ~= Node.code(indent, [c]);
- }
- vals ~= "]";
- buf ~= vals;
- continue;
- }
- foreach (i, var; node.beforeVars) { mixin(S_TRACE);
- buf ~= .format("%s%s\n", indentValue, Node.code(indent, [var]));
- }
- string attrs = "";
- if (node.token.kind is Kind.START) { mixin(S_TRACE);
- attrs = " " ~ calcCode(node.texts);
- } else { mixin(S_TRACE);
- foreach (i, a; node.attr) { mixin(S_TRACE);
- auto ac = Node.code(indent, [a]);
- if (a.type is NodeType.VALUES && i > 0) { mixin(S_TRACE);
- attrs ~= "\n";
- attrs ~= indentValue;
- attrs ~= .rightJustify("", node.token.value.length + 1);
- attrs ~= ac;
- } else { mixin(S_TRACE);
- attrs ~= i == 0 ? " " : ", ";
- attrs ~= ac;
- }
- }
- }
- buf ~= .format("%s%s%s", indentValue, node.token.value, attrs);
- if (node.nextIsChild) continue;
- bool startBlock = true;
- bool nextIsStartPoint = true;
- const(Node)[][] block;
- foreach (i, c; node.childs) { mixin(S_TRACE);
- if (startBlock && nextIsStartPoint) { mixin(S_TRACE);
- block ~= new const(Node)[0];
- }
- startBlock = false;
- if (c.type !is NodeType.VAR_SET) { mixin(S_TRACE);
- nextIsStartPoint = !c.nextIsChild;
- startBlock = true;
- }
- block[$ - 1] ~= c;
- }
- if (block.length > 1) { mixin(S_TRACE);
- foreach (i, b; block) { mixin(S_TRACE);
- string f = i == 0 ? "if" : "elif";
- buf ~= "\n";
- buf ~= Node.code(indent, indentValue, indentValue ~ indent, b, f);
- }
- buf ~= "\n";
- buf ~= indentValue ~ "fi";
- } else if (block.length == 1) { mixin(S_TRACE);
- buf ~= "\n";
- buf ~= Node.code(indent, indentValue, node.token.kind is Kind.START ? indentValue ~ indent : indentValue, block[0], "sif");
- }
- }
- return buf;
- }
- }
- /// ?????????????
- private string attrValue(in Node node, in const(Node)[][string] varTable, size_t strWidth) { mixin(S_TRACE);
- switch (node.token.kind) {
- case Kind.SYMBOL: return std.string.toLower(node.token.value);
- case Kind.VAR_NAME:
- if (1 < node.calc.length) goto case Kind.STRING;
- auto nodes = var(node, varTable);
- if (!nodes.length) return "";
- auto tok = nodes[0].token;
- if (tok.kind is Kind.STRING) { mixin(S_TRACE);
- return stringValue(tok, strWidth);
- } else if (tok.kind is Kind.SYMBOL) { mixin(S_TRACE);
- return std.string.toLower(tok.value);
- }
- return attrValue(nodes[0], varTable, strWidth);
- case Kind.STRING, Kind.NUMBER, Kind.PLU, Kind.MIN, Kind.O_PAR:
- size_t i = 0;
- auto r = calc(node.calc, i, varTable, strWidth);
- final switch (r.kind) {
- case CRKind.STR: return r.str;
- case CRKind.INT: return to!(string)(r.numInt);
- case CRKind.REAL: return to!(string)(r.numReal);
- }
- case Kind.O_BRA: return "";
- case Kind.COMMA: return "";
- default:
- throwError(_prop.msgs.scriptErrorInvalidAttr, node.token);
- return "";
- }
- assert (0);
- }
- /// ???????
- private const(Node)[] varValue(in Node node, const(Node)[] values, in const(Node)[][string] varTable, size_t strWidth) { mixin(S_TRACE);
- if (!values.length) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorInvalidVar, node.token);
- return values;
- }
- const(Node)[] r;
- foreach (v; values) { mixin(S_TRACE);
- const(Node)[] vs;
- if (v.token.kind is Kind.VAR_NAME) { mixin(S_TRACE);
- vs ~= var(v, varTable);
- } else { mixin(S_TRACE);
- vs = [v];
- }
- foreach (v2; vs) { mixin(S_TRACE);
- switch (v2.token.kind) {
- case Kind.STRING, Kind.NUMBER, Kind.PLU, Kind.MIN, Kind.O_PAR:
- // ??????????
- size_t i = 0;
- Node rNode = v2.dup;
- auto cr = calc(v2.calc, i, varTable, strWidth);
- rNode.token.kind = cr.kind is CRKind.STR ? Kind.STRING : Kind.NUMBER;
- final switch (cr.kind) {
- case CRKind.STR:
- rNode.token.value = createString(cr.str);
- break;
- case CRKind.INT:
- rNode.token.value = to!(string)(cr.numInt);
- break;
- case CRKind.REAL:
- rNode.token.value = to!(string)(cr.numReal);
- break;
- }
- r ~= rNode;
- break;
- default:
- r ~= v2;
- break;
- }
- }
- }
- return r;
- }
- private const(Node)[] var(in Node[] nodes, in const(Node)[][string] varTable) { mixin(S_TRACE);
- const(Node)[] r;
- foreach (node; nodes) { mixin(S_TRACE);
- r ~= var(node, varTable);
- }
- return r;
- }
- private const(Node)[] var(in Node node, in const(Node)[][string] varTable) { mixin(S_TRACE);
- if (node.token.kind is Kind.VAR_NAME) { mixin(S_TRACE);
- auto ptr = std.string.toLower(node.token.value) in varTable;
- if (ptr) { mixin(S_TRACE);
- const(Node)[] r;
- foreach (v; *ptr) { mixin(S_TRACE);
- r ~= var(v, varTable);
- }
- return r;
- }
- throwError(_prop.msgs.scriptErrorUndefinedVar, node.token);
- return [];
- }
- return [node];
- }
- private Token var(in Token tok, in const(Node)[][string] varTable) { mixin(S_TRACE);
- if (tok.kind is Kind.VAR_NAME) { mixin(S_TRACE);
- auto ptr = std.string.toLower(tok.value) in varTable;
- if (ptr && ptr.length) { mixin(S_TRACE);
- return var((*ptr)[0].token, varTable);
- }
- throwError(_prop.msgs.scriptErrorUndefinedVar, tok);
- }
- return tok;
- }
- /// tokens?????Node???????????
- Node[] analyzeSyntax(in Token[] tokens) { mixin(S_TRACE);
- Node[] r;
- size_t i = 0;
- Node[] vars;
- const(Token)[] tokens2;
- foreach (ref tok; tokens) { mixin(S_TRACE);
- if (tok.kind !is Kind.COMMENT) { mixin(S_TRACE);
- tokens2 ~= tok;
- }
- }
- while (i < tokens2.length) { mixin(S_TRACE);
- vars ~= eatVarSet(tokens2, i, KEYS);
- if (tokens2.length <= i) { mixin(S_TRACE);
- break;
- }
- Token tok = tokens2[i];
- if (tok.kind !is Kind.START) { mixin(S_TRACE);
- r ~= analyzeSyntaxBranch(tokens2, i, KEYS, vars);
- continue;
- }
- i++;
- Node node;
- node.type = NodeType.START;
- node.token = tok;
- node.texts = analyzeSyntaxAttr(tokens2, i, KEYS);
- if (!node.texts.length) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorNoStartText, tok);
- }
- node.beforeVars = vars;
- vars = [];
- node.nextIsChild = false;
- c: while (i < tokens2.length) { mixin(S_TRACE);
- vars ~= eatVarSet(tokens2, i, KEYS);
- string cText = "";
- switch (tokens2[i].kind) {
- case Kind.START, Kind.FI: break c;
- case Kind.IF, Kind.ELIF, Kind.SYMBOL, Kind.SIF:
- node.childs ~= analyzeSyntaxBranch(tokens2, i, KEYS, vars);
- continue;
- default:
- throwError(_prop.msgs.scriptErrorInvalidStatement, tokens2[i]);
- i++;
- }
- }
- r ~= node;
- }
- return r;
- } unittest { mixin(S_TRACE);
- debug mixin(UTPerf);
- auto s = new CWXScript(new CProps("", null), null);
- string statement
- = `
- $var1 = 'oops'
- Start "First start"
- if 'abc'
- chback ['mapofwirth.bmp', '', 0, 0, 632, 420]
- ['definn.bmp', '', 50, 50, 200*2, 260]
- ['card.bmp', 'card\mate1', 230, 50, 74, 94, mask],
- 1
- hideparty
- $var2 = 3.5
- wait ($var2 + 1.5)
- msg M
- @ 3
- Talk!
- Talk!
- Talk!
- @
- sif "Single IF"
- brflag 'card\mate1'
- if true
- $var3 = 'what?'
- showparty
- endsc true
- $var_dummy = nothing
- elif false
- gameover // comment1
- fi
- elif 'def' effect 1, 2, 3 + 2
- fi
- // comment2
- $var4 = true
- start "second start"
- showparty
- getskill 1`;
- auto tokens = s.tokenize(statement);
- auto starts = s.analyzeSyntax(tokens);
- assert (Node.code(" ", starts)
- == "$var1 = 'oops'\n"
- ~ "Start \"First start\"\n"
- ~ "if 'abc'\n"
- ~ " chback ['mapofwirth.bmp', '', 0, 0, 632, 420]\n"
- ~ " ['definn.bmp', '', 50, 50, 200 * 2, 260]\n"
- ~ " ['card.bmp', 'card\\mate1', 230, 50, 74, 94, mask], 1\n"
- ~ " hideparty\n"
- ~ " $var2 = 3.5\n"
- ~ " wait ($var2 + 1.5)\n"
- ~ " msg M, @ 3\n"
- ~ " Talk!\n"
- ~ " Talk!\n"
- ~ " Talk!\n"
- ~ " @\n"
- ~ " sif \"Single IF\"\n"
- ~ " brflag 'card\\mate1'\n"
- ~ " if true\n"
- ~ " $var3 = 'what?'\n"
- ~ " showparty\n"
- ~ " endsc true\n"
- ~ " elif false\n"
- ~ " $var_dummy = nothing\n" /* ???????????????? */
- ~ " gameover\n"
- ~ " fi\n"
- ~ "elif 'def'\n"
- ~ " effect 1, 2, 3 + 2\n"
- ~ "fi\n"
- ~ "$var4 = true\n"
- ~ "start \"second start\"\n"
- ~ " showparty\n"
- ~ " getskill 1", Node.code(" ", starts));
- string statement2
- = `
- brflag 'card\mate1'
- if true
- $var3 = 'what?'
- showparty
- endsc true
- $var_dummy = nothing
- elif false
- gameover // comment1
- fi`;
- auto tokens2 = s.tokenize(statement2);
- auto contents = s.analyzeSyntax(tokens2);
- assert (Node.code(" ", contents)
- == "brflag 'card\\mate1'\n"
- ~ "if true\n"
- ~ " $var3 = 'what?'\n"
- ~ " showparty\n"
- ~ " endsc true\n"
- ~ "elif false\n"
- ~ " $var_dummy = nothing\n"
- ~ " gameover\n"
- ~ "fi");
- // ????????????????
- s.analyzeSyntax(s.tokenize("dialog M, @c\n...\n@]"));
- string statement3 = `chback [] goarea 1`;
- auto tokens3 = s.tokenize(statement3);
- auto contents2 = s.analyzeSyntax(tokens3);
- assert (contents2.length == 2);
- assert (contents2[0].nextIsChild);
- assert (!contents2[1].nextIsChild);
- }
- private Node[] analyzeSyntaxBranch(in Token[] tokens, ref size_t i, in Keywords keys, ref Node[] vars) { mixin(S_TRACE);
- Node[] r;
- auto tok = tokens[i];
- switch (tok.kind) {
- case Kind.START: return r;
- case Kind.IF, Kind.VAR_NAME:
- while (i < tokens.length) { mixin(S_TRACE);
- Node[] texts;
- if (tokens[i].kind is Kind.IF || tokens[i].kind is Kind.ELIF) { mixin(S_TRACE);
- i++;
- texts = analyzeSyntaxAttr(tokens, i, keys);
- if (!texts.length) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorNoIfText, tok);
- }
- if (tokens.length <= i) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorNoIfContents, tok);
- }
- }
- auto node = analyzeSyntaxStatement(tokens, i, keys, vars);
- if (node.length) { mixin(S_TRACE);
- node[0].texts = texts;
- }
- r ~= node;
- if (tokens.length <= i) return r;
- switch (tokens[i].kind) {
- case Kind.START: return r;
- case Kind.FI:
- i++;
- return r;
- case Kind.ELIF: continue;
- case Kind.VAR_NAME: continue;
- default:
- throwError(_prop.msgs.scriptErrorInvalidStatement, tokens[i]);
- i++;
- }
- }
- break;
- case Kind.SYMBOL, Kind.SIF:
- r ~= analyzeSyntaxStatement(tokens, i, keys, vars);
- break;
- default:
- throwError(_prop.msgs.scriptErrorInvalidBranch, tok);
- i++;
- break;
- }
- return r;
- }
- private Node[] eatVarSet(in Token[] tokens, ref size_t i, in Keywords keys) { mixin(S_TRACE);
- Node[] r;
- while (i < tokens.length && tokens[i].kind is Kind.VAR_NAME) { mixin(S_TRACE);
- r ~= analyzeSyntaxVar(tokens, i, keys);
- }
- return r;
- }
- private Node[] analyzeSyntaxStatement(in Token[] tokens, ref size_t i, in Keywords keys, ref Node[] vars) { mixin(S_TRACE);
- Node[] r;
- while (true) { mixin(S_TRACE);
- assert (i < tokens.length);
- vars ~= eatVarSet(tokens, i, keys);
- Token tok = tokens[i];
- Node[] sifTexts;
- if (tok.kind is Kind.SIF) { mixin(S_TRACE);
- i++;
- if (tokens.length <= i) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorNoSifText, tok);
- return r;
- }
- sifTexts = analyzeSyntaxAttr(tokens, i, keys);
- if (tokens.length <= i) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorInvalidSif, tok);
- }
- tok = tokens[i];
- } else if (tok.kind !is Kind.SYMBOL) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorInvalidStatement, tok);
- }
- Node node;
- node.type = NodeType.COMMAND;
- node.token = tok;
- node.texts = sifTexts;
- node.nextIsChild = false;
- auto symbol = std.string.toLower(tok.value);
- if (!(symbol in keys.keywords)) { mixin(S_TRACE);
- throwError(_prop.msgs.scriptErrorInvalidKeyword, tok);
- }
- node.beforeVars = vars;
- vars = [];
- i++;
- if (tokens.length <= i) return r ~ node;
- node.attr = analyzeSyntaxAttr(tokens, i, keys);
- vars ~= eatVarSet(tokens, i, keys);
- if (tokens.length <= i) return r ~ node;
- switch (tokens[i].kind) {
- case Kind.START, Kind.ELIF, Kind.FI:
- return r ~ node;
- case Kind.IF:
- node.childs ~= analyzeSyntaxBranch(tokens, i, keys, vars);
- return r ~ node;
- case Kind.SIF, Kind.SYMBOL:
- node.nextIsChild = true;
- r ~= node;
- continue;
- default:
- throwError(_prop.msgs.scriptErrorInvalidStatement, tok);
- return r ~ node;
- }
- }
- return r;
- }
- private Node[] analyzeSyntaxAttr(in Token[] tokens, ref size_t i, in Keywords keys) { mixin(S_TRACE);
- …
Large files files are truncated, but you can click here to view the full file