/// <summary>インラインテーブルを解析する。</summary> /// <param name="iter">イテレータ。</param> /// <returns>値情報。</returns> private ITomlValue GetInlineTable(TomlInnerBuffer.TomlIter iter) { var table = new TomlTable(); UTF8 c; while ((c = iter.GetChar(0)).ch1 != 0) { // 改行、空白部を取り除く iter.SkipLineFeedAndSpace(); // キー/値部分を取り込む this.AnalisysKeyAndValue(iter, table, true); // インラインテーブルが閉じられているか確認 // // 1. テーブルが閉じられている // 2. 次のキー/値を取得 // 3. エラー switch (iter.CloseInlineTable()) { case 1: // 1 return(table); case 2: // 2 // 空実装 break; default: // 3 break; } } throw new TomlAnalisysException(Resources.INLINE_TABLE_NOT_CLOSE_ERR, iter); }
/// <summary>文字列を部分的に切り取り、数値表現か確認、取得する。</summary> /// <param name="iter">イテレータ。</param> /// <param name="nest">ネスト位置。</param> /// <param name="digit">必要桁数。</param> /// <param name="result">読込結果(戻り値)</param> /// <returns>取得できたら真。</returns> private static bool ConvertPartitionNumber(TomlInnerBuffer.TomlIter iter, int nest, int digit, out int result) { if (iter.RemnantLength < digit) { // 指定桁数未満の数値であるためエラー result = 0; return(false); } else { // 必要桁数分ループし、全てが数値表現であることを確認 // 1. ループ // 2. 数値判定し、結果を作成する // 3. 結果を返す UTF8 c; int num = 0; for (int i = 0; i < digit; ++i) // 1 { c = iter.GetChar(nest + i); if (c.ch1 >= '0' && c.ch1 <= '9') // 2 { num = num * 10 + (c.ch1 - '0'); } else { result = 0; return(false); } } result = num; // 3 return(true); } }
//--------------------------------------------------------------------- // 文字解析 //--------------------------------------------------------------------- /// <summary>キー文字列の取得。</summary> /// <param name="buffer">内部バッファ。</param> /// <param name="iter">イテレータ。</param> /// <returns>キーリスト。</returns> internal static List <string> GetKeys(this TomlInnerBuffer.TomlIter iter) { var res = new List <string>(); while (iter.GetChar(0).ch1 > 0) { iter.SkipSpace(); // 1 var key = iter.GetKey(); // 2 if (key != null) { res.Add(key); if (iter.GetChar(0).ch1 != '.') // 3 { break; } iter.Skip(1); } else { break; } } return(res); }
/// <summary>値の解析を行う。</summary> /// <param name="iter">イテレータ。</param> /// <returns>値情報。</returns> private ITomlValue AnalisysValue(TomlInnerBuffer.TomlIter iter) { ITomlValue value = null; // インラインテーブル、配列、日付の確認 // // 1. インラインテーブルを解析する // 2. 配列を解析する // 3. 値を取得する iter.SkipSpace(); if (iter.GetChar(0).ch1 == '{') // 1 { iter.Skip(1); value = this.GetInlineTable(iter); } else if (iter.GetChar(0).ch1 == '[') // 2 { iter.Skip(1); value = this.GetValueArray(iter); } else { value = this.ConvertValue(iter); // 3 } return(value); }
/// <summary>数値(整数/実数)を取得する。</summary> /// <param name="iter">イテレータ。</param> /// <param name="numberSign">符号。</param> /// <returns>取得した値。</returns> internal static ITomlValue GetNumberValue(this TomlInnerBuffer.TomlIter iter, bool numberSign) { if (!numberSign && iter.RemnantLength >= 3 && iter.GetChar(0).ch1 == '0') { switch (iter.GetChar(1).ch1) { case (byte)'x': iter.Skip(2); return(Get16NumberValue(iter)); case (byte)'o': iter.Skip(2); return(Get8NumberValue(iter)); case (byte)'b': iter.Skip(2); return(Get2NumberValue(iter)); default: break; } } return(Get10NumberValue(iter, numberSign)); }
/// <summary>配列が閉じられていることを確認する。</summary> /// <param name="iter"></param> /// <returns>閉じられていたら 1、次のキー取得ならば 2。</returns> internal static int CloseValueArray(this TomlInnerBuffer.TomlIter iter) { UTF8 c; while ((c = iter.GetChar(0)).ch1 != 0) { iter.SkipLineFeedAndSpace(); switch (iter.GetChar(0).ch1) { case (byte)']': // 配列が閉じられた iter.Skip(1); return(1); case (byte)',': // 続けて次のキー/値判定へ iter.Skip(1); return(2); default: // インラインテーブルが閉じられていない goto EXIT_WHILE; } } EXIT_WHILE: throw new TomlAnalisysException(Resources.ARRAY_NOT_CLOSE_ERR, iter); }
/// <summary>" で囲まれた文字列を取得する。</summary> /// <param name="iter">イテレータ。</param> /// <returns>取得した文字列。</returns> public static string GetStringValue(this TomlInnerBuffer.TomlIter iter) { var buf = new List <byte>(); UTF8 c; while ((c = iter.GetChar(0)).ch1 != 0) { switch (c.ch1) { case (byte)'"': // " を取得したら文字列終了 iter.Skip(1); return(buf.Count > 0 ? Encoding.UTF8.GetString(buf.ToArray()) : ""); case (byte)'\\': // \のエスケープ文字判定 AppendEscapeChar(iter, buf); break; default: // 上記以外は普通の文字として取得 c.Expand(buf); iter.Skip(1); break; } } // " で終了できなかったためエラー throw new TomlAnalisysException(Resources.QUOAT_STRING_ERR, iter); }
/// <summary>数値(2進数)を取得する。</summary> /// <param name="iter">イテレータ。</param> /// <returns>数値。</returns> private static ITomlValue Get2NumberValue(TomlInnerBuffer.TomlIter iter) { ulong v = 0; bool ud = false; UTF8 c; int point = iter.Pointer; // 数値を取得する while ((c = iter.GetChar(0)).ch1 != 0) { // 1. '_'の連続の判定 // 2. 数値の計算をする // 2-1. 数値の有効範囲を超えるならばエラー if (c.ch1 == '_') { if (ud) // 1 { throw new TomlAnalisysException(Resources.UNDERBAR_CONTINUE_ERR, iter); } ud = true; } else if (c.ch1 == '0' || c.ch1 == '1') { if (v <= ulong.MaxValue / 2) // 2 { v = v * 2 + (ulong)(c.ch1 - '0'); } else // 2-1 { throw new TomlAnalisysException(Resources.INTEGER_VALUE_RANGE_ERR, iter); } ud = false; } else { break; } iter.Skip(1); } // 一文字の数値もなければ None値を返す if (iter.Pointer == point) { return(TomlValue.Empty); } if (v <= ulong.MaxValue) { return(TomlValue.Create(v)); } else { throw new TomlAnalisysException(Resources.INTEGER_VALUE_RANGE_ERR, iter); } }
//--------------------------------------------------------------------- // テーブル解析 //--------------------------------------------------------------------- /// <summary>テーブルを作成する。</summary> /// <param name="iter">イテレータ。</param> /// <param name="table">カレントテーブル。</param> private void AnalisysTable(TomlInnerBuffer.TomlIter iter, TomlTable table) { // '.' で区切られたテーブル名を事前に収集する var keyPtr = iter.GetKeys(); // テーブルが閉じられているか確認 if (!iter.CloseTable()) { throw new TomlAnalisysException(Resources.TABLE_SYNTAX_ERR, iter); } // テーブルを作成する // // 1. テーブル参照を取得 // 2. エラーが有れば終了 // 3. 既に作成済みならばカレントを変更 // 4. 作成されていなければテーブルを作成し、カレントに設定 TomlTable curTable = table; TomlTable newTable = null; foreach (var keystr in keyPtr) { switch (curTable.SearchPathTable(keystr, out newTable)) // 1 { case 0: // 2 throw new TomlAnalisysException(Resources.TABLE_REDEFINITION_ERR, iter); case 1: curTable = newTable; // 3 break; default: newTable = new TomlTable(); // 4 curTable.AddKeyAndValue(keystr, newTable); curTable = newTable; break; } } // 空白は読み捨てておく iter.SkipSpace(); // カレントのテーブルを設定 this.current = curTable; if (!this.current.IsDefined) { this.current.IsDefined = true; } else { throw new TomlAnalisysException(Resources.DEFINED_KEY_ERR, iter); } }
/// <summary>日付表現の日にち部分を解析する。</summary> /// <param name="iter">イテレータ。</param> /// <param name="year">年。</param> /// <returns>日付情報。</returns> private static TomlDate ConvertDateFormat(TomlInnerBuffer.TomlIter iter, int year) { int month, day, hour; UTF8 c; // 月日の判定 // // 1. 取得できたら格納 // 2. 取得できなかったらエラーを返す if (ConvertPartitionNumber(iter, 5, 2, out month) && // 1 iter.GetChar(7).ch1 == '-' && ConvertPartitionNumber(iter, 8, 2, out day)) { // 空実装 } else // 2 { throw new TomlAnalisysException(Resources.ANALISYS_DATE_ERR, iter); } // 'T' の指定がなければ日にちのみ、終了 iter.Skip(10); c = iter.GetChar(0); if (c.ch1 != 'T' && c.ch1 != 't' && c.ch1 != ' ' && (c.ch1 == '\t' || c.ch1 == '#' || c.ch1 == '\r' || c.ch1 == '\n')) { return(new TomlDate((ushort)year, (byte)month, (byte)day, 0, 0, 0, 0, 0, 0)); } // 時間情報を判定して返す iter.Skip(1); if (ConvertPartitionNumber(iter, 0, 2, out hour) && iter.GetChar(2).ch1 == ':') { var tm = ConvertTimeFormat(iter, hour); return(new TomlDate((ushort)year, (byte)month, (byte)day, tm.Hour, tm.Minute, tm.Second, tm.DecSecond, tm.ZoneHour, tm.ZoneMinute)); } else if (c.ch1 == ' ') { return(new TomlDate((ushort)year, (byte)month, (byte)day, 0, 0, 0, 0, 0, 0)); } else { throw new TomlAnalisysException(Resources.ANALISYS_DATE_ERR, iter); } }
/// <summary>配列内の値の型が全て一致するか判定する。</summary> /// <param name="array">判定する配列参照。</param> /// <param name="iter">イテレータ。</param> /// <returns>配列情報。</returns> private ITomlValue CheckArrayValueType(List <ITomlValue> array, TomlInnerBuffer.TomlIter iter) { TomlValueType type = TomlValueType.TomlNoneValue; // 全ての要素の型が一致することを確認 if (array.Count > 0) { type = array[0].ValueType; for (int i = 1; i < array.Count; ++i) { if (type != array[i].ValueType) { throw new TomlAnalisysException(Resources.ARRAY_VALUE_DIFFERENT_ERR, iter); } } } // 配列を作成して返す switch (type) { case TomlValueType.TomlBooleanValue: var bools = array.Select(x => (bool)x.Raw).ToArray(); return(TomlValue.Create(bools)); case TomlValueType.TomlDateValue: var dates = array.Select(x => (TomlDate)x.Raw).ToArray(); return(TomlValue.Create(dates)); case TomlValueType.TomlFloatValue: var floats = array.Select(x => (double)x.Raw).ToArray(); return(TomlValue.Create(floats)); case TomlValueType.TomlIntegerValue: var longs = array.Select(x => (long)x.Raw).ToArray(); return(TomlValue.Create(longs)); case TomlValueType.TomlStringValue: var strings = array.Select(x => (string)x.Raw).ToArray(); return(TomlValue.Create(strings)); default: return(TomlValue.Create(array.ToArray())); } }
/// <summary>配列を解析する。</summary> /// <param name="iter">イテレータ。</param> /// <returns>値情報。</returns> private ITomlValue GetValueArray(TomlInnerBuffer.TomlIter iter) { var array = new List <ITomlValue>(); while (iter.GetChar(0).ch1 != 0) { // 改行、空白部を取り除く iter.SkipLineFeedAndSpace(); // 値を取り込む var value = this.AnalisysValue(iter); // 空値以外は取り込む if (value.ValueType != TomlValueType.TomlNoneValue) { array.Add(value); } // 配列が閉じられているか確認 // // 1. テーブルが閉じられている // 2. 次のキー/値を取得 // 3. エラー switch (iter.CloseValueArray()) { case 1: // 1 return(CheckArrayValueType(array, iter)); case 2: // 2 if (value.ValueType != TomlValueType.TomlNoneValue) { break; } else { throw new TomlAnalisysException(Resources.EMPTY_COMMA_ERR, iter); } default: // 3 break; } } // 配列が閉じられていない throw new TomlAnalisysException(Resources.ARRAY_NOT_CLOSE_ERR, iter); }
/// <summary>"'" で囲まれた文字列を取得する。</summary> /// <param name="iter">イテレータ。</param> /// <returns>取得した文字列。</returns> internal static string GetMultiLiteralStringValue(this TomlInnerBuffer.TomlIter iter) { var buf = new List <byte>(); UTF8 c; bool eof; // 先頭の改行は取り除く iter.SkipHeadLineFeed(); do { eof = iter.IsEndStream; while ((c = iter.GetChar(0)).ch1 != 0) { switch (c.ch1) { case (byte)'\'': // " を取得したら文字列終了 if (iter.GetChar(2).ch1 == '\'' && iter.GetChar(1).ch1 == '\'') { iter.Skip(3); return(Encoding.UTF8.GetString(buf.ToArray())); } else { buf.Add(c.ch1); iter.Skip(1); } break; default: // 上記以外は普通の文字として取得 c.Expand(buf); iter.Skip(1); break; } } iter.ReadLine(); } while (!eof); // " で終了できなかったためエラー throw new TomlAnalisysException(Resources.MULTI_LITERAL_STRING_ERR, iter); }
/// <summary>行の開始を判定する。</summary> /// <returns>行のデータ種類。</returns> internal static TomlInnerBuffer.LineType CheckStartLineToken(this TomlInnerBuffer.TomlIter iter) { // テーブル配列の開始か判定する if (iter.RemnantLength >= 2) { if (iter.GetChar(0).ch1 == '[' && iter.GetChar(1).ch1 == '[') { iter.Skip(2); return(TomlInnerBuffer.LineType.TomlTableArrayLine); } } // その他の開始を判定する // // 1. テーブルの開始か判定する // 2. コメントの開始か判定する // 4. キーの開始か判定する // 5. それ以外はエラー if (iter.RemnantLength > 0) { switch (iter.GetChar(0).ch1) { case (byte)'[': // 1 iter.Skip(1); return(TomlInnerBuffer.LineType.TomlTableLine); case (byte)'#': // 2 iter.Skip(1); return(TomlInnerBuffer.LineType.TomlCommenntLine); case (byte)'\r': // 3 case (byte)'\n': case (byte)'\0': return(TomlInnerBuffer.LineType.TomlLineNone); default: // 4 return(TomlInnerBuffer.LineType.TomlKeyValueLine); } } else // 5 { return(TomlInnerBuffer.LineType.TomlLineNone); } }
/// <summary>キー文字列を取得する。</summary> /// <param name="iter">イテレータ。</param> /// <returns>キー文字列。</returns> internal static string GetKey(this TomlInnerBuffer.TomlIter iter) { var buf = new List <byte>(); UTF8 c; // 空白を読み飛ばす iter.SkipSpace(); // 範囲の開始、終了位置で評価 // // 1. 一文字取得する // 2. キー使用可能文字か判定する // 3. " なら文字列としてキー文字列を取得する while ((c = iter.GetChar(0)).ch1 != 0) // 1 { if ((c.ch1 >= 'a' && c.ch1 <= 'z') || // 2 (c.ch1 >= 'A' && c.ch1 <= 'Z') || (c.ch1 >= '0' && c.ch1 <= '9') || c.ch1 == '_' || c.ch1 == '-') { c.Expand(buf); iter.Skip(1); } else if (c.ch1 == '"' && buf.Count <= 0) // 3 { iter.Skip(1); return(iter.GetStringValue()); } else { break; } } // バイトリストを文字列に変換して返す if (buf.Count > 0) { return(Encoding.UTF8.GetString(buf.ToArray())); } else { throw new TomlAnalisysException(Resources.KEY_ANALISYS_ERR, iter); } }
/// <summary>']' の後、次が改行/終端/コメントならば真を返す。</summary> /// <param name="iter">イテレータ。</param> /// <returns>改行/終端/コメントならば真。</returns> internal static bool CloseTable(this TomlInnerBuffer.TomlIter iter) { UTF8 c; // 空白を読み飛ばす iter.SkipSpace(); // ] の判定 if (iter.GetChar(0).ch1 != ']') { return(false); } iter.Skip(1); // 空白を読み飛ばす iter.SkipSpace(); // 文字を判定 c = iter.GetChar(0); return(c.ch1 == '#' || c.ch1 == '\r' || c.ch1 == '\n' || c.ch1 == 0); }
//----------------------------------------------------------------------------- // 数値/日付解析 //----------------------------------------------------------------------------- /// <summary>数値(日付/整数/実数)を取得する。</summary> /// <param name="iter">イテレータ。</param> /// <returns>値。</returns> internal static ITomlValue GetNumberOrDateValue(this TomlInnerBuffer.TomlIter iter) { int year, hour; if (ConvertPartitionNumber(iter, 0, 4, out year) && iter.GetChar(4).ch1 == '-') { // 日付(年月日)を取得する return(TomlValue.Create(ConvertDateFormat(iter, year))); } else if (ConvertPartitionNumber(iter, 0, 2, out hour) && iter.GetChar(2).ch1 == ':') { // 日付(時分秒)を取得する return(TomlValue.Create(ConvertTimeFormat(iter, hour))); } else { // 数値(整数/実数)を取得する return(iter.GetNumberValue(false)); } }
//--------------------------------------------------------------------- // 文字列解析 //--------------------------------------------------------------------- /// <summary>""" で囲まれた文字列を取得する。</summary> /// <param name="iter">イテレータ。</param> /// <returns>取得した文字列。</returns> internal static string GetMultiStringValue(this TomlInnerBuffer.TomlIter iter) { var buf = new List <byte>(); UTF8 c, nc; bool eof; bool skipSpace = false; byte[] lastC = new byte[2]; // 先頭の改行は取り除く iter.SkipHeadLineFeed(); do { eof = iter.IsEndStream; while ((c = iter.GetChar(0)).ch1 != 0) { switch (c.ch1) { case (byte)'"': // " を取得したら文字列終了 if (iter.GetChar(2).ch1 == '"' && iter.GetChar(1).ch1 == '"') { iter.Skip(3); return(Encoding.UTF8.GetString(buf.ToArray())); } else { buf.Add(c.ch1); iter.Skip(1); } break; case (byte)'\\': // \のエスケープ文字判定 nc = iter.GetChar(1); if ((nc.ch1 == '\r' || nc.ch1 == '\n' || nc.ch1 == '\t' || nc.ch1 == ' ') && iter.CheckLineEnd()) { skipSpace = true; iter.Skip(1); } else { AppendEscapeChar(iter, buf); } break; case (byte)' ': case (byte)'\t': // 空白文字を追加する if (!skipSpace) { buf.Add(c.ch1); } iter.Skip(1); break; default: // 上記以外は普通の文字として取得 if (c.ch1 > 0x1f) { skipSpace = false; c.Expand(buf); } else { lastC[1] = lastC[0]; lastC[0] = (byte)(c.ch1 & 0x1f); } iter.Skip(1); break; } } // 改行の読み飛ばし指定がなければ追加する if (!skipSpace) { if (lastC[1] == '\r' && lastC[0] == '\n') { buf.Add((byte)'\r'); buf.Add((byte)'\n'); } else if (lastC[0] == '\n') { buf.Add((byte)'\n'); } } iter.ReadLine(); } while (!eof); // " で終了できなかったためエラー throw new TomlAnalisysException(Resources.MULTI_QUOAT_STRING_ERR, iter); }
/// <summary>数値(整数/実数)を取得する。</summary> /// <param name="iter">イテレータ。</param> /// <param name="numberSign">符号。</param> /// <returns>数値。</returns> private static ITomlValue Get10NumberValue(TomlInnerBuffer.TomlIter iter, bool numberSign) { ulong v = 0; bool ud = false; UTF8 c; int point = iter.Pointer; int digit = -1; int expo = -1; int exp_v = -1; bool ld_zero = false, lst_zero = false; // 仮数部を取得する while ((c = iter.GetChar(0)).ch1 != 0) { // 1. '_'の連続の判定 // 2. 数値の計算をする // 2-1. 数値の有効範囲を超えるならばエラー // 3. 小数点位置を取得する // 4. 指数部(e)を取得する if (c.ch1 == '_') { if (ud) // 1 { throw new TomlAnalisysException(Resources.UNDERBAR_CONTINUE_ERR, iter); } ud = true; } else if (c.ch1 >= '0' && c.ch1 <= '9') { if (v < ulong.MaxValue / 10) // 2 { v = v * 10 + (ulong)(c.ch1 - '0'); if (digit >= 0) { digit++; lst_zero = true; } else { ld_zero = true; } } else // 2-1 { throw new TomlAnalisysException(Resources.INTEGER_VALUE_RANGE_ERR, iter); } ud = false; } else if (c.ch1 == '.') // 3 { if (ld_zero && digit < 0) { digit = 0; } else if (ld_zero) { throw new TomlAnalisysException(Resources.MULTI_DECIMAL_ERR, iter); } else { throw new TomlAnalisysException(Resources.NO_LEADING_ZERO_ERR, iter); } } else if (c.ch1 == 'e' || c.ch1 == 'E') { expo = 0; // 4 break; } else { break; } iter.Skip(1); } // 一文字の数値もなければ None値を返す if (iter.Pointer == point) { return(TomlValue.Empty); } // 仮数部を取得する CalcExponentConvert(iter, digit, expo, out exp_v); if (digit < 0 && expo < 0) { // 整数値を取得する // // 1. 負の整数変換 // 2. 正の整数変換 if (numberSign) { if (v <= (ulong)long.MaxValue) // 1 { return(TomlValue.Create(-(long)v)); } else if (v == (ulong)long.MaxValue + 1) { return(TomlValue.Create(long.MinValue)); } else { throw new TomlAnalisysException(Resources.DOUBLE_VALUE_RANGE_ERR, iter); } } else { if (v <= long.MaxValue) //2 { return(TomlValue.Create((long)v)); } else { throw new TomlAnalisysException(Resources.DOUBLE_VALUE_RANGE_ERR, iter); } } } else { if (digit >= 0 && !lst_zero) { throw new TomlAnalisysException(Resources.NO_LAST_ZERO_ERR, iter); } // 実数値を取得する // // 1. 負の指数なら除算 // 2. 正の指数なら積算 // 3. 0の指数なら使用しない // 4. 値を保持 double dv = 0; if (exp_v < 0) { double abs_e = 1; // 1 for (int i = 0; i < Math.Abs(exp_v); ++i) { abs_e *= 10; } dv = (double)v / abs_e; } else if (exp_v > 0) { double abs_e = 1; // 2 for (int i = 0; i < Math.Abs(exp_v); ++i) { abs_e *= 10; } dv = (double)v * abs_e; } else { dv = (double)v; // 3 } if (dv <= double.MaxValue) // 4 { return(TomlValue.Create(numberSign ? (double)-dv : (double)dv)); } else { throw new TomlAnalisysException(Resources.DOUBLE_VALUE_RANGE_ERR, iter); } } }
//--------------------------------------------------------------------- // キー/値解析 //--------------------------------------------------------------------- /// <summary>キーと値のペアを取得する。</summary> /// <param name="iter">イテレータ。</param> /// <param name="table">対象テーブル。</param> /// <param name="lastNoCheck">改行確認するならば真。</param> /// <returns>追加できたならば真。</returns> private bool AnalisysKeyAndValue(TomlInnerBuffer.TomlIter iter, TomlTable table, bool lastNoCheck) { // キー文字列を取得する // // 1. キー文字列を取得 // 2. キー以降の空白を読み飛ばす // 3. = で連結しているか確認 var keyPtr = iter.GetKeys(); // 1 iter.SkipSpace(); // 2 if (iter.GetChar(0).ch1 != '=') // 3 { throw new TomlAnalisysException(Resources.KEY_ANALISYS_ERR, iter); } // 値を取得する // // 1. 値が取得できるか // 2. 無効値であるか iter.Skip(1); var val = this.AnalisysValue(iter); // 1 if (val.ValueType == TomlValueType.TomlNoneValue) { return(false); // 2 } // 改行まで確認 if (!lastNoCheck && !iter.CheckLineEndOrComment()) { throw new TomlAnalisysException(Resources.KEY_VALUE_ERR, iter); } // '.' で指定されたテーブル参照を収集する // // 1. テーブル参照を取得 // 2. エラーが有れば終了 // 3. 既に作成済みならばカレントを変更 // 4. 作成されていなければテーブルを作成し、カレントに設定 TomlTable curTable = table; TomlTable newTable = null; for (int i = 0; i < keyPtr.Count - 1; ++i) { var keystr = keyPtr[i]; switch (curTable.SearchPathTable(keystr, out newTable)) // 1 { case 0: // 2 throw new TomlAnalisysException(Resources.TABLE_REDEFINITION_ERR, iter); case 1: curTable = newTable; // 3 break; default: newTable = new TomlTable(); // 4 curTable.AddKeyAndValue(keystr, newTable); curTable = newTable; break; } } // 最終のテーブルに値を割り当てる var laststr = keyPtr[keyPtr.Count - 1]; if (!curTable.Contains(laststr)) { curTable.AddKeyAndValue(laststr, val); return(true); } else { throw new TomlAnalisysException(Resources.DEFINED_KEY_ERR, iter); } }
/// <summary>日付表現の時間部分を解析する。</summary> /// <param name="iter">イテレータ。</param> /// <param name="hour">時値。</param> /// <returns>日付値。</returns> private static TomlDate ConvertTimeFormat(TomlInnerBuffer.TomlIter iter, int hour) { int minute, second, decSec = 0, z_hor, z_min; UTF8 c; // 分、秒の判定 // // 1. 取得できたら格納 // 2. 取得できなかったらエラーを返す if (ConvertPartitionNumber(iter, 3, 2, out minute) && // 1 iter.GetChar(5).ch1 == ':' && ConvertPartitionNumber(iter, 6, 2, out second)) { // 空実装 } else // 2 { throw new TomlAnalisysException(Resources.ANALISYS_TIME_ERR, iter); } // ミリ秒の解析 // // 1. '.' があればミリ秒解析開始 // 2. ミリ秒値を計算 iter.Skip(8); if (iter.GetChar(0).ch1 == '.') // 1 { iter.Skip(1); while (iter.GetChar(0).ch1 >= '0' && // 2 iter.GetChar(0).ch1 <= '9') { decSec = decSec * 10 + (iter.GetChar(0).ch1 - '0'); iter.Skip(1); } } // 時差の解析 // // 1. UTC指定ならば終了 // 2. 時差指定ならば、時刻を取り込む // 2-1. 時刻の書式に問題がなければ終了 // 2-2. 時刻の書式に問題があればエラー // 3. 時差指定なし、正常終了 c = iter.GetChar(0); if (c.ch1 == 'Z' || c.ch1 == 'z') // 1 { iter.Skip(1); return(new TomlDate(0, 0, 0, (byte)hour, (byte)minute, (byte)second, (uint)decSec, 0, 0)); } else if (c.ch1 == '+' || c.ch1 == '-') { if (ConvertPartitionNumber(iter, 1, 2, out z_hor) && iter.GetChar(3).ch1 == ':' && ConvertPartitionNumber(iter, 4, 2, out z_min)) { iter.Skip(6); return(new TomlDate(0, 0, 0, (byte)hour, (byte)minute, (byte)second, (uint)decSec, (sbyte)(c.ch1 == '+' ? z_hor : -z_hor), (byte)z_min)); } else { throw new TomlAnalisysException(Resources.ANALISYS_TIME_DIFF_ERR, iter); } } else { return(new TomlDate(0, 0, 0, // 3 (byte)hour, (byte)minute, (byte)second, (uint)decSec, 0, 0)); } }
/// <summary>実数の指数部を取得する。</summary> /// <param name="iter"></param> /// <param name="digit">小数点位置。</param> /// <param name="expo">指数(e)。</param> /// <param name="resExpo">指数値(戻り値)</param> private static void CalcExponentConvert(TomlInnerBuffer.TomlIter iter, int digit, int expo, out int resExpo) { bool ud = false; UTF8 c; bool sign = false; int exp_v = 0; int point = iter.Pointer; if (expo >= 0) { // 符号を取得する iter.Skip(1); c = iter.GetChar(0); if (c.ch1 == '+') { sign = false; iter.Skip(1); } else if (c.ch1 == '-') { sign = true; iter.Skip(1); } ud = false; while ((c = iter.GetChar(0)).ch1 != 0) { // 1. '_'の連続の判定 // 2. 指数部を計算する if (c.ch1 == '_') { if (ud) // 1 { throw new TomlAnalisysException(Resources.UNDERBAR_CONTINUE_ERR, iter); } ud = true; } else if (c.ch1 >= '0' && c.ch1 <= '9') { exp_v = exp_v * 10 + (c.ch1 - '0'); // 2 if (exp_v >= 308) { throw new TomlAnalisysException(Resources.DOUBLE_VALUE_RANGE_ERR, iter); } ud = false; } else { break; } iter.Skip(1); } // 指数値を確認する // 1. 指数値が 0以下でないことを確認 // 2. 指数値が '0'始まりでないことを確認 if (exp_v <= 0) // 1 { throw new TomlAnalisysException(Resources.DOUBLE_VALUE_RANGE_ERR, iter); } else if (iter.GetChar(0).ch1 == '0') // 2 { throw new TomlAnalisysException(Resources.ZERO_NUMBER_ERR, iter); } } // 符号を設定 if (sign) { exp_v = -exp_v; } // 小数点位置とマージ exp_v -= (digit > 0 ? digit : 0); if (exp_v > 308 || exp_v < -308) { throw new TomlAnalisysException(Resources.DOUBLE_VALUE_RANGE_ERR, iter); } resExpo = exp_v; }
/// <summary>数値(整数/実数/日付)を取得する。</summary> /// <param name="iter">イテレータ。</param> /// <returns>値情報。</returns> private ITomlValue ConvertValue(TomlInnerBuffer.TomlIter iter) { ITomlValue value = null; // 値リテラルを解析する if (iter.AnalisysKeyword(out value)) { return(value); } // 複数ライン文字列を返す if (iter.RemnantLength >= 3) { // 複数ライン文字列を返す if (iter.GetChar(2).ch1 == '"' && iter.GetChar(1).ch1 == '"' && iter.GetChar(0).ch1 == '"') { iter.Skip(3); return(TomlValue.Create(iter.GetMultiStringValue())); } // 複数ライン文字列(リテラル)を返す if (iter.GetChar(2).ch1 == '\'' && iter.GetChar(1).ch1 == '\'' && iter.GetChar(0).ch1 == '\'') { iter.Skip(3); return(TomlValue.Create(iter.GetMultiLiteralStringValue())); } } // 数値/日付/文字列 if (iter.RemnantLength > 0) { switch (iter.GetChar(0).ch1) { case (byte)'"': // 文字列を取得する iter.Skip(1); return(TomlValue.Create(iter.GetStringValue())); case (byte)'\'': // リテラル文字列を取得する iter.Skip(1); return(TomlValue.Create(iter.GetLiteralStringValue())); case (byte)'#': // コメントを取得する iter.Skip(1); return(null); case (byte)'+': // 数値を取得する iter.Skip(1); return(iter.GetNumberValue(false)); case (byte)'-': // 数値を取得する iter.Skip(1); return(iter.GetNumberValue(true)); default: // 日付/数値を取得する return(iter.GetNumberOrDateValue()); } } else { throw new TomlAnalisysException(Resources.NOT_DEFINED_VALUE_ERR, iter); } }
/// <summary>'\'エスケープ文字を取得する。</summary> /// <param name="iter">イテレータ。</param> /// <param name="buf">バイトリスト。</param> private static void AppendEscapeChar(TomlInnerBuffer.TomlIter iter, List <byte> buf) { if (iter.RemnantLength > 2) { UTF8 c = iter.GetChar(1); switch (c.ch1) { case (byte)'b': buf.Add((byte)'\b'); iter.Skip(2); break; case (byte)'t': buf.Add((byte)'\t'); iter.Skip(2); break; case (byte)'n': buf.Add((byte)'\n'); iter.Skip(2); break; case (byte)'f': buf.Add((byte)'\f'); iter.Skip(2); break; case (byte)'r': buf.Add((byte)'\r'); iter.Skip(2); break; case (byte)'"': buf.Add((byte)'"'); iter.Skip(2); break; case (byte)'/': buf.Add((byte)'/'); iter.Skip(2); break; case (byte)'\\': buf.Add((byte)'\\'); iter.Skip(2); break; case (byte)'u': if (iter.RemnantLength >= 6) { iter.Skip(2); AppendUnicode(iter, 4, buf); } break; case (byte)'U': if (iter.RemnantLength >= 10) { iter.Skip(2); AppendUnicode(iter, 8, buf); } break; default: throw new TomlAnalisysException(Resources.INVALID_ESCAPE_CHAR_ERR, iter); } } }
/// <summary>UNICODEエスケープ判定。</summary> /// <param name="iter">イテレータ。</param> /// <param name="len">読み込む文字数。</param> /// <param name="buf">バイトリスト。</param> private static void AppendUnicode(TomlInnerBuffer.TomlIter iter, int len, List <byte> buf) { // 16進数文字を判定し、数値化 uint val = 0; for (int i = 0; i < len; ++i) { UTF8 c = iter.GetChar(i); val <<= 4; if (c.ch1 >= '0' && c.ch1 <= '9') { val |= (uint)(c.ch1 - '0'); } else if (c.ch1 >= 'a' && c.ch1 <= 'f') { val |= (uint)(10 + c.ch1 - 'a'); } else if (c.ch1 >= 'A' && c.ch1 <= 'F') { val |= (uint)(10 + c.ch1 - 'A'); } else { throw new TomlAnalisysException(Resources.UNICODE_DEFINE_ERR, iter); } } // UTF8へ変換 if (val < 0x80) { buf.Add((byte)(val & 0xff)); } else if (val < 0x800) { buf.Add((byte)(0xc0 | (val >> 6))); buf.Add((byte)(0x80 | (val & 0x3f))); } else if (val < 0x10000) { buf.Add((byte)(0xe0 | (val >> 12))); buf.Add((byte)(0x80 | ((val >> 6) & 0x3f))); buf.Add((byte)(0x80 | (val & 0x3f))); } else if (val < 0x200000) { buf.Add((byte)(0xf0 | (val >> 18))); buf.Add((byte)(0x80 | ((val >> 12) & 0x3f))); buf.Add((byte)(0x80 | ((val >> 6) & 0x3f))); buf.Add((byte)(0x80 | (val & 0x3f))); } else if (val < 0x4000000) { buf.Add((byte)(0xf8 | (val >> 24))); buf.Add((byte)(0x80 | ((val >> 18) & 0x3f))); buf.Add((byte)(0x80 | ((val >> 12) & 0x3f))); buf.Add((byte)(0x80 | ((val >> 6) & 0x3f))); buf.Add((byte)(0x80 | (val & 0x3f))); } else { buf.Add((byte)(0xfc | (val >> 30))); buf.Add((byte)(0x80 | ((val >> 24) & 0x3f))); buf.Add((byte)(0x80 | ((val >> 18) & 0x3f))); buf.Add((byte)(0x80 | ((val >> 12) & 0x3f))); buf.Add((byte)(0x80 | ((val >> 6) & 0x3f))); buf.Add((byte)(0x80 | (val & 0x3f))); } iter.Skip(len); }
/// <summary>テーブル配列を作成する。</summary> /// <param name="iter">イテレータ。</param> /// <param name="table">カレントテーブル。</param> private void AnalisysTableArray(TomlInnerBuffer.TomlIter iter, TomlTable table) { // '.' で区切られたテーブル名を事前に収集する var keyPtr = iter.GetKeys(); // テーブル配列が閉じられているか確認 if (!iter.CloseTableArray()) { throw new TomlAnalisysException(Resources.TABLE_ARRAY_SYNTAX_ERR, iter); } // 最下層のテーブル以外のテーブル参照を収集する // // 1. テーブル参照を取得 // 2. エラーが有れば終了 // 3. 既に作成済みならばカレントを変更 // 4. 作成されていなければテーブルを作成し、カレントに設定 TomlTable curTable = table; TomlTable newTable = null; string keystr = ""; for (int i = 0; i < keyPtr.Count - 1; ++i) { keystr = keyPtr[i]; switch (curTable.SearchPathTable(keystr, out newTable)) // 1 { case 0: // 2 throw new TomlAnalisysException(Resources.TABLE_REDEFINITION_ERR, iter); case 1: curTable = newTable; // 3 break; default: newTable = new TomlTable(); // 4 curTable.AddKeyAndValue(keystr, newTable); curTable = newTable; break; } } // 最下層のテーブルは新規作成となる // // 1. 登録する名前(キー文字列)を取得 // 2. 親のテーブルに最下層のテーブル名が登録されている // 2-1. 登録されている名前のデータを取得する // 2-2. テーブル配列が登録されているならば、新規テーブルを作成し、カレントテーブルとする // 2-3. テーブル配列でないならばエラーとする // 3. 親のテーブルに最下層のテーブル名が登録されていない // 3-1. テーブル配列を作成し、テーブルを追加、追加されたテーブルが次のカレントテーブルになる keystr = keyPtr[keyPtr.Count - 1]; // 1 if (curTable.Contains(keystr)) // 2-1 { var val = curTable.Member(keystr); if (val.ValueType == TomlValueType.TomlTableArrayValue) // 2-2 { newTable = new TomlTable(); ((Array <TomlTable>)val).Add(newTable); } else // 2-3 { throw new TomlAnalisysException(Resources.DEFINED_KEY_ERR, iter); } } else { newTable = new TomlTable(); // 3-1 var newArr = TomlValue.Create(new TomlTable[] { newTable }); curTable.AddKeyAndValue(keystr, newArr); } // カレントのテーブルを作成したテーブルに変更 this.current = newTable; iter.SkipSpace(); }
//--------------------------------------------------------------------- // 定数リテラル解析 //--------------------------------------------------------------------- /// <summary>数値(定数)を取得する。</summary> /// <param name="iter">イテレータ。</param> /// <param name="value">値(戻り値)</param> /// <returns>値が取得できたならば真。</returns> internal static bool AnalisysKeyword(this TomlInnerBuffer.TomlIter iter, out ITomlValue value) { // 真偽値(偽)を返す if (iter.RemnantLength >= 5) { if (iter.GetChar(4).ch1 == 'e' && iter.GetChar(3).ch1 == 's' && iter.GetChar(2).ch1 == 'l' && iter.GetChar(1).ch1 == 'a' && iter.GetChar(0).ch1 == 'f') { value = TomlValue.False; iter.Skip(5); return(true); } } if (iter.RemnantLength >= 4) { // 真偽値(真)を返す if (iter.GetChar(3).ch1 == 'e' && iter.GetChar(2).ch1 == 'u' && iter.GetChar(1).ch1 == 'r' && iter.GetChar(0).ch1 == 't') { value = TomlValue.True; iter.Skip(4); return(true); } // 正の無限値を返す if (iter.GetChar(3).ch1 == 'f' && iter.GetChar(2).ch1 == 'n' && iter.GetChar(1).ch1 == 'i' && iter.GetChar(0).ch1 == '+') { value = TomlValue.Create(double.PositiveInfinity); iter.Skip(4); return(true); } // 負の無限値を返す if (iter.GetChar(3).ch1 == 'f' && iter.GetChar(2).ch1 == 'n' && iter.GetChar(1).ch1 == 'i' && iter.GetChar(0).ch1 == '-') { value = TomlValue.Create(double.NegativeInfinity); iter.Skip(4); return(true); } // 正の非数値を返す if (iter.GetChar(3).ch1 == 'n' && iter.GetChar(2).ch1 == 'a' && iter.GetChar(1).ch1 == 'n' && iter.GetChar(0).ch1 == '+') { value = TomlValue.Create(double.NaN); iter.Skip(4); return(true); } // 負の非数値を返す if (iter.GetChar(3).ch1 == 'n' && iter.GetChar(2).ch1 == 'a' && iter.GetChar(1).ch1 == 'n' && iter.GetChar(0).ch1 == '-') { value = TomlValue.Create(-double.NaN); iter.Skip(4); return(true); } } if (iter.RemnantLength >= 3) { // 無限値を返す if (iter.GetChar(2).ch1 == 'f' && iter.GetChar(1).ch1 == 'n' && iter.GetChar(0).ch1 == 'i') { value = TomlValue.Create(double.PositiveInfinity); iter.Skip(3); return(true); } // 非数値を返す if (iter.GetChar(2).ch1 == 'n' && iter.GetChar(1).ch1 == 'a' && iter.GetChar(0).ch1 == 'n') { value = TomlValue.Create(double.NaN); iter.Skip(3); return(true); } } value = null; return(false); }