2012/06/17(日)Jsonほぼ互換の似非バリアント的な何か。
似非バリアント もしくは、似非JSONオブジェクト。
Jsonパーサ・フォーマッタの独自実装ですはい。
ゲームとかのセーブデータをスキーマレスに読み書きしたいなという欲求があるからして。
class VarTest { public static void TestVar() { Var v = new VarList { true, false, 12345, "ほげ", new byte[]{0x1, 0x2, 3, 4, 5, }, new VarDictionary() { {"あ", true}, {"い", false}, {"X", Var.Null}, {"Y", 123}, {"辞書", new VarDictionary() { {"あ", true}, {"い", false}, {"X", new Var()}, {"Y", 123}, } }, {"辞書内配列", new VarDictionary() { {"あ", new int[]{123,456 } }, {"い", new bool[]{true, true, false, false, true} }, {"X", new VarList { Var.Null, "えー", new byte[]{99, 99, 99}, } }, {"Y", new string[]{"う゛ぁ", "う゛ぃ",} }, } } }, "にゃー", }; int a = v[5]["辞書内配列"]["あ"][1]; // → 456 string b = v[5]["辞書内配列"]["X"][1]; // → "えー" Console.WriteLine(a); Console.WriteLine(b); string serialized = v.ToFormattedString(); // JSON形式の文字列になる Console.WriteLine(serialized); Var readed = Var.FromFormattedString(serialized); // JSON形式の文字列から読み込む Console.WriteLine(readed.ToFormattedString()); // ちゃんと読み込めたか? } }
のような。
読み込み時に備えて、Nullとは別にundefindも定義しておくべきだったかしら。
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.IO; namespace Tsukikage.GameSDK.Util { /// <summary> /// 似非バリアント型。Jsonのような(実数が扱えない、byte配列を扱う独自拡張をしてる、文法チェックが緩い) /// </summary> [StructLayout(LayoutKind.Explicit)] public struct Var : IComparable<Var>, IEquatable<Var>, ICloneable { [FieldOffset(0)] private VarType type; [FieldOffset(4)] private object asObject; [FieldOffset(4)] private VarList asList; [FieldOffset(4)] private VarDictionary asDictionary; [FieldOffset(4)] private string asString; [FieldOffset(4)] private byte[] asByteArray; [FieldOffset(8)] private bool asBool; [FieldOffset(8)] private long asInt; /// <summary> /// サポートしている型 /// </summary> public enum VarType : byte { Null = 0x00, Boolean = 0x01, Int = 0x02, String = 0x03, ByteArray = 0x04, List = 0x10, Dictionary = 0x20, } public bool IsNull { get { return type == VarType.Null; } } public bool IsBoolean { get { return type == VarType.Boolean; } } public bool IsInt { get { return type == VarType.Int; } } public bool IsString { get { return type == VarType.String; } } public bool IsBinary { get { return type == VarType.ByteArray; } } public bool IsList { get { return type == VarType.List; } } public bool IsDictionary { get { return type == VarType.Dictionary; } } public bool AsBoolean { get { return (bool)this; } } public int AsInt { get { return (int)this; } } public string AsString { get { return (string)this; } } public byte[] AsBinary { get { return (byte[])this; } } public VarList AsList { get { return (VarList)this; } } public VarDictionary AsDictionary { get { return (VarDictionary)this; } } public static implicit operator bool(Var v) { if (v.IsNull) return default(bool); if (v.IsBoolean) return v.asBool; throw new InvalidCastException("Var[" + v.type + "]型からbool型への変換はサポートしません。"); } public static implicit operator int(Var v) { if (v.IsNull) return default(int); if (v.IsInt) return (int)v.asInt; throw new InvalidCastException("Var[" + v.type + "]型からint型への変換はサポートしません。"); } public static implicit operator long(Var v) { if (v.IsNull) return default(int); if (v.IsInt) return v.asInt; throw new InvalidCastException("Var[" + v.type + "]型からlong型への変換はサポートしません。"); } public static implicit operator double(Var v) { if (v.IsNull) return default(int); if (v.IsInt) return v.asInt; throw new InvalidCastException("Var[" + v.type + "]型からdouble型への変換はサポートしません。"); } public static implicit operator string(Var v) { if (v.IsNull) return default(string); if (v.IsString) return v.asString; throw new InvalidCastException("Var[" + v.type + "]型からstring型への変換はサポートしません。"); } public static implicit operator byte[](Var v) { if (v.IsNull) return default(byte[]); if (v.IsBinary) return v.asByteArray; throw new InvalidCastException("Var[" + v.type + "]型からbyte[]型への変換はサポートしません。"); } public static implicit operator VarList(Var v) { if (v.IsNull) return default(VarList); if (v.IsList) return v.asList; throw new InvalidCastException("Var[" + v.type + "]型からVarList型への変換はサポートしません。"); } public static implicit operator VarDictionary(Var v) { if (v.IsNull) return default(VarDictionary); if (v.IsDictionary) return v.asDictionary; throw new InvalidCastException("Var[" + v.type + "]型からVarDictionary型への変換はサポートしません。"); } public static implicit operator Var(bool data) { return new Var(data); } public static implicit operator Var(byte[] data) { return new Var(data); } public static implicit operator Var(long data) { return new Var(data); } public static implicit operator Var(string data) { return new Var(data); } public static implicit operator Var(VarList data) { return new Var(data); } public static implicit operator Var(VarDictionary data) { return new Var(data); } public int Count { get { if (IsList) return asList.Count; if (IsDictionary) return asDictionary.Count; if (IsNull) return 0; throw new InvalidOperationException("指定されたVar型変数はListまたはDictionaryではありません。"); } } public Var this[int index] { get { if (IsList) return asList[index]; throw new InvalidOperationException("指定されたVar型変数は配列ではありません。"); } set { if (IsList) { asList[index] = value; return; } throw new InvalidOperationException("指定されたVar型変数は配列ではありません。"); } } public Var this[string key] { get { if (IsDictionary) return asDictionary[key]; throw new InvalidOperationException("指定されたVar型変数はVarDictionaryではありません。"); } set { if (IsDictionary) { asDictionary[key] = value; return; } throw new InvalidOperationException("指定されたVar型変数はVarDictionaryではありません。"); } } public new VarType GetType() { return type; } public override string ToString() { StringBuilder sb = new StringBuilder(); switch (type) { case VarType.Null: return null; case VarType.Boolean: return asBool.ToString(); case VarType.Int: return asInt.ToString(); case VarType.String: return asString; case VarType.ByteArray: return "{Binary}"; case VarType.List: return ToFormattedString(); case VarType.Dictionary: return ToFormattedString(); default: throw new Exception("指定されたVar型は状態が変です。"); } } public static Var Null { get { return new Var(); } } private Var(bool data) : this() { this.type = VarType.Boolean; this.asBool = data; } private Var(long data) : this() { this.type = VarType.Int; this.asInt = data; } private Var(byte[] data) : this() { this.type = VarType.ByteArray; this.asByteArray = data; } private Var(string data) : this() { this.type = VarType.String; this.asString = data; } private Var(VarList data) : this() { this.type = VarType.List; this.asList = data; } private Var(VarDictionary data) : this() { this.type = VarType.Dictionary; this.asDictionary = data; } public static bool operator <(Var a, Var b) { return a.CompareTo(b) < 0; } public static bool operator >(Var a, Var b) { return a.CompareTo(b) > 0; } public static bool operator <=(Var a, Var b) { return a.CompareTo(b) <= 0; } public static bool operator >=(Var a, Var b) { return a.CompareTo(b) >= 0; } public static bool operator ==(Var a, Var b) { return a.Equals(b); } public static bool operator !=(Var a, Var b) { return !a.Equals(b); } public static int operator +(Var a) { if (a.IsInt) return a; throw new NotImplementedException(a.type + "型を+できません。"); } public static Var operator +(Var a, Var b) { if (a.IsInt && b.IsInt) return (int)a + (int)b; if (a.IsString || b.IsString) return a.ToString() + b.ToString(); throw new NotImplementedException(a.type + "型に" + b.type + "型を+できません。"); } public static string operator +(string a, Var b) { return a + b.ToString(); } public static string operator +(Var a, string b) { return a.ToString() + b; } public struct VarInt { long value; public VarInt(long value) { this.value = value; } public static implicit operator int(VarInt v) { return (int)v.value; } public static implicit operator long(VarInt v) { return v.value; } public static implicit operator double(VarInt v) { return v.value; } public static implicit operator Var(VarInt v) { return new Var(v.value); } public static implicit operator VarInt(long v) { return new VarInt(v); } } public struct VarBool { bool value; public VarBool(bool value) { this.value = value; } public static implicit operator bool(VarBool v) { return v.value; } public static implicit operator Var(VarBool v) { return new Var(v.value); } public static implicit operator VarBool(bool v) { return new VarBool(v); } } public static Var operator ++(Var a) { if (a.IsInt) return (int)a + 1; throw new NotImplementedException(a.type + "型を++できません。"); } public static VarInt operator -(Var a) { if (a.IsInt) return -(long)a; throw new NotImplementedException(a.type + "型を-できません。"); } public static VarInt operator -(Var a, Var b) { if (a.IsInt && b.IsInt) return (int)a - (int)b; throw new NotImplementedException(a.type + "型に" + b.type + "型を-できません。"); } public static Var operator --(Var a) { if (a.IsInt) return (long)a - 1; throw new NotImplementedException(a.type + "型を--できません。"); } public static VarInt operator *(Var a, Var b) { if (a.IsInt && b.IsInt) return (long)a * (long)b; throw new NotImplementedException(a.type + "型に" + b.type + "型を*できません。"); } public static VarInt operator /(Var a, Var b) { if (a.IsInt && b.IsInt) return (long)a / (long)b; throw new NotImplementedException(a.type + "型に" + b.type + "型を/できません。"); } public static VarInt operator %(Var a, int b) { if (a.IsInt) return (long)a % b; throw new NotImplementedException(a.type + "型にint型を%できません。"); } public static VarInt operator <<(Var a, int b) { if (a.IsInt) return (long)a << b; throw new NotImplementedException(a.type + "型にint型を<<できません。"); } public static VarInt operator >>(Var a, int b) { if (a.IsInt) return (long)a >> b; throw new NotImplementedException(a.type + "型にint型を>>できません。"); } public static VarInt operator |(long a, Var b) { if (b.IsInt) return (long)a | (long)b; throw new NotImplementedException(VarType.Int + "型に" + b.type + "型を|できません。"); } public static VarInt operator |(Var a, long b) { if (a.IsInt) return (long)a | (long)b; throw new NotImplementedException(a.type + "型に" + VarType.Int + "型を|できません。"); } public static VarBool operator |(bool a, Var b) { if (b.IsBoolean) return (bool)a | (bool)b; throw new NotImplementedException(VarType.Boolean + "型に" + b.type + "型を|できません。"); } public static VarBool operator |(Var a, bool b) { if (a.IsBoolean) return (bool)a | (bool)b; throw new NotImplementedException(a.type + "型に" + VarType.Boolean + "型を|できません。"); } public static Var operator |(Var a, Var b) { if (a.IsBoolean && b.IsBoolean) return (bool)a | (bool)b; if (a.IsInt && b.IsInt) return (long)a | (long)b; throw new NotImplementedException(a.type + "型に" + b.type + "型を|できません。"); } public static VarInt operator &(long a, Var b) { if (b.IsInt) return (long)a & (long)b; throw new NotImplementedException(VarType.Int + "型と" + b.type + "型を&できません。"); } public static VarInt operator &(Var a, long b) { if (a.IsInt) return (long)a & (long)b; throw new NotImplementedException(a.type + "型と" + VarType.Int + "型を&できません。"); } public static VarBool operator &(bool a, Var b) { if (b.IsBoolean) return (bool)a & (bool)b; throw new NotImplementedException(VarType.Boolean + "型と" + b.type + "型を&できません。"); } public static VarBool operator &(Var a, bool b) { if (a.IsBoolean) return (bool)a & (bool)b; throw new NotImplementedException(a.type + "型と" + VarType.Boolean + "型を&できません。"); } public static Var operator &(Var a, Var b) { if (a.IsBoolean && b.IsBoolean) return (bool)a & (bool)b; if (a.IsInt && b.IsInt) return (long)a & (long)b; throw new NotImplementedException(a.type + "型と" + b.type + "型を&できません。"); } public static VarInt operator ^(long a, Var b) { if (b.IsInt) return (long)a ^ (long)b; throw new NotImplementedException(VarType.Int + "型と" + b.type + "型を^できません。"); } public static VarInt operator ^(Var a, long b) { if (a.IsInt) return (long)a ^ (long)b; throw new NotImplementedException(a.type + "型と" + VarType.Int + "型を^できません。"); } public static VarBool operator ^(bool a, Var b) { if (b.IsBoolean) return (bool)a ^ (bool)b; throw new NotImplementedException(VarType.Boolean + "型と" + b.type + "型を^できません。"); } public static VarBool operator ^(Var a, bool b) { if (a.IsBoolean) return (bool)a ^ (bool)b; throw new NotImplementedException(a.type + "型と" + VarType.Boolean + "型を^できません。"); } public static Var operator ^(Var a, Var b) { if (a.IsBoolean && b.IsBoolean) return (bool)a ^ (bool)b; if (a.IsInt && b.IsInt) return (long)a ^ (long)b; throw new NotImplementedException(a.type + "型と" + b.type + "型を^できません。"); } public static VarInt operator ~(Var a) { if (a.IsInt) return ~(long)a; throw new NotImplementedException(a.type + "型を~できません"); } public static VarBool operator !(Var a) { if (a.IsBoolean) return !(bool)a; throw new NotImplementedException(a.type + "型を!できません"); } public override int GetHashCode() { if (IsNull) throw new NullReferenceException(); return ToString().GetHashCode(); } public override bool Equals(object obj) { return obj is Var ? Equals((Var)obj) : false; } public bool Equals(Var obj) { if (this.type != obj.type) return false; if (IsInt) return asInt == obj.asInt; if (IsBoolean) return asBool == obj.asBool; return asObject.Equals(obj.asObject); } public int CompareTo(Var other) { if (IsBoolean && other.IsBoolean) return ((bool)this ? 1 : 0) - ((bool)other ? 1 : 0); if (IsInt && other.IsInt) return ((long)this).CompareTo((long)other); if (IsString && other.IsString) return ((string)this).CompareTo((string)other); throw new InvalidOperationException(type + "型と" + other.type + "型は比較できません。"); } public static implicit operator Var(bool[] array) { return new VarList(Array.ConvertAll<bool, Var>(array, delegate(bool v) { return v; })); } public static implicit operator Var(int[] array) { return new VarList(Array.ConvertAll<int, Var>(array, delegate(int v) { return v; })); } public static implicit operator Var(long[] array) { return new VarList(Array.ConvertAll<long, Var>(array, delegate(long v) { return v; })); } public static implicit operator Var(string[] array) { return new VarList(Array.ConvertAll<string, Var>(array, delegate(string v) { return v; })); } public static implicit operator int[](Var array) { return Array.ConvertAll<Var, int>(array.AsList.ToArray(), delegate(Var v) { return v; }); } public static implicit operator long[](Var array) { return Array.ConvertAll<Var, long>(array.AsList.ToArray(), delegate(Var v) { return v; }); } public static implicit operator string[](Var array) { return Array.ConvertAll<Var, string>(array.AsList.ToArray(), delegate(Var v) { return v; }); } public static implicit operator bool[](Var array) { return Array.ConvertAll<Var, bool>(array.AsList.ToArray(), delegate(Var v) { return v; }); } string EncodeString(string input) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < input.Length; i++) { if (input[i] == '\\n') sb.Append("\\\\n"); else if (input[i] == '\\\\') sb.Append("\\\\\\\\"); else if (input[i] == '\\b') sb.Append("\\\\b"); else if (input[i] == '\\f') sb.Append("\\\\f"); else if (input[i] == '\\r') sb.Append("\\\\r"); else if (input[i] == '\\t') sb.Append("\\\\t"); else if (input[i] == '\\"') sb.Append("\\\\\\""); else if (input[i] == '<') sb.Append("\\\\<"); else if (input[i] == '>') sb.Append("\\\\>"); //else if (input[i] > 127) sb.Append(string.Format("\\\\u{0:X4}", (int)input[i])); else sb.Append(input[i]); } return sb.ToString(); } string ToFormattedString(int indent, bool uncompressed) { switch (type) { case VarType.Null: return "null"; case VarType.Boolean: return asBool ? "true" : "false"; case VarType.Int: return asInt.ToString(); case VarType.String: return "\\"" + EncodeString(asString) + "\\""; case VarType.ByteArray: { StringBuilder sb = new StringBuilder(); sb.Append("B["); for (int i = 0; i < asByteArray.Length; i++) sb.Append(string.Format("{0:X2}", asByteArray[i])); sb.Append(']'); return sb.ToString(); } case VarType.List: { if (asList.Count == 0) return "[]"; StringBuilder sb = new StringBuilder(); sb.Append('['); if (uncompressed) sb.Append('\\n'); foreach (Var v in asList) { if (uncompressed) sb.Append(new string('\\t', indent + 1)); sb.Append(v.ToFormattedString(indent + 1, uncompressed)); sb.Append(","); if (uncompressed) sb.Append("\\n"); } if (uncompressed) sb.Remove(sb.Length - 1, 1); sb.Remove(sb.Length - 1, 1); if (uncompressed) sb.Append('\\n'); if (uncompressed) sb.Append(new string('\\t', indent)); sb.Append("]"); return sb.ToString(); } case VarType.Dictionary: { if (asDictionary.Count == 0) return "{}"; StringBuilder sb = new StringBuilder(); sb.Append('{'); if (uncompressed) sb.Append('\\n'); foreach (string key in asDictionary.Keys) { if (uncompressed) sb.Append(new string('\\t', indent + 1)); sb.Append("\\"" + EncodeString(key) + "\\""); if (uncompressed) sb.Append(' '); sb.Append(':'); if (uncompressed) sb.Append(' '); sb.Append(asDictionary[key].ToFormattedString(indent + 1, uncompressed)); sb.Append(','); if (uncompressed) sb.Append('\\n'); } if (uncompressed) sb.Remove(sb.Length - 1, 1); sb.Remove(sb.Length - 1, 1); if (uncompressed) sb.Append('\\n'); if (uncompressed) sb.Append(new string('\\t', indent)); sb.Append("}"); return sb.ToString(); } default: throw new Exception("指定されたVar型は状態が変です。"); } } /// <summary> /// 比較的読みやすい文字列形式に変換します。 /// </summary> /// <returns></returns> public string ToFormattedString() { return ToFormattedString(0, true); } /// <summary> /// インデントや改行を省いた1行の文字列形式に変換します。 /// </summary> /// <returns></returns> public string ToCompressedFormattedString() { return ToFormattedString(0, false); } /// <summary> /// 一切の参照を共有しないオブジェクトの完全なコピーを作ります。でかいデータを含んでると遅いかも。 /// </summary> /// <returns>コピー</returns> public Var Clone() { return FromFormattedString(ToCompressedFormattedString()); } /// <summary> /// 一切の参照を共有しないオブジェクトの完全なコピーを作ります。でかいデータを含んでると遅いかも。 /// </summary> /// <returns>コピー</returns> object ICloneable.Clone() { return Clone(); } /// <summary> /// 文字列形式からVarを生成します。 /// </summary> /// <param name="serialized">文字列</param> /// <exception cref="FormatException">文法エラー</exception> /// <returns></returns> public static Var FromFormattedString(string serialized) { return VarSerializer.Parse(serialized); } /// <summary> /// ストリームを読んでVarを生成します。ストリームはUtf8でエンコードされています。 /// </summary> /// <param name="serialized">文字列</param> /// <exception cref="FormatException">文法エラー</exception> /// <returns></returns> public static Var FromFormattedStream(Stream serialized) { return VarSerializer.Load(serialized); } class VarSerializer { private VarSerializer(TextReader textReader) { this.textReader = textReader; this.readString = new StringBuilder(4096); next(); } TextReader textReader; StringBuilder readString; char cur; void next() { int x = textReader.Read(); if (x != -1) { readString.Append((char)x); cur = (char)x; } else { cur = '\\0'; } } void abort(string message) { string[] lines = readString.ToString().Split('\\n'); throw new FormatException("文法エラー。" + message + "\\n" + lines.Length + "行目 " + lines[lines.Length - 1] + " ←ここが変。"); } void assert(bool exp, string message) { if (!exp) abort(message); } void assert(bool exp) { if (cur == '\\0') assert(exp, "予期せぬEOFに出会いました。"); else assert(exp, "解釈できませんでした。"); } Var ReadObject() { SkipWhiteSpace(); assert(cur != '\\0'); switch (cur) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': return ReadNumber(); case 'N': case 'n': return ReadNull(); case 'T': case 't': return ReadTrue(); case 'F': case 'f': return ReadFalse(); case '"': return ReadString(); case '[': return ReadList(); case '{': return ReadDictionary(); case 'B': return ReadByteArray(); case '\\0': assert(false); break; default: assert(false); break; } return Var.Null; /* not reachable */ } Var ReadNull() { assert(cur == 'n' || cur == 'N'); next(); assert(cur == 'u' || cur == 'U'); next(); assert(cur == 'l' || cur == 'L'); next(); assert(cur == 'l' || cur == 'L'); next(); return Var.Null; } Var ReadTrue() { assert(cur == 't' || cur == 'T'); next(); assert(cur == 'r' || cur == 'R'); next(); assert(cur == 'u' || cur == 'U'); next(); assert(cur == 'e' || cur == 'E'); next(); return true; } Var ReadFalse() { assert(cur == 'f' || cur == 'F'); next(); assert(cur == 'a' || cur == 'A'); next(); assert(cur == 'l' || cur == 'L'); next(); assert(cur == 's' || cur == 'S'); next(); assert(cur == 'e' || cur == 'E'); next(); return false; } Var ReadNumber() { bool minus = false; long value = 0; if (cur == '-') { minus = true; next(); } while (cur >= '0' && cur <= '9') { value = value * 10 + (cur - '0'); next(); } if (minus) return -value; return value; } Var ReadByteArray() { assert(cur == 'B'); next(); assert(cur == '['); next(); List<byte> bytes = new List<byte>(); while (true) { assert(cur != '\\0'); if (cur == ']') break; int b = Hex(cur); next(); b = b * 16 + Hex(cur); next(); bytes.Add((byte)b); } assert(cur == ']'); next(); return bytes.ToArray(); } VarList ReadList() { assert(cur == '['); next(); VarList list = new VarList(); while (true) { SkipWhiteSpace(); if (cur == ']') break; assert(cur != '\\0'); list.Add(ReadObject()); SkipWhiteSpace(); assert(cur == ']' || cur == ','); if (cur == ',') next(); } next(); return list; } VarDictionary ReadDictionary() { assert(cur == '{'); next(); VarDictionary dict = new VarDictionary(); while (true) { SkipWhiteSpace(); if (cur == '}') break; string key = ReadKey(); SkipWhiteSpace(); assert(cur == ':'); next(); SkipWhiteSpace(); dict.Add(key, ReadObject()); SkipWhiteSpace(); assert(cur == ',' || cur == '}'); if (cur == ',') next(); } next(); return dict; } void SkipWhiteSpace() { bool inBlockComment = false; bool inLinerComment = false; while (cur != '\\0') { if (inBlockComment) { if (cur == '*') { next(); if (cur == '/') { next(); inBlockComment = false; } } else { next(); } } else if (inLinerComment) { if (cur == '\\n') { inLinerComment = false; next(); } else { next(); } } else { if (cur <= ' ') { next(); } else if (cur == '/') { next(); if (cur == '*') { inBlockComment = true; next(); } else if (cur == '/') { inLinerComment = true; next(); } else { abort("コメントかと思ったら違いました。"); } } else { break; } } } } string ReadKey() { if (cur == '"' || cur == '\\'') return ReadString(); StringBuilder sb = new StringBuilder(); while (cur > ' ' && cur != ':') { assert(cur != '\\0'); sb.Append(cur); next(); } return sb.ToString(); } string ReadString() { assert(cur == '\\"' || cur == '\\'', "文字列?がQuoteで始まっていません。"); char q = cur; StringBuilder sb = new StringBuilder(); bool escape = false; while (true) { assert(cur != '\\0'); next(); if (escape) { escape = false; switch (cur) { case 'n': sb.Append('\\n'); break; case 't': sb.Append('\\t'); break; case 'b': sb.Append('\\b'); break; case 'f': sb.Append('\\f'); break; case 'r': sb.Append('\\r'); break; case '"': sb.Append('"'); break; case '\\'': sb.Append('\\''); break; case '<': sb.Append('<'); break; case '>': sb.Append('>'); break; case 'u': int u = Hex(cur); u = u * 16 + Hex(cur); u = u * 16 + Hex(cur); u = u * 16 + Hex(cur); sb.Append((char)u); break; } } else if (cur == '\\\\') { escape = true; } else if (cur == q) { next(); break; } else { sb.Append(cur); } } return sb.ToString(); } int Hex(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'A' && c <= 'F') return c - 'A' + 10; if (c >= 'a' && c <= 'f') return c - 'a' + 10; abort("16進数に変換できませんでした。"); return 0; /* no reachable */ } public static Var Load(Stream s) { return Read(new StreamReader(s)); } public static Var Parse(string s) { return Read(new StringReader(s)); } public static string ToString(Var v) { return v.ToString(); } public static Var Read(TextReader textReader) { return new VarSerializer(textReader).ReadObject(); } } } /// <summary> /// Var配列型 /// </summary> public class VarList : List<Var> { /// <summary> /// 新しいVar配列を準備します。 /// </summary> public VarList() : base() { } /// <summary> /// 新しいVar配列を準備します。 /// </summary> /// <param name="collection">新しいオブジェクトにあらかじめコピーしておくデータ</param> public VarList(IEnumerable<Var> collection) : base(collection) { } } /// <summary> /// Varオブジェクト型 /// </summary> public class VarDictionary : Dictionary<string, Var> { /// <summary> /// 新しいVarオブジェクトを準備します。 /// </summary> public VarDictionary() : base() { } /// <summary> /// 新しいVarオブジェクト型を準備します。 /// </summary> /// <param name="dictionary">新しいオブジェクトにあらかじめコピーしておくデータ</param> public VarDictionary(IDictionary<string, Var> dictionary) : base(dictionary) { } /// <summary> /// 指定したキーに関連付けられている値を取得します。 /// おかしな値をとりだそうとするとVar.Nullが返ります。 /// </summary> /// <param name="key">キー</param> /// <returns>値</returns> public new Var this[string key] { get { Var v; if (TryGetValue(key, out v)) return v; return Var.Null; } set { base[key] = value; } } } }