@@ -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