現在、ParserScriptは非推奨です。
ParserScriptはバージョン2.0から大きく分けて新たに3つ(実質2つ)のコマンドが追加されました。
CSREGEXコマンド
このコマンドには3つ機能があります。と言っても、実質2つだけです。
CSREGEX MATCHコマンド
例: 以下のコードは、実数にマッチするようになっています。
CSREGEX MATCH "((-)?[0-9]+(\.[0-9]+)?)"
これは見ての通りパースする文字列の先頭からマッチングを行います。
CSREGEX ISMATCHコマンド
例: 以下のコードは、パースする文字列の先頭がaまたはbの時、「OK」と出力するコードです。
CSREGEX ISMATCH "(a|b)"
STRING
PUSH "... OK."
END
これも見ての通り、パースする文字列の先頭がISMATCHの正規表現にマッチしたらそのままコードを実行し、マッチしなかったら対応するENDに飛びます。
CSREGEX ISNOMATCH
名前の通りISMATCHの逆です。
ISSTART?, ISNOSTART?コマンド
例: 以下のコードはパースする文字列の先頭がaなら「OK」、それ以外なら、「aではない」と出力するコードです。
PUSH "\""
ISSTART? "a"
STRING
PUSH "\""
PUSH " is OK."
END
ISNOSTART? ""
STRING
PUSH "\""
PUSH " isn't \"a\""
END
これも見ての通りですね。え、ISNOSTART?の文字列は"a"じゃないのかって?
いえいえ、これが正しいのです。説明しましょう。
まず、ISNOSTART?の時に残る文字列は"(a以外の文字)..."か、""かです。ISSTART?にマッチしなかったら文字列を喰わずに、マッチしたら全て喰うからです。ここでISSTART?では喰えなかった文字列を喰うために、""にして"(a以外の文字)..."を喰うようにします。だからISNOSTART? ""
と書くのです。
その他追加された機能
コマンドの解釈
ParserScriptは以前まで
MATCH " "
や
PUSH " "
などと書くと
MATCH, ", "
PUSH, ", "
と解釈されていました。
しかし、以下のC#のコード
internal static class Converter
{
public static string[] Split2(this string s, string split) => [.. Regex.Split(s.EndsWith(split) ? s : s + split, @$"("".*""{split}|[^{split[0]}]*{split})").Where(x => x != "").Select(x => string.Join("", x.SkipLast(split.Length)))];
}
のSplit2メソッドを使って先のParserScriptのコードを
MATCH, " "
PUSH, " "
と解釈するようになりました。
エスケープ文字対応
ParserScriptは以前までエスケープ文字をReplaceメソッドで書き換えていたのですが、それだとReplaceメソッドの置く場所を変えたら、例えば"\\n"
を\と改行文字が並んだものや\とnが並んだものと解釈されます。実際、バージョン2.0までは"\\n"
を\と改行文字が並んだものと解釈していました。この問題をバージョン3.0では先ほどのConverterクラスに
public static string Replace(this string s) => string.Join("", Replace().Replace(string.Join("", s[(int.TryParse("0", out int i) ? 0 : 0)..]), "$1").Aggregate(Array.Empty<string>(), (x, y) => [.. x.Append(s[i].ToString() is "\\" ? s[i++].ToString() + s[i++].ToString() : s[i++].ToString())]).Select(x => x is "\\n" ? "\n" : (x is "\\t" ? "\t" : (x is "\\s" ? " " : (x is "\\\\" ? "\\" : (x is "\\\"" ? "\"" : x))))));
[GeneratedRegex(@"(\\).")]
private static partial Regex Replace();
を追加することで解決しました。これは
a\\nb\\sc\\td\\\\e\\"f(エスケープで\を表した時)
を
a, \\n, b, \\s, c, \\t, d, \\\\, e, \\", f
と解釈し、
a, \n, b, \s, c, \t, d, \\, e, \", f
に変換した後、連結して
a\nb\sc\td\\e\"f
にします。
結果は
a
b c d\e"f
になります。
終わりに
ParserScriptはバージョン3.0になってC#の正規表現を使えるようになり、一応あの長いコードを短くできますが条件分岐やブロックの終わりを示すENDとLASTの数が条件分岐のコマンドとASTの数と違っても許されるので、それを許さないものをバージョン3.1に実装しようと思います。