| @@ -0,0 +1,46 | |||||
|  | 1 | using System; | |||
|  | 2 | using System.Collections.Generic; | |||
|  | 3 | using System.Linq; | |||
|  | 4 | ||||
|  | 5 | namespace Implab.Automaton { | |||
|  | 6 | public class DummyAlphabet : IAlphabet<int> { | |||
|  | 7 | readonly int m_size; | |||
|  | 8 | public DummyAlphabet(int size) { | |||
|  | 9 | Safe.ArgumentAssert(size > 0); | |||
|  | 10 | m_size = 0; | |||
|  | 11 | } | |||
|  | 12 | ||||
|  | 13 | #region IAlphabet implementation | |||
|  | 14 | ||||
|  | 15 | public List<int>[] CreateReverseMap() { | |||
|  | 16 | Enumerable.Range(0, m_size).ToArray(); | |||
|  | 17 | } | |||
|  | 18 | ||||
|  | 19 | public int[] Reclassify(IAlphabetBuilder<int> newAlphabet, IEnumerable<IEnumerable<int>> classes) { | |||
|  | 20 | Safe.ArgumentNotNull(newAlphabet, "newAlphabet"); | |||
|  | 21 | Safe.ArgumentNotNull(classes, "classes"); | |||
|  | 22 | var map = new int[m_size]; | |||
|  | 23 | foreach (var cls in classes) { | |||
|  | 24 | var newid = newAlphabet.DefineClass(cls); | |||
|  | 25 | foreach (var id in cls) | |||
|  | 26 | map[id] = newid; | |||
|  | 27 | } | |||
|  | 28 | ||||
|  | 29 | return map; | |||
|  | 30 | } | |||
|  | 31 | ||||
|  | 32 | public int Translate(int symobl) { | |||
|  | 33 | Safe.ArgumentInRange(symobl, 0, m_size, "symbol"); | |||
|  | 34 | return symobl; | |||
|  | 35 | } | |||
|  | 36 | ||||
|  | 37 | public int Count { | |||
|  | 38 | get { | |||
|  | 39 | return m_size; | |||
|  | 40 | } | |||
|  | 41 | } | |||
|  | 42 | ||||
|  | 43 | #endregion | |||
|  | 44 | } | |||
|  | 45 | } | |||
|  | 46 | ||||
| @@ -0,0 +1,103 | |||||
|  | 1 | using System; | |||
|  | 2 | using System.Collections.Generic; | |||
|  | 3 | using System.Linq; | |||
|  | 4 | ||||
|  | 5 | namespace Implab.Automaton { | |||
|  | 6 | public class MapAlphabet<T> : IAlphabetBuilder<T> { | |||
|  | 7 | readonly Dictionary<T,int> m_map; | |||
|  | 8 | int m_nextCls; | |||
|  | 9 | ||||
|  | 10 | public MapAlphabet(IEqualityComparer<T> comparer) { | |||
|  | 11 | m_map = new Dictionary<T, int>(comparer); | |||
|  | 12 | m_nextCls = 1; | |||
|  | 13 | } | |||
|  | 14 | ||||
|  | 15 | #region IAlphabetBuilder implementation | |||
|  | 16 | ||||
|  | 17 | public int DefineSymbol(T symbol) { | |||
|  | 18 | int cls; | |||
|  | 19 | if (m_map.TryGetValue(symbol, out cls)) | |||
|  | 20 | return cls; | |||
|  | 21 | ||||
|  | 22 | cls = m_nextCls++; | |||
|  | 23 | ||||
|  | 24 | m_map.Add(symbol, cls); | |||
|  | 25 | ||||
|  | 26 | return cls; | |||
|  | 27 | } | |||
|  | 28 | ||||
|  | 29 | public int DefineClass(IEnumerable<T> symbols) { | |||
|  | 30 | Safe.ArgumentNotNull(symbols, "symbols"); | |||
|  | 31 | symbols = symbols.Distinct(); | |||
|  | 32 | ||||
|  | 33 | foreach (var symbol in symbols) { | |||
|  | 34 | if (!m_map.Contains(symbol)) | |||
|  | 35 | m_map.Add(symbol, m_nextCls); | |||
|  | 36 | else | |||
|  | 37 | throw new InvalidOperationException(String.Format("Symbol '{0}' already in use", symbol)); | |||
|  | 38 | } | |||
|  | 39 | return m_nextCls++; | |||
|  | 40 | } | |||
|  | 41 | ||||
|  | 42 | #endregion | |||
|  | 43 | ||||
|  | 44 | #region IAlphabet implementation | |||
|  | 45 | ||||
|  | 46 | public List<T>[] CreateReverseMap() { | |||
|  | 47 | var empty = new List<T>(); | |||
|  | 48 | var rmap = new List<T>[m_nextCls]; | |||
|  | 49 | ||||
|  | 50 | for (int i = 0; i < rmap.Length; i++) | |||
|  | 51 | rmap[i] = empty; | |||
|  | 52 | ||||
|  | 53 | foreach (var pair in m_map) { | |||
|  | 54 | var symbols = rmap[pair.Value]; | |||
|  | 55 | if (symbols == null) { | |||
|  | 56 | symbols = new List<T>(); | |||
|  | 57 | rmap[pair.Value] = symbols; | |||
|  | 58 | } | |||
|  | 59 | ||||
|  | 60 | symbols.Add(pair.Key); | |||
|  | 61 | } | |||
|  | 62 | ||||
|  | 63 | return rmap; | |||
|  | 64 | } | |||
|  | 65 | ||||
|  | 66 | public int[] Reclassify(IAlphabetBuilder<T> newAlphabet, IEnumerable<IEnumerable<int>> classes) { | |||
|  | 67 | Safe.ArgumentNotNull(newAlphabet, "newAlphabet"); | |||
|  | 68 | Safe.ArgumentNotNull(classes, "classes"); | |||
|  | 69 | ||||
|  | 70 | var rmap = CreateReverseMap(); | |||
|  | 71 | var map = new int[rmap.Length]; | |||
|  | 72 | ||||
|  | 73 | foreach (var cls in classes) { | |||
|  | 74 | var symbols = new List<T>(); | |||
|  | 75 | foreach (var id in cls) { | |||
|  | 76 | if (id < 0 || id >= rmap.Length) | |||
|  | 77 | throw new ArgumentOutOfRangeException(String.Format("Class {0} is not valid for the current alphabet", id)); | |||
|  | 78 | if (rmap[id] != null) | |||
|  | 79 | symbols.AddRange(rmap[id]); | |||
|  | 80 | } | |||
|  | 81 | ||||
|  | 82 | var newId = newAlphabet.DefineClass(symbols); | |||
|  | 83 | ||||
|  | 84 | foreach (var id in cls) | |||
|  | 85 | map[id] = newId; | |||
|  | 86 | } | |||
|  | 87 | } | |||
|  | 88 | ||||
|  | 89 | public int Translate(T symobl) { | |||
|  | 90 | int cls; | |||
|  | 91 | return m_map.TryGetValue(symobl, out cls) ? cls : DFAConst.UNCLASSIFIED_INPUT; | |||
|  | 92 | } | |||
|  | 93 | ||||
|  | 94 | public int Count { | |||
|  | 95 | get { | |||
|  | 96 | return m_nextCls; | |||
|  | 97 | } | |||
|  | 98 | } | |||
|  | 99 | ||||
|  | 100 | #endregion | |||
|  | 101 | } | |||
|  | 102 | } | |||
|  | 103 | ||||
| @@ -0,0 +1,179 | |||||
|  | 1 | using Implab; | |||
|  | 2 | using System; | |||
|  | 3 | using System.Collections.Generic; | |||
|  | 4 | using System.Diagnostics; | |||
|  | 5 | using System.Linq; | |||
|  | 6 | ||||
|  | 7 | namespace Implab.Automaton.RegularExpressions { | |||
|  | 8 | /// <summary> | |||
|  | 9 | /// Используется для построения ДКА по регулярному выражению, сначала обходит | |||
|  | 10 | /// регулярное выражение и вычисляет followpos, затем используется метод | |||
|  | 11 | /// <see cref="BuildDFA(IDFADefinition)"/> для построения автомата. | |||
|  | 12 | /// </summary> | |||
|  | 13 | public class RegularDFABuilder<TTag> : IVisitor<TTag> { | |||
|  | 14 | int m_idx = 0; | |||
|  | 15 | Token<TTag> m_root; | |||
|  | 16 | HashSet<int> m_firstpos; | |||
|  | 17 | HashSet<int> m_lastpos; | |||
|  | 18 | ||||
|  | 19 | readonly Dictionary<int, HashSet<int>> m_followpos = new Dictionary<int, HashSet<int>>(); | |||
|  | 20 | readonly Dictionary<int, int> m_indexes = new Dictionary<int, int>(); | |||
|  | 21 | readonly Dictionary<int, TTag> m_ends = new Dictionary<int, TTag>(); | |||
|  | 22 | ||||
|  | 23 | public Dictionary<int, HashSet<int>> FollowposMap { | |||
|  | 24 | get { return m_followpos; } | |||
|  | 25 | } | |||
|  | 26 | ||||
|  | 27 | public HashSet<int> Followpos(int pos) { | |||
|  | 28 | HashSet<int> set; | |||
|  | 29 | if (m_followpos.TryGetValue(pos, out set)) | |||
|  | 30 | return set; | |||
|  | 31 | return m_followpos[pos] = new HashSet<int>(); | |||
|  | 32 | } | |||
|  | 33 | ||||
|  | 34 | bool Nullable(object n) { | |||
|  | 35 | if (n is EmptyToken<TTag> || n is StarToken<TTag>) | |||
|  | 36 | return true; | |||
|  | 37 | if (n is AltToken<TTag>) | |||
|  | 38 | return Nullable(((AltToken<TTag>)n).Left) || Nullable(((AltToken<TTag>)n).Right); | |||
|  | 39 | if (n is CatToken<TTag>) | |||
|  | 40 | return Nullable(((CatToken<TTag>)n).Left) && Nullable(((CatToken<TTag>)n).Right); | |||
|  | 41 | return false; | |||
|  | 42 | } | |||
|  | 43 | ||||
|  | 44 | ||||
|  | 45 | public void Visit(AltToken<TTag> token) { | |||
|  | 46 | if (m_root == null) | |||
|  | 47 | m_root = token; | |||
|  | 48 | var firtspos = new HashSet<int>(); | |||
|  | 49 | var lastpos = new HashSet<int>(); | |||
|  | 50 | ||||
|  | 51 | token.Left.Accept(this); | |||
|  | 52 | firtspos.UnionWith(m_firstpos); | |||
|  | 53 | lastpos.UnionWith(m_lastpos); | |||
|  | 54 | ||||
|  | 55 | token.Right.Accept(this); | |||
|  | 56 | firtspos.UnionWith(m_firstpos); | |||
|  | 57 | lastpos.UnionWith(m_lastpos); | |||
|  | 58 | ||||
|  | 59 | m_firstpos = firtspos; | |||
|  | 60 | m_lastpos = lastpos; | |||
|  | 61 | } | |||
|  | 62 | ||||
|  | 63 | public void Visit(StarToken<TTag> token) { | |||
|  | 64 | if (m_root == null) | |||
|  | 65 | m_root = token; | |||
|  | 66 | token.Token.Accept(this); | |||
|  | 67 | ||||
|  | 68 | foreach (var i in m_lastpos) | |||
|  | 69 | Followpos(i).UnionWith(m_firstpos); | |||
|  | 70 | } | |||
|  | 71 | ||||
|  | 72 | public void Visit(CatToken<TTag> token) { | |||
|  | 73 | if (m_root == null) | |||
|  | 74 | m_root = token; | |||
|  | 75 | ||||
|  | 76 | var firtspos = new HashSet<int>(); | |||
|  | 77 | var lastpos = new HashSet<int>(); | |||
|  | 78 | token.Left.Accept(this); | |||
|  | 79 | firtspos.UnionWith(m_firstpos); | |||
|  | 80 | var leftLastpos = m_lastpos; | |||
|  | 81 | ||||
|  | 82 | token.Right.Accept(this); | |||
|  | 83 | lastpos.UnionWith(m_lastpos); | |||
|  | 84 | var rightFirstpos = m_firstpos; | |||
|  | 85 | ||||
|  | 86 | if (Nullable(token.Left)) | |||
|  | 87 | firtspos.UnionWith(rightFirstpos); | |||
|  | 88 | ||||
|  | 89 | if (Nullable(token.Right)) | |||
|  | 90 | lastpos.UnionWith(leftLastpos); | |||
|  | 91 | ||||
|  | 92 | m_firstpos = firtspos; | |||
|  | 93 | m_lastpos = lastpos; | |||
|  | 94 | ||||
|  | 95 | foreach (var i in leftLastpos) | |||
|  | 96 | Followpos(i).UnionWith(rightFirstpos); | |||
|  | 97 | ||||
|  | 98 | } | |||
|  | 99 | ||||
|  | 100 | public void Visit(EmptyToken<TTag> token) { | |||
|  | 101 | if (m_root == null) | |||
|  | 102 | m_root = token; | |||
|  | 103 | } | |||
|  | 104 | ||||
|  | 105 | public void Visit(SymbolToken<TTag> token) { | |||
|  | 106 | if (m_root == null) | |||
|  | 107 | m_root = token; | |||
|  | 108 | m_idx++; | |||
|  | 109 | m_indexes[m_idx] = token.Value; | |||
|  | 110 | m_firstpos = new HashSet<int>(new[] { m_idx }); | |||
|  | 111 | m_lastpos = new HashSet<int>(new[] { m_idx }); | |||
|  | 112 | } | |||
|  | 113 | ||||
|  | 114 | public void Visit(EndToken<TTag> token) { | |||
|  | 115 | if (m_root == null) | |||
|  | 116 | m_root = token; | |||
|  | 117 | m_idx++; | |||
|  | 118 | m_indexes[m_idx] = DFAConst.UNCLASSIFIED_INPUT; | |||
|  | 119 | m_firstpos = new HashSet<int>(new[] { m_idx }); | |||
|  | 120 | m_lastpos = new HashSet<int>(new[] { m_idx }); | |||
|  | 121 | Followpos(m_idx); | |||
|  | 122 | m_ends.Add(m_idx, token.Tag); | |||
|  | 123 | } | |||
|  | 124 | ||||
|  | 125 | public void BuildDFA(IDFADefinitionBuilder<TTag> dfa) { | |||
|  | 126 | Safe.ArgumentNotNull(dfa,"dfa"); | |||
|  | 127 | ||||
|  | 128 | var states = new MapAlphabet<HashSet<int>>(new CustomEqualityComparer<HashSet<int>>( | |||
|  | 129 | (x, y) => x.SetEquals(y), | |||
|  | 130 | x => x.Sum(n => n.GetHashCode()) | |||
|  | 131 | )); | |||
|  | 132 | ||||
|  | 133 | var initialState = states.DefineSymbol(m_firstpos); | |||
|  | 134 | ||||
|  | 135 | var tags = GetStateTags(m_firstpos); | |||
|  | 136 | if (tags != null && tags.Length > 0) | |||
|  | 137 | dfa.MarkFinalState(initialState, tags); | |||
|  | 138 | ||||
|  | 139 | var inputMax = m_indexes.Values.Max(); | |||
|  | 140 | var queue = new Queue<HashSet<int>>(); | |||
|  | 141 | ||||
|  | 142 | queue.Enqueue(m_firstpos); | |||
|  | 143 | ||||
|  | 144 | while (queue.Count > 0) { | |||
|  | 145 | var state = queue.Dequeue(); | |||
|  | 146 | var s1 = states.Translate(state); | |||
|  | 147 | Debug.Assert(s1 != DFAConst.UNCLASSIFIED_INPUT); | |||
|  | 148 | ||||
|  | 149 | for (int a = 0; a <= inputMax; a++) { | |||
|  | 150 | var next = new HashSet<int>(); | |||
|  | 151 | foreach (var p in state) { | |||
|  | 152 | if (m_indexes[p] == a) { | |||
|  | 153 | next.UnionWith(Followpos(p)); | |||
|  | 154 | } | |||
|  | 155 | } | |||
|  | 156 | if (next.Count > 0) { | |||
|  | 157 | int s2 = states.Translate(next); | |||
|  | 158 | if (s2 == DFAConst.UNCLASSIFIED_INPUT) { | |||
|  | 159 | s2 = states.DefineSymbol(next); | |||
|  | 160 | ||||
|  | 161 | tags = GetStateTags(next); | |||
|  | 162 | if (tags != null && tags.Length > 0) | |||
|  | 163 | dfa.MarkFinalState(s2, tags); | |||
|  | 164 | ||||
|  | 165 | queue.Enqueue(next); | |||
|  | 166 | } | |||
|  | 167 | dfa.DefineTransition(s1, s2, a); | |||
|  | 168 | } | |||
|  | 169 | } | |||
|  | 170 | } | |||
|  | 171 | } | |||
|  | 172 | ||||
|  | 173 | TTag[] GetStateTags(IEnumerable<int> state) { | |||
|  | 174 | Debug.Assert(state != null); | |||
|  | 175 | return state.Where(m_ends.ContainsKey).Select(pos => m_ends[pos]).ToArray(); | |||
|  | 176 | } | |||
|  | 177 | ||||
|  | 178 | } | |||
|  | 179 | } | |||
| @@ -0,0 +1,17 | |||||
|  | 1 | using System; | |||
|  | 2 | using System.Collections.Generic; | |||
|  | 3 | using System.Linq; | |||
|  | 4 | using System.Text; | |||
|  | 5 | using System.Threading.Tasks; | |||
|  | 6 | ||||
|  | 7 | namespace Implab.JSON { | |||
|  | 8 | /// <summary> | |||
|  | 9 | /// internal | |||
|  | 10 | /// </summary> | |||
|  | 11 | public enum JSONElementContext { | |||
|  | 12 | None, | |||
|  | 13 | Object, | |||
|  | 14 | Array, | |||
|  | 15 | Closed | |||
|  | 16 | } | |||
|  | 17 | } | |||
| @@ -0,0 +1,34 | |||||
|  | 1 | using System; | |||
|  | 2 | using System.Collections.Generic; | |||
|  | 3 | using System.Linq; | |||
|  | 4 | using System.Text; | |||
|  | 5 | using System.Threading.Tasks; | |||
|  | 6 | ||||
|  | 7 | namespace Implab.JSON { | |||
|  | 8 | /// <summary> | |||
|  | 9 | /// Тип элемента на котором находится парсер | |||
|  | 10 | /// </summary> | |||
|  | 11 | public enum JSONElementType { | |||
|  | 12 | None, | |||
|  | 13 | /// <summary> | |||
|  | 14 | /// Начало объекта | |||
|  | 15 | /// </summary> | |||
|  | 16 | BeginObject, | |||
|  | 17 | /// <summary> | |||
|  | 18 | /// Конец объекта | |||
|  | 19 | /// </summary> | |||
|  | 20 | EndObject, | |||
|  | 21 | /// <summary> | |||
|  | 22 | /// Начало массива | |||
|  | 23 | /// </summary> | |||
|  | 24 | BeginArray, | |||
|  | 25 | /// <summary> | |||
|  | 26 | /// Конец массива | |||
|  | 27 | /// </summary> | |||
|  | 28 | EndArray, | |||
|  | 29 | /// <summary> | |||
|  | 30 | /// Простое значение | |||
|  | 31 | /// </summary> | |||
|  | 32 | Value | |||
|  | 33 | } | |||
|  | 34 | } | |||
| @@ -0,0 +1,99 | |||||
|  | 1 | using System.Linq; | |||
|  | 2 | using Implab.Automaton.RegularExpressions; | |||
|  | 3 | ||||
|  | 4 | namespace Implab.Formats.JSON { | |||
|  | 5 | class JSONGrammar : Grammar<JSONGrammar> { | |||
|  | 6 | public enum TokenType { | |||
|  | 7 | None, | |||
|  | 8 | BeginObject, | |||
|  | 9 | EndObject, | |||
|  | 10 | BeginArray, | |||
|  | 11 | EndArray, | |||
|  | 12 | String, | |||
|  | 13 | Number, | |||
|  | 14 | Literal, | |||
|  | 15 | NameSeparator, | |||
|  | 16 | ValueSeparator, | |||
|  | 17 | ||||
|  | 18 | StringBound, | |||
|  | 19 | EscapedChar, | |||
|  | 20 | UnescapedChar, | |||
|  | 21 | EscapedUnicode, | |||
|  | 22 | ||||
|  | 23 | Minus, | |||
|  | 24 | Plus, | |||
|  | 25 | Sign, | |||
|  | 26 | Integer, | |||
|  | 27 | Dot, | |||
|  | 28 | Exp | |||
|  | 29 | } | |||
|  | 30 | ||||
|  | 31 | readonly CDFADefinition m_jsonDFA; | |||
|  | 32 | readonly CDFADefinition m_stringDFA; | |||
|  | 33 | ||||
|  | 34 | public JSONGrammar() { | |||
|  | 35 | DefineAlphabet(Enumerable.Range(0, 0x20).Select(x => (char)x)); | |||
|  | 36 | var hexDigit = SymbolRangeToken('a','f').Or(SymbolRangeToken('A','F')).Or(SymbolRangeToken('0','9')); | |||
|  | 37 | var digit9 = SymbolRangeToken('1', '9'); | |||
|  | 38 | var zero = SymbolToken('0'); | |||
|  | 39 | var digit = zero.Or(digit9); | |||
|  | 40 | var dot = SymbolToken('.'); | |||
|  | 41 | var minus = SymbolToken('-'); | |||
|  | 42 | var sign = SymbolSetToken('-', '+'); | |||
|  | 43 | var expSign = SymbolSetToken('e', 'E'); | |||
|  | 44 | var letters = SymbolRangeToken('a', 'z'); | |||
|  | 45 | var integer = zero.Or(digit9.Cat(digit.EClosure())); | |||
|  | 46 | var frac = dot.Cat(digit.Closure()); | |||
|  | 47 | var exp = expSign.Cat(sign.Optional()).Cat(digit.Closure()); | |||
|  | 48 | var quote = SymbolToken('"'); | |||
|  | 49 | var backSlash = SymbolToken('\\'); | |||
|  | 50 | var specialEscapeChars = SymbolSetToken('\\', '"', '/', 'b', 'f', 't', 'n', 'r'); | |||
|  | 51 | var unicodeEspace = SymbolToken('u').Cat(hexDigit.Repeat(4)); | |||
|  | 52 | var whitespace = SymbolSetToken('\n', '\r', '\t', ' ').EClosure(); | |||
|  | 53 | var beginObject = whitespace.Cat(SymbolToken('{')).Cat(whitespace); | |||
|  | 54 | var endObject = whitespace.Cat(SymbolToken('}')).Cat(whitespace); | |||
|  | 55 | var beginArray = whitespace.Cat(SymbolToken('[')).Cat(whitespace); | |||
|  | 56 | var endArray = whitespace.Cat(SymbolToken(']')).Cat(whitespace); | |||
|  | 57 | var nameSep = whitespace.Cat(SymbolToken(':')).Cat(whitespace); | |||
|  | 58 | var valueSep = whitespace.Cat(SymbolToken(',')).Cat(whitespace); | |||
|  | 59 | ||||
|  | 60 | var number = minus.Optional().Cat(integer).Cat(frac.Optional()).Cat(exp.Optional()); | |||
|  | 61 | var literal = letters.Closure(); | |||
|  | 62 | var unescaped = SymbolTokenExcept(Enumerable.Range(0, 0x20).Union(new int[] { '\\', '"' }).Select(x => (char)x)); | |||
|  | 63 | ||||
|  | 64 | var jsonExpression = | |||
|  | 65 | number.Tag(TokenType.Number) | |||
|  | 66 | .Or(literal.Tag(TokenType.Literal)) | |||
|  | 67 | .Or(quote.Tag(TokenType.StringBound)) | |||
|  | 68 | .Or(beginObject.Tag(TokenType.BeginObject)) | |||
|  | 69 | .Or(endObject.Tag(TokenType.EndObject)) | |||
|  | 70 | .Or(beginArray.Tag(TokenType.BeginArray)) | |||
|  | 71 | .Or(endArray.Tag(TokenType.EndArray)) | |||
|  | 72 | .Or(nameSep.Tag(TokenType.NameSeparator)) | |||
|  | 73 | .Or(valueSep.Tag(TokenType.ValueSeparator)); | |||
|  | 74 | ||||
|  | 75 | ||||
|  | 76 | var jsonStringExpression = | |||
|  | 77 | quote.Tag(TokenType.StringBound) | |||
|  | 78 | .Or(backSlash.Cat(specialEscapeChars).Tag(TokenType.EscapedChar)) | |||
|  | 79 | .Or(backSlash.Cat(unicodeEspace).Tag(TokenType.EscapedUnicode)) | |||
|  | 80 | .Or(unescaped.Closure().Tag(TokenType.UnescapedChar)); | |||
|  | 81 | ||||
|  | 82 | ||||
|  | 83 | m_jsonDFA = BuildDFA(jsonExpression); | |||
|  | 84 | m_stringDFA = BuildDFA(jsonStringExpression); | |||
|  | 85 | } | |||
|  | 86 | ||||
|  | 87 | public CDFADefinition JsonDFA { | |||
|  | 88 | get { | |||
|  | 89 | return m_jsonDFA; | |||
|  | 90 | } | |||
|  | 91 | } | |||
|  | 92 | ||||
|  | 93 | public CDFADefinition JsonStringDFA { | |||
|  | 94 | get { | |||
|  | 95 | return m_stringDFA; | |||
|  | 96 | } | |||
|  | 97 | } | |||
|  | 98 | } | |||
|  | 99 | } | |||
| @@ -0,0 +1,277 | |||||
|  | 1 | using Implab.Parsing; | |||
|  | 2 | using System; | |||
|  | 3 | using System.Diagnostics; | |||
|  | 4 | using System.IO; | |||
|  | 5 | ||||
|  | 6 | namespace Implab.JSON { | |||
|  | 7 | /// <summary> | |||
|  | 8 | /// internal | |||
|  | 9 | /// </summary> | |||
|  | 10 | public struct JSONParserContext { | |||
|  | 11 | public string memberName; | |||
|  | 12 | public JSONElementContext elementContext; | |||
|  | 13 | } | |||
|  | 14 | ||||
|  | 15 | /// <summary> | |||
|  | 16 | /// Pull парсер JSON данных. | |||
|  | 17 | /// </summary> | |||
|  | 18 | /// <remarks> | |||
|  | 19 | /// Следует отметить отдельную интерпретацию свойства <see cref="Level"/>, | |||
|  | 20 | /// оно означает текущий уровень вложенности объектов, однако закрывающий | |||
|  | 21 | /// элемент объекта и массива имеет уровень меньше, чем сам объект. | |||
|  | 22 | /// <code> | |||
|  | 23 | /// { // Level = 1 | |||
|  | 24 | /// "name" : "Peter", // Level = 1 | |||
|  | 25 | /// "address" : { // Level = 2 | |||
|  | 26 | /// city : "Stern" // Level = 2 | |||
|  | 27 | /// } // Level = 1 | |||
|  | 28 | /// } // Level = 0 | |||
|  | 29 | /// </code> | |||
|  | 30 | /// </remarks> | |||
|  | 31 | public class JSONParser : DFAutomaton<JSONParserContext>, IDisposable { | |||
|  | 32 | ||||
|  | 33 | enum MemberContext { | |||
|  | 34 | MemberName, | |||
|  | 35 | MemberValue | |||
|  | 36 | } | |||
|  | 37 | ||||
|  | 38 | static readonly EnumAlphabet<JsonTokenType> _alphabet = EnumAlphabet<JsonTokenType>.FullAlphabet; | |||
|  | 39 | static readonly DFAStateDescriptior[] _jsonDFA; | |||
|  | 40 | static readonly DFAStateDescriptior[] _objectDFA; | |||
|  | 41 | static readonly DFAStateDescriptior[] _arrayDFA; | |||
|  | 42 | ||||
|  | 43 | static JSONParser() { | |||
|  | 44 | ||||
|  | 45 | ||||
|  | 46 | var valueExpression = Token.New(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String); | |||
|  | 47 | var memberExpression = Token.New(JsonTokenType.String).Cat(Token.New(JsonTokenType.NameSeparator)).Cat(valueExpression); | |||
|  | 48 | ||||
|  | 49 | var objectExpression = memberExpression | |||
|  | 50 | .Cat( | |||
|  | 51 | Token.New(JsonTokenType.ValueSeparator) | |||
|  | 52 | .Cat(memberExpression) | |||
|  | 53 | .EClosure() | |||
|  | 54 | ) | |||
|  | 55 | .Optional() | |||
|  | 56 | .Cat(Token.New(JsonTokenType.EndObject)) | |||
|  | 57 | .Tag(0); | |||
|  | 58 | var arrayExpression = valueExpression | |||
|  | 59 | .Cat( | |||
|  | 60 | Token.New(JsonTokenType.ValueSeparator) | |||
|  | 61 | .Cat(valueExpression) | |||
|  | 62 | .EClosure() | |||
|  | 63 | ) | |||
|  | 64 | .Optional() | |||
|  | 65 | .Cat(Token.New(JsonTokenType.EndArray)) | |||
|  | 66 | .Tag(0); | |||
|  | 67 | ||||
|  | 68 | var jsonExpression = valueExpression.Tag(0); | |||
|  | 69 | ||||
|  | 70 | _jsonDFA = BuildDFA(jsonExpression).States; | |||
|  | 71 | _objectDFA = BuildDFA(objectExpression).States; | |||
|  | 72 | _arrayDFA = BuildDFA(arrayExpression).States; | |||
|  | 73 | } | |||
|  | 74 | ||||
|  | 75 | static EDFADefinition<JsonTokenType> BuildDFA(Token expr) { | |||
|  | 76 | var builder = new DFABuilder(); | |||
|  | 77 | var dfa = new EDFADefinition<JsonTokenType>(_alphabet); | |||
|  | 78 | expr.Accept(builder); | |||
|  | 79 | ||||
|  | 80 | builder.BuildDFA(dfa); | |||
|  | 81 | return dfa; | |||
|  | 82 | } | |||
|  | 83 | ||||
|  | 84 | JSONScanner m_scanner; | |||
|  | 85 | MemberContext m_memberContext; | |||
|  | 86 | ||||
|  | 87 | JSONElementType m_elementType; | |||
|  | 88 | object m_elementValue; | |||
|  | 89 | ||||
|  | 90 | /// <summary> | |||
|  | 91 | /// Создает новый парсер на основе строки, содержащей JSON | |||
|  | 92 | /// </summary> | |||
|  | 93 | /// <param name="text"></param> | |||
|  | 94 | public JSONParser(string text) | |||
|  | 95 | : base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty }) { | |||
|  | 96 | Safe.ArgumentNotEmpty(text, "text"); | |||
|  | 97 | m_scanner = new JSONScanner(); | |||
|  | 98 | m_scanner.Feed(text.ToCharArray()); | |||
|  | 99 | } | |||
|  | 100 | ||||
|  | 101 | /// <summary> | |||
|  | 102 | /// Создает новый экземпляр парсера, на основе текстового потока. | |||
|  | 103 | /// </summary> | |||
|  | 104 | /// <param name="reader">Текстовый поток.</param> | |||
|  | 105 | /// <param name="dispose">Признак того, что парсер должен конролировать время жизни входного потока.</param> | |||
|  | 106 | public JSONParser(TextReader reader, bool dispose) | |||
|  | 107 | : base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty }) { | |||
|  | 108 | Safe.ArgumentNotNull(reader, "reader"); | |||
|  | 109 | m_scanner = new JSONScanner(); | |||
|  | 110 | m_scanner.Feed(reader, dispose); | |||
|  | 111 | } | |||
|  | 112 | ||||
|  | 113 | /// <summary> | |||
|  | 114 | /// Тип текущего элемента на котором стоит парсер. | |||
|  | 115 | /// </summary> | |||
|  | 116 | public JSONElementType ElementType { | |||
|  | 117 | get { return m_elementType; } | |||
|  | 118 | } | |||
|  | 119 | ||||
|  | 120 | /// <summary> | |||
|  | 121 | /// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда | |||
|  | 122 | /// пустая строка. | |||
|  | 123 | /// </summary> | |||
|  | 124 | public string ElementName { | |||
|  | 125 | get { return m_context.info.memberName; } | |||
|  | 126 | } | |||
|  | 127 | ||||
|  | 128 | /// <summary> | |||
|  | 129 | /// Значение элемента. Только для элементов типа <see cref="JSONElementType.Value"/>, для остальных <c>null</c> | |||
|  | 130 | /// </summary> | |||
|  | 131 | public object ElementValue { | |||
|  | 132 | get { return m_elementValue; } | |||
|  | 133 | } | |||
|  | 134 | ||||
|  | 135 | /// <summary> | |||
|  | 136 | /// Читает слеюудущий объект из потока | |||
|  | 137 | /// </summary> | |||
|  | 138 | /// <returns><c>true</c> - операция чтения прошла успешно, <c>false</c> - конец данных</returns> | |||
|  | 139 | public bool Read() { | |||
|  | 140 | if (m_context.current == UNREACHEBLE_STATE) | |||
|  | 141 | throw new InvalidOperationException("The parser is in invalid state"); | |||
|  | 142 | object tokenValue; | |||
|  | 143 | JsonTokenType tokenType; | |||
|  | 144 | m_context.info.memberName = String.Empty; | |||
|  | 145 | while (m_scanner.ReadToken(out tokenValue, out tokenType)) { | |||
|  | 146 | Move((int)tokenType); | |||
|  | 147 | if (m_context.current == UNREACHEBLE_STATE) | |||
|  | 148 | UnexpectedToken(tokenValue, tokenType); | |||
|  | 149 | switch (tokenType) { | |||
|  | 150 | case JsonTokenType.BeginObject: | |||
|  | 151 | Switch( | |||
|  | 152 | _objectDFA, | |||
|  | 153 | INITIAL_STATE, | |||
|  | 154 | new JSONParserContext { | |||
|  | 155 | memberName = m_context.info.memberName, | |||
|  | 156 | elementContext = JSONElementContext.Object | |||
|  | 157 | } | |||
|  | 158 | ); | |||
|  | 159 | m_elementValue = null; | |||
|  | 160 | m_memberContext = MemberContext.MemberName; | |||
|  | 161 | m_elementType = JSONElementType.BeginObject; | |||
|  | 162 | return true; | |||
|  | 163 | case JsonTokenType.EndObject: | |||
|  | 164 | Restore(); | |||
|  | 165 | m_elementValue = null; | |||
|  | 166 | m_elementType = JSONElementType.EndObject; | |||
|  | 167 | return true; | |||
|  | 168 | case JsonTokenType.BeginArray: | |||
|  | 169 | Switch( | |||
|  | 170 | _arrayDFA, | |||
|  | 171 | INITIAL_STATE, | |||
|  | 172 | new JSONParserContext { | |||
|  | 173 | memberName = m_context.info.memberName, | |||
|  | 174 | elementContext = JSONElementContext.Array | |||
|  | 175 | } | |||
|  | 176 | ); | |||
|  | 177 | m_elementValue = null; | |||
|  | 178 | m_memberContext = MemberContext.MemberValue; | |||
|  | 179 | m_elementType = JSONElementType.BeginArray; | |||
|  | 180 | return true; | |||
|  | 181 | case JsonTokenType.EndArray: | |||
|  | 182 | Restore(); | |||
|  | 183 | m_elementValue = null; | |||
|  | 184 | m_elementType = JSONElementType.EndArray; | |||
|  | 185 | return true; | |||
|  | 186 | case JsonTokenType.String: | |||
|  | 187 | if (m_memberContext == MemberContext.MemberName) { | |||
|  | 188 | m_context.info.memberName = (string)tokenValue; | |||
|  | 189 | break; | |||
|  | 190 | } | |||
|  | 191 | m_elementType = JSONElementType.Value; | |||
|  | 192 | m_elementValue = tokenValue; | |||
|  | 193 | return true; | |||
|  | 194 | case JsonTokenType.Number: | |||
|  | 195 | m_elementType = JSONElementType.Value; | |||
|  | 196 | m_elementValue = tokenValue; | |||
|  | 197 | return true; | |||
|  | 198 | case JsonTokenType.Literal: | |||
|  | 199 | m_elementType = JSONElementType.Value; | |||
|  | 200 | m_elementValue = ParseLiteral((string)tokenValue); | |||
|  | 201 | return true; | |||
|  | 202 | case JsonTokenType.NameSeparator: | |||
|  | 203 | m_memberContext = MemberContext.MemberValue; | |||
|  | 204 | break; | |||
|  | 205 | case JsonTokenType.ValueSeparator: | |||
|  | 206 | m_memberContext = m_context.info.elementContext == JSONElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue; | |||
|  | 207 | break; | |||
|  | 208 | default: | |||
|  | 209 | UnexpectedToken(tokenValue, tokenType); | |||
|  | 210 | break; | |||
|  | 211 | } | |||
|  | 212 | } | |||
|  | 213 | if (m_context.info.elementContext != JSONElementContext.None) | |||
|  | 214 | throw new ParserException("Unexpedted end of data"); | |||
|  | 215 | return false; | |||
|  | 216 | } | |||
|  | 217 | ||||
|  | 218 | object ParseLiteral(string literal) { | |||
|  | 219 | switch (literal) { | |||
|  | 220 | case "null": | |||
|  | 221 | return null; | |||
|  | 222 | case "false": | |||
|  | 223 | return false; | |||
|  | 224 | case "true": | |||
|  | 225 | return true; | |||
|  | 226 | default: | |||
|  | 227 | UnexpectedToken(literal, JsonTokenType.Literal); | |||
|  | 228 | return null; // avoid compliler error | |||
|  | 229 | } | |||
|  | 230 | } | |||
|  | 231 | ||||
|  | 232 | void UnexpectedToken(object value, JsonTokenType tokenType) { | |||
|  | 233 | throw new ParserException(String.Format("Unexpected token {0}: '{1}'", tokenType, value)); | |||
|  | 234 | } | |||
|  | 235 | ||||
|  | 236 | ||||
|  | 237 | /// <summary> | |||
|  | 238 | /// Признак конца потока | |||
|  | 239 | /// </summary> | |||
|  | 240 | public bool EOF { | |||
|  | 241 | get { | |||
|  | 242 | return m_scanner.EOF; | |||
|  | 243 | } | |||
|  | 244 | } | |||
|  | 245 | ||||
|  | 246 | protected virtual void Dispose(bool disposing) { | |||
|  | 247 | if (disposing) { | |||
|  | 248 | m_scanner.Dispose(); | |||
|  | 249 | } | |||
|  | 250 | } | |||
|  | 251 | ||||
|  | 252 | /// <summary> | |||
|  | 253 | /// Освобождает парсер и связанный с ним сканнер. | |||
|  | 254 | /// </summary> | |||
|  | 255 | public void Dispose() { | |||
|  | 256 | Dispose(true); | |||
|  | 257 | GC.SuppressFinalize(this); | |||
|  | 258 | } | |||
|  | 259 | ||||
|  | 260 | ~JSONParser() { | |||
|  | 261 | Dispose(false); | |||
|  | 262 | } | |||
|  | 263 | ||||
|  | 264 | /// <summary> | |||
|  | 265 | /// Переходит в конец текущего объекта. | |||
|  | 266 | /// </summary> | |||
|  | 267 | public void SeekElementEnd() { | |||
|  | 268 | var level = Level - 1; | |||
|  | 269 | ||||
|  | 270 | Debug.Assert(level >= 0); | |||
|  | 271 | ||||
|  | 272 | while (Level != level) | |||
|  | 273 | Read(); | |||
|  | 274 | } | |||
|  | 275 | } | |||
|  | 276 | ||||
|  | 277 | } | |||
| @@ -0,0 +1,100 | |||||
|  | 1 | using Implab.Parsing; | |||
|  | 2 | using System; | |||
|  | 3 | using System.Collections.Generic; | |||
|  | 4 | using System.Globalization; | |||
|  | 5 | using System.Linq; | |||
|  | 6 | using System.Text; | |||
|  | 7 | using System.Threading.Tasks; | |||
|  | 8 | ||||
|  | 9 | namespace Implab.JSON { | |||
|  | 10 | /// <summary> | |||
|  | 11 | /// Сканнер (лексер), разбивающий поток символов на токены JSON. | |||
|  | 12 | /// </summary> | |||
|  | 13 | public class JSONScanner : Scanner { | |||
|  | 14 | char[] m_stringBuffer; | |||
|  | 15 | DFAStateDescriptior[] m_stringDFA; | |||
|  | 16 | int[] m_stringAlphabet; | |||
|  | 17 | ||||
|  | 18 | /// <summary> | |||
|  | 19 | /// Создает новый экземпляр сканнера | |||
|  | 20 | /// </summary> | |||
|  | 21 | public JSONScanner() | |||
|  | 22 | : base(JSONGrammar.Instance.JsonDFA.States, JSONGrammar.Instance.JsonDFA.Alphabet.GetTranslationMap()) { | |||
|  | 23 | m_stringBuffer = new char[1024]; | |||
|  | 24 | var dfa = JSONGrammar.Instance.JsonStringDFA; | |||
|  | 25 | m_stringAlphabet = dfa.Alphabet.GetTranslationMap(); | |||
|  | 26 | m_stringDFA = dfa.States; | |||
|  | 27 | } | |||
|  | 28 | ||||
|  | 29 | /// <summary> | |||
|  | 30 | /// Читает следующий лексический элемент из входных данных. | |||
|  | 31 | /// </summary> | |||
|  | 32 | /// <param name="tokenValue">Возвращает значение прочитанного токена.</param> | |||
|  | 33 | /// <param name="tokenType">Возвращает тип прочитанного токена.</param> | |||
|  | 34 | /// <returns><c>true</c> - чтение произведено успешно. <c>false</c> - достигнут конец входных данных</returns> | |||
|  | 35 | /// <remarks>В случе если токен не распознается, возникает исключение. Значения токенов обрабатываются, т.е. | |||
|  | 36 | /// в строках обрабатываются экранированные символы, числа становтся типа double.</remarks> | |||
|  | 37 | public bool ReadToken(out object tokenValue, out JsonTokenType tokenType) { | |||
|  | 38 | if (ReadTokenInternal()) { | |||
|  | 39 | switch ((JSONGrammar.TokenType)m_currentState.tag[0]) { | |||
|  | 40 | case JSONGrammar.TokenType.StringBound: | |||
|  | 41 | tokenValue = ReadString(); | |||
|  | 42 | tokenType = JsonTokenType.String; | |||
|  | 43 | break; | |||
|  | 44 | case JSONGrammar.TokenType.Number: | |||
|  | 45 | tokenValue = Double.Parse(new String(m_buffer, m_tokenOffset, m_tokenLen), CultureInfo.InvariantCulture); | |||
|  | 46 | tokenType = JsonTokenType.Number; | |||
|  | 47 | break; | |||
|  | 48 | default: | |||
|  | 49 | tokenType = (JsonTokenType)m_currentState.tag[0]; | |||
|  | 50 | tokenValue = new String(m_buffer, m_tokenOffset, m_tokenLen); | |||
|  | 51 | break; | |||
|  | 52 | } | |||
|  | 53 | return true; | |||
|  | 54 | } | |||
|  | 55 | tokenValue = null; | |||
|  | 56 | tokenType = JsonTokenType.None; | |||
|  | 57 | return false; | |||
|  | 58 | } | |||
|  | 59 | ||||
|  | 60 | string ReadString() { | |||
|  | 61 | int pos = 0; | |||
|  | 62 | Switch(m_stringDFA, m_stringAlphabet); | |||
|  | 63 | while (ReadTokenInternal()) { | |||
|  | 64 | switch ((JSONGrammar.TokenType)m_currentState.tag[0]) { | |||
|  | 65 | case JSONGrammar.TokenType.StringBound: | |||
|  | 66 | Restore(); | |||
|  | 67 | return new String(m_stringBuffer, 0, pos); | |||
|  | 68 | case JSONGrammar.TokenType.UnescapedChar: | |||
|  | 69 | EnsureStringBufferSize(pos + m_tokenLen); | |||
|  | 70 | Array.Copy(m_buffer, m_tokenOffset, m_stringBuffer, pos, m_tokenLen); | |||
|  | 71 | pos += m_tokenLen; | |||
|  | 72 | break; | |||
|  | 73 | case JSONGrammar.TokenType.EscapedUnicode: | |||
|  | 74 | EnsureStringBufferSize(pos + 1); | |||
|  | 75 | m_stringBuffer[pos] = StringTranslator.TranslateHexUnicode(m_buffer, m_tokenOffset + 2); | |||
|  | 76 | pos++; | |||
|  | 77 | break; | |||
|  | 78 | case JSONGrammar.TokenType.EscapedChar: | |||
|  | 79 | EnsureStringBufferSize(pos + 1); | |||
|  | 80 | m_stringBuffer[pos] = StringTranslator.TranslateEscapedChar(m_buffer[m_tokenOffset + 1]); | |||
|  | 81 | pos++; | |||
|  | 82 | break; | |||
|  | 83 | default: | |||
|  | 84 | break; | |||
|  | 85 | } | |||
|  | 86 | ||||
|  | 87 | } | |||
|  | 88 | ||||
|  | 89 | throw new ParserException("Unexpected end of data"); | |||
|  | 90 | } | |||
|  | 91 | ||||
|  | 92 | void EnsureStringBufferSize(int size) { | |||
|  | 93 | if (size > m_stringBuffer.Length) { | |||
|  | 94 | var newBuffer = new char[size]; | |||
|  | 95 | m_stringBuffer.CopyTo(newBuffer, 0); | |||
|  | 96 | m_stringBuffer = newBuffer; | |||
|  | 97 | } | |||
|  | 98 | } | |||
|  | 99 | } | |||
|  | 100 | } | |||
| @@ -0,0 +1,319 | |||||
|  | 1 | using System; | |||
|  | 2 | using System.Collections.Generic; | |||
|  | 3 | using System.IO; | |||
|  | 4 | using System.Globalization; | |||
|  | 5 | using System.Diagnostics; | |||
|  | 6 | ||||
|  | 7 | namespace Implab.JSON { | |||
|  | 8 | public class JSONWriter { | |||
|  | 9 | struct Context { | |||
|  | 10 | public bool needComma; | |||
|  | 11 | public JSONElementContext element; | |||
|  | 12 | } | |||
|  | 13 | Stack<Context> m_contextStack = new Stack<Context>(); | |||
|  | 14 | Context m_context; | |||
|  | 15 | ||||
|  | 16 | const int BUFFER_SIZE = 64; | |||
|  | 17 | ||||
|  | 18 | TextWriter m_writer; | |||
|  | 19 | readonly bool m_indent = true; | |||
|  | 20 | readonly int m_indentSize = 4; | |||
|  | 21 | readonly char[] m_buffer = new char[BUFFER_SIZE]; | |||
|  | 22 | int m_bufferPos; | |||
|  | 23 | ||||
|  | 24 | static readonly char [] _hex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; | |||
|  | 25 | static readonly char [] _escapeBKS, | |||
|  | 26 | _escapeFWD, | |||
|  | 27 | _escapeCR, | |||
|  | 28 | _escapeNL, | |||
|  | 29 | _escapeTAB, | |||
|  | 30 | _escapeBSLASH, | |||
|  | 31 | _escapeQ; | |||
|  | 32 | ||||
|  | 33 | static JSONWriter() { | |||
|  | 34 | _escapeBKS = "\\b".ToCharArray(); | |||
|  | 35 | _escapeFWD = "\\f".ToCharArray(); | |||
|  | 36 | _escapeCR = "\\r".ToCharArray(); | |||
|  | 37 | _escapeNL = "\\n".ToCharArray(); | |||
|  | 38 | _escapeTAB = "\\t".ToCharArray(); | |||
|  | 39 | _escapeBSLASH = "\\\\".ToCharArray(); | |||
|  | 40 | _escapeQ = "\\\"".ToCharArray(); | |||
|  | 41 | } | |||
|  | 42 | ||||
|  | 43 | public JSONWriter(TextWriter writer) { | |||
|  | 44 | Safe.ArgumentNotNull(writer, "writer"); | |||
|  | 45 | m_writer = writer; | |||
|  | 46 | } | |||
|  | 47 | ||||
|  | 48 | public JSONWriter(TextWriter writer, bool indent) { | |||
|  | 49 | Safe.ArgumentNotNull(writer, "writer"); | |||
|  | 50 | ||||
|  | 51 | m_writer = writer; | |||
|  | 52 | m_indent = indent; | |||
|  | 53 | } | |||
|  | 54 | ||||
|  | 55 | void WriteIndent() { | |||
|  | 56 | if (m_indent) { | |||
|  | 57 | var indent = new char[m_contextStack.Count * m_indentSize + 1]; | |||
|  | 58 | indent[0] = '\n'; | |||
|  | 59 | for (int i = 1; i < indent.Length; i++) | |||
|  | 60 | indent[i] = ' '; | |||
|  | 61 | m_writer.Write(new String(indent)); | |||
|  | 62 | } else { | |||
|  | 63 | m_writer.Write(' '); | |||
|  | 64 | } | |||
|  | 65 | } | |||
|  | 66 | ||||
|  | 67 | void WriteMemberName(string name) { | |||
|  | 68 | Safe.ArgumentNotEmpty(name, "name"); | |||
|  | 69 | if (m_context.element != JSONElementContext.Object) | |||
|  | 70 | OperationNotApplicable("WriteMember"); | |||
|  | 71 | if (m_context.needComma) | |||
|  | 72 | m_writer.Write(","); | |||
|  | 73 | ||||
|  | 74 | WriteIndent(); | |||
|  | 75 | m_context.needComma = true; | |||
|  | 76 | Write(name); | |||
|  | 77 | m_writer.Write(" : "); | |||
|  | 78 | } | |||
|  | 79 | ||||
|  | 80 | public void WriteValue(string name, string value) { | |||
|  | 81 | WriteMemberName(name); | |||
|  | 82 | Write(value); | |||
|  | 83 | } | |||
|  | 84 | ||||
|  | 85 | public void WriteValue(string name, bool value) { | |||
|  | 86 | WriteMemberName(name); | |||
|  | 87 | Write(value); | |||
|  | 88 | } | |||
|  | 89 | ||||
|  | 90 | public void WriteValue(string name, double value) { | |||
|  | 91 | WriteMemberName(name); | |||
|  | 92 | Write(value); | |||
|  | 93 | } | |||
|  | 94 | ||||
|  | 95 | public void WriteValue(string value) { | |||
|  | 96 | if (m_context.element == JSONElementContext.Array) { | |||
|  | 97 | ||||
|  | 98 | if (m_context.needComma) | |||
|  | 99 | m_writer.Write(","); | |||
|  | 100 | WriteIndent(); | |||
|  | 101 | m_context.needComma = true; | |||
|  | 102 | ||||
|  | 103 | Write(value); | |||
|  | 104 | } else if (m_context.element == JSONElementContext.None) { | |||
|  | 105 | Write(value); | |||
|  | 106 | m_context.element = JSONElementContext.Closed; | |||
|  | 107 | } else { | |||
|  | 108 | OperationNotApplicable("WriteValue"); | |||
|  | 109 | } | |||
|  | 110 | } | |||
|  | 111 | ||||
|  | 112 | public void WriteValue(bool value) { | |||
|  | 113 | if (m_context.element == JSONElementContext.Array) { | |||
|  | 114 | ||||
|  | 115 | if (m_context.needComma) | |||
|  | 116 | m_writer.Write(","); | |||
|  | 117 | WriteIndent(); | |||
|  | 118 | m_context.needComma = true; | |||
|  | 119 | ||||
|  | 120 | Write(value); | |||
|  | 121 | } else if (m_context.element == JSONElementContext.None) { | |||
|  | 122 | Write(value); | |||
|  | 123 | m_context.element = JSONElementContext.Closed; | |||
|  | 124 | } else { | |||
|  | 125 | OperationNotApplicable("WriteValue"); | |||
|  | 126 | } | |||
|  | 127 | } | |||
|  | 128 | ||||
|  | 129 | public void WriteValue(double value) { | |||
|  | 130 | if (m_context.element == JSONElementContext.Array) { | |||
|  | 131 | ||||
|  | 132 | if (m_context.needComma) | |||
|  | 133 | m_writer.Write(","); | |||
|  | 134 | WriteIndent(); | |||
|  | 135 | m_context.needComma = true; | |||
|  | 136 | ||||
|  | 137 | Write(value); | |||
|  | 138 | } else if (m_context.element == JSONElementContext.None) { | |||
|  | 139 | Write(value); | |||
|  | 140 | m_context.element = JSONElementContext.Closed; | |||
|  | 141 | } else { | |||
|  | 142 | OperationNotApplicable("WriteValue"); | |||
|  | 143 | } | |||
|  | 144 | } | |||
|  | 145 | ||||
|  | 146 | public void BeginObject() { | |||
|  | 147 | if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array) | |||
|  | 148 | OperationNotApplicable("BeginObject"); | |||
|  | 149 | if (m_context.needComma) | |||
|  | 150 | m_writer.Write(","); | |||
|  | 151 | ||||
|  | 152 | WriteIndent(); | |||
|  | 153 | ||||
|  | 154 | m_context.needComma = true; | |||
|  | 155 | ||||
|  | 156 | m_contextStack.Push(m_context); | |||
|  | 157 | ||||
|  | 158 | m_context = new Context { element = JSONElementContext.Object, needComma = false }; | |||
|  | 159 | m_writer.Write("{"); | |||
|  | 160 | } | |||
|  | 161 | ||||
|  | 162 | public void BeginObject(string name) { | |||
|  | 163 | WriteMemberName(name); | |||
|  | 164 | ||||
|  | 165 | m_contextStack.Push(m_context); | |||
|  | 166 | ||||
|  | 167 | m_context = new Context { element = JSONElementContext.Object, needComma = false }; | |||
|  | 168 | m_writer.Write("{"); | |||
|  | 169 | } | |||
|  | 170 | ||||
|  | 171 | public void EndObject() { | |||
|  | 172 | if (m_context.element != JSONElementContext.Object) | |||
|  | 173 | OperationNotApplicable("EndObject"); | |||
|  | 174 | ||||
|  | 175 | m_context = m_contextStack.Pop(); | |||
|  | 176 | if (m_contextStack.Count == 0) | |||
|  | 177 | m_context.element = JSONElementContext.Closed; | |||
|  | 178 | WriteIndent(); | |||
|  | 179 | m_writer.Write("}"); | |||
|  | 180 | } | |||
|  | 181 | ||||
|  | 182 | public void BeginArray() { | |||
|  | 183 | if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array) | |||
|  | 184 | throw new InvalidOperationException(); | |||
|  | 185 | if (m_context.needComma) { | |||
|  | 186 | m_writer.Write(","); | |||
|  | 187 | ||||
|  | 188 | } | |||
|  | 189 | m_context.needComma = true; | |||
|  | 190 | ||||
|  | 191 | WriteIndent(); | |||
|  | 192 | m_contextStack.Push(m_context); | |||
|  | 193 | m_context = new Context { element = JSONElementContext.Array, needComma = false }; | |||
|  | 194 | m_writer.Write("["); | |||
|  | 195 | } | |||
|  | 196 | ||||
|  | 197 | public void BeginArray(string name) { | |||
|  | 198 | WriteMemberName(name); | |||
|  | 199 | ||||
|  | 200 | m_contextStack.Push(m_context); | |||
|  | 201 | ||||
|  | 202 | m_context = new Context { element = JSONElementContext.Array, needComma = false }; | |||
|  | 203 | m_writer.Write("["); | |||
|  | 204 | } | |||
|  | 205 | ||||
|  | 206 | public void EndArray() { | |||
|  | 207 | if (m_context.element != JSONElementContext.Array) | |||
|  | 208 | OperationNotApplicable("EndArray"); | |||
|  | 209 | ||||
|  | 210 | m_context = m_contextStack.Pop(); | |||
|  | 211 | if (m_contextStack.Count == 0) | |||
|  | 212 | m_context.element = JSONElementContext.Closed; | |||
|  | 213 | WriteIndent(); | |||
|  | 214 | m_writer.Write("]"); | |||
|  | 215 | } | |||
|  | 216 | ||||
|  | 217 | void Write(bool value) { | |||
|  | 218 | m_writer.Write(value ? "true" : "false"); | |||
|  | 219 | } | |||
|  | 220 | ||||
|  | 221 | void FlushBuffer() { | |||
|  | 222 | if (m_bufferPos > 0) { | |||
|  | 223 | m_writer.Write(m_buffer, 0, m_bufferPos); | |||
|  | 224 | m_bufferPos = 0; | |||
|  | 225 | } | |||
|  | 226 | } | |||
|  | 227 | ||||
|  | 228 | void Write(string value) { | |||
|  | 229 | if (value == null) { | |||
|  | 230 | m_writer.Write("null"); | |||
|  | 231 | return; | |||
|  | 232 | } | |||
|  | 233 | ||||
|  | 234 | Debug.Assert(m_bufferPos == 0); | |||
|  | 235 | ||||
|  | 236 | var chars = value.ToCharArray(); | |||
|  | 237 | m_buffer[m_bufferPos++] = '"'; | |||
|  | 238 | ||||
|  | 239 | // Analysis disable once ForCanBeConvertedToForeach | |||
|  | 240 | for (int i = 0; i < chars.Length; i++) { | |||
|  | 241 | var ch = chars[i]; | |||
|  | 242 | ||||
|  | 243 | char[] escapeSeq; | |||
|  | 244 | ||||
|  | 245 | switch (ch) { | |||
|  | 246 | case '\b': | |||
|  | 247 | escapeSeq = _escapeBKS; | |||
|  | 248 | break; | |||
|  | 249 | case '\f': | |||
|  | 250 | escapeSeq = _escapeFWD; | |||
|  | 251 | break; | |||
|  | 252 | case '\r': | |||
|  | 253 | escapeSeq = _escapeCR; | |||
|  | 254 | break; | |||
|  | 255 | case '\n': | |||
|  | 256 | escapeSeq = _escapeNL; | |||
|  | 257 | break; | |||
|  | 258 | case '\t': | |||
|  | 259 | escapeSeq = _escapeTAB; | |||
|  | 260 | break; | |||
|  | 261 | case '\\': | |||
|  | 262 | escapeSeq = _escapeBSLASH; | |||
|  | 263 | break; | |||
|  | 264 | case '"': | |||
|  | 265 | escapeSeq = _escapeQ; | |||
|  | 266 | break; | |||
|  | 267 | default: | |||
|  | 268 | if (ch < 0x20) { | |||
|  | 269 | if (m_bufferPos + 6 > BUFFER_SIZE) | |||
|  | 270 | FlushBuffer(); | |||
|  | 271 | ||||
|  | 272 | m_buffer[m_bufferPos++] = '\\'; | |||
|  | 273 | m_buffer[m_bufferPos++] = 'u'; | |||
|  | 274 | m_buffer[m_bufferPos++] = '0'; | |||
|  | 275 | m_buffer[m_bufferPos++] = '0'; | |||
|  | 276 | m_buffer[m_bufferPos++] = _hex[ch >> 4 & 0xf]; | |||
|  | 277 | m_buffer[m_bufferPos++] = _hex[ch & 0xf]; | |||
|  | 278 | ||||
|  | 279 | } else { | |||
|  | 280 | if (m_bufferPos >= BUFFER_SIZE) | |||
|  | 281 | FlushBuffer(); | |||
|  | 282 | m_buffer[m_bufferPos++] = ch; | |||
|  | 283 | } | |||
|  | 284 | continue; | |||
|  | 285 | } | |||
|  | 286 | ||||
|  | 287 | if (m_bufferPos + escapeSeq.Length > BUFFER_SIZE) | |||
|  | 288 | FlushBuffer(); | |||
|  | 289 | ||||
|  | 290 | Array.Copy(escapeSeq, 0, m_buffer, m_bufferPos, escapeSeq.Length); | |||
|  | 291 | m_bufferPos += escapeSeq.Length; | |||
|  | 292 | ||||
|  | 293 | } | |||
|  | 294 | ||||
|  | 295 | if (m_bufferPos >= BUFFER_SIZE) | |||
|  | 296 | FlushBuffer(); | |||
|  | 297 | ||||
|  | 298 | m_buffer[m_bufferPos++] = '"'; | |||
|  | 299 | ||||
|  | 300 | FlushBuffer(); | |||
|  | 301 | } | |||
|  | 302 | ||||
|  | 303 | void Write(double value) { | |||
|  | 304 | if (double.IsNaN(value)) | |||
|  | 305 | Write("NaN"); | |||
|  | 306 | else if (double.IsNegativeInfinity(value)) | |||
|  | 307 | Write("-Infinity"); | |||
|  | 308 | else if (double.IsPositiveInfinity(value)) | |||
|  | 309 | Write("Infinity"); | |||
|  | 310 | else | |||
|  | 311 | m_writer.Write(value.ToString(CultureInfo.InvariantCulture)); | |||
|  | 312 | } | |||
|  | 313 | ||||
|  | 314 | void OperationNotApplicable(string opName) { | |||
|  | 315 | throw new InvalidOperationException(String.Format("The operation '{0}' isn't applicable in the context of '{1}'", opName, m_context.element )); | |||
|  | 316 | } | |||
|  | 317 | ||||
|  | 318 | } | |||
|  | 319 | } | |||
| @@ -0,0 +1,343 | |||||
|  | 1 | using Implab; | |||
|  | 2 | using Implab.Parsing; | |||
|  | 3 | using System; | |||
|  | 4 | using System.Collections.Generic; | |||
|  | 5 | using System.Globalization; | |||
|  | 6 | using System.IO; | |||
|  | 7 | using System.Linq; | |||
|  | 8 | using System.Text; | |||
|  | 9 | using System.Threading.Tasks; | |||
|  | 10 | using System.Xml; | |||
|  | 11 | ||||
|  | 12 | namespace Implab.JSON { | |||
|  | 13 | public class JSONXmlReader : XmlReader { | |||
|  | 14 | ||||
|  | 15 | enum ValueContext { | |||
|  | 16 | Undefined, | |||
|  | 17 | ElementStart, | |||
|  | 18 | ElementValue, | |||
|  | 19 | ElementEnd, | |||
|  | 20 | ElementEmpty | |||
|  | 21 | } | |||
|  | 22 | ||||
|  | 23 | struct LocalNameContext { | |||
|  | 24 | public string localName; | |||
|  | 25 | public bool isArray; | |||
|  | 26 | } | |||
|  | 27 | ||||
|  | 28 | JSONParser m_parser; | |||
|  | 29 | ValueContext m_valueContext; | |||
|  | 30 | ReadState m_state = ReadState.Initial; | |||
|  | 31 | Stack<LocalNameContext> m_localNameStack = new Stack<LocalNameContext>(); | |||
|  | 32 | LocalNameContext m_localName; | |||
|  | 33 | int m_depthCorrection = 0; | |||
|  | 34 | ||||
|  | 35 | readonly string m_rootName; | |||
|  | 36 | readonly string m_prefix; | |||
|  | 37 | readonly string m_namespaceUri; | |||
|  | 38 | readonly bool m_flattenArrays; | |||
|  | 39 | readonly string m_arrayItemName; | |||
|  | 40 | readonly XmlNameTable m_nameTable; | |||
|  | 41 | ||||
|  | 42 | JSONXmlReader(JSONParser parser, JSONXmlReaderOptions options) { | |||
|  | 43 | m_parser = parser; | |||
|  | 44 | ||||
|  | 45 | if (options != null) { | |||
|  | 46 | m_prefix = options.NodesPrefix ?? String.Empty; | |||
|  | 47 | m_namespaceUri = options.NamespaceURI ?? String.Empty; | |||
|  | 48 | m_rootName = options.RootName ?? "json"; | |||
|  | 49 | m_flattenArrays = options.FlattenArrays; | |||
|  | 50 | m_arrayItemName = options.ArrayItemName ?? "item"; | |||
|  | 51 | m_nameTable = options.NameTable ?? new NameTable(); | |||
|  | 52 | } else { | |||
|  | 53 | m_prefix = String.Empty; | |||
|  | 54 | m_namespaceUri = String.Empty; | |||
|  | 55 | m_rootName = "json"; | |||
|  | 56 | m_flattenArrays = false; | |||
|  | 57 | m_arrayItemName = "item"; | |||
|  | 58 | m_nameTable = new NameTable(); | |||
|  | 59 | } | |||
|  | 60 | } | |||
|  | 61 | ||||
|  | 62 | /// <summary> | |||
|  | 63 | /// Always 0, JSON doesn't support attributes | |||
|  | 64 | /// </summary> | |||
|  | 65 | public override int AttributeCount { | |||
|  | 66 | get { return 0; } | |||
|  | 67 | } | |||
|  | 68 | ||||
|  | 69 | public override string BaseURI { | |||
|  | 70 | get { return String.Empty; } | |||
|  | 71 | } | |||
|  | 72 | ||||
|  | 73 | public override int Depth { | |||
|  | 74 | get { | |||
|  | 75 | return m_localNameStack.Count + m_depthCorrection; | |||
|  | 76 | } | |||
|  | 77 | } | |||
|  | 78 | ||||
|  | 79 | public override bool EOF { | |||
|  | 80 | get { return m_parser.EOF; } | |||
|  | 81 | } | |||
|  | 82 | ||||
|  | 83 | /// <summary> | |||
|  | 84 | /// Always throws an exception | |||
|  | 85 | /// </summary> | |||
|  | 86 | /// <param name="i"></param> | |||
|  | 87 | /// <returns></returns> | |||
|  | 88 | public override string GetAttribute(int i) { | |||
|  | 89 | throw new ArgumentOutOfRangeException(); | |||
|  | 90 | } | |||
|  | 91 | ||||
|  | 92 | /// <summary> | |||
|  | 93 | /// Always returns empty string | |||
|  | 94 | /// </summary> | |||
|  | 95 | /// <param name="name"></param> | |||
|  | 96 | /// <param name="namespaceURI"></param> | |||
|  | 97 | /// <returns></returns> | |||
|  | 98 | public override string GetAttribute(string name, string namespaceURI) { | |||
|  | 99 | return String.Empty; | |||
|  | 100 | } | |||
|  | 101 | ||||
|  | 102 | /// <summary> | |||
|  | 103 | /// Always returns empty string | |||
|  | 104 | /// </summary> | |||
|  | 105 | /// <param name="name"></param> | |||
|  | 106 | /// <returns></returns> | |||
|  | 107 | public override string GetAttribute(string name) { | |||
|  | 108 | return String.Empty; | |||
|  | 109 | } | |||
|  | 110 | ||||
|  | 111 | public override bool IsEmptyElement { | |||
|  | 112 | get { return m_parser.ElementType == JSONElementType.Value && m_valueContext == ValueContext.ElementEmpty; } | |||
|  | 113 | } | |||
|  | 114 | ||||
|  | 115 | public override string LocalName { | |||
|  | 116 | get { return m_localName.localName; } | |||
|  | 117 | } | |||
|  | 118 | ||||
|  | 119 | public override string LookupNamespace(string prefix) { | |||
|  | 120 | if (String.IsNullOrEmpty(prefix) || prefix == m_prefix) | |||
|  | 121 | return m_namespaceUri; | |||
|  | 122 | else | |||
|  | 123 | return String.Empty; | |||
|  | 124 | } | |||
|  | 125 | ||||
|  | 126 | public override bool MoveToAttribute(string name, string ns) { | |||
|  | 127 | return false; | |||
|  | 128 | } | |||
|  | 129 | ||||
|  | 130 | public override bool MoveToAttribute(string name) { | |||
|  | 131 | return false; | |||
|  | 132 | } | |||
|  | 133 | ||||
|  | 134 | public override bool MoveToElement() { | |||
|  | 135 | return false; | |||
|  | 136 | } | |||
|  | 137 | ||||
|  | 138 | public override bool MoveToFirstAttribute() { | |||
|  | 139 | return false; | |||
|  | 140 | } | |||
|  | 141 | ||||
|  | 142 | public override bool MoveToNextAttribute() { | |||
|  | 143 | return false; | |||
|  | 144 | } | |||
|  | 145 | ||||
|  | 146 | public override XmlNameTable NameTable { | |||
|  | 147 | get { return m_nameTable; } | |||
|  | 148 | } | |||
|  | 149 | ||||
|  | 150 | public override string NamespaceURI { | |||
|  | 151 | get { return m_namespaceUri; } | |||
|  | 152 | } | |||
|  | 153 | ||||
|  | 154 | public override XmlNodeType NodeType { | |||
|  | 155 | get { | |||
|  | 156 | switch (m_parser.ElementType) { | |||
|  | 157 | case JSONElementType.BeginObject: | |||
|  | 158 | case JSONElementType.BeginArray: | |||
|  | 159 | return XmlNodeType.Element; | |||
|  | 160 | case JSONElementType.EndObject: | |||
|  | 161 | case JSONElementType.EndArray: | |||
|  | 162 | return XmlNodeType.EndElement; | |||
|  | 163 | case JSONElementType.Value: | |||
|  | 164 | switch (m_valueContext) { | |||
|  | 165 | case ValueContext.ElementStart: | |||
|  | 166 | case ValueContext.ElementEmpty: | |||
|  | 167 | return XmlNodeType.Element; | |||
|  | 168 | case ValueContext.ElementValue: | |||
|  | 169 | return XmlNodeType.Text; | |||
|  | 170 | case ValueContext.ElementEnd: | |||
|  | 171 | return XmlNodeType.EndElement; | |||
|  | 172 | default: | |||
|  | 173 | throw new InvalidOperationException(); | |||
|  | 174 | } | |||
|  | 175 | default: | |||
|  | 176 | throw new InvalidOperationException(); | |||
|  | 177 | } | |||
|  | 178 | } | |||
|  | 179 | } | |||
|  | 180 | ||||
|  | 181 | public override string Prefix { | |||
|  | 182 | get { return m_prefix; } | |||
|  | 183 | } | |||
|  | 184 | ||||
|  | 185 | public override bool Read() { | |||
|  | 186 | if (m_state != System.Xml.ReadState.Interactive && m_state != System.Xml.ReadState.Initial) | |||
|  | 187 | return false; | |||
|  | 188 | ||||
|  | 189 | if (m_state == ReadState.Initial) | |||
|  | 190 | m_state = System.Xml.ReadState.Interactive; | |||
|  | 191 | ||||
|  | 192 | try { | |||
|  | 193 | switch (m_parser.ElementType) { | |||
|  | 194 | case JSONElementType.Value: | |||
|  | 195 | switch (m_valueContext) { | |||
|  | 196 | case ValueContext.ElementStart: | |||
|  | 197 | SetLocalName(String.Empty); | |||
|  | 198 | m_valueContext = ValueContext.ElementValue; | |||
|  | 199 | return true; | |||
|  | 200 | case ValueContext.ElementValue: | |||
|  | 201 | RestoreLocalName(); | |||
|  | 202 | m_valueContext = ValueContext.ElementEnd; | |||
|  | 203 | return true; | |||
|  | 204 | case ValueContext.ElementEmpty: | |||
|  | 205 | case ValueContext.ElementEnd: | |||
|  | 206 | RestoreLocalName(); | |||
|  | 207 | break; | |||
|  | 208 | } | |||
|  | 209 | break; | |||
|  | 210 | case JSONElementType.EndArray: | |||
|  | 211 | case JSONElementType.EndObject: | |||
|  | 212 | RestoreLocalName(); | |||
|  | 213 | break; | |||
|  | 214 | } | |||
|  | 215 | string itemName = m_parser.ElementType == JSONElementType.None ? m_rootName : m_flattenArrays ? m_localName.localName : m_arrayItemName; | |||
|  | 216 | while (m_parser.Read()) { | |||
|  | 217 | if (!String.IsNullOrEmpty(m_parser.ElementName)) | |||
|  | 218 | itemName = m_parser.ElementName; | |||
|  | 219 | ||||
|  | 220 | switch (m_parser.ElementType) { | |||
|  | 221 | case JSONElementType.BeginArray: | |||
|  | 222 | if (m_flattenArrays && !m_localName.isArray) { | |||
|  | 223 | m_depthCorrection--; | |||
|  | 224 | SetLocalName(itemName, true); | |||
|  | 225 | continue; | |||
|  | 226 | } else { | |||
|  | 227 | SetLocalName(itemName, true); | |||
|  | 228 | } | |||
|  | 229 | break; | |||
|  | 230 | case JSONElementType.BeginObject: | |||
|  | 231 | SetLocalName(itemName); | |||
|  | 232 | break; | |||
|  | 233 | case JSONElementType.EndArray: | |||
|  | 234 | if (m_flattenArrays && !m_localNameStack.Peek().isArray) { | |||
|  | 235 | RestoreLocalName(); | |||
|  | 236 | m_depthCorrection++; | |||
|  | 237 | continue; | |||
|  | 238 | } | |||
|  | 239 | break; | |||
|  | 240 | case JSONElementType.EndObject: | |||
|  | 241 | break; | |||
|  | 242 | case JSONElementType.Value: | |||
|  | 243 | SetLocalName(itemName); | |||
|  | 244 | m_valueContext = m_parser.ElementValue == null ? ValueContext.ElementEmpty : ValueContext.ElementStart; | |||
|  | 245 | break; | |||
|  | 246 | default: | |||
|  | 247 | break; | |||
|  | 248 | } | |||
|  | 249 | return true; | |||
|  | 250 | } | |||
|  | 251 | ||||
|  | 252 | m_state = System.Xml.ReadState.EndOfFile; | |||
|  | 253 | return false; | |||
|  | 254 | } catch { | |||
|  | 255 | m_state = System.Xml.ReadState.Error; | |||
|  | 256 | throw; | |||
|  | 257 | } | |||
|  | 258 | } | |||
|  | 259 | ||||
|  | 260 | public override bool ReadAttributeValue() { | |||
|  | 261 | return false; | |||
|  | 262 | } | |||
|  | 263 | ||||
|  | 264 | public override ReadState ReadState { | |||
|  | 265 | get { return m_state; } | |||
|  | 266 | } | |||
|  | 267 | ||||
|  | 268 | public override void ResolveEntity() { | |||
|  | 269 | // do nothing | |||
|  | 270 | } | |||
|  | 271 | ||||
|  | 272 | public override string Value { | |||
|  | 273 | get { | |||
|  | 274 | if (m_parser.ElementValue == null) | |||
|  | 275 | return String.Empty; | |||
|  | 276 | if (Convert.GetTypeCode(m_parser.ElementValue) == TypeCode.Double) | |||
|  | 277 | return ((double)m_parser.ElementValue).ToString(CultureInfo.InvariantCulture); | |||
|  | 278 | else | |||
|  | 279 | return m_parser.ElementValue.ToString(); | |||
|  | 280 | } | |||
|  | 281 | } | |||
|  | 282 | ||||
|  | 283 | void SetLocalName(string name) { | |||
|  | 284 | m_localNameStack.Push(m_localName); | |||
|  | 285 | m_localName.localName = name; | |||
|  | 286 | m_localName.isArray = false; | |||
|  | 287 | } | |||
|  | 288 | ||||
|  | 289 | void SetLocalName(string name, bool isArray) { | |||
|  | 290 | m_localNameStack.Push(m_localName); | |||
|  | 291 | m_localName.localName = name; | |||
|  | 292 | m_localName.isArray = isArray; | |||
|  | 293 | } | |||
|  | 294 | ||||
|  | 295 | void RestoreLocalName() { | |||
|  | 296 | m_localName = m_localNameStack.Pop(); | |||
|  | 297 | } | |||
|  | 298 | ||||
|  | 299 | public override void Close() { | |||
|  | 300 | ||||
|  | 301 | } | |||
|  | 302 | ||||
|  | 303 | protected override void Dispose(bool disposing) { | |||
|  | 304 | #if MONO | |||
|  | 305 | disposing = true; | |||
|  | 306 | #endif | |||
|  | 307 | if (disposing) { | |||
|  | 308 | m_parser.Dispose(); | |||
|  | 309 | } | |||
|  | 310 | base.Dispose(disposing); | |||
|  | 311 | } | |||
|  | 312 | ||||
|  | 313 | public static JSONXmlReader Create(string file, JSONXmlReaderOptions options) { | |||
|  | 314 | return Create(File.OpenText(file), options); | |||
|  | 315 | } | |||
|  | 316 | ||||
|  | 317 | /// <summary> | |||
|  | 318 | /// Creates the XmlReader for the specified text stream with JSON data. | |||
|  | 319 | /// </summary> | |||
|  | 320 | /// <param name="reader">Text reader.</param> | |||
|  | 321 | /// <param name="options">Options.</param> | |||
|  | 322 | /// <remarks> | |||
|  | 323 | /// The reader will be disposed when the XmlReader is disposed. | |||
|  | 324 | /// </remarks> | |||
|  | 325 | public static JSONXmlReader Create(TextReader reader, JSONXmlReaderOptions options) { | |||
|  | 326 | return new JSONXmlReader(new JSONParser(reader, true), options); | |||
|  | 327 | } | |||
|  | 328 | ||||
|  | 329 | /// <summary> | |||
|  | 330 | /// Creates the XmlReader for the specified stream with JSON data. | |||
|  | 331 | /// </summary> | |||
|  | 332 | /// <param name="stream">Stream.</param> | |||
|  | 333 | /// <param name="options">Options.</param> | |||
|  | 334 | /// <remarks> | |||
|  | 335 | /// The stream will be disposed when the XmlReader is disposed. | |||
|  | 336 | /// </remarks> | |||
|  | 337 | public static JSONXmlReader Create(Stream stream, JSONXmlReaderOptions options) { | |||
|  | 338 | Safe.ArgumentNotNull(stream, "stream"); | |||
|  | 339 | // HACK don't dispose StreaReader to keep stream opened | |||
|  | 340 | return Create(new StreamReader(stream), options); | |||
|  | 341 | } | |||
|  | 342 | } | |||
|  | 343 | } | |||
| @@ -0,0 +1,65 | |||||
|  | 1 | using System; | |||
|  | 2 | using System.Collections.Generic; | |||
|  | 3 | using System.Linq; | |||
|  | 4 | using System.Text; | |||
|  | 5 | using System.Xml; | |||
|  | 6 | ||||
|  | 7 | namespace Implab.JSON { | |||
|  | 8 | /// <summary> | |||
|  | 9 | /// Набор необязательных параметров для <see cref="JSONXmlReader"/>, позволяющий управлять процессом | |||
|  | 10 | /// интерпретации <c>JSON</c> документа. | |||
|  | 11 | /// </summary> | |||
|  | 12 | public class JSONXmlReaderOptions { | |||
|  | 13 | /// <summary> | |||
|  | 14 | /// Пространство имен в котором будут располагаться читаемые элементы документа | |||
|  | 15 | /// </summary> | |||
|  | 16 | public string NamespaceURI { | |||
|  | 17 | get; | |||
|  | 18 | set; | |||
|  | 19 | } | |||
|  | 20 | ||||
|  | 21 | /// <summary> | |||
|  | 22 | /// Интерпретировать массивы как множественные элементы (убирает один уровень вложенности), иначе массив | |||
|  | 23 | /// представляется в виде узла, дочерними элементами которого являются элементы массива, имена дочерних элементов | |||
|  | 24 | /// определяются свойством <see cref="ArrayItemName"/>. По умолчанию <c>false</c>. | |||
|  | 25 | /// </summary> | |||
|  | 26 | public bool FlattenArrays { | |||
|  | 27 | get; | |||
|  | 28 | set; | |||
|  | 29 | } | |||
|  | 30 | ||||
|  | 31 | /// <summary> | |||
|  | 32 | /// Префикс, для узлов документа | |||
|  | 33 | /// </summary> | |||
|  | 34 | public string NodesPrefix { | |||
|  | 35 | get; | |||
|  | 36 | set; | |||
|  | 37 | } | |||
|  | 38 | ||||
|  | 39 | /// <summary> | |||
|  | 40 | /// Имя корневого элемента в xml документе | |||
|  | 41 | /// </summary> | |||
|  | 42 | public string RootName { | |||
|  | 43 | get; | |||
|  | 44 | set; | |||
|  | 45 | } | |||
|  | 46 | ||||
|  | 47 | /// <summary> | |||
|  | 48 | /// Имя элемента для массивов, если не включена опция <see cref="FlattenArrays"/>. | |||
|  | 49 | /// По умолчанию <c>item</c>. | |||
|  | 50 | /// </summary> | |||
|  | 51 | public string ArrayItemName { | |||
|  | 52 | get; | |||
|  | 53 | set; | |||
|  | 54 | } | |||
|  | 55 | ||||
|  | 56 | /// <summary> | |||
|  | 57 | /// Таблица атомизированных строк для построения документа. | |||
|  | 58 | /// </summary> | |||
|  | 59 | public XmlNameTable NameTable { | |||
|  | 60 | get; | |||
|  | 61 | set; | |||
|  | 62 | } | |||
|  | 63 | ||||
|  | 64 | } | |||
|  | 65 | } | |||
| @@ -0,0 +1,50 | |||||
|  | 1 | using System; | |||
|  | 2 | using System.Collections.Generic; | |||
|  | 3 | using System.Linq; | |||
|  | 4 | using System.Text; | |||
|  | 5 | using System.Threading.Tasks; | |||
|  | 6 | ||||
|  | 7 | namespace Implab.JSON { | |||
|  | 8 | /// <summary> | |||
|  | 9 | /// Тип токенов, возвращаемых <see cref="JSONScanner"/>. | |||
|  | 10 | /// </summary> | |||
|  | 11 | public enum JsonTokenType : int { | |||
|  | 12 | None = 0, | |||
|  | 13 | /// <summary> | |||
|  | 14 | /// Начало объекта | |||
|  | 15 | /// </summary> | |||
|  | 16 | BeginObject, | |||
|  | 17 | /// <summary> | |||
|  | 18 | /// Конец объекта | |||
|  | 19 | /// </summary> | |||
|  | 20 | EndObject, | |||
|  | 21 | /// <summary> | |||
|  | 22 | /// Начало массива | |||
|  | 23 | /// </summary> | |||
|  | 24 | BeginArray, | |||
|  | 25 | /// <summary> | |||
|  | 26 | /// Конец массива | |||
|  | 27 | /// </summary> | |||
|  | 28 | EndArray, | |||
|  | 29 | /// <summary> | |||
|  | 30 | /// Строка | |||
|  | 31 | /// </summary> | |||
|  | 32 | String, | |||
|  | 33 | /// <summary> | |||
|  | 34 | /// Число | |||
|  | 35 | /// </summary> | |||
|  | 36 | Number, | |||
|  | 37 | /// <summary> | |||
|  | 38 | /// Литерал | |||
|  | 39 | /// </summary> | |||
|  | 40 | Literal, | |||
|  | 41 | /// <summary> | |||
|  | 42 | /// Разделитель имени <c>:</c> | |||
|  | 43 | /// </summary> | |||
|  | 44 | NameSeparator, | |||
|  | 45 | /// <summary> | |||
|  | 46 | /// Разделитель имени <c>,</c> | |||
|  | 47 | /// </summary> | |||
|  | 48 | ValueSeparator | |||
|  | 49 | } | |||
|  | 50 | } | |||
| @@ -0,0 +1,96 | |||||
|  | 1 | using Implab; | |||
|  | 2 | using Implab.Parsing; | |||
|  | 3 | using System; | |||
|  | 4 | using System.Collections.Generic; | |||
|  | 5 | using System.Diagnostics; | |||
|  | 6 | using System.Linq; | |||
|  | 7 | using System.Text; | |||
|  | 8 | using System.Threading.Tasks; | |||
|  | 9 | ||||
|  | 10 | namespace Implab.JSON { | |||
|  | 11 | /// <summary> | |||
|  | 12 | /// Класс для преобразования экранированной строки JSON | |||
|  | 13 | /// </summary> | |||
|  | 14 | public class StringTranslator : Scanner { | |||
|  | 15 | static readonly char[] _escMap; | |||
|  | 16 | static readonly int[] _hexMap; | |||
|  | 17 | ||||
|  | 18 | static StringTranslator() { | |||
|  | 19 | var chars = new char[] { 'b', 'f', 't', 'r', 'n', '\\', '/' }; | |||
|  | 20 | var vals = new char[] { '\b', '\f', '\t', '\r', '\n', '\\', '/' }; | |||
|  | 21 | ||||
|  | 22 | _escMap = new char[chars.Max() + 1]; | |||
|  | 23 | ||||
|  | 24 | for (int i = 0; i < chars.Length; i++) | |||
|  | 25 | _escMap[chars[i]] = vals[i]; | |||
|  | 26 | ||||
|  | 27 | var hexs = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F' }; | |||
|  | 28 | var ints = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 10, 11, 12, 13, 14, 15 }; | |||
|  | 29 | ||||
|  | 30 | _hexMap = new int[hexs.Max() + 1]; | |||
|  | 31 | ||||
|  | 32 | for (int i = 0; i < hexs.Length; i++) | |||
|  | 33 | _hexMap[hexs[i]] = ints[i]; | |||
|  | 34 | ||||
|  | 35 | } | |||
|  | 36 | ||||
|  | 37 | public StringTranslator() | |||
|  | 38 | : base(JSONGrammar.Instance.JsonStringDFA.States, JSONGrammar.Instance.JsonStringDFA.Alphabet.GetTranslationMap()) { | |||
|  | 39 | } | |||
|  | 40 | ||||
|  | 41 | public string Translate(string data) { | |||
|  | 42 | Safe.ArgumentNotNull(data, "data"); | |||
|  | 43 | return Translate(data.ToCharArray()); | |||
|  | 44 | } | |||
|  | 45 | ||||
|  | 46 | public string Translate(char[] data) { | |||
|  | 47 | Safe.ArgumentNotNull(data, "data"); | |||
|  | 48 | return Translate(data, data.Length); | |||
|  | 49 | } | |||
|  | 50 | ||||
|  | 51 | public string Translate(char[] data, int length) { | |||
|  | 52 | Safe.ArgumentNotNull(data, "data"); | |||
|  | 53 | Safe.ArgumentInRange(length, 0, data.Length, "length"); | |||
|  | 54 | ||||
|  | 55 | var translated = new char[length]; | |||
|  | 56 | ||||
|  | 57 | Feed(data,length); | |||
|  | 58 | ||||
|  | 59 | int pos = 0; | |||
|  | 60 | ||||
|  | 61 | while (ReadTokenInternal()) { | |||
|  | 62 | switch ((JSONGrammar.TokenType)TokenTags[0]) { | |||
|  | 63 | case JSONGrammar.TokenType.UnescapedChar: | |||
|  | 64 | Array.Copy(m_buffer,m_tokenOffset,translated,pos,m_tokenLen); | |||
|  | 65 | pos += m_tokenLen; | |||
|  | 66 | break; | |||
|  | 67 | case JSONGrammar.TokenType.EscapedChar: | |||
|  | 68 | translated[pos] = _escMap[m_buffer[m_tokenOffset + 1]]; | |||
|  | 69 | pos++; | |||
|  | 70 | break; | |||
|  | 71 | case JSONGrammar.TokenType.EscapedUnicode: | |||
|  | 72 | translated[pos] = TranslateHexUnicode(m_buffer,m_tokenOffset + 2); | |||
|  | 73 | pos++; | |||
|  | 74 | break; | |||
|  | 75 | } | |||
|  | 76 | } | |||
|  | 77 | ||||
|  | 78 | return new String(translated, 0, pos); | |||
|  | 79 | } | |||
|  | 80 | ||||
|  | 81 | internal static char TranslateEscapedChar(char symbol) { | |||
|  | 82 | return _escMap[symbol]; | |||
|  | 83 | } | |||
|  | 84 | ||||
|  | 85 | internal static char TranslateHexUnicode(char[] symbols, int offset) { | |||
|  | 86 | Debug.Assert(symbols != null); | |||
|  | 87 | Debug.Assert(symbols.Length - offset >= 4); | |||
|  | 88 | ||||
|  | 89 | int value = (_hexMap[symbols[offset]] << 12) | |||
|  | 90 | | (_hexMap[symbols[offset + 1]] << 8) | |||
|  | 91 | | (_hexMap[symbols[offset + 2]] << 4) | |||
|  | 92 | | (_hexMap[symbols[offset + 3]]); | |||
|  | 93 | return (char)value; | |||
|  | 94 | } | |||
|  | 95 | } | |||
|  | 96 | } | |||
| @@ -9,86 +9,71 namespace Implab.Automaton.RegularExpres | |||||
| 9 | /// <summary> |  | 9 | /// <summary> | |
| 10 | /// Базовый абстрактный класс. Грамматика, позволяет формулировать выражения над алфавитом типа <c>char</c>. |  | 10 | /// Базовый абстрактный класс. Грамматика, позволяет формулировать выражения над алфавитом типа <c>char</c>. | |
| 11 | /// </summary> |  | 11 | /// </summary> | |
| 12 | /// <typeparam name="TGrammar"></typeparam> |  | 12 | public abstract class Grammar<TSymbol, TTag> { | |
| 13 | public abstract class Grammar<TGrammar> where TGrammar: Grammar<TGrammar>, new() { |  | |||
| 14 | static TGrammar _instance; |  | |||
| 15 |  | 13 | |||
| 16 | public static TGrammar Instance{ |  | 14 | public abstract IAlphabetBuilder<TSymbol> Alphabet { | |
| 17 | get |  | 15 | get; | |
| 18 | if (_instance == null) |  | |||
| 19 | _instance = new TGrammar(); |  | |||
| 20 | return _instance; |  | |||
| 21 | } |  | |||
| 22 | } |  | 16 | } | |
| 23 |  | 17 | |||
| 24 | readonly CharAlphabet m_alphabet = new CharAlphabet(); |  | 18 | public SymbolToken<TTag> UnclassifiedToken() { | |
| 25 |  | 19 | return new SymbolToken<TTag>(DFAConst.UNCLASSIFIED_INPUT); | ||
| 26 | public CharAlphabet Alphabet { |  | |||
| 27 | get { return m_alphabet; } |  | |||
| 28 | } |  | 20 | } | |
| 29 |  | 21 | |||
| 30 | public SymbolToken UnclassifiedToken() { |  | 22 | public void DefineAlphabet(IEnumerable<TSymbol> alphabet) { | |
| 31 | return new SymbolToken(CharAlphabet.UNCLASSIFIED); |  | |||
| 32 | } |  | |||
| 33 |  | ||||
| 34 | public void DefineAlphabet(IEnumerable<char> alphabet) { |  | |||
| 35 | Safe.ArgumentNotNull(alphabet, "alphabet"); |  | 23 | Safe.ArgumentNotNull(alphabet, "alphabet"); | |
| 36 |  | 24 | |||
| 37 | foreach (var ch in alphabet) |  | 25 | foreach (var ch in alphabet) | |
| 38 |  |  | 26 | Alphabet.DefineSymbol(ch); | |
| 39 | } |  | 27 | } | |
| 40 | public Token SymbolRangeToken(char start, char end) { |  | 28 | ||
| 41 | return SymbolToken(Enumerable.Range(start, end - start + 1).Select(x => (char)x)); |  | 29 | public Token<TTag> SymbolToken(TSymbol symbol) { | |
|  | 30 | return Token<TTag>.New(TranslateOrAdd(symbol)); | |||
| 42 | } |  | 31 | } | |
| 43 |  | 32 | |||
| 44 | public Token SymbolToken( |  | 33 | public Token<TTag> SymbolToken(IEnumerable<TSymbol> symbols) { | |
| 45 | return Token.New(TranslateOrAdd(symbol)); |  | 34 | Safe.ArgumentNotNull(symbols, "symbols"); | |
|  | 35 | ||||
|  | 36 | return Token<TTag>.New(TranslateOrAdd(symbols).ToArray()); | |||
| 46 | } |  | 37 | } | |
| 47 |  | 38 | |||
| 48 | public Token |  | 39 | public Token<TTag> SymbolSetToken(params TSymbol[] set) { | |
| 49 | Safe.ArgumentNotNull(symbols, "symbols"); |  | |||
| 50 |  | ||||
| 51 | return Token.New(TranslateOrAdd(symbols).ToArray()); |  | |||
| 52 | } |  | |||
| 53 |  | ||||
| 54 | public Token SymbolSetToken(params char[] set) { |  | |||
| 55 | return SymbolToken(set); |  | 40 | return SymbolToken(set); | |
| 56 | } |  | 41 | } | |
| 57 |  | 42 | |||
| 58 | int TranslateOrAdd( |  | 43 | int TranslateOrAdd(TSymbol ch) { | |
| 59 | var t = |  | 44 | var t = Alphabet.Translate(ch); | |
| 60 | if (t == |  | 45 | if (t == DFAConst.UNCLASSIFIED_INPUT) | |
| 61 | t = |  | 46 | t = Alphabet.DefineSymbol(ch); | |
| 62 | return t; |  | 47 | return t; | |
| 63 | } |  | 48 | } | |
| 64 |  | 49 | |||
| 65 | IEnumerable<int> TranslateOrAdd(IEnumerable< |  | 50 | IEnumerable<int> TranslateOrAdd(IEnumerable<TSymbol> symbols) { | |
| 66 | return symbols.Distinct().Select(TranslateOrAdd); |  | 51 | return symbols.Distinct().Select(TranslateOrAdd); | |
| 67 | } |  | 52 | } | |
| 68 |  | 53 | |||
| 69 | int TranslateOrDie( |  | 54 | int TranslateOrDie(TSymbol ch) { | |
| 70 | var t = |  | 55 | var t = Alphabet.Translate(ch); | |
| 71 |  |  | 56 | if (t == DFAConst.UNCLASSIFIED_INPUT) | |
| 72 | throw new ApplicationException(String.Format("Symbol '{0}' is UNCLASSIFIED", ch)); |  | 57 | throw new ApplicationException(String.Format("Symbol '{0}' is UNCLASSIFIED", ch)); | |
| 73 | return t; |  | 58 | return t; | |
| 74 | } |  | 59 | } | |
| 75 |  | 60 | |||
| 76 | IEnumerable<int> TranslateOrDie(IEnumerable< |  | 61 | IEnumerable<int> TranslateOrDie(IEnumerable<TSymbol> symbols) { | |
| 77 | return symbols.Distinct().Select(TranslateOrDie); |  | 62 | return symbols.Distinct().Select(TranslateOrDie); | |
| 78 | } |  | 63 | } | |
| 79 |  | 64 | |||
| 80 | public Token SymbolTokenExcept(IEnumerable< |  | 65 | public Token<TTag> SymbolTokenExcept(IEnumerable<TSymbol> symbols) { | |
| 81 | Safe.ArgumentNotNull(symbols, "symbols"); |  | 66 | Safe.ArgumentNotNull(symbols, "symbols"); | |
| 82 |  | 67 | |||
| 83 | return Token.New( Enumerable.Range(0, |  | 68 | return Token<TTag>.New( Enumerable.Range(0, Alphabet.Count).Except(TranslateOrDie(symbols)).ToArray() ); | |
| 84 | } |  | 69 | } | |
| 85 |  | 70 | |||
| 86 | protected CDFADefinition BuildDFA(Token lang) { |  | 71 | protected CDFADefinition BuildDFA(Token<TTag> lang) { | |
| 87 | Safe.ArgumentNotNull(lang, "lang"); |  | 72 | Safe.ArgumentNotNull(lang, "lang"); | |
| 88 |  | 73 | |||
| 89 | var dfa = new CDFADefinition( |  | 74 | var dfa = new CDFADefinition(Alphabet); | |
| 90 |  | 75 | |||
| 91 | var builder = new DFABuilder(); |  | 76 | var builder = new RegularDFABuilder<TTag>(); | |
| 92 |  | 77 | |||
| 93 | lang.Accept( builder ); |  | 78 | lang.Accept( builder ); | |
| 94 |  | 79 | |||
| @@ -88,16 +88,6 | |||||
| 88 | <Compile Include="IPromise.cs" /> |  | 88 | <Compile Include="IPromise.cs" /> | |
| 89 | <Compile Include="IServiceLocator.cs" /> |  | 89 | <Compile Include="IServiceLocator.cs" /> | |
| 90 | <Compile Include="ITaskController.cs" /> |  | 90 | <Compile Include="ITaskController.cs" /> | |
| 91 | <Compile Include="JSON\JSONElementContext.cs" /> |  | |||
| 92 | <Compile Include="JSON\JSONElementType.cs" /> |  | |||
| 93 | <Compile Include="JSON\JSONGrammar.cs" /> |  | |||
| 94 | <Compile Include="JSON\JSONParser.cs" /> |  | |||
| 95 | <Compile Include="JSON\JSONScanner.cs" /> |  | |||
| 96 | <Compile Include="JSON\JsonTokenType.cs" /> |  | |||
| 97 | <Compile Include="JSON\JSONWriter.cs" /> |  | |||
| 98 | <Compile Include="JSON\JSONXmlReader.cs" /> |  | |||
| 99 | <Compile Include="JSON\JSONXmlReaderOptions.cs" /> |  | |||
| 100 | <Compile Include="JSON\StringTranslator.cs" /> |  | |||
| 101 | <Compile Include="Parallels\DispatchPool.cs" /> |  | 91 | <Compile Include="Parallels\DispatchPool.cs" /> | |
| 102 | <Compile Include="Parallels\ArrayTraits.cs" /> |  | 92 | <Compile Include="Parallels\ArrayTraits.cs" /> | |
| 103 | <Compile Include="Parallels\MTQueue.cs" /> |  | 93 | <Compile Include="Parallels\MTQueue.cs" /> | |
| @@ -183,9 +173,21 | |||||
| 183 | <Compile Include="Automaton\RegularExpressions\EndToken.cs" /> |  | 173 | <Compile Include="Automaton\RegularExpressions\EndToken.cs" /> | |
| 184 | <Compile Include="Automaton\RegularExpressions\Token.cs" /> |  | 174 | <Compile Include="Automaton\RegularExpressions\Token.cs" /> | |
| 185 | <Compile Include="Automaton\RegularExpressions\IVisitor.cs" /> |  | 175 | <Compile Include="Automaton\RegularExpressions\IVisitor.cs" /> | |
| 186 | <Compile Include="Automaton\RegularExpressions\DFABuilder.cs" /> |  | |||
| 187 | <Compile Include="Automaton\AutomatonTransition.cs" /> |  | 176 | <Compile Include="Automaton\AutomatonTransition.cs" /> | |
| 188 | <Compile Include="Automaton\ByteAlphabet.cs" /> |  | 177 | <Compile Include="Automaton\ByteAlphabet.cs" /> | |
|  | 178 | <Compile Include="Automaton\RegularExpressions\RegularDFABuilder.cs" /> | |||
|  | 179 | <Compile Include="Formats\JSON\JSONElementContext.cs" /> | |||
|  | 180 | <Compile Include="Formats\JSON\JSONElementType.cs" /> | |||
|  | 181 | <Compile Include="Formats\JSON\JSONGrammar.cs" /> | |||
|  | 182 | <Compile Include="Formats\JSON\JSONParser.cs" /> | |||
|  | 183 | <Compile Include="Formats\JSON\JSONScanner.cs" /> | |||
|  | 184 | <Compile Include="Formats\JSON\JsonTokenType.cs" /> | |||
|  | 185 | <Compile Include="Formats\JSON\JSONWriter.cs" /> | |||
|  | 186 | <Compile Include="Formats\JSON\JSONXmlReader.cs" /> | |||
|  | 187 | <Compile Include="Formats\JSON\JSONXmlReaderOptions.cs" /> | |||
|  | 188 | <Compile Include="Formats\JSON\StringTranslator.cs" /> | |||
|  | 189 | <Compile Include="Automaton\MapAlphabet.cs" /> | |||
|  | 190 | <Compile Include="Automaton\DummyAlphabet.cs" /> | |||
| 189 | </ItemGroup> |  | 191 | </ItemGroup> | |
| 190 | <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> |  | 192 | <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> | |
| 191 | <ItemGroup /> |  | 193 | <ItemGroup /> | |
| @@ -261,5 +263,7 | |||||
| 261 | <ItemGroup> |  | 263 | <ItemGroup> | |
| 262 | <Folder Include="Components\" /> |  | 264 | <Folder Include="Components\" /> | |
| 263 | <Folder Include="Automaton\RegularExpressions\" /> |  | 265 | <Folder Include="Automaton\RegularExpressions\" /> | |
|  | 266 | <Folder Include="Formats\" /> | |||
|  | 267 | <Folder Include="Formats\JSON\" /> | |||
| 264 | </ItemGroup> |  | 268 | </ItemGroup> | |
| 265 | </Project> No newline at end of file |  | 269 | </Project> | |
| 1 | NO CONTENT: file was removed |  | NO CONTENT: file was removed | 
| 1 | NO CONTENT: file was removed |  | NO CONTENT: file was removed | 
| 1 | NO CONTENT: file was removed |  | NO CONTENT: file was removed | 
| 1 | NO CONTENT: file was removed |  | NO CONTENT: file was removed | 
| 1 | NO CONTENT: file was removed |  | NO CONTENT: file was removed | 
| 1 | NO CONTENT: file was removed |  | NO CONTENT: file was removed | 
| 1 | NO CONTENT: file was removed |  | NO CONTENT: file was removed | 
| 1 | NO CONTENT: file was removed |  | NO CONTENT: file was removed | 
| 1 | NO CONTENT: file was removed |  | NO CONTENT: file was removed | 
| 1 | NO CONTENT: file was removed |  | NO CONTENT: file was removed | 
| 1 | NO CONTENT: file was removed |  | NO CONTENT: file was removed | 
        
        General Comments 0
    
    
  
  
                      You need to be logged in to leave comments.
                      Login now
                    
                