From cab3ec2d06df2d6dd01cb10f08cd0474380259e2 Mon Sep 17 00:00:00 2001 From: BlubbFish Date: Wed, 24 Jul 2019 17:07:28 +0200 Subject: [PATCH] Refactoring and fixing a bug when add json text --- litjson/IJsonWrapper.cs | 81 +- litjson/JsonException.cs | 102 +-- litjson/JsonMapper.cs | 15 +- litjson/JsonMockWrapper.cs | 237 ++--- litjson/JsonReader.cs | 808 +++++++---------- litjson/JsonWriter.cs | 23 +- litjson/Lexer.cs | 1765 +++++++++++++++++------------------- litjson/ParserToken.cs | 62 +- 8 files changed, 1467 insertions(+), 1626 deletions(-) diff --git a/litjson/IJsonWrapper.cs b/litjson/IJsonWrapper.cs index 9b7e2d1..9f3b800 100644 --- a/litjson/IJsonWrapper.cs +++ b/litjson/IJsonWrapper.cs @@ -11,50 +11,47 @@ #endregion +using System; using System.Collections; using System.Collections.Specialized; -namespace LitJson -{ - public enum JsonType - { - None, - - Object, - Array, - String, - Int, - Long, - Double, - Boolean - } - - public interface IJsonWrapper : IList, IOrderedDictionary - { - bool IsArray { get; } - bool IsBoolean { get; } - bool IsDouble { get; } - bool IsInt { get; } - bool IsLong { get; } - bool IsObject { get; } - bool IsString { get; } - - bool GetBoolean (); - double GetDouble (); - int GetInt (); - JsonType GetJsonType (); - long GetLong (); - string GetString (); - - void SetBoolean (bool val); - void SetDouble (double val); - void SetInt (int val); - void SetJsonType (JsonType type); - void SetLong (long val); - void SetString (string val); - - string ToJson (); - void ToJson (JsonWriter writer); - } +namespace LitJson { + public enum JsonType { + None, + Object, + Array, + String, + Int, + Long, + Double, + Boolean + } + + public interface IJsonWrapper : IList, IOrderedDictionary { + Boolean IsArray { get; } + Boolean IsBoolean { get; } + Boolean IsDouble { get; } + Boolean IsInt { get; } + Boolean IsLong { get; } + Boolean IsObject { get; } + Boolean IsString { get; } + + Boolean GetBoolean(); + Double GetDouble(); + Int32 GetInt(); + JsonType GetJsonType(); + Int64 GetLong(); + String GetString(); + + void SetBoolean(Boolean val); + void SetDouble(Double val); + void SetInt(Int32 val); + void SetJsonType(JsonType type); + void SetLong(Int64 val); + void SetString(String val); + + String ToJson(); + void ToJson(JsonWriter writer); + } } diff --git a/litjson/JsonException.cs b/litjson/JsonException.cs index 4efd890..ccc136f 100644 --- a/litjson/JsonException.cs +++ b/litjson/JsonException.cs @@ -1,65 +1,37 @@ -#region Header -/** - * JsonException.cs - * Base class throwed by LitJSON when a parsing error occurs. - * - * The authors disclaim copyright to this source code. For more details, see - * the COPYING file included with this distribution. - **/ -#endregion - - -using System; - - -namespace LitJson -{ - public class JsonException : -#if NETSTANDARD1_5 - Exception -#else - ApplicationException -#endif - { - public JsonException () : base () - { - } - - internal JsonException (ParserToken token) : - base (String.Format ( - "Invalid token '{0}' in input string", token)) - { - } - - internal JsonException (ParserToken token, - Exception inner_exception) : - base (String.Format ( - "Invalid token '{0}' in input string", token), - inner_exception) - { - } - - internal JsonException (int c) : - base (String.Format ( - "Invalid character '{0}' in input string", (char) c)) - { - } - - internal JsonException (int c, Exception inner_exception) : - base (String.Format ( - "Invalid character '{0}' in input string", (char) c), - inner_exception) - { - } - - - public JsonException (string message) : base (message) - { - } - - public JsonException (string message, Exception inner_exception) : - base (message, inner_exception) - { - } - } -} +#region Header +/** + * JsonException.cs + * Base class throwed by LitJSON when a parsing error occurs. + * + * The authors disclaim copyright to this source code. For more details, see + * the COPYING file included with this distribution. + **/ +#endregion + + +using System; + + +namespace LitJson { + public class JsonException : +#if NETSTANDARD1_5 + Exception +#else + ApplicationException +#endif + { + public JsonException() : base() { } + + internal JsonException(ParserToken token) : base(String.Format("Invalid token '{0}' in input string", token)) { } + + internal JsonException(ParserToken token, Exception inner_exception) : base(String.Format("Invalid token '{0}' in input string", token), inner_exception) { } + + internal JsonException(Int32 c) : base(String.Format("Invalid character '{0}' in input string", (Char)c)) { } + + internal JsonException(Int32 c, Exception inner_exception) : base(String.Format("Invalid character '{0}' in input string", (Char)c), inner_exception) { } + + public JsonException(String message) : base(message) { } + + public JsonException(String message, Exception inner_exception) : base(message, inner_exception) { } + } +} diff --git a/litjson/JsonMapper.cs b/litjson/JsonMapper.cs index ff84a1f..7809733 100644 --- a/litjson/JsonMapper.cs +++ b/litjson/JsonMapper.cs @@ -436,7 +436,7 @@ namespace LitJson { } private static IJsonWrapper ReadValue(WrapperFactory factory, JsonReader reader) { - reader.Read(); + _ = reader.Read(); if (reader.Token == JsonToken.ArrayEnd || reader.Token == JsonToken.Null) { @@ -478,13 +478,13 @@ namespace LitJson { if (item == null && reader.Token == JsonToken.ArrayEnd) { break; } - instance.Add(item); + _ = instance.Add(item); } } else if (reader.Token == JsonToken.ObjectStart) { instance.SetJsonType(JsonType.Object); while (true) { - reader.Read(); + _ = reader.Read(); if (reader.Token == JsonToken.ObjectEnd) { break; @@ -627,7 +627,10 @@ namespace LitJson { if (obj is IJsonWrapper) { if (writer_is_private) { - writer.TextWriter.Write(((IJsonWrapper)obj).ToJson()); + String t = ((IJsonWrapper)obj).ToJson(); + writer.WriteJson(t); + //writer.TextWriter.Write(t); + //this.context.ExpectingValue = false; } else { ((IJsonWrapper)obj).ToJson(writer); } @@ -783,9 +786,9 @@ namespace LitJson { public static IJsonWrapper ToWrapper(WrapperFactory factory, String json) => ReadValue(factory, new JsonReader(json)); - public static void RegisterExporter(ExporterFunc exporter) => custom_exporters_table[typeof(T)] = (Object obj, JsonWriter writer) => { exporter((T)obj, writer); }; + public static void RegisterExporter(ExporterFunc exporter) => custom_exporters_table[typeof(T)] = (Object obj, JsonWriter writer) => exporter((T)obj, writer); - public static void RegisterImporter(ImporterFunc importer) => RegisterImporter(custom_importers_table, typeof(TJson), typeof(TValue), (Object input) => { return importer((TJson)input); }); + public static void RegisterImporter(ImporterFunc importer) => RegisterImporter(custom_importers_table, typeof(TJson), typeof(TValue), (Object input) => importer((TJson)input)); public static void UnregisterExporters() => custom_exporters_table.Clear(); diff --git a/litjson/JsonMockWrapper.cs b/litjson/JsonMockWrapper.cs index dfe7adb..46fab0c 100644 --- a/litjson/JsonMockWrapper.cs +++ b/litjson/JsonMockWrapper.cs @@ -1,105 +1,132 @@ -#region Header -/** - * JsonMockWrapper.cs - * Mock object implementing IJsonWrapper, to facilitate actions like - * skipping data more efficiently. - * - * The authors disclaim copyright to this source code. For more details, see - * the COPYING file included with this distribution. - **/ -#endregion - - -using System; -using System.Collections; -using System.Collections.Specialized; - - -namespace LitJson -{ - public class JsonMockWrapper : IJsonWrapper - { - public bool IsArray { get { return false; } } - public bool IsBoolean { get { return false; } } - public bool IsDouble { get { return false; } } - public bool IsInt { get { return false; } } - public bool IsLong { get { return false; } } - public bool IsObject { get { return false; } } - public bool IsString { get { return false; } } - - public bool GetBoolean () { return false; } - public double GetDouble () { return 0.0; } - public int GetInt () { return 0; } - public JsonType GetJsonType () { return JsonType.None; } - public long GetLong () { return 0L; } - public string GetString () { return ""; } - - public void SetBoolean (bool val) {} - public void SetDouble (double val) {} - public void SetInt (int val) {} - public void SetJsonType (JsonType type) {} - public void SetLong (long val) {} - public void SetString (string val) {} - - public string ToJson () { return ""; } - public void ToJson (JsonWriter writer) {} - - - bool IList.IsFixedSize { get { return true; } } - bool IList.IsReadOnly { get { return true; } } - - object IList.this[int index] { - get { return null; } - set {} - } - - int IList.Add (object value) { return 0; } - void IList.Clear () {} - bool IList.Contains (object value) { return false; } - int IList.IndexOf (object value) { return -1; } - void IList.Insert (int i, object v) {} - void IList.Remove (object value) {} - void IList.RemoveAt (int index) {} - - - int ICollection.Count { get { return 0; } } - bool ICollection.IsSynchronized { get { return false; } } - object ICollection.SyncRoot { get { return null; } } - - void ICollection.CopyTo (Array array, int index) {} - - - IEnumerator IEnumerable.GetEnumerator () { return null; } - - - bool IDictionary.IsFixedSize { get { return true; } } - bool IDictionary.IsReadOnly { get { return true; } } - - ICollection IDictionary.Keys { get { return null; } } - ICollection IDictionary.Values { get { return null; } } - - object IDictionary.this[object key] { - get { return null; } - set {} - } - - void IDictionary.Add (object k, object v) {} - void IDictionary.Clear () {} - bool IDictionary.Contains (object key) { return false; } - void IDictionary.Remove (object key) {} - - IDictionaryEnumerator IDictionary.GetEnumerator () { return null; } - - - object IOrderedDictionary.this[int idx] { - get { return null; } - set {} - } - - IDictionaryEnumerator IOrderedDictionary.GetEnumerator () { - return null; - } - void IOrderedDictionary.Insert (int i, object k, object v) {} - void IOrderedDictionary.RemoveAt (int i) {} - } -} +#region Header +/** + * JsonMockWrapper.cs + * Mock object implementing IJsonWrapper, to facilitate actions like + * skipping data more efficiently. + * + * The authors disclaim copyright to this source code. For more details, see + * the COPYING file included with this distribution. + **/ +#endregion + + +using System; +using System.Collections; +using System.Collections.Specialized; + + +namespace LitJson { + public class JsonMockWrapper : IJsonWrapper { + public Boolean IsArray => false; + + public Boolean IsBoolean => false; + + public Boolean IsDouble => false; + + public Boolean IsInt => false; + + public Boolean IsLong => false; + + public Boolean IsObject => false; + + public Boolean IsString => false; + + public Boolean GetBoolean() => false; + + public Double GetDouble() => 0.0; + + public Int32 GetInt() => 0; + + public JsonType GetJsonType() => JsonType.None; + + public Int64 GetLong() => 0L; + + public String GetString() => ""; + + public void SetBoolean(Boolean val) { } + + public void SetDouble(Double val) { } + + public void SetInt(Int32 val) { } + + public void SetJsonType(JsonType type) { } + + public void SetLong(Int64 val) { } + + public void SetString(String val) { } + + public String ToJson() => ""; + + public void ToJson(JsonWriter writer) { } + + Boolean IList.IsFixedSize => true; + + Boolean IList.IsReadOnly => true; + + Object IList.this[Int32 index] { + get => null; + set { + } + } + + Int32 IList.Add(Object value) => 0; + + void IList.Clear() { } + + Boolean IList.Contains(Object value) => false; + + Int32 IList.IndexOf(Object value) => -1; + + void IList.Insert(Int32 i, Object v) { } + + void IList.Remove(Object value) { } + + void IList.RemoveAt(Int32 index) { } + + Int32 ICollection.Count => 0; + + Boolean ICollection.IsSynchronized => false; + + Object ICollection.SyncRoot => null; + + void ICollection.CopyTo(Array array, Int32 index) { } + + IEnumerator IEnumerable.GetEnumerator() => null; + + Boolean IDictionary.IsFixedSize => true; + + Boolean IDictionary.IsReadOnly => true; + + ICollection IDictionary.Keys => null; + + ICollection IDictionary.Values => null; + + Object IDictionary.this[Object key] { + get => null; + set { + } + } + + void IDictionary.Add(Object k, Object v) { } + + void IDictionary.Clear() { } + + Boolean IDictionary.Contains(Object key) => false; + + void IDictionary.Remove(Object key) { } + + IDictionaryEnumerator IDictionary.GetEnumerator() => null; + + Object IOrderedDictionary.this[Int32 idx] { + get => null; + set { + } + } + + IDictionaryEnumerator IOrderedDictionary.GetEnumerator() => null; + + void IOrderedDictionary.Insert(Int32 i, Object k, Object v) { } + + void IOrderedDictionary.RemoveAt(Int32 i) { } + } +} diff --git a/litjson/JsonReader.cs b/litjson/JsonReader.cs index e47eabc..7db6fda 100644 --- a/litjson/JsonReader.cs +++ b/litjson/JsonReader.cs @@ -16,463 +16,353 @@ using System.IO; using System.Text; -namespace LitJson -{ - public enum JsonToken - { - None, - - ObjectStart, - PropertyName, - ObjectEnd, - - ArrayStart, - ArrayEnd, - - Int, - Long, - Double, - - String, - - Boolean, - Null - } - - - public class JsonReader - { - #region Fields - private static readonly IDictionary> parse_table; - - private Stack automaton_stack; - private int current_input; - private int current_symbol; - private bool end_of_json; - private bool end_of_input; - private Lexer lexer; - private bool parser_in_string; - private bool parser_return; - private bool read_started; - private TextReader reader; - private bool reader_is_owned; - private bool skip_non_members; - private object token_value; - private JsonToken token; - #endregion - - - #region Public Properties - public bool AllowComments { - get { return lexer.AllowComments; } - set { lexer.AllowComments = value; } - } - - public bool AllowSingleQuotedStrings { - get { return lexer.AllowSingleQuotedStrings; } - set { lexer.AllowSingleQuotedStrings = value; } - } - - public bool SkipNonMembers { - get { return skip_non_members; } - set { skip_non_members = value; } - } - - public bool EndOfInput { - get { return end_of_input; } - } - - public bool EndOfJson { - get { return end_of_json; } - } - - public JsonToken Token { - get { return token; } - } - - public object Value { - get { return token_value; } - } - #endregion - - - #region Constructors - static JsonReader () - { - parse_table = PopulateParseTable (); - } - - public JsonReader (string json_text) : - this (new StringReader (json_text), true) - { - } - - public JsonReader (TextReader reader) : - this (reader, false) - { - } - - private JsonReader (TextReader reader, bool owned) - { - if (reader == null) - throw new ArgumentNullException ("reader"); - - parser_in_string = false; - parser_return = false; - - read_started = false; - automaton_stack = new Stack (); - automaton_stack.Push ((int) ParserToken.End); - automaton_stack.Push ((int) ParserToken.Text); - - lexer = new Lexer (reader); - - end_of_input = false; - end_of_json = false; - - skip_non_members = true; - - this.reader = reader; - reader_is_owned = owned; - } - #endregion - - - #region Static Methods - private static IDictionary> PopulateParseTable () - { - // See section A.2. of the manual for details - IDictionary> parse_table = new Dictionary> (); - - TableAddRow (parse_table, ParserToken.Array); - TableAddCol (parse_table, ParserToken.Array, '[', - '[', - (int) ParserToken.ArrayPrime); - - TableAddRow (parse_table, ParserToken.ArrayPrime); - TableAddCol (parse_table, ParserToken.ArrayPrime, '"', - (int) ParserToken.Value, - - (int) ParserToken.ValueRest, - ']'); - TableAddCol (parse_table, ParserToken.ArrayPrime, '[', - (int) ParserToken.Value, - (int) ParserToken.ValueRest, - ']'); - TableAddCol (parse_table, ParserToken.ArrayPrime, ']', - ']'); - TableAddCol (parse_table, ParserToken.ArrayPrime, '{', - (int) ParserToken.Value, - (int) ParserToken.ValueRest, - ']'); - TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.Number, - (int) ParserToken.Value, - (int) ParserToken.ValueRest, - ']'); - TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.True, - (int) ParserToken.Value, - (int) ParserToken.ValueRest, - ']'); - TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.False, - (int) ParserToken.Value, - (int) ParserToken.ValueRest, - ']'); - TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.Null, - (int) ParserToken.Value, - (int) ParserToken.ValueRest, - ']'); - - TableAddRow (parse_table, ParserToken.Object); - TableAddCol (parse_table, ParserToken.Object, '{', - '{', - (int) ParserToken.ObjectPrime); - - TableAddRow (parse_table, ParserToken.ObjectPrime); - TableAddCol (parse_table, ParserToken.ObjectPrime, '"', - (int) ParserToken.Pair, - (int) ParserToken.PairRest, - '}'); - TableAddCol (parse_table, ParserToken.ObjectPrime, '}', - '}'); - - TableAddRow (parse_table, ParserToken.Pair); - TableAddCol (parse_table, ParserToken.Pair, '"', - (int) ParserToken.String, - ':', - (int) ParserToken.Value); - - TableAddRow (parse_table, ParserToken.PairRest); - TableAddCol (parse_table, ParserToken.PairRest, ',', - ',', - (int) ParserToken.Pair, - (int) ParserToken.PairRest); - TableAddCol (parse_table, ParserToken.PairRest, '}', - (int) ParserToken.Epsilon); - - TableAddRow (parse_table, ParserToken.String); - TableAddCol (parse_table, ParserToken.String, '"', - '"', - (int) ParserToken.CharSeq, - '"'); - - TableAddRow (parse_table, ParserToken.Text); - TableAddCol (parse_table, ParserToken.Text, '[', - (int) ParserToken.Array); - TableAddCol (parse_table, ParserToken.Text, '{', - (int) ParserToken.Object); - - TableAddRow (parse_table, ParserToken.Value); - TableAddCol (parse_table, ParserToken.Value, '"', - (int) ParserToken.String); - TableAddCol (parse_table, ParserToken.Value, '[', - (int) ParserToken.Array); - TableAddCol (parse_table, ParserToken.Value, '{', - (int) ParserToken.Object); - TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.Number, - (int) ParserToken.Number); - TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.True, - (int) ParserToken.True); - TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.False, - (int) ParserToken.False); - TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.Null, - (int) ParserToken.Null); - - TableAddRow (parse_table, ParserToken.ValueRest); - TableAddCol (parse_table, ParserToken.ValueRest, ',', - ',', - (int) ParserToken.Value, - (int) ParserToken.ValueRest); - TableAddCol (parse_table, ParserToken.ValueRest, ']', - (int) ParserToken.Epsilon); - - return parse_table; - } - - private static void TableAddCol (IDictionary> parse_table, ParserToken row, int col, - params int[] symbols) - { - parse_table[(int) row].Add (col, symbols); - } - - private static void TableAddRow (IDictionary> parse_table, ParserToken rule) - { - parse_table.Add ((int) rule, new Dictionary ()); - } - #endregion - - - #region Private Methods - private void ProcessNumber (string number) - { - if (number.IndexOf ('.') != -1 || - number.IndexOf ('e') != -1 || - number.IndexOf ('E') != -1) { - - double n_double; - if (double.TryParse (number, NumberStyles.Any, CultureInfo.InvariantCulture, out n_double)) { - token = JsonToken.Double; - token_value = n_double; - - return; - } - } - - int n_int32; - if (int.TryParse (number, NumberStyles.Integer, CultureInfo.InvariantCulture, out n_int32)) { - token = JsonToken.Int; - token_value = n_int32; - - return; - } - - long n_int64; - if (long.TryParse (number, NumberStyles.Integer, CultureInfo.InvariantCulture, out n_int64)) { - token = JsonToken.Long; - token_value = n_int64; - - return; - } - - ulong n_uint64; - if (ulong.TryParse(number, NumberStyles.Integer, CultureInfo.InvariantCulture, out n_uint64)) - { - token = JsonToken.Long; - token_value = n_uint64; - - return; - } - - // Shouldn't happen, but just in case, return something - token = JsonToken.Int; - token_value = 0; - } - - private void ProcessSymbol () - { - if (current_symbol == '[') { - token = JsonToken.ArrayStart; - parser_return = true; - - } else if (current_symbol == ']') { - token = JsonToken.ArrayEnd; - parser_return = true; - - } else if (current_symbol == '{') { - token = JsonToken.ObjectStart; - parser_return = true; - - } else if (current_symbol == '}') { - token = JsonToken.ObjectEnd; - parser_return = true; - - } else if (current_symbol == '"') { - if (parser_in_string) { - parser_in_string = false; - - parser_return = true; - - } else { - if (token == JsonToken.None) - token = JsonToken.String; - - parser_in_string = true; - } - - } else if (current_symbol == (int) ParserToken.CharSeq) { - token_value = lexer.StringValue; - - } else if (current_symbol == (int) ParserToken.False) { - token = JsonToken.Boolean; - token_value = false; - parser_return = true; - - } else if (current_symbol == (int) ParserToken.Null) { - token = JsonToken.Null; - parser_return = true; - - } else if (current_symbol == (int) ParserToken.Number) { - ProcessNumber (lexer.StringValue); - - parser_return = true; - - } else if (current_symbol == (int) ParserToken.Pair) { - token = JsonToken.PropertyName; - - } else if (current_symbol == (int) ParserToken.True) { - token = JsonToken.Boolean; - token_value = true; - parser_return = true; - - } - } - - private bool ReadToken () - { - if (end_of_input) - return false; - - lexer.NextToken (); - - if (lexer.EndOfInput) { - Close (); - - return false; - } - - current_input = lexer.Token; - - return true; - } - #endregion - - - public void Close () - { - if (end_of_input) - return; - - end_of_input = true; - end_of_json = true; - - if (reader_is_owned) - { - using(reader){} - } - - reader = null; - } - - public bool Read () - { - if (end_of_input) - return false; - - if (end_of_json) { - end_of_json = false; - automaton_stack.Clear (); - automaton_stack.Push ((int) ParserToken.End); - automaton_stack.Push ((int) ParserToken.Text); - } - - parser_in_string = false; - parser_return = false; - - token = JsonToken.None; - token_value = null; - - if (! read_started) { - read_started = true; - - if (! ReadToken ()) - return false; - } - - - int[] entry_symbols; - - while (true) { - if (parser_return) { - if (automaton_stack.Peek () == (int) ParserToken.End) - end_of_json = true; - - return true; - } - - current_symbol = automaton_stack.Pop (); - - ProcessSymbol (); - - if (current_symbol == current_input) { - if (! ReadToken ()) { - if (automaton_stack.Peek () != (int) ParserToken.End) - throw new JsonException ( - "Input doesn't evaluate to proper JSON text"); - - if (parser_return) - return true; - - return false; - } - - continue; - } - - try { - - entry_symbols = - parse_table[current_symbol][current_input]; - - } catch (KeyNotFoundException e) { - throw new JsonException ((ParserToken) current_input, e); - } - - if (entry_symbols[0] == (int) ParserToken.Epsilon) - continue; - - for (int i = entry_symbols.Length - 1; i >= 0; i--) - automaton_stack.Push (entry_symbols[i]); - } - } - - } +namespace LitJson { + public enum JsonToken { + None, + ObjectStart, + PropertyName, + ObjectEnd, + ArrayStart, + ArrayEnd, + Int, + Long, + Double, + String, + Boolean, + Null + } + + + public class JsonReader { + #region Fields + private static readonly IDictionary> parse_table; + + private readonly Stack automaton_stack; + private Int32 current_input; + private Int32 current_symbol; + private readonly Lexer lexer; + private Boolean parser_in_string; + private Boolean parser_return; + private Boolean read_started; + private TextReader reader; + private readonly Boolean reader_is_owned; + #endregion + + #region Public Properties + public Boolean AllowComments { + get => this.lexer.AllowComments; + set => this.lexer.AllowComments = value; + } + + public Boolean AllowSingleQuotedStrings { + get => this.lexer.AllowSingleQuotedStrings; + set => this.lexer.AllowSingleQuotedStrings = value; + } + + public Boolean SkipNonMembers { get; set; } + + public Boolean EndOfInput { get; private set; } + + public Boolean EndOfJson { get; private set; } + + public JsonToken Token { get; private set; } + + public Object Value { get; private set; } + #endregion + + #region Constructors + static JsonReader() => parse_table = PopulateParseTable(); + + public JsonReader(String json_text) : this(new StringReader(json_text), true) { } + + public JsonReader(TextReader reader) : this(reader, false) { } + + private JsonReader(TextReader reader, Boolean owned) { + if(reader == null) { + throw new ArgumentNullException("reader"); + } + + this.parser_in_string = false; + this.parser_return = false; + + this.read_started = false; + this.automaton_stack = new Stack(); + this.automaton_stack.Push((Int32)ParserToken.End); + this.automaton_stack.Push((Int32)ParserToken.Text); + + this.lexer = new Lexer(reader); + + this.EndOfInput = false; + this.EndOfJson = false; + + this.SkipNonMembers = true; + + this.reader = reader; + this.reader_is_owned = owned; + } + #endregion + + #region Static Methods + private static IDictionary> PopulateParseTable() => + // See section A.2. of the manual for details + new Dictionary> { + { + (Int32)ParserToken.Array, + new Dictionary { + { '[', new Int32[] { '[', (Int32)ParserToken.ArrayPrime } } + } + }, + { + (Int32)ParserToken.ArrayPrime, + new Dictionary { + { '"', new Int32[] { (Int32)ParserToken.Value, (Int32)ParserToken.ValueRest, ']' } }, + { '[', new Int32[] { (Int32)ParserToken.Value, (Int32)ParserToken.ValueRest, ']' } }, + { ']', new Int32[] { ']' } }, + { '{', new Int32[] { (Int32)ParserToken.Value, (Int32)ParserToken.ValueRest, ']' } }, + { (Int32)ParserToken.Number, new Int32[] { (Int32)ParserToken.Value, (Int32)ParserToken.ValueRest, ']' } }, + { (Int32)ParserToken.True, new Int32[] { (Int32)ParserToken.Value, (Int32)ParserToken.ValueRest, ']' } }, + { (Int32)ParserToken.False, new Int32[] { (Int32)ParserToken.Value, (Int32)ParserToken.ValueRest, ']' } }, + { (Int32)ParserToken.Null, new Int32[] { (Int32)ParserToken.Value, (Int32)ParserToken.ValueRest, ']' } } + } + }, + { + (Int32)ParserToken.Object, + new Dictionary { + { '{', new Int32[] { '{', (Int32)ParserToken.ObjectPrime } } + } + }, + { + (Int32)ParserToken.ObjectPrime, + new Dictionary { + { '"', new Int32[] { (Int32)ParserToken.Pair, (Int32)ParserToken.PairRest, '}' } }, + { '}', new Int32[] { '}' } } + } + }, + { + (Int32)ParserToken.Pair, + new Dictionary { + { '"', new Int32[] { (Int32)ParserToken.String, ':', (Int32)ParserToken.Value } } + } + }, + { + (Int32)ParserToken.PairRest, + new Dictionary { + { ',', new Int32[] { ',', (Int32)ParserToken.Pair, (Int32)ParserToken.PairRest } }, + { '}', new Int32[] { (Int32)ParserToken.Epsilon } } + } + }, + { + (Int32)ParserToken.String, + new Dictionary { + { '"', new Int32[] { '"', (Int32)ParserToken.CharSeq, '"' } } + } + }, + { + (Int32)ParserToken.Text, + new Dictionary { + { '[', new Int32[] { (Int32)ParserToken.Array } }, + { '{', new Int32[] { (Int32)ParserToken.Object } } + } + }, + { + (Int32)ParserToken.Value, + new Dictionary { + { '"', new Int32[] { (Int32)ParserToken.String } }, + { '[', new Int32[] { (Int32)ParserToken.Array } }, + { '{', new Int32[] { (Int32)ParserToken.Object } }, + { (Int32)ParserToken.Number, new Int32[] { (Int32)ParserToken.Number } }, + { (Int32)ParserToken.True, new Int32[] { (Int32)ParserToken.True } }, + { (Int32)ParserToken.False, new Int32[] { (Int32)ParserToken.False } }, + { (Int32)ParserToken.Null, new Int32[] { (Int32)ParserToken.Null } } + } + }, + { + (Int32)ParserToken.ValueRest, + new Dictionary { + { ',', new Int32[] { ',', (Int32)ParserToken.Value, (Int32)ParserToken.ValueRest } }, + { ']', new Int32[] { (Int32)ParserToken.Epsilon } } + } + } + }; + #endregion + + #region Private Methods + private void ProcessNumber(String number) { + if(number.IndexOf('.') != -1 || number.IndexOf('e') != -1 || number.IndexOf('E') != -1) { + if(Double.TryParse(number, NumberStyles.Any, CultureInfo.InvariantCulture, out Double n_double)) { + this.Token = JsonToken.Double; + this.Value = n_double; + return; + } + } + + if(Int32.TryParse(number, NumberStyles.Integer, CultureInfo.InvariantCulture, out Int32 n_int32)) { + this.Token = JsonToken.Int; + this.Value = n_int32; + return; + } + + if(Int64.TryParse(number, NumberStyles.Integer, CultureInfo.InvariantCulture, out Int64 n_int64)) { + this.Token = JsonToken.Long; + this.Value = n_int64; + return; + } + + if(UInt64.TryParse(number, NumberStyles.Integer, CultureInfo.InvariantCulture, out UInt64 n_uint64)) { + this.Token = JsonToken.Long; + this.Value = n_uint64; + return; + } + + // Shouldn't happen, but just in case, return something + this.Token = JsonToken.Int; + this.Value = 0; + } + + private void ProcessSymbol() { + if(this.current_symbol == '[') { + this.Token = JsonToken.ArrayStart; + this.parser_return = true; + } else if(this.current_symbol == ']') { + this.Token = JsonToken.ArrayEnd; + this.parser_return = true; + } else if(this.current_symbol == '{') { + this.Token = JsonToken.ObjectStart; + this.parser_return = true; + } else if(this.current_symbol == '}') { + this.Token = JsonToken.ObjectEnd; + this.parser_return = true; + } else if(this.current_symbol == '"') { + if(this.parser_in_string) { + this.parser_in_string = false; + this.parser_return = true; + } else { + if(this.Token == JsonToken.None) { + this.Token = JsonToken.String; + } + this.parser_in_string = true; + } + } else if(this.current_symbol == (Int32)ParserToken.CharSeq) { + this.Value = this.lexer.StringValue; + } else if(this.current_symbol == (Int32)ParserToken.False) { + this.Token = JsonToken.Boolean; + this.Value = false; + this.parser_return = true; + } else if(this.current_symbol == (Int32)ParserToken.Null) { + this.Token = JsonToken.Null; + this.parser_return = true; + } else if(this.current_symbol == (Int32)ParserToken.Number) { + this.ProcessNumber(this.lexer.StringValue); + this.parser_return = true; + } else if(this.current_symbol == (Int32)ParserToken.Pair) { + this.Token = JsonToken.PropertyName; + } else if(this.current_symbol == (Int32)ParserToken.True) { + this.Token = JsonToken.Boolean; + this.Value = true; + this.parser_return = true; + } + } + + private Boolean ReadToken() { + if(this.EndOfInput) { + return false; + } + _ = this.lexer.NextToken(); + if(this.lexer.EndOfInput) { + this.Close(); + return false; + } + this.current_input = this.lexer.Token; + return true; + } + #endregion + + + public void Close() { + if(this.EndOfInput) { + return; + } + this.EndOfInput = true; + this.EndOfJson = true; + if(this.reader_is_owned) { + using(this.reader) { + } + } + this.reader = null; + } + + public Boolean Read() { + if(this.EndOfInput) { + return false; + } + if(this.EndOfJson) { + this.EndOfJson = false; + this.automaton_stack.Clear(); + this.automaton_stack.Push((Int32)ParserToken.End); + this.automaton_stack.Push((Int32)ParserToken.Text); + } + this.parser_in_string = false; + this.parser_return = false; + this.Token = JsonToken.None; + this.Value = null; + if(!this.read_started) { + this.read_started = true; + if(!this.ReadToken()) { + return false; + } + } + + + Int32[] entry_symbols; + + while(true) { + if(this.parser_return) { + if(this.automaton_stack.Peek() == (Int32)ParserToken.End) { + this.EndOfJson = true; + } + + return true; + } + + this.current_symbol = this.automaton_stack.Pop(); + + this.ProcessSymbol(); + + if(this.current_symbol == this.current_input) { + if(!this.ReadToken()) { + if(this.automaton_stack.Peek() != (Int32)ParserToken.End) { + throw new JsonException( + "Input doesn't evaluate to proper JSON text"); + } + + if(this.parser_return) { + return true; + } + + return false; + } + + continue; + } + + try { + + entry_symbols = + parse_table[this.current_symbol][this.current_input]; + + } catch(KeyNotFoundException e) { + throw new JsonException((ParserToken)this.current_input, e); + } + + if(entry_symbols[0] == (Int32)ParserToken.Epsilon) { + continue; + } + + for(Int32 i = entry_symbols.Length - 1; i >= 0; i--) { + this.automaton_stack.Push(entry_symbols[i]); + } + } + } + + } } diff --git a/litjson/JsonWriter.cs b/litjson/JsonWriter.cs index abf15f0..f0c09e1 100644 --- a/litjson/JsonWriter.cs +++ b/litjson/JsonWriter.cs @@ -51,14 +51,14 @@ namespace LitJson public Int32 IndentValue { get => this.indent_value; set { - this.indentation = (this.indentation / this.indent_value) * value; + this.indentation = this.indentation / this.indent_value * value; this.indent_value = value; } } public Boolean PrettyPrint { get; set; } - public TextWriter TextWriter { get; } + private TextWriter TextWriter { get; } public Boolean Validate { get; set; } @@ -249,7 +249,7 @@ namespace LitJson this.ctx_stack.Push(this.context); if (this.inst_string_builder != null) { - this.inst_string_builder.Remove(0, this.inst_string_builder.Length); + _ = this.inst_string_builder.Remove(0, this.inst_string_builder.Length); } } @@ -316,6 +316,19 @@ namespace LitJson this.context.ExpectingValue = false; } + public void WriteJson(String str) { + this.DoValidation(Condition.Value); + this.PutNewline(); + + if(str == null) { + this.Put("null"); + } else { + this.TextWriter.Write(str); + } + + this.context.ExpectingValue = false; + } + public void Write(UInt64 number) { this.DoValidation(Condition.Value); this.PutNewline(); @@ -329,7 +342,7 @@ namespace LitJson this.DoValidation(Condition.InArray); this.PutNewline(false); - this.ctx_stack.Pop(); + _ = this.ctx_stack.Pop(); if (this.ctx_stack.Count == 1) { this.has_reached_end = true; } else { @@ -359,7 +372,7 @@ namespace LitJson this.DoValidation(Condition.InObject); this.PutNewline(false); - this.ctx_stack.Pop(); + _ = this.ctx_stack.Pop(); if (this.ctx_stack.Count == 1) { this.has_reached_end = true; } else { diff --git a/litjson/Lexer.cs b/litjson/Lexer.cs index cb62d55..4f64159 100644 --- a/litjson/Lexer.cs +++ b/litjson/Lexer.cs @@ -1,912 +1,853 @@ -#region Header -/** - * Lexer.cs - * JSON lexer implementation based on a finite state machine. - * - * The authors disclaim copyright to this source code. For more details, see - * the COPYING file included with this distribution. - **/ -#endregion - - -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; - - -namespace LitJson -{ - internal class FsmContext - { - public bool Return; - public int NextState; - public Lexer L; - public int StateStack; - } - - - internal class Lexer - { - #region Fields - private delegate bool StateHandler (FsmContext ctx); - - private static readonly int[] fsm_return_table; - private static readonly StateHandler[] fsm_handler_table; - - private bool allow_comments; - private bool allow_single_quoted_strings; - private bool end_of_input; - private FsmContext fsm_context; - private int input_buffer; - private int input_char; - private TextReader reader; - private int state; - private StringBuilder string_buffer; - private string string_value; - private int token; - private int unichar; - #endregion - - - #region Properties - public bool AllowComments { - get { return allow_comments; } - set { allow_comments = value; } - } - - public bool AllowSingleQuotedStrings { - get { return allow_single_quoted_strings; } - set { allow_single_quoted_strings = value; } - } - - public bool EndOfInput { - get { return end_of_input; } - } - - public int Token { - get { return token; } - } - - public string StringValue { - get { return string_value; } - } - #endregion - - - #region Constructors - static Lexer () - { - PopulateFsmTables (out fsm_handler_table, out fsm_return_table); - } - - public Lexer (TextReader reader) - { - allow_comments = true; - allow_single_quoted_strings = true; - - input_buffer = 0; - string_buffer = new StringBuilder (128); - state = 1; - end_of_input = false; - this.reader = reader; - - fsm_context = new FsmContext (); - fsm_context.L = this; - } - #endregion - - - #region Static Methods - private static int HexValue (int digit) - { - switch (digit) { - case 'a': - case 'A': - return 10; - - case 'b': - case 'B': - return 11; - - case 'c': - case 'C': - return 12; - - case 'd': - case 'D': - return 13; - - case 'e': - case 'E': - return 14; - - case 'f': - case 'F': - return 15; - - default: - return digit - '0'; - } - } - - private static void PopulateFsmTables (out StateHandler[] fsm_handler_table, out int[] fsm_return_table) - { - // See section A.1. of the manual for details of the finite - // state machine. - fsm_handler_table = new StateHandler[28] { - State1, - State2, - State3, - State4, - State5, - State6, - State7, - State8, - State9, - State10, - State11, - State12, - State13, - State14, - State15, - State16, - State17, - State18, - State19, - State20, - State21, - State22, - State23, - State24, - State25, - State26, - State27, - State28 - }; - - fsm_return_table = new int[28] { - (int) ParserToken.Char, - 0, - (int) ParserToken.Number, - (int) ParserToken.Number, - 0, - (int) ParserToken.Number, - 0, - (int) ParserToken.Number, - 0, - 0, - (int) ParserToken.True, - 0, - 0, - 0, - (int) ParserToken.False, - 0, - 0, - (int) ParserToken.Null, - (int) ParserToken.CharSeq, - (int) ParserToken.Char, - 0, - 0, - (int) ParserToken.CharSeq, - (int) ParserToken.Char, - 0, - 0, - 0, - 0 - }; - } - - private static char ProcessEscChar (int esc_char) - { - switch (esc_char) { - case '"': - case '\'': - case '\\': - case '/': - return Convert.ToChar (esc_char); - - case 'n': - return '\n'; - - case 't': - return '\t'; - - case 'r': - return '\r'; - - case 'b': - return '\b'; - - case 'f': - return '\f'; - - default: - // Unreachable - return '?'; - } - } - - private static bool State1 (FsmContext ctx) - { - while (ctx.L.GetChar ()) { - if (ctx.L.input_char == ' ' || - ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') - continue; - - if (ctx.L.input_char >= '1' && ctx.L.input_char <= '9') { - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 3; - return true; - } - - switch (ctx.L.input_char) { - case '"': - ctx.NextState = 19; - ctx.Return = true; - return true; - - case ',': - case ':': - case '[': - case ']': - case '{': - case '}': - ctx.NextState = 1; - ctx.Return = true; - return true; - - case '-': - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 2; - return true; - - case '0': - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 4; - return true; - - case 'f': - ctx.NextState = 12; - return true; - - case 'n': - ctx.NextState = 16; - return true; - - case 't': - ctx.NextState = 9; - return true; - - case '\'': - if (! ctx.L.allow_single_quoted_strings) - return false; - - ctx.L.input_char = '"'; - ctx.NextState = 23; - ctx.Return = true; - return true; - - case '/': - if (! ctx.L.allow_comments) - return false; - - ctx.NextState = 25; - return true; - - default: - return false; - } - } - - return true; - } - - private static bool State2 (FsmContext ctx) - { - ctx.L.GetChar (); - - if (ctx.L.input_char >= '1' && ctx.L.input_char<= '9') { - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 3; - return true; - } - - switch (ctx.L.input_char) { - case '0': - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 4; - return true; - - default: - return false; - } - } - - private static bool State3 (FsmContext ctx) - { - while (ctx.L.GetChar ()) { - if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - continue; - } - - if (ctx.L.input_char == ' ' || - ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { - ctx.Return = true; - ctx.NextState = 1; - return true; - } - - switch (ctx.L.input_char) { - case ',': - case ']': - case '}': - ctx.L.UngetChar (); - ctx.Return = true; - ctx.NextState = 1; - return true; - - case '.': - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 5; - return true; - - case 'e': - case 'E': - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 7; - return true; - - default: - return false; - } - } - return true; - } - - private static bool State4 (FsmContext ctx) - { - ctx.L.GetChar (); - - if (ctx.L.input_char == ' ' || - ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { - ctx.Return = true; - ctx.NextState = 1; - return true; - } - - switch (ctx.L.input_char) { - case ',': - case ']': - case '}': - ctx.L.UngetChar (); - ctx.Return = true; - ctx.NextState = 1; - return true; - - case '.': - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 5; - return true; - - case 'e': - case 'E': - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 7; - return true; - - default: - return false; - } - } - - private static bool State5 (FsmContext ctx) - { - ctx.L.GetChar (); - - if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 6; - return true; - } - - return false; - } - - private static bool State6 (FsmContext ctx) - { - while (ctx.L.GetChar ()) { - if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - continue; - } - - if (ctx.L.input_char == ' ' || - ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { - ctx.Return = true; - ctx.NextState = 1; - return true; - } - - switch (ctx.L.input_char) { - case ',': - case ']': - case '}': - ctx.L.UngetChar (); - ctx.Return = true; - ctx.NextState = 1; - return true; - - case 'e': - case 'E': - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 7; - return true; - - default: - return false; - } - } - - return true; - } - - private static bool State7 (FsmContext ctx) - { - ctx.L.GetChar (); - - if (ctx.L.input_char >= '0' && ctx.L.input_char<= '9') { - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 8; - return true; - } - - switch (ctx.L.input_char) { - case '+': - case '-': - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 8; - return true; - - default: - return false; - } - } - - private static bool State8 (FsmContext ctx) - { - while (ctx.L.GetChar ()) { - if (ctx.L.input_char >= '0' && ctx.L.input_char<= '9') { - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - continue; - } - - if (ctx.L.input_char == ' ' || - ctx.L.input_char >= '\t' && ctx.L.input_char<= '\r') { - ctx.Return = true; - ctx.NextState = 1; - return true; - } - - switch (ctx.L.input_char) { - case ',': - case ']': - case '}': - ctx.L.UngetChar (); - ctx.Return = true; - ctx.NextState = 1; - return true; - - default: - return false; - } - } - - return true; - } - - private static bool State9 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'r': - ctx.NextState = 10; - return true; - - default: - return false; - } - } - - private static bool State10 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'u': - ctx.NextState = 11; - return true; - - default: - return false; - } - } - - private static bool State11 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'e': - ctx.Return = true; - ctx.NextState = 1; - return true; - - default: - return false; - } - } - - private static bool State12 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'a': - ctx.NextState = 13; - return true; - - default: - return false; - } - } - - private static bool State13 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'l': - ctx.NextState = 14; - return true; - - default: - return false; - } - } - - private static bool State14 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 's': - ctx.NextState = 15; - return true; - - default: - return false; - } - } - - private static bool State15 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'e': - ctx.Return = true; - ctx.NextState = 1; - return true; - - default: - return false; - } - } - - private static bool State16 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'u': - ctx.NextState = 17; - return true; - - default: - return false; - } - } - - private static bool State17 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'l': - ctx.NextState = 18; - return true; - - default: - return false; - } - } - - private static bool State18 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'l': - ctx.Return = true; - ctx.NextState = 1; - return true; - - default: - return false; - } - } - - private static bool State19 (FsmContext ctx) - { - while (ctx.L.GetChar ()) { - switch (ctx.L.input_char) { - case '"': - ctx.L.UngetChar (); - ctx.Return = true; - ctx.NextState = 20; - return true; - - case '\\': - ctx.StateStack = 19; - ctx.NextState = 21; - return true; - - default: - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - continue; - } - } - - return true; - } - - private static bool State20 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case '"': - ctx.Return = true; - ctx.NextState = 1; - return true; - - default: - return false; - } - } - - private static bool State21 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'u': - ctx.NextState = 22; - return true; - - case '"': - case '\'': - case '/': - case '\\': - case 'b': - case 'f': - case 'n': - case 'r': - case 't': - ctx.L.string_buffer.Append ( - ProcessEscChar (ctx.L.input_char)); - ctx.NextState = ctx.StateStack; - return true; - - default: - return false; - } - } - - private static bool State22 (FsmContext ctx) - { - int counter = 0; - int mult = 4096; - - ctx.L.unichar = 0; - - while (ctx.L.GetChar ()) { - - if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9' || - ctx.L.input_char >= 'A' && ctx.L.input_char <= 'F' || - ctx.L.input_char >= 'a' && ctx.L.input_char <= 'f') { - - ctx.L.unichar += HexValue (ctx.L.input_char) * mult; - - counter++; - mult /= 16; - - if (counter == 4) { - ctx.L.string_buffer.Append ( - Convert.ToChar (ctx.L.unichar)); - ctx.NextState = ctx.StateStack; - return true; - } - - continue; - } - - return false; - } - - return true; - } - - private static bool State23 (FsmContext ctx) - { - while (ctx.L.GetChar ()) { - switch (ctx.L.input_char) { - case '\'': - ctx.L.UngetChar (); - ctx.Return = true; - ctx.NextState = 24; - return true; - - case '\\': - ctx.StateStack = 23; - ctx.NextState = 21; - return true; - - default: - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - continue; - } - } - - return true; - } - - private static bool State24 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case '\'': - ctx.L.input_char = '"'; - ctx.Return = true; - ctx.NextState = 1; - return true; - - default: - return false; - } - } - - private static bool State25 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case '*': - ctx.NextState = 27; - return true; - - case '/': - ctx.NextState = 26; - return true; - - default: - return false; - } - } - - private static bool State26 (FsmContext ctx) - { - while (ctx.L.GetChar ()) { - if (ctx.L.input_char == '\n') { - ctx.NextState = 1; - return true; - } - } - - return true; - } - - private static bool State27 (FsmContext ctx) - { - while (ctx.L.GetChar ()) { - if (ctx.L.input_char == '*') { - ctx.NextState = 28; - return true; - } - } - - return true; - } - - private static bool State28 (FsmContext ctx) - { - while (ctx.L.GetChar ()) { - if (ctx.L.input_char == '*') - continue; - - if (ctx.L.input_char == '/') { - ctx.NextState = 1; - return true; - } - - ctx.NextState = 27; - return true; - } - - return true; - } - #endregion - - - private bool GetChar () - { - if ((input_char = NextChar ()) != -1) - return true; - - end_of_input = true; - return false; - } - - private int NextChar () - { - if (input_buffer != 0) { - int tmp = input_buffer; - input_buffer = 0; - - return tmp; - } - - return reader.Read (); - } - - public bool NextToken () - { - StateHandler handler; - fsm_context.Return = false; - - while (true) { - handler = fsm_handler_table[state - 1]; - - if (! handler (fsm_context)) - throw new JsonException (input_char); - - if (end_of_input) - return false; - - if (fsm_context.Return) { - string_value = string_buffer.ToString (); - string_buffer.Remove (0, string_buffer.Length); - token = fsm_return_table[state - 1]; - - if (token == (int) ParserToken.Char) - token = input_char; - - state = fsm_context.NextState; - - return true; - } - - state = fsm_context.NextState; - } - } - - private void UngetChar () - { - input_buffer = input_char; - } - } -} +#region Header +/** + * Lexer.cs + * JSON lexer implementation based on a finite state machine. + * + * The authors disclaim copyright to this source code. For more details, see + * the COPYING file included with this distribution. + **/ +#endregion + + +using System; +using System.IO; +using System.Text; + + +namespace LitJson { + internal class FsmContext { + public Boolean Return; + public Int32 NextState; + public Lexer L; + public Int32 StateStack; + } + + internal class Lexer { + #region Fields + private delegate Boolean StateHandler(FsmContext ctx); + + private static readonly Int32[] fsm_return_table; + private static readonly StateHandler[] fsm_handler_table; + private readonly FsmContext fsm_context; + private Int32 input_buffer; + private Int32 input_char; + private readonly TextReader reader; + private Int32 state; + private readonly StringBuilder string_buffer; + private Int32 unichar; + #endregion + + #region Properties + public Boolean AllowComments { get; set; } + + public Boolean AllowSingleQuotedStrings { get; set; } + + public Boolean EndOfInput { get; private set; } + + public Int32 Token { get; private set; } + + public String StringValue { get; private set; } + #endregion + + #region Constructors + static Lexer() => PopulateFsmTables(out fsm_handler_table, out fsm_return_table); + + public Lexer(TextReader reader) { + this.AllowComments = true; + this.AllowSingleQuotedStrings = true; + + this.input_buffer = 0; + this.string_buffer = new StringBuilder(128); + this.state = 1; + this.EndOfInput = false; + this.reader = reader; + + this.fsm_context = new FsmContext { + L = this + }; + } + #endregion + + #region Static Methods + private static Int32 HexValue(Int32 digit) { + switch(digit) { + case 'a': + case 'A': + return 10; + + case 'b': + case 'B': + return 11; + + case 'c': + case 'C': + return 12; + + case 'd': + case 'D': + return 13; + + case 'e': + case 'E': + return 14; + + case 'f': + case 'F': + return 15; + + default: + return digit - '0'; + } + } + + private static void PopulateFsmTables(out StateHandler[] fsm_handler_table, out Int32[] fsm_return_table) { + // See section A.1. of the manual for details of the finite + // state machine. + fsm_handler_table = new StateHandler[28] { + State1, + State2, + State3, + State4, + State5, + State6, + State7, + State8, + State9, + State10, + State11, + State12, + State13, + State14, + State15, + State16, + State17, + State18, + State19, + State20, + State21, + State22, + State23, + State24, + State25, + State26, + State27, + State28 + }; + + fsm_return_table = new Int32[28] { + (Int32) ParserToken.Char, + 0, + (Int32) ParserToken.Number, + (Int32) ParserToken.Number, + 0, + (Int32) ParserToken.Number, + 0, + (Int32) ParserToken.Number, + 0, + 0, + (Int32) ParserToken.True, + 0, + 0, + 0, + (Int32) ParserToken.False, + 0, + 0, + (Int32) ParserToken.Null, + (Int32) ParserToken.CharSeq, + (Int32) ParserToken.Char, + 0, + 0, + (Int32) ParserToken.CharSeq, + (Int32) ParserToken.Char, + 0, + 0, + 0, + 0 + }; + } + + private static Char ProcessEscChar(Int32 esc_char) { + switch(esc_char) { + case '"': + case '\'': + case '\\': + case '/': + return Convert.ToChar(esc_char); + + case 'n': + return '\n'; + + case 't': + return '\t'; + + case 'r': + return '\r'; + + case 'b': + return '\b'; + + case 'f': + return '\f'; + + default: + // Unreachable + return '?'; + } + } + + private static Boolean State1(FsmContext ctx) { + while(ctx.L.GetChar()) { + if(ctx.L.input_char == ' ' || + ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { + continue; + } + + if(ctx.L.input_char >= '1' && ctx.L.input_char <= '9') { + _ = ctx.L.string_buffer.Append((Char)ctx.L.input_char); + ctx.NextState = 3; + return true; + } + + switch(ctx.L.input_char) { + case '"': + ctx.NextState = 19; + ctx.Return = true; + return true; + + case ',': + case ':': + case '[': + case ']': + case '{': + case '}': + ctx.NextState = 1; + ctx.Return = true; + return true; + + case '-': + _ = ctx.L.string_buffer.Append((Char)ctx.L.input_char); + ctx.NextState = 2; + return true; + + case '0': + _ = ctx.L.string_buffer.Append((Char)ctx.L.input_char); + ctx.NextState = 4; + return true; + + case 'f': + ctx.NextState = 12; + return true; + + case 'n': + ctx.NextState = 16; + return true; + + case 't': + ctx.NextState = 9; + return true; + + case '\'': + if(!ctx.L.AllowSingleQuotedStrings) { + return false; + } + + ctx.L.input_char = '"'; + ctx.NextState = 23; + ctx.Return = true; + return true; + + case '/': + if(!ctx.L.AllowComments) { + return false; + } + + ctx.NextState = 25; + return true; + + default: + return false; + } + } + + return true; + } + + private static Boolean State2(FsmContext ctx) { + _ = ctx.L.GetChar(); + + if(ctx.L.input_char >= '1' && ctx.L.input_char <= '9') { + _ = ctx.L.string_buffer.Append((Char)ctx.L.input_char); + ctx.NextState = 3; + return true; + } + + switch(ctx.L.input_char) { + case '0': + _ = ctx.L.string_buffer.Append((Char)ctx.L.input_char); + ctx.NextState = 4; + return true; + + default: + return false; + } + } + + private static Boolean State3(FsmContext ctx) { + while(ctx.L.GetChar()) { + if(ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { + _ = ctx.L.string_buffer.Append((Char)ctx.L.input_char); + continue; + } + + if(ctx.L.input_char == ' ' || + ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { + ctx.Return = true; + ctx.NextState = 1; + return true; + } + + switch(ctx.L.input_char) { + case ',': + case ']': + case '}': + ctx.L.UngetChar(); + ctx.Return = true; + ctx.NextState = 1; + return true; + + case '.': + _ = ctx.L.string_buffer.Append((Char)ctx.L.input_char); + ctx.NextState = 5; + return true; + + case 'e': + case 'E': + _ = ctx.L.string_buffer.Append((Char)ctx.L.input_char); + ctx.NextState = 7; + return true; + + default: + return false; + } + } + return true; + } + + private static Boolean State4(FsmContext ctx) { + _ = ctx.L.GetChar(); + + if(ctx.L.input_char == ' ' || + ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { + ctx.Return = true; + ctx.NextState = 1; + return true; + } + + switch(ctx.L.input_char) { + case ',': + case ']': + case '}': + ctx.L.UngetChar(); + ctx.Return = true; + ctx.NextState = 1; + return true; + + case '.': + _ = ctx.L.string_buffer.Append((Char)ctx.L.input_char); + ctx.NextState = 5; + return true; + + case 'e': + case 'E': + _ = ctx.L.string_buffer.Append((Char)ctx.L.input_char); + ctx.NextState = 7; + return true; + + default: + return false; + } + } + + private static Boolean State5(FsmContext ctx) { + _ = ctx.L.GetChar(); + + if(ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { + _ = ctx.L.string_buffer.Append((Char)ctx.L.input_char); + ctx.NextState = 6; + return true; + } + + return false; + } + + private static Boolean State6(FsmContext ctx) { + while(ctx.L.GetChar()) { + if(ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { + _ = ctx.L.string_buffer.Append((Char)ctx.L.input_char); + continue; + } + + if(ctx.L.input_char == ' ' || + ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { + ctx.Return = true; + ctx.NextState = 1; + return true; + } + + switch(ctx.L.input_char) { + case ',': + case ']': + case '}': + ctx.L.UngetChar(); + ctx.Return = true; + ctx.NextState = 1; + return true; + + case 'e': + case 'E': + _ = ctx.L.string_buffer.Append((Char)ctx.L.input_char); + ctx.NextState = 7; + return true; + + default: + return false; + } + } + + return true; + } + + private static Boolean State7(FsmContext ctx) { + _ = ctx.L.GetChar(); + + if(ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { + _ = ctx.L.string_buffer.Append((Char)ctx.L.input_char); + ctx.NextState = 8; + return true; + } + + switch(ctx.L.input_char) { + case '+': + case '-': + _ = ctx.L.string_buffer.Append((Char)ctx.L.input_char); + ctx.NextState = 8; + return true; + + default: + return false; + } + } + + private static Boolean State8(FsmContext ctx) { + while(ctx.L.GetChar()) { + if(ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { + _ = ctx.L.string_buffer.Append((Char)ctx.L.input_char); + continue; + } + + if(ctx.L.input_char == ' ' || + ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { + ctx.Return = true; + ctx.NextState = 1; + return true; + } + + switch(ctx.L.input_char) { + case ',': + case ']': + case '}': + ctx.L.UngetChar(); + ctx.Return = true; + ctx.NextState = 1; + return true; + + default: + return false; + } + } + + return true; + } + + private static Boolean State9(FsmContext ctx) { + _ = ctx.L.GetChar(); + + switch(ctx.L.input_char) { + case 'r': + ctx.NextState = 10; + return true; + + default: + return false; + } + } + + private static Boolean State10(FsmContext ctx) { + _ = ctx.L.GetChar(); + + switch(ctx.L.input_char) { + case 'u': + ctx.NextState = 11; + return true; + + default: + return false; + } + } + + private static Boolean State11(FsmContext ctx) { + _ = ctx.L.GetChar(); + + switch(ctx.L.input_char) { + case 'e': + ctx.Return = true; + ctx.NextState = 1; + return true; + + default: + return false; + } + } + + private static Boolean State12(FsmContext ctx) { + _ = ctx.L.GetChar(); + + switch(ctx.L.input_char) { + case 'a': + ctx.NextState = 13; + return true; + + default: + return false; + } + } + + private static Boolean State13(FsmContext ctx) { + _ = ctx.L.GetChar(); + + switch(ctx.L.input_char) { + case 'l': + ctx.NextState = 14; + return true; + + default: + return false; + } + } + + private static Boolean State14(FsmContext ctx) { + _ = ctx.L.GetChar(); + + switch(ctx.L.input_char) { + case 's': + ctx.NextState = 15; + return true; + + default: + return false; + } + } + + private static Boolean State15(FsmContext ctx) { + _ = ctx.L.GetChar(); + + switch(ctx.L.input_char) { + case 'e': + ctx.Return = true; + ctx.NextState = 1; + return true; + + default: + return false; + } + } + + private static Boolean State16(FsmContext ctx) { + _ = ctx.L.GetChar(); + + switch(ctx.L.input_char) { + case 'u': + ctx.NextState = 17; + return true; + + default: + return false; + } + } + + private static Boolean State17(FsmContext ctx) { + _ = ctx.L.GetChar(); + + switch(ctx.L.input_char) { + case 'l': + ctx.NextState = 18; + return true; + + default: + return false; + } + } + + private static Boolean State18(FsmContext ctx) { + _ = ctx.L.GetChar(); + + switch(ctx.L.input_char) { + case 'l': + ctx.Return = true; + ctx.NextState = 1; + return true; + + default: + return false; + } + } + + private static Boolean State19(FsmContext ctx) { + while(ctx.L.GetChar()) { + switch(ctx.L.input_char) { + case '"': + ctx.L.UngetChar(); + ctx.Return = true; + ctx.NextState = 20; + return true; + + case '\\': + ctx.StateStack = 19; + ctx.NextState = 21; + return true; + + default: + _ = ctx.L.string_buffer.Append((Char)ctx.L.input_char); + continue; + } + } + + return true; + } + + private static Boolean State20(FsmContext ctx) { + _ = ctx.L.GetChar(); + + switch(ctx.L.input_char) { + case '"': + ctx.Return = true; + ctx.NextState = 1; + return true; + + default: + return false; + } + } + + private static Boolean State21(FsmContext ctx) { + _ = ctx.L.GetChar(); + + switch(ctx.L.input_char) { + case 'u': + ctx.NextState = 22; + return true; + + case '"': + case '\'': + case '/': + case '\\': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + _ = ctx.L.string_buffer.Append( + ProcessEscChar(ctx.L.input_char)); + ctx.NextState = ctx.StateStack; + return true; + + default: + return false; + } + } + + private static Boolean State22(FsmContext ctx) { + Int32 counter = 0; + Int32 mult = 4096; + + ctx.L.unichar = 0; + + while(ctx.L.GetChar()) { + + if(ctx.L.input_char >= '0' && ctx.L.input_char <= '9' || + ctx.L.input_char >= 'A' && ctx.L.input_char <= 'F' || + ctx.L.input_char >= 'a' && ctx.L.input_char <= 'f') { + + ctx.L.unichar += HexValue(ctx.L.input_char) * mult; + + counter++; + mult /= 16; + + if(counter == 4) { + _ = ctx.L.string_buffer.Append( + Convert.ToChar(ctx.L.unichar)); + ctx.NextState = ctx.StateStack; + return true; + } + + continue; + } + + return false; + } + + return true; + } + + private static Boolean State23(FsmContext ctx) { + while(ctx.L.GetChar()) { + switch(ctx.L.input_char) { + case '\'': + ctx.L.UngetChar(); + ctx.Return = true; + ctx.NextState = 24; + return true; + + case '\\': + ctx.StateStack = 23; + ctx.NextState = 21; + return true; + + default: + _ = ctx.L.string_buffer.Append((Char)ctx.L.input_char); + continue; + } + } + + return true; + } + + private static Boolean State24(FsmContext ctx) { + _ = ctx.L.GetChar(); + + switch(ctx.L.input_char) { + case '\'': + ctx.L.input_char = '"'; + ctx.Return = true; + ctx.NextState = 1; + return true; + + default: + return false; + } + } + + private static Boolean State25(FsmContext ctx) { + _ = ctx.L.GetChar(); + + switch(ctx.L.input_char) { + case '*': + ctx.NextState = 27; + return true; + + case '/': + ctx.NextState = 26; + return true; + + default: + return false; + } + } + + private static Boolean State26(FsmContext ctx) { + while(ctx.L.GetChar()) { + if(ctx.L.input_char == '\n') { + ctx.NextState = 1; + return true; + } + } + + return true; + } + + private static Boolean State27(FsmContext ctx) { + while(ctx.L.GetChar()) { + if(ctx.L.input_char == '*') { + ctx.NextState = 28; + return true; + } + } + + return true; + } + + private static Boolean State28(FsmContext ctx) { + while(ctx.L.GetChar()) { + if(ctx.L.input_char == '*') { + continue; + } + + if(ctx.L.input_char == '/') { + ctx.NextState = 1; + return true; + } + + ctx.NextState = 27; + return true; + } + + return true; + } + #endregion + + private Boolean GetChar() { + if((this.input_char = this.NextChar()) != -1) { + return true; + } + + this.EndOfInput = true; + return false; + } + + private Int32 NextChar() { + if(this.input_buffer != 0) { + Int32 tmp = this.input_buffer; + this.input_buffer = 0; + + return tmp; + } + + return this.reader.Read(); + } + + public Boolean NextToken() { + StateHandler handler; + this.fsm_context.Return = false; + + while(true) { + handler = fsm_handler_table[this.state - 1]; + + if(!handler(this.fsm_context)) { + throw new JsonException(this.input_char); + } + + if(this.EndOfInput) { + return false; + } + + if(this.fsm_context.Return) { + this.StringValue = this.string_buffer.ToString(); + _ = this.string_buffer.Remove(0, this.string_buffer.Length); + this.Token = fsm_return_table[this.state - 1]; + + if(this.Token == (Int32)ParserToken.Char) { + this.Token = this.input_char; + } + + this.state = this.fsm_context.NextState; + + return true; + } + + this.state = this.fsm_context.NextState; + } + } + + private void UngetChar() => this.input_buffer = this.input_char; + } +} diff --git a/litjson/ParserToken.cs b/litjson/ParserToken.cs index e23d477..3b7b362 100644 --- a/litjson/ParserToken.cs +++ b/litjson/ParserToken.cs @@ -9,36 +9,34 @@ #endregion -namespace LitJson -{ - internal enum ParserToken - { - // Lexer tokens (see section A.1.1. of the manual) - None = System.Char.MaxValue + 1, - Number, - True, - False, - Null, - CharSeq, - // Single char - Char, - - // Parser Rules (see section A.2.1 of the manual) - Text, - Object, - ObjectPrime, - Pair, - PairRest, - Array, - ArrayPrime, - Value, - ValueRest, - String, - - // End of input - End, - - // The empty rule - Epsilon - } +namespace LitJson { + internal enum ParserToken { + // Lexer tokens (see section A.1.1. of the manual) + None = System.Char.MaxValue + 1, + Number, + True, + False, + Null, + CharSeq, + // Single char + Char, + + // Parser Rules (see section A.2.1 of the manual) + Text, + Object, + ObjectPrime, + Pair, + PairRest, + Array, + ArrayPrime, + Value, + ValueRest, + String, + + // End of input + End, + + // The empty rule + Epsilon + } }