##// END OF EJS Templates
Слияние с ref20160224
cin -
r190:1c2a16d071a7 merge v2
parent child
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,195
1 using System;
2 using System.Reflection;
3 using System.Threading;
4 using Implab.Parallels;
5 using Implab.Components;
6
7 #if MONO
8
9 using NUnit.Framework;
10 using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
11 using TestMethodAttribute = NUnit.Framework.TestAttribute;
12 using AssertFailedException = NUnit.Framework.AssertionException;
13 #else
14
15 using Microsoft.VisualStudio.TestTools.UnitTesting;
16
17 #endif
18
19 namespace Implab.Test {
20 [TestClass]
21 public class RunnableComponentTests {
22
23 static void ShouldThrow(Action action) {
24 try {
25 action();
26 Assert.Fail();
27 } catch (AssertFailedException) {
28 throw;
29 } catch {
30 }
31 }
32
33 class Runnable : RunnableComponent {
34 public Runnable(bool initialized) : base(initialized) {
35 }
36
37 public Action MockInit {
38 get;
39 set;
40 }
41
42 public Func<IPromise> MockStart {
43 get;
44 set;
45 }
46
47 public Func<IPromise> MockStop {
48 get;
49 set;
50 }
51
52 protected override IPromise OnStart() {
53 return MockStart != null ? MockStart() : base.OnStart();
54 }
55
56 protected override IPromise OnStop() {
57 return MockStop != null ? MockStop() : base.OnStart();
58 }
59
60 protected override void OnInitialize() {
61 if (MockInit != null)
62 MockInit();
63 }
64 }
65
66 [TestMethod]
67 public void NormalFlowTest() {
68 var comp = new Runnable(false);
69
70 Assert.AreEqual(ExecutionState.Created, comp.State);
71
72 comp.Init();
73
74 Assert.AreEqual(ExecutionState.Ready, comp.State);
75
76 comp.Start().Join(1000);
77
78 Assert.AreEqual(ExecutionState.Running, comp.State);
79
80 comp.Stop().Join(1000);
81
82 Assert.AreEqual(ExecutionState.Disposed, comp.State);
83
84 }
85
86 [TestMethod]
87 public void InitFailTest() {
88 var comp = new Runnable(false) {
89 MockInit = () => {
90 throw new Exception("BAD");
91 }
92 };
93
94 ShouldThrow(() => comp.Start());
95 ShouldThrow(() => comp.Stop());
96 Assert.AreEqual(ExecutionState.Created, comp.State);
97
98 ShouldThrow(comp.Init);
99
100 Assert.AreEqual(ExecutionState.Failed, comp.State);
101
102 ShouldThrow(() => comp.Start());
103 ShouldThrow(() => comp.Stop());
104 Assert.AreEqual(ExecutionState.Failed, comp.State);
105
106 comp.Dispose();
107 Assert.AreEqual(ExecutionState.Disposed, comp.State);
108 }
109
110 [TestMethod]
111 public void DisposedTest() {
112
113 var comp = new Runnable(false);
114 comp.Dispose();
115
116 ShouldThrow(() => comp.Start());
117 ShouldThrow(() => comp.Stop());
118 ShouldThrow(comp.Init);
119
120 Assert.AreEqual(ExecutionState.Disposed, comp.State);
121 }
122
123 [TestMethod]
124 public void StartCancelTest() {
125 var comp = new Runnable(true) {
126 MockStart = () => PromiseHelper.Sleep(100000, 0)
127 };
128
129 var p = comp.Start();
130 Assert.AreEqual(ExecutionState.Starting, comp.State);
131 p.Cancel();
132 ShouldThrow(() => p.Join(1000));
133 Assert.AreEqual(ExecutionState.Failed, comp.State);
134
135 Assert.IsInstanceOfType(comp.LastError, typeof(OperationCanceledException));
136
137 comp.Dispose();
138 }
139
140 [TestMethod]
141 public void StartStopTest() {
142 var stop = new Signal();
143 var comp = new Runnable(true) {
144 MockStart = () => PromiseHelper.Sleep(100000, 0),
145 MockStop = () => AsyncPool.RunThread(stop.Wait)
146 };
147
148 var p1 = comp.Start();
149 var p2 = comp.Stop();
150 // should enter stopping state
151
152 ShouldThrow(p1.Join);
153 Assert.IsTrue(p1.IsCancelled);
154 Assert.AreEqual(ExecutionState.Stopping, comp.State);
155
156 stop.Set();
157 p2.Join(1000);
158 Assert.AreEqual(ExecutionState.Disposed, comp.State);
159 }
160
161 [TestMethod]
162 public void StartStopFailTest() {
163 var comp = new Runnable(true) {
164 MockStart = () => PromiseHelper.Sleep(100000, 0).Then(null,null,x => { throw new Exception("I'm dead"); })
165 };
166
167 comp.Start();
168 var p = comp.Stop();
169 // if Start fails to cancel, should fail to stop
170 ShouldThrow(() => p.Join(1000));
171 Assert.AreEqual(ExecutionState.Failed, comp.State);
172 Assert.IsNotNull(comp.LastError);
173 Assert.AreEqual("I'm dead", comp.LastError.Message);
174 }
175
176 [TestMethod]
177 public void StopCancelTest() {
178 var comp = new Runnable(true) {
179 MockStop = () => PromiseHelper.Sleep(100000, 0)
180 };
181
182 comp.Start();
183 var p = comp.Stop();
184 Assert.AreEqual(ExecutionState.Stopping, comp.State);
185 p.Cancel();
186 ShouldThrow(() => p.Join(1000));
187 Assert.AreEqual(ExecutionState.Failed, comp.State);
188 Assert.IsInstanceOfType(comp.LastError, typeof(OperationCanceledException));
189
190 comp.Dispose();
191 }
192
193 }
194 }
195
@@ -0,0 +1,45
1 using System;
2 using System.Threading;
3
4 namespace Implab {
5 /// <summary>
6 /// Базовый класс для реализации задачь. Задача представляет собой некторое
7 /// действие, которое можно иницировать и обработать результат его выполнения
8 /// в виде обещания, для этого оно реализует интерфейс <see cref="IPromise"/>.
9 /// </summary>
10 /// <remarks>
11 /// Данный класс определяет стандартное поведение при обработки результатов, в частности
12 /// обработку <see cref="System.OperationCanceledException"/> и <see cref="PromiseTransientException"/>
13 /// </remarks>
14 public abstract class AbstractTask : AbstractPromise {
15 int m_cancelationLock;
16
17 /// <summary>
18 /// Получает эксклюзивное право отмены задания, используется для отмены задания до начала его выполнения.
19 /// </summary>
20 /// <returns><c>true</c>, if cancelation was locked, <c>false</c> otherwise.</returns>
21 protected bool LockCancelation() {
22 return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0);
23 }
24
25
26
27 protected void SetErrorInternal(Exception error) {
28 // unwrap
29 while (error is PromiseTransientException && error.InnerException != null)
30 error = error.InnerException;
31
32 if (error is OperationCanceledException)
33 SetCancelled(error);
34 else
35 SetError(error);
36 }
37
38 protected void SetCancelledInternal(Exception reason) {
39 SetCancelled(
40 reason == null ? new OperationCanceledException() : reason is OperationCanceledException ? reason : new OperationCanceledException(null, reason)
41 );
42 }
43 }
44 }
45
@@ -0,0 +1,36
1 using System;
2 using System.Threading;
3
4 namespace Implab {
5 public abstract class AbstractTask<T> : AbstractPromise<T> {
6 int m_cancelationLock;
7
8 /// <summary>
9 /// Получает эксклюзивное право отмены задания, используется для отмены задания до начала его выполнения.
10 /// </summary>
11 /// <returns><c>true</c>, if cancelation was locked, <c>false</c> otherwise.</returns>
12 protected bool LockCancelation() {
13 return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0);
14 }
15
16
17
18 protected void SetErrorInternal(Exception error) {
19 // unwrap
20 while (error is PromiseTransientException && error.InnerException != null)
21 error = error.InnerException;
22
23 if (error is OperationCanceledException)
24 SetCancelled(error);
25 else
26 SetError(error);
27 }
28
29 protected void SetCancelledInternal(Exception reason) {
30 SetCancelled(
31 reason == null ? new OperationCanceledException() : reason is OperationCanceledException ? reason : new OperationCanceledException(null, reason)
32 );
33 }
34 }
35 }
36
@@ -0,0 +1,9
1
2 namespace Implab.Automaton {
3 public static class AutomatonConst {
4 public const int UNREACHABLE_STATE = -1;
5
6 public const int UNCLASSIFIED_INPUT = 0;
7 }
8 }
9
@@ -0,0 +1,33
1 using System;
2
3 namespace Implab.Automaton {
4 public struct AutomatonTransition : IEquatable<AutomatonTransition> {
5 public readonly int s1;
6 public readonly int s2;
7 public readonly int edge;
8
9 public AutomatonTransition(int s1, int s2, int edge) {
10 this.s1 = s1;
11 this.s2 = s2;
12 this.edge = edge;
13 }
14
15
16 #region IEquatable implementation
17 public bool Equals(AutomatonTransition other) {
18 return other.s1 == s1 && other.s2 == s2 && other.edge == edge ;
19 }
20 #endregion
21
22 public override bool Equals(object obj) {
23 if (obj is AutomatonTransition)
24 return Equals((AutomatonTransition)obj);
25 return base.Equals(obj);
26 }
27
28 public override int GetHashCode() {
29 return s1 + s2 + edge;
30 }
31 }
32 }
33
@@ -0,0 +1,348
1 using Implab;
2 using System;
3 using System.Collections.Generic;
4 using System.Linq;
5 using System.Diagnostics;
6 using System.IO;
7 using System.CodeDom.Compiler;
8 using System.CodeDom;
9
10 namespace Implab.Automaton {
11 public class DFATable : IDFATableBuilder {
12 int m_stateCount;
13 int m_symbolCount;
14 int m_initialState;
15
16 readonly HashSet<int> m_finalStates = new HashSet<int>();
17 readonly HashSet<AutomatonTransition> m_transitions = new HashSet<AutomatonTransition>();
18
19
20 #region IDFADefinition implementation
21
22 public bool IsFinalState(int s) {
23 Safe.ArgumentInRange(s, 0, m_stateCount, "s");
24
25 return m_finalStates.Contains(s);
26 }
27
28 public IEnumerable<int> FinalStates {
29 get {
30 return m_finalStates;
31 }
32 }
33
34 public int StateCount {
35 get { return m_stateCount; }
36 }
37
38 public int AlphabetSize {
39 get { return m_symbolCount; }
40 }
41
42 public int InitialState {
43 get { return m_initialState; }
44 }
45
46 #endregion
47
48 public void SetInitialState(int s) {
49 Safe.ArgumentAssert(s >= 0, "s");
50 m_stateCount = Math.Max(m_stateCount, s + 1);
51 m_initialState = s;
52 }
53
54 public void MarkFinalState(int state) {
55 m_stateCount = Math.Max(m_stateCount, state + 1);
56 m_finalStates.Add(state);
57 }
58
59 public void Add(AutomatonTransition item) {
60 Safe.ArgumentAssert(item.s1 >= 0, "item");
61 Safe.ArgumentAssert(item.s2 >= 0, "item");
62 Safe.ArgumentAssert(item.edge >= 0, "item");
63
64 m_stateCount = Math.Max(m_stateCount, Math.Max(item.s1, item.s2) + 1);
65 m_symbolCount = Math.Max(m_symbolCount, item.edge + 1);
66
67 m_transitions.Add(item);
68 }
69
70 public void Clear() {
71 m_stateCount = 0;
72 m_symbolCount = 0;
73 m_finalStates.Clear();
74 m_transitions.Clear();
75 }
76
77 public bool Contains(AutomatonTransition item) {
78 return m_transitions.Contains(item);
79 }
80
81 public void CopyTo(AutomatonTransition[] array, int arrayIndex) {
82 m_transitions.CopyTo(array, arrayIndex);
83 }
84
85 public bool Remove(AutomatonTransition item) {
86 return m_transitions.Remove(item);
87 }
88
89 public int Count {
90 get {
91 return m_transitions.Count;
92 }
93 }
94
95 public bool IsReadOnly {
96 get {
97 return false;
98 }
99 }
100
101 public IEnumerator<AutomatonTransition> GetEnumerator() {
102 return m_transitions.GetEnumerator();
103 }
104
105 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
106 return GetEnumerator();
107 }
108
109 public void AddSymbol(int symbol) {
110 Safe.ArgumentAssert(symbol >= 0, "symbol");
111 m_symbolCount = Math.Max(symbol + 1, m_symbolCount);
112 }
113
114 public int[,] CreateTransitionTable() {
115 var table = new int[StateCount,AlphabetSize];
116
117 for (int i = 0; i < StateCount; i++)
118 for (int j = 0; j < AlphabetSize; j++)
119 table[i, j] = AutomatonConst.UNREACHABLE_STATE;
120
121 foreach (var t in this)
122 table[t.s1,t.edge] = t.s2;
123
124 return table;
125 }
126
127 public bool[] CreateFinalStateTable() {
128 var table = new bool[StateCount];
129
130 foreach (var s in FinalStates)
131 table[s] = true;
132
133 return table;
134 }
135
136 /// <summary>Формирует множества конечных состояний перед началом работы алгоритма минимизации.</summary>
137 /// <remarks>
138 /// В процессе построения минимального автомата требуется разделить множество состояний,
139 /// на два подмножества - конечные состояния и все остальные, после чего эти подмножества
140 /// будут резделены на более мелкие. Иногда требуется гарантировать различия конечных сосотяний,
141 /// для этого необходимо переопределить даннцю фукнцию, для получения множеств конечных состояний.
142 /// </remarks>
143 /// <returns>The final states.</returns>
144 protected virtual IEnumerable<HashSet<int>> SplitFinalStates(IEnumerable<int> states) {
145 return new [] { new HashSet<int>(states) };
146 }
147
148 protected void Optimize(
149 IDFATableBuilder optimalDFA,
150 IDictionary<int,int> alphabetMap,
151 IDictionary<int,int> stateMap
152 ) {
153 Safe.ArgumentNotNull(optimalDFA, "dfa");
154 Safe.ArgumentNotNull(alphabetMap, "alphabetMap");
155 Safe.ArgumentNotNull(stateMap, "stateMap");
156
157
158 var setComparer = new CustomEqualityComparer<HashSet<int>>(
159 (x, y) => x.SetEquals(y),
160 s => s.Sum(x => x.GetHashCode())
161 );
162
163 var optimalStates = new HashSet<HashSet<int>>(setComparer);
164 var queue = new HashSet<HashSet<int>>(setComparer);
165
166 optimalStates.Add(new HashSet<int>(FinalStates));
167
168 var state = new HashSet<int>(
169 Enumerable
170 .Range(0, m_stateCount)
171 .Where(i => !m_finalStates.Contains(i))
172 );
173
174 optimalStates.Add(state);
175 queue.Add(state);
176
177 var rmap = m_transitions
178 .GroupBy(t => t.s2)
179 .ToDictionary(
180 g => g.Key, // s2
181 g => g.ToLookup(t => t.edge, t => t.s1)//.ToDictionary(p => p.Key)
182 );
183
184 while (queue.Count > 0) {
185 var stateA = queue.First();
186 queue.Remove(stateA);
187
188 for (int c = 0; c < m_symbolCount; c++) {
189 var stateX = new HashSet<int>();
190 foreach(var a in stateA.Where(rmap.ContainsKey))
191 stateX.UnionWith(rmap[a][c]); // all states from wich the symbol 'c' leads to the state 'a'
192
193 var tmp = optimalStates.ToArray();
194 foreach (var stateY in tmp) {
195 var stateR1 = new HashSet<int>(stateY);
196 var stateR2 = new HashSet<int>(stateY);
197
198 stateR1.IntersectWith(stateX);
199 stateR2.ExceptWith(stateX);
200
201 if (stateR1.Count > 0 && stateR2.Count > 0) {
202
203
204 optimalStates.Remove(stateY);
205 optimalStates.Add(stateR1);
206 optimalStates.Add(stateR2);
207
208 if (queue.Contains(stateY)) {
209 queue.Remove(stateY);
210 queue.Add(stateR1);
211 queue.Add(stateR2);
212 } else {
213 queue.Add(stateR1.Count <= stateR2.Count ? stateR1 : stateR2);
214 }
215 }
216 }
217 }
218 }
219
220 // дополнительно разбиваем конечные состояния
221 foreach (var final in optimalStates.Where(s => s.Overlaps(m_finalStates)).ToArray()) {
222 optimalStates.Remove(final);
223 foreach (var split in SplitFinalStates(final))
224 optimalStates.Add(split);
225 }
226
227
228 // карта получения оптимального состояния по соотвествующему ему простому состоянию
229 var nextState = 0;
230 foreach (var item in optimalStates) {
231 var id = nextState++;
232 foreach (var s in item)
233 stateMap[s] = id;
234 }
235
236 // получаем минимальный алфавит
237 // входные символы не различимы, если Move(s,a1) == Move(s,a2), для любого s
238 // для этого используем алгоритм кластеризации, сначала
239 // считаем, что все символы не различимы
240
241 var minClasses = new HashSet<HashSet<int>>(setComparer);
242 var alphaQueue = new Queue<HashSet<int>>();
243 alphaQueue.Enqueue(new HashSet<int>(Enumerable.Range(0,AlphabetSize)));
244
245 // для всех состояний, будем проверять каждый класс на различимость,
246 // т.е. символы различимы, если они приводят к разным состояниям
247 for (int s = 0 ; s < optimalStates.Count; s++) {
248 var newQueue = new Queue<HashSet<int>>();
249
250 foreach (var A in alphaQueue) {
251 // классы из одного символа делить бесполезно, переводим их сразу в
252 // результирующий алфавит
253 if (A.Count == 1) {
254 minClasses.Add(A);
255 continue;
256 }
257
258 // различаем классы символов, которые переводят в различные оптимальные состояния
259 // optimalState -> alphaClass
260 var classes = new Dictionary<int, HashSet<int>>();
261
262 foreach (var term in A) {
263 // ищем все переходы класса по символу term
264 var s2 = m_transitions.Where(t => stateMap[t.s1] == s && t.edge == term).Select(t => stateMap[t.s2]).DefaultIfEmpty(-1).First();
265
266 HashSet<int> a2;
267 if (!classes.TryGetValue(s2, out a2)) {
268 a2 = new HashSet<int>();
269 newQueue.Enqueue(a2);
270 classes[s2] = a2;
271 }
272 a2.Add(term);
273 }
274 }
275
276 if (newQueue.Count == 0)
277 break;
278 alphaQueue = newQueue;
279 }
280
281 // после окончания работы алгоритма в очереди останутся минимальные различимые классы
282 // входных символов
283 foreach (var A in alphaQueue)
284 minClasses.Add(A);
285
286 // построение отображения алфавитов входных символов.
287 // поскольку символ DFAConst.UNCLASSIFIED_INPUT может иметь
288 // специальное значение, тогда сохраним минимальный класс,
289 // содержащий этот символ на томже месте.
290
291 var nextCls = 0;
292 foreach (var item in minClasses) {
293 if (nextCls == AutomatonConst.UNCLASSIFIED_INPUT)
294 nextCls++;
295
296 // сохраняем DFAConst.UNCLASSIFIED_INPUT
297 var cls = item.Contains(AutomatonConst.UNCLASSIFIED_INPUT) ? AutomatonConst.UNCLASSIFIED_INPUT : nextCls++;
298 optimalDFA.AddSymbol(cls);
299
300 foreach (var a in item)
301 alphabetMap[a] = cls;
302 }
303
304 // построение автомата
305 optimalDFA.SetInitialState(stateMap[m_initialState]);
306
307 foreach (var sf in m_finalStates.Select(s => stateMap[s]).Distinct())
308 optimalDFA.MarkFinalState(sf);
309
310 foreach (var t in m_transitions.Select(t => new AutomatonTransition(stateMap[t.s1],stateMap[t.s2],alphabetMap[t.edge])).Distinct())
311 optimalDFA.Add(t);
312 }
313
314 protected string PrintDFA<TInput, TState>(IAlphabet<TInput> inputAlphabet, IAlphabet<TState> stateAlphabet) {
315 Safe.ArgumentNotNull(inputAlphabet, "inputAlphabet");
316 Safe.ArgumentNotNull(stateAlphabet, "stateAlphabet");
317
318 var data = new List<string>();
319
320 data.Add("digraph dfa {");
321
322 foreach (var final in m_finalStates)
323 data.Add(String.Format("{0} [shape=box];",String.Join("", stateAlphabet.GetSymbols(final))));
324
325 foreach (var t in m_transitions)
326 data.Add(String.Format(
327 "{0} -> {2} [label={1}];",
328 String.Join("", stateAlphabet.GetSymbols(t.s1)),
329 ToLiteral(ToLiteral(String.Join("", t.edge == AutomatonConst.UNCLASSIFIED_INPUT ? new [] { "@" } : inputAlphabet.GetSymbols(t.edge).Select(x => x.ToString())))),
330 String.Join("", stateAlphabet.GetSymbols(t.s2))
331 ));
332 data.Add("}");
333 return String.Join("\n", data);
334 }
335
336 static string ToLiteral(string input)
337 {
338 using (var writer = new StringWriter())
339 {
340 using (var provider = CodeDomProvider.CreateProvider("CSharp"))
341 {
342 provider.GenerateCodeFromExpression(new CodePrimitiveExpression(input), writer, null);
343 return writer.ToString();
344 }
345 }
346 }
347 }
348 }
@@ -0,0 +1,66
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.Globalization;
5 using System.Linq;
6 using System.Diagnostics.CodeAnalysis;
7
8 namespace Implab.Automaton {
9 /// <summary>
10 /// Алфавит символами которого являются элементы перечислений.
11 /// </summary>
12 /// <typeparam name="T">Тип перечислений</typeparam>
13 public class EnumAlphabet<T> : IndexedAlphabetBase<T> where T : struct, IConvertible {
14 [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
15 static readonly Lazy<T[]> _symbols = new Lazy<T[]>(GetSymbols);
16
17 [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
18 static readonly Lazy<EnumAlphabet<T>> _fullAlphabet = new Lazy<EnumAlphabet<T>>(CreateEnumAlphabet);
19
20 static EnumAlphabet<T> CreateEnumAlphabet() {
21 var symbols = _symbols.Value;
22
23 if (
24 symbols[symbols.Length - 1].ToInt32(CultureInfo.InvariantCulture) >= symbols.Length
25 || symbols[0].ToInt32(CultureInfo.InvariantCulture) != 0
26 )
27 throw new InvalidOperationException("The specified enumeration must be zero-based and continuously numbered");
28
29 return new EnumAlphabet<T>(symbols.Select(x => x.ToInt32(CultureInfo.InvariantCulture)).ToArray());
30 }
31
32 static T[] GetSymbols() {
33 if (!typeof(T).IsEnum)
34 throw new InvalidOperationException("Invalid generic parameter, enumeration is required");
35
36 if (Enum.GetUnderlyingType(typeof(T)) != typeof(Int32))
37 throw new InvalidOperationException("Only enums based on Int32 are supported");
38
39 return ((T[])Enum.GetValues(typeof(T)))
40 .OrderBy(x => x.ToInt32(CultureInfo.InvariantCulture))
41 .ToArray();
42 }
43
44 public static EnumAlphabet<T> FullAlphabet {
45 get {
46 return _fullAlphabet.Value;
47 }
48 }
49
50
51 public EnumAlphabet()
52 : base(_symbols.Value.Length) {
53 }
54
55 public EnumAlphabet(int[] map)
56 : base(map) {
57 Debug.Assert(map.Length == _symbols.Value.Length);
58 }
59
60
61 public override int GetSymbolIndex(T symbol) {
62 return symbol.ToInt32(CultureInfo.InvariantCulture);
63 }
64
65 }
66 }
@@ -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.Automaton {
8 /// <summary>
9 /// Алфавит. Множество символов, которые разбиты на классы, при этом классы имеют непрерывную нумерацию,
10 /// что позволяет использовать их в качестве индексов массивов.
11 /// </summary>
12 /// <remarks>
13 /// <para>Алфавит является сюрьективным отображением множества символов в множество индексов, это позволяет сократить размер таблицы переходов автомата
14 /// для входных символов, которые для него не различимы.</para>
15 /// </remarks>
16 /// <typeparam name="TSymbol">Тип символов.</typeparam>
17 public interface IAlphabet<TSymbol> {
18 /// <summary>
19 /// Количество классов символов в алфавите.
20 /// </summary>
21 int Count { get; }
22
23 /// <summary>
24 /// Преобразует входной символ в индекс символа из алфавита.
25 /// </summary>
26 /// <param name="symobl">Исходный символ</param>
27 /// <returns>Индекс в алфавите</returns>
28 int Translate(TSymbol symobl);
29
30 bool Contains(TSymbol symbol);
31
32 IEnumerable<TSymbol> GetSymbols(int cls);
33 }
34 }
@@ -0,0 +1,26
1
2 using System.Collections.Generic;
3
4 namespace Implab.Automaton {
5 public interface IAlphabetBuilder<TSymbol> : IAlphabet<TSymbol> {
6 /// <summary>
7 /// Добавляет новый символ в алфавит, если символ уже был добавлен, то
8 /// возвращается ранее сопоставленный с символом класс.
9 /// </summary>
10 /// <param name="symbol">Символ для добавления.</param>
11 /// <returns>Индекс класса, который попоставлен с символом.</returns>
12 int DefineSymbol(TSymbol symbol);
13
14 int DefineSymbol(TSymbol symbol, int cls);
15 /// <summary>
16 /// Доабвляем класс символов. Множеству указанных исходных символов
17 /// будет сопоставлен символ в алфавите.
18 /// </summary>
19 /// <param name="symbols">Множестов исходных символов</param>
20 /// <returns>Идентификатор символа алфавита.</returns>
21 int DefineClass(IEnumerable<TSymbol> symbols);
22
23 int DefineClass(IEnumerable<TSymbol> symbols, int cls);
24 }
25 }
26
@@ -0,0 +1,53
1 using System.Collections.Generic;
2
3
4 namespace Implab.Automaton {
5 /// <summary>
6 /// Полностью описывает DFA автомат, его поведение, состояние и входные символы.
7 /// </summary>
8 /// <example>
9 /// class MyAutomaton {
10 /// int m_current;
11 /// readonly DFAStateDescriptor<string>[] m_automaton;
12 /// readonly IAlphabet<MyCommands> m_commands;
13 ///
14 /// public MyAutomaton(IDFADefinition&lt;MyCommands,MyStates,string&gt; definition) {
15 /// m_current = definition.StateAlphabet.Translate(MyStates.Initial);
16 /// m_automaton = definition.GetTransitionTable();
17 /// m_commands = definition.InputAlphabet;
18 /// }
19 ///
20 /// // defined a method which will move the automaton to the next state
21 /// public void Move(MyCommands cmd) {
22 /// // use transition map to determine the next state
23 /// var next = m_automaton[m_current].transitions[m_commands.Translate(cmd)];
24 ///
25 /// // validate that we aren't in the unreachable state
26 /// if (next == DFAConst.UNREACHABLE_STATE)
27 /// throw new InvalidOperationException("The specified command is invalid");
28 ///
29 /// // if everything is ok
30 /// m_current = next;
31 /// }
32 /// }
33 /// </example>
34 public interface IDFATable : IEnumerable<AutomatonTransition> {
35 int StateCount {
36 get;
37 }
38
39 int AlphabetSize {
40 get;
41 }
42
43 int InitialState {
44 get;
45 }
46
47 bool IsFinalState(int s);
48
49 IEnumerable<int> FinalStates {
50 get;
51 }
52 }
53 }
@@ -0,0 +1,26
1 using System;
2 using System.Collections.Generic;
3
4 namespace Implab.Automaton {
5 public interface IDFATableBuilder : IDFATable, ICollection<AutomatonTransition> {
6 /// <summary>
7 /// Marks the state as final.
8 /// </summary>
9 /// <param name="state">State.</param>
10 void MarkFinalState(int state);
11
12 void SetInitialState(int s);
13
14 /// <summary>
15 /// Increases if needed the input alphabet size to hold the specified symbol.
16 /// </summary>
17 /// <remarks>
18 /// <code>
19 /// AlphabetSize = Math.Max(AlphabetSize, symbol + 1)
20 /// </code>
21 /// </remarks>
22 /// <param name="symbol">Symbol.</param>
23 void AddSymbol(int symbol);
24 }
25 }
26
@@ -0,0 +1,50
1 using Implab;
2 using System;
3 using System.Collections.Generic;
4 using System.Diagnostics;
5 using System.Linq;
6
7 namespace Implab.Automaton {
8 /// <summary>
9 /// Indexed alphabet is the finite set of symbols where each symbol has a zero-based unique index.
10 /// </summary>
11 /// <remarks>
12 /// Indexed alphabets are usefull in bulting efficient translations from source alphabet
13 /// to the input alphabet of the automaton. It's assumed that the index to the symbol match
14 /// is well known and documented.
15 /// </remarks>
16 public abstract class IndexedAlphabetBase<T> : MapAlphabet<T> {
17
18 protected IndexedAlphabetBase() :base(true, null) {
19 }
20
21 public abstract int GetSymbolIndex(T symbol);
22
23 /// <summary>
24 /// Gets the translation map from the index of the symbol to it's class this is usefull for the optimized input symbols transtaion.
25 /// </summary>
26 /// <remarks>
27 /// The map is continous and start from the symbol with zero code. The last symbol
28 /// in the map is the last classified symbol in the alphabet, i.e. the map can be
29 /// shorter then the whole alphabet.
30 /// </remarks>
31 /// <returns>The translation map.</returns>
32 public int[] GetTranslationMap() {
33 var map = new Dictionary<int, int>();
34
35 int max = 0;
36 foreach (var p in Mappings) {
37 var index = GetSymbolIndex(p.Key);
38 max = Math.Max(max, index);
39 map[index] = p.Value;
40 }
41
42 var result = new int[max + 1];
43
44 for (int i = 0; i < result.Length; i++)
45 map.TryGetValue(i, out result[i]);
46
47 return result;
48 }
49 }
50 }
@@ -0,0 +1,84
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 readonly bool m_supportUnclassified;
10
11 public MapAlphabet(bool supportUnclassified, IEqualityComparer<T> comparer) {
12 m_map = comparer != null ? new Dictionary<T, int>(comparer) : new Dictionary<T,int>();
13 m_supportUnclassified = supportUnclassified;
14 m_nextCls = supportUnclassified ? 1 : 0;
15 }
16
17 #region IAlphabetBuilder implementation
18
19 public int DefineSymbol(T symbol) {
20 int cls;
21 return m_map.TryGetValue(symbol, out cls) ? cls : DefineSymbol(symbol, m_nextCls);
22 }
23
24 public int DefineSymbol(T symbol, int cls) {
25 Safe.ArgumentAssert(cls >= 0, "cls");
26
27 m_nextCls = Math.Max(cls + 1, m_nextCls);
28 m_map.Add(symbol, cls);
29 return cls;
30 }
31
32 public int DefineClass(IEnumerable<T> symbols) {
33 return DefineClass(symbols, m_nextCls);
34 }
35
36 public int DefineClass(IEnumerable<T> symbols, int cls) {
37 Safe.ArgumentAssert(cls >= 0, "cls");
38 Safe.ArgumentNotNull(symbols, "symbols");
39
40 m_nextCls = Math.Max(cls + 1, m_nextCls);
41
42 foreach (var symbol in symbols)
43 m_map[symbol] = cls;
44 return cls;
45 }
46
47 #endregion
48
49 #region IAlphabet implementation
50
51 public int Translate(T symbol) {
52 int cls;
53 if (m_map.TryGetValue(symbol, out cls))
54 return cls;
55 if (!m_supportUnclassified)
56 throw new ArgumentOutOfRangeException("symbol", "The specified symbol isn't in the alphabet");
57 return AutomatonConst.UNCLASSIFIED_INPUT;
58 }
59
60 public int Count {
61 get {
62 return m_nextCls;
63 }
64 }
65
66 public bool Contains(T symbol) {
67 return m_supportUnclassified || m_map.ContainsKey(symbol);
68 }
69
70
71 public IEnumerable<T> GetSymbols(int cls) {
72 Safe.ArgumentAssert(!m_supportUnclassified || cls > 0, "cls");
73 return m_map.Where(p => p.Value == cls).Select(p => p.Key);
74 }
75 #endregion
76
77 public IEnumerable<KeyValuePair<T,int>> Mappings {
78 get {
79 return m_map;
80 }
81 }
82 }
83 }
84
@@ -0,0 +1,17
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace Implab.Automaton {
7 [Serializable]
8 public class ParserException : Exception {
9 public ParserException() { }
10 public ParserException(string message) : base(message) { }
11 public ParserException(string message, Exception inner) : base(message, inner) { }
12 protected ParserException(
13 System.Runtime.Serialization.SerializationInfo info,
14 System.Runtime.Serialization.StreamingContext context)
15 : base(info, context) { }
16 }
17 }
@@ -0,0 +1,17
1 using System;
2
3 namespace Implab.Automaton.RegularExpressions {
4 public class AltToken: BinaryToken {
5 public AltToken(Token left, Token right)
6 : base(left, right) {
7 }
8
9 public override void Accept(IVisitor visitor) {
10 Safe.ArgumentNotNull(visitor, "visitor");
11 visitor.Visit(this);
12 }
13 public override string ToString() {
14 return String.Format(Right is BinaryToken ? "{0}|({1})" : "{0}|{1}", Left, Right);
15 }
16 }
17 }
@@ -0,0 +1,21
1 using Implab;
2
3 namespace Implab.Automaton.RegularExpressions {
4 public abstract class BinaryToken: Token {
5 readonly Token m_left;
6 readonly Token m_right;
7
8 public Token Left {
9 get { return m_left; }
10 }
11
12 public Token Right {
13 get { return m_right; }
14 }
15
16 protected BinaryToken(Token left, Token right) {
17 Safe.ArgumentNotNull(m_left = left, "left");
18 Safe.ArgumentNotNull(m_right = right, "right");
19 }
20 }
21 }
@@ -0,0 +1,22
1 using System;
2
3 namespace Implab.Automaton.RegularExpressions {
4 public class CatToken : BinaryToken {
5 public CatToken(Token left, Token right)
6 : base(left, right) {
7 }
8
9 public override void Accept(IVisitor visitor) {
10 Safe.ArgumentNotNull(visitor, "visitor");
11 visitor.Visit(this);
12 }
13
14 public override string ToString() {
15 return String.Format("{0}{1}", FormatToken(Left), FormatToken(Right));
16 }
17
18 static string FormatToken(Token token) {
19 return String.Format(token is AltToken ? "({0})" : "{0}", token);
20 }
21 }
22 }
@@ -0,0 +1,13
1 using Implab;
2
3 namespace Implab.Automaton.RegularExpressions {
4 public class EmptyToken: Token {
5 public override void Accept(IVisitor visitor) {
6 Safe.ArgumentNotNull(visitor, "visitor");
7 visitor.Visit(this);
8 }
9 public override string ToString() {
10 return "$";
11 }
12 }
13 }
@@ -0,0 +1,18
1 using Implab;
2
3 namespace Implab.Automaton.RegularExpressions {
4 /// <summary>
5 /// Конечный символ расширенного регулярного выражения, при построении ДКА
6 /// используется для определения конечных состояний.
7 /// </summary>
8 public class EndToken: Token {
9
10 public override void Accept(IVisitor visitor) {
11 Safe.ArgumentNotNull(visitor, "visitor");
12 visitor.Visit(this);
13 }
14 public override string ToString() {
15 return "#";
16 }
17 }
18 }
@@ -0,0 +1,23
1 namespace Implab.Automaton.RegularExpressions {
2 /// <summary>
3 /// Конечный символ расширенного регулярного выражения, при построении ДКА
4 /// используется для определения конечных состояний.
5 /// </summary>
6 public class EndToken<TTag>: EndToken {
7
8 readonly TTag m_tag;
9
10 public EndToken(TTag tag) {
11 m_tag = tag;
12 }
13
14 public EndToken()
15 : this(default(TTag)) {
16 }
17
18 public TTag Tag {
19 get { return m_tag; }
20 }
21
22 }
23 }
@@ -0,0 +1,7
1
2 namespace Implab.Automaton.RegularExpressions {
3 public interface ITaggedDFABuilder<TTag> : IDFATableBuilder {
4 void SetStateTag(int s, TTag[] tags);
5 }
6 }
7
@@ -0,0 +1,13
1 namespace Implab.Automaton.RegularExpressions {
2 /// <summary>
3 /// Интерфейс обходчика синтаксического дерева регулярного выражения
4 /// </summary>
5 public interface IVisitor {
6 void Visit(AltToken token);
7 void Visit(StarToken token);
8 void Visit(CatToken token);
9 void Visit(EmptyToken token);
10 void Visit(EndToken token);
11 void Visit(SymbolToken token);
12 }
13 }
@@ -0,0 +1,91
1 using System.Collections.Generic;
2 using System.Linq;
3
4 namespace Implab.Automaton.RegularExpressions {
5 public class RegularDFA<TInput, TTag> : DFATable, ITaggedDFABuilder<TTag> {
6
7 readonly Dictionary<int,TTag[]> m_tags = new Dictionary<int, TTag[]>();
8 readonly IAlphabet<TInput> m_alphabet;
9
10 public RegularDFA(IAlphabet<TInput> alphabet) {
11 Safe.ArgumentNotNull(alphabet, "aplhabet");
12
13 m_alphabet = alphabet;
14 }
15
16
17 public IAlphabet<TInput> InputAlphabet {
18 get {
19 return m_alphabet;
20 }
21 }
22
23 public void MarkFinalState(int s, TTag[] tags) {
24 MarkFinalState(s);
25 SetStateTag(s, tags);
26 }
27
28 public void SetStateTag(int s, TTag[] tags) {
29 Safe.ArgumentNotNull(tags, "tags");
30 m_tags[s] = tags;
31 }
32
33 public TTag[] GetStateTag(int s) {
34 TTag[] tags;
35 return m_tags.TryGetValue(s, out tags) ? tags : new TTag[0];
36 }
37
38 public TTag[][] CreateTagTable() {
39 var table = new TTag[StateCount][];
40
41 foreach (var pair in m_tags)
42 table[pair.Key] = pair.Value;
43
44 return table;
45 }
46
47 /// <summary>
48 /// Optimize the specified alphabet.
49 /// </summary>
50 /// <param name="alphabet">Пустой алфавит, который будет зполнен в процессе оптимизации.</param>
51 public RegularDFA<TInput,TTag> Optimize(IAlphabetBuilder<TInput> alphabet) {
52 Safe.ArgumentNotNull(alphabet, "alphabet");
53
54 var dfa = new RegularDFA<TInput, TTag>(alphabet);
55
56 var alphaMap = new Dictionary<int,int>();
57 var stateMap = new Dictionary<int,int>();
58
59 Optimize(dfa, alphaMap, stateMap);
60
61 // mark tags in the new DFA
62 foreach (var g in m_tags.Where(x => x.Key < StateCount).GroupBy(x => stateMap[x.Key], x => x.Value ))
63 dfa.SetStateTag(g.Key, g.SelectMany(x => x).ToArray());
64
65 // make the alphabet for the new DFA
66 // skip all unclassified symbols
67 foreach (var pair in alphaMap.Where(x => x.Value != 0))
68 alphabet.DefineClass(m_alphabet.GetSymbols(pair.Key), pair.Value);
69 return dfa;
70 }
71
72 protected override IEnumerable<HashSet<int>> SplitFinalStates(IEnumerable<int> states) {
73 var arrayComparer = new CustomEqualityComparer<TTag[]>(
74 (x,y) => x.Length == y.Length && x.All(it => y.Contains(it)),
75 x => x.Sum(it => x.GetHashCode())
76 );
77 return states.GroupBy(x => m_tags[x] ?? new TTag[0], arrayComparer).Select(g => new HashSet<int>(g));
78 }
79
80 public override string ToString() {
81 var states = new MapAlphabet<string>(false, null);
82
83 for (int i = 0; i < StateCount; i++)
84 states.DefineSymbol(string.Format("s{0}", i), i);
85
86 return string.Format("//[RegularDFA {1} x {2}]\n{0}", PrintDFA(InputAlphabet, states),StateCount, AlphabetSize);
87 }
88
89 }
90 }
91
@@ -0,0 +1,212
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 RegularExpressionVisitor : IVisitor {
14 int m_idx;
15 Token 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 HashSet<int> m_ends = new HashSet<int>();
22
23 readonly IDFATableBuilder m_builder;
24 readonly IAlphabetBuilder<HashSet<int>> m_states = new MapAlphabet<HashSet<int>>(
25 false,
26 new CustomEqualityComparer<HashSet<int>>(
27 (x, y) => x.SetEquals(y),
28 x => x.Sum(n => n.GetHashCode())
29 )
30 );
31
32 public RegularExpressionVisitor(IDFATableBuilder builder) {
33 Safe.ArgumentNotNull(builder, "builder");
34
35 m_builder = builder;
36 }
37
38 HashSet<int> Followpos(int pos) {
39 HashSet<int> set;
40 return m_followpos.TryGetValue(pos, out set) ? set : m_followpos[pos] = new HashSet<int>();
41 }
42
43 bool Nullable(object n) {
44 if (n is EmptyToken || n is StarToken)
45 return true;
46 var altToken = n as AltToken;
47 if (altToken != null)
48 return Nullable(altToken.Left) || Nullable(altToken.Right);
49 var catToken = n as CatToken;
50 if (catToken != null)
51 return Nullable(catToken.Left) && Nullable(catToken.Right);
52 return false;
53 }
54
55 protected int Index {
56 get { return m_idx; }
57 }
58
59 public void Visit(AltToken token) {
60 if (m_root == null)
61 m_root = token;
62 var firtspos = new HashSet<int>();
63 var lastpos = new HashSet<int>();
64
65 token.Left.Accept(this);
66 firtspos.UnionWith(m_firstpos);
67 lastpos.UnionWith(m_lastpos);
68
69 token.Right.Accept(this);
70 firtspos.UnionWith(m_firstpos);
71 lastpos.UnionWith(m_lastpos);
72
73 m_firstpos = firtspos;
74 m_lastpos = lastpos;
75 }
76
77 public void Visit(StarToken token) {
78 if (m_root == null)
79 m_root = token;
80 token.Token.Accept(this);
81
82 foreach (var i in m_lastpos)
83 Followpos(i).UnionWith(m_firstpos);
84 }
85
86 public void Visit(CatToken token) {
87 if (m_root == null)
88 m_root = token;
89
90 var firtspos = new HashSet<int>();
91 var lastpos = new HashSet<int>();
92 token.Left.Accept(this);
93 firtspos.UnionWith(m_firstpos);
94 var leftLastpos = m_lastpos;
95
96 token.Right.Accept(this);
97 lastpos.UnionWith(m_lastpos);
98 var rightFirstpos = m_firstpos;
99
100 if (Nullable(token.Left))
101 firtspos.UnionWith(rightFirstpos);
102
103 if (Nullable(token.Right))
104 lastpos.UnionWith(leftLastpos);
105
106 m_firstpos = firtspos;
107 m_lastpos = lastpos;
108
109 foreach (var i in leftLastpos)
110 Followpos(i).UnionWith(rightFirstpos);
111
112 }
113
114 public void Visit(EmptyToken token) {
115 if (m_root == null)
116 m_root = token;
117 }
118
119 public void Visit(SymbolToken token) {
120 if (m_root == null)
121 m_root = token;
122 m_idx++;
123 m_indexes[m_idx] = token.Value;
124 m_firstpos = new HashSet<int>(new[] { m_idx });
125 m_lastpos = new HashSet<int>(new[] { m_idx });
126 }
127
128 public virtual void Visit(EndToken token) {
129 if (m_root == null)
130 m_root = token;
131 m_idx++;
132 m_indexes[m_idx] = AutomatonConst.UNCLASSIFIED_INPUT;
133 m_firstpos = new HashSet<int>(new[] { m_idx });
134 m_lastpos = new HashSet<int>(new[] { m_idx });
135 Followpos(m_idx);
136 m_ends.Add(m_idx);
137 }
138
139 public void BuildDFA() {
140 AddState(m_firstpos);
141 SetInitialState(m_firstpos);
142
143 if(IsFinal(m_firstpos))
144 MarkFinalState(m_firstpos);
145
146 var inputMax = m_indexes.Values.Max();
147 var queue = new Queue<HashSet<int>>();
148
149 queue.Enqueue(m_firstpos);
150
151 while (queue.Count > 0) {
152 var s1 = queue.Dequeue();
153
154 for (int a = 0; a <= inputMax; a++) {
155 var s2 = new HashSet<int>();
156 foreach (var p in s1) {
157 if (m_indexes[p] == a) {
158 s2.UnionWith(Followpos(p));
159 }
160 }
161 if (s2.Count > 0) {
162 if (!HasState(s2)) {
163 AddState(s2);
164 if (IsFinal(s2))
165 MarkFinalState(s2);
166
167 queue.Enqueue(s2);
168 }
169
170 DefineTransition(s1, s2, a);
171 }
172
173 }
174 }
175 }
176
177 protected bool HasState(HashSet<int> state) {
178 return m_states.Contains(state);
179 }
180
181 protected void AddState(HashSet<int> state) {
182 Debug.Assert(!HasState(state));
183
184 m_states.DefineSymbol(state);
185 }
186
187 protected int Translate(HashSet<int> state) {
188 Debug.Assert(HasState(state));
189
190 return m_states.Translate(state);
191 }
192
193 protected virtual void SetInitialState(HashSet<int> state) {
194 m_builder.SetInitialState(Translate(state));
195 }
196
197 protected virtual void MarkFinalState(HashSet<int> state) {
198 m_builder.MarkFinalState(Translate(state));
199 }
200
201 protected virtual void DefineTransition(HashSet<int> s1, HashSet<int> s2, int ch) {
202
203 m_builder.Add(new AutomatonTransition(Translate(s1), Translate(s2), ch));
204 }
205
206 bool IsFinal(IEnumerable<int> state) {
207 Debug.Assert(state != null);
208 return state.Any(m_ends.Contains);
209 }
210
211 }
212 }
@@ -0,0 +1,37
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 /// </summary>
10 public class RegularExpressionVisitor<TTag> : RegularExpressionVisitor {
11 readonly Dictionary<int, TTag> m_tags = new Dictionary<int, TTag>();
12
13 readonly ITaggedDFABuilder<TTag> m_builder;
14
15 public RegularExpressionVisitor(ITaggedDFABuilder<TTag> builder) : base(builder) {
16 m_builder = builder;
17 }
18
19 public override void Visit(EndToken token) {
20 base.Visit(token);
21 var tagged = token as EndToken<TTag>;
22 if (tagged != null)
23 m_tags.Add(Index, tagged.Tag);
24 }
25
26 protected override void MarkFinalState(HashSet<int> state) {
27 base.MarkFinalState(state);
28 m_builder.SetStateTag(Translate(state), GetStateTags(state));
29 }
30
31 TTag[] GetStateTags(IEnumerable<int> state) {
32 Debug.Assert(state != null);
33 return state.Where(m_tags.ContainsKey).Select(pos => m_tags[pos]).ToArray();
34 }
35
36 }
37 }
@@ -0,0 +1,31
1 using Implab;
2 using System;
3
4
5 namespace Implab.Automaton.RegularExpressions {
6 /// <summary>
7 /// Замыкание выражения с 0 и более повторов.
8 /// </summary>
9 public class StarToken: Token {
10
11 Token m_token;
12
13 public Token Token {
14 get { return m_token; }
15 }
16
17 public StarToken(Token token) {
18 Safe.ArgumentNotNull(token, "token");
19 m_token = token;
20 }
21
22 public override void Accept(IVisitor visitor) {
23 Safe.ArgumentNotNull(visitor, "visitor");
24 visitor.Visit(this);
25 }
26
27 public override string ToString() {
28 return String.Format("({0})*", Token);
29 }
30 }
31 }
@@ -0,0 +1,27
1 using Implab;
2
3 namespace Implab.Automaton.RegularExpressions {
4 /// <summary>
5 /// Выражение, соответсвующее одному символу.
6 /// </summary>
7 public class SymbolToken: Token {
8 int m_value;
9
10 public int Value {
11 get { return m_value; }
12 }
13
14 public SymbolToken(int value) {
15 m_value = value;
16 }
17 public override void Accept(IVisitor visitor) {
18 Safe.ArgumentNotNull(visitor, "visitor");
19
20 visitor.Visit(this);
21 }
22
23 public override string ToString() {
24 return Value.ToString();
25 }
26 }
27 }
@@ -0,0 +1,63
1 using Implab;
2 using System;
3 using System.Linq;
4
5 namespace Implab.Automaton.RegularExpressions {
6 public abstract class Token {
7 public abstract void Accept(IVisitor visitor);
8
9 public Token End() {
10 return Cat(new EndToken());
11 }
12
13 public Token Tag<TTag>(TTag tag) {
14 return Cat(new EndToken<TTag>(tag));
15 }
16
17 public Token Cat(Token right) {
18 return new CatToken(this, right);
19 }
20
21 public Token Or(Token right) {
22 return new AltToken(this, right);
23 }
24
25 public Token Optional() {
26 return Or(new EmptyToken());
27 }
28
29 public Token EClosure() {
30 return new StarToken(this);
31 }
32
33 public Token Closure() {
34 return Cat(new StarToken(this));
35 }
36
37 public Token Repeat(int count) {
38 Token token = null;
39
40 for (int i = 0; i < count; i++)
41 token = token != null ? token.Cat(this) : this;
42 return token ?? new EmptyToken();
43 }
44
45 public Token Repeat(int min, int max) {
46 if (min > max || min < 1)
47 throw new ArgumentOutOfRangeException();
48 var token = Repeat(min);
49
50 for (int i = min; i < max; i++)
51 token = token.Cat( Optional() );
52 return token;
53 }
54
55 public static Token New(params int[] set) {
56 Safe.ArgumentNotNull(set, "set");
57 Token token = null;
58 foreach(var c in set.Distinct())
59 token = token == null ? new SymbolToken(c) : token.Or(new SymbolToken(c));
60 return token;
61 }
62 }
63 }
@@ -0,0 +1,64
1 using System;
2 using System.Threading;
3
4 namespace Implab.Components {
5 /// <summary>
6 /// Creates an instace on-demand and allows it to be garbage collected.
7 /// </summary>
8 /// <remarks>
9 /// Usefull when dealing with memory-intensive objects which are frequently used.
10 /// This class is similar to <see cref="ObjectPool{T}"/> except it is a singleton.
11 /// </remarks>
12 public class LazyAndWeak<T> where T : class {
13
14 readonly Func<T> m_factory;
15 readonly object m_lock;
16 WeakReference m_reference;
17
18
19 public LazyAndWeak(Func<T> factory, bool useLock) {
20 Safe.ArgumentNotNull(factory, "factory");
21 m_factory = factory;
22 m_lock = useLock ? new object() : null;
23 }
24
25 public LazyAndWeak(Func<T> factory) : this(factory, false) {
26 }
27
28 public T Value {
29 get {
30 while (true) {
31 var weak = m_reference;
32 T value;
33 if (weak != null) {
34 value = weak.Target as T;
35 if (value != null)
36 return value;
37 }
38
39 if (m_lock == null) {
40 value = m_factory();
41
42 if (Interlocked.CompareExchange(ref m_reference, new WeakReference(value), weak) == weak)
43 return value;
44 } else {
45 lock (m_lock) {
46 // double check
47 weak = m_reference;
48 if (weak != null) {
49 value = weak.Target as T;
50 if (value != null)
51 return value;
52 }
53 // we are safe to write
54 value = m_factory();
55 m_reference = new WeakReference(value);
56 return value;
57 }
58 }
59 }
60 }
61 }
62 }
63 }
64
@@ -0,0 +1,23
1 using System.Collections.Generic;
2 using System.Linq;
3 using Implab.Automaton;
4
5 namespace Implab.Formats {
6 public class ByteAlphabet : IndexedAlphabetBase<byte> {
7
8 #region implemented abstract members of IndexedAlphabetBase
9
10 public override int GetSymbolIndex(byte symbol) {
11 return (int)symbol;
12 }
13
14 public IEnumerable<byte> InputSymbols {
15 get {
16 return Enumerable.Range(byte.MinValue, byte.MaxValue).Cast<byte>();
17 }
18 }
19
20 #endregion
21 }
22 }
23
@@ -0,0 +1,16
1 using System.Collections.Generic;
2 using System.Linq;
3 using Implab.Automaton;
4
5 namespace Implab.Formats {
6 public class CharAlphabet: IndexedAlphabetBase<char> {
7
8 public override int GetSymbolIndex(char symbol) {
9 return symbol;
10 }
11
12 public IEnumerable<char> InputSymbols {
13 get { return Enumerable.Range(char.MinValue, char.MaxValue).Cast<char>(); }
14 }
15 }
16 }
@@ -0,0 +1,99
1 using Implab;
2 using System;
3 using System.Collections.Generic;
4 using System.Linq;
5 using Implab.Automaton;
6 using Implab.Automaton.RegularExpressions;
7
8 namespace Implab.Formats {
9 /// <summary>
10 /// Базовый абстрактный класс. Грамматика, позволяет формулировать выражения над алфавитом типа <c>char</c>.
11 /// </summary>
12 public abstract class Grammar<TSymbol> {
13
14 protected abstract IAlphabetBuilder<TSymbol> AlphabetBuilder {
15 get;
16 }
17
18 protected SymbolToken UnclassifiedToken() {
19 return new SymbolToken(AutomatonConst.UNCLASSIFIED_INPUT);
20 }
21
22 protected void DefineAlphabet(IEnumerable<TSymbol> alphabet) {
23 Safe.ArgumentNotNull(alphabet, "alphabet");
24
25 foreach (var ch in alphabet)
26 AlphabetBuilder.DefineSymbol(ch);
27 }
28
29 protected Token SymbolToken(TSymbol symbol) {
30 return Token.New(TranslateOrAdd(symbol));
31 }
32
33 protected Token SymbolToken(IEnumerable<TSymbol> symbols) {
34 Safe.ArgumentNotNull(symbols, "symbols");
35
36 return Token.New(TranslateOrAdd(symbols).ToArray());
37 }
38
39 protected Token SymbolSetToken(params TSymbol[] set) {
40 return SymbolToken(set);
41 }
42
43 int TranslateOrAdd(TSymbol ch) {
44 var t = AlphabetBuilder.Translate(ch);
45 if (t == AutomatonConst.UNCLASSIFIED_INPUT)
46 t = AlphabetBuilder.DefineSymbol(ch);
47 return t;
48 }
49
50 IEnumerable<int> TranslateOrAdd(IEnumerable<TSymbol> symbols) {
51 return symbols.Distinct().Select(TranslateOrAdd);
52 }
53
54 int TranslateOrDie(TSymbol ch) {
55 var t = AlphabetBuilder.Translate(ch);
56 if (t == AutomatonConst.UNCLASSIFIED_INPUT)
57 throw new ApplicationException(String.Format("Symbol '{0}' is UNCLASSIFIED", ch));
58 return t;
59 }
60
61 IEnumerable<int> TranslateOrDie(IEnumerable<TSymbol> symbols) {
62 return symbols.Distinct().Select(TranslateOrDie);
63 }
64
65 protected Token SymbolTokenExcept(IEnumerable<TSymbol> symbols) {
66 Safe.ArgumentNotNull(symbols, "symbols");
67
68 return Token.New( Enumerable.Range(0, AlphabetBuilder.Count).Except(TranslateOrDie(symbols)).ToArray() );
69 }
70
71 protected abstract IndexedAlphabetBase<TSymbol> CreateAlphabet();
72
73 protected ScannerContext<TTag> BuildScannerContext<TTag>(Token regexp) {
74
75 var dfa = new RegularDFA<TSymbol, TTag>(AlphabetBuilder);
76
77 var visitor = new RegularExpressionVisitor<TTag>(dfa);
78 regexp.Accept(visitor);
79 visitor.BuildDFA();
80
81 if (dfa.IsFinalState(dfa.InitialState))
82 throw new ApplicationException("The specified language contains empty token");
83
84 var ab = CreateAlphabet();
85 var optimal = dfa.Optimize(ab);
86
87 return new ScannerContext<TTag>(
88 optimal.CreateTransitionTable(),
89 optimal.CreateFinalStateTable(),
90 optimal.CreateTagTable(),
91 optimal.InitialState,
92 ab.GetTranslationMap()
93 );
94 }
95
96 }
97
98
99 }
@@ -0,0 +1,11
1 namespace Implab.Formats.JSON {
2 /// <summary>
3 /// internal
4 /// </summary>
5 enum JSONElementContext {
6 None,
7 Object,
8 Array,
9 Closed
10 }
11 }
@@ -0,0 +1,28
1 namespace Implab.Formats.JSON {
2 /// <summary>
3 /// Тип элемента на котором находится парсер
4 /// </summary>
5 public enum JSONElementType {
6 None,
7 /// <summary>
8 /// Начало объекта
9 /// </summary>
10 BeginObject,
11 /// <summary>
12 /// Конец объекта
13 /// </summary>
14 EndObject,
15 /// <summary>
16 /// Начало массива
17 /// </summary>
18 BeginArray,
19 /// <summary>
20 /// Конец массива
21 /// </summary>
22 EndArray,
23 /// <summary>
24 /// Простое значение
25 /// </summary>
26 Value
27 }
28 }
@@ -0,0 +1,121
1 using System.Linq;
2 using Implab.Automaton.RegularExpressions;
3 using System;
4 using Implab.Automaton;
5 using Implab.Components;
6
7 namespace Implab.Formats.JSON {
8 class JSONGrammar : Grammar<char> {
9 public enum TokenType {
10 None,
11 BeginObject,
12 EndObject,
13 BeginArray,
14 EndArray,
15 String,
16 Number,
17 Literal,
18 NameSeparator,
19 ValueSeparator,
20 Whitespace,
21
22 StringBound,
23 EscapedChar,
24 UnescapedChar,
25 EscapedUnicode
26 }
27
28 static LazyAndWeak<JSONGrammar> _instance = new LazyAndWeak<JSONGrammar>(() => new JSONGrammar());
29
30 public static JSONGrammar Instance {
31 get { return _instance.Value; }
32 }
33
34 readonly ScannerContext<TokenType> m_jsonExpression;
35 readonly ScannerContext<TokenType> m_stringExpression;
36 readonly CharAlphabet m_defaultAlphabet = new CharAlphabet();
37
38 public JSONGrammar() {
39 DefineAlphabet(Enumerable.Range(0, 0x20).Select(x => (char)x));
40 var hexDigit = SymbolRangeToken('a','f').Or(SymbolRangeToken('A','F')).Or(SymbolRangeToken('0','9'));
41 var digit9 = SymbolRangeToken('1', '9');
42 var zero = SymbolToken('0');
43 var digit = zero.Or(digit9);
44 var dot = SymbolToken('.');
45 var minus = SymbolToken('-');
46 var sign = SymbolSetToken('-', '+');
47 var expSign = SymbolSetToken('e', 'E');
48 var letters = SymbolRangeToken('a', 'z');
49 var integer = zero.Or(digit9.Cat(digit.EClosure()));
50 var frac = dot.Cat(digit.Closure());
51 var exp = expSign.Cat(sign.Optional()).Cat(digit.Closure());
52 var quote = SymbolToken('"');
53 var backSlash = SymbolToken('\\');
54 var specialEscapeChars = SymbolSetToken('\\', '"', '/', 'b', 'f', 't', 'n', 'r');
55 var unicodeEspace = SymbolToken('u').Cat(hexDigit.Repeat(4));
56 var whitespace = SymbolSetToken('\n', '\r', '\t', ' ').EClosure();
57 var beginObject = whitespace.Cat(SymbolToken('{')).Cat(whitespace);
58 var endObject = whitespace.Cat(SymbolToken('}')).Cat(whitespace);
59 var beginArray = whitespace.Cat(SymbolToken('[')).Cat(whitespace);
60 var endArray = whitespace.Cat(SymbolToken(']')).Cat(whitespace);
61 var nameSep = whitespace.Cat(SymbolToken(':')).Cat(whitespace);
62 var valueSep = whitespace.Cat(SymbolToken(',')).Cat(whitespace);
63
64 var number = minus.Optional().Cat(integer).Cat(frac.Optional()).Cat(exp.Optional());
65 var literal = letters.Closure();
66 var unescaped = SymbolTokenExcept(Enumerable.Range(0, 0x20).Union(new int[] { '\\', '"' }).Select(x => (char)x));
67
68 var jsonExpression =
69 number.Tag(TokenType.Number)
70 .Or(literal.Tag(TokenType.Literal))
71 .Or(quote.Tag(TokenType.StringBound))
72 .Or(beginObject.Tag(TokenType.BeginObject))
73 .Or(endObject.Tag(TokenType.EndObject))
74 .Or(beginArray.Tag(TokenType.BeginArray))
75 .Or(endArray.Tag(TokenType.EndArray))
76 .Or(nameSep.Tag(TokenType.NameSeparator))
77 .Or(valueSep.Tag(TokenType.ValueSeparator))
78 .Or(SymbolSetToken('\n', '\r', '\t', ' ').Closure().Tag(TokenType.Whitespace));
79
80
81 var jsonStringExpression =
82 quote.Tag(TokenType.StringBound)
83 .Or(backSlash.Cat(specialEscapeChars).Tag(TokenType.EscapedChar))
84 .Or(backSlash.Cat(unicodeEspace).Tag(TokenType.EscapedUnicode))
85 .Or(unescaped.Closure().Tag(TokenType.UnescapedChar));
86
87
88 m_jsonExpression = BuildScannerContext<TokenType>(jsonExpression);
89 m_stringExpression = BuildScannerContext<TokenType>(jsonStringExpression);
90
91
92 }
93
94 protected override IAlphabetBuilder<char> AlphabetBuilder {
95 get {
96 return m_defaultAlphabet;
97 }
98 }
99
100 public ScannerContext<TokenType> JsonExpression {
101 get {
102 return m_jsonExpression;
103 }
104 }
105
106 public ScannerContext<TokenType> JsonStringExpression {
107 get {
108 return m_stringExpression;
109 }
110 }
111
112 Token SymbolRangeToken(char start, char stop) {
113 return SymbolToken(Enumerable.Range(start, stop - start + 1).Select(x => (char)x));
114 }
115
116 protected override IndexedAlphabetBase<char> CreateAlphabet() {
117 return new CharAlphabet();
118 }
119
120 }
121 }
@@ -0,0 +1,293
1 using System;
2 using System.Diagnostics;
3 using System.IO;
4 using Implab.Automaton;
5 using Implab.Automaton.RegularExpressions;
6 using System.Linq;
7 using Implab.Components;
8 using System.Collections.Generic;
9
10 namespace Implab.Formats.JSON {
11 /// <summary>
12 /// Pull парсер JSON данных.
13 /// </summary>
14 /// <remarks>
15 /// Следует отметить отдельную интерпретацию свойства <see cref="Level"/>,
16 /// оно означает текущий уровень вложенности объектов, однако закрывающий
17 /// элемент объекта и массива имеет уровень меньше, чем сам объект.
18 /// <code>
19 /// { // Level = 1
20 /// "name" : "Peter", // Level = 1
21 /// "address" : { // Level = 2
22 /// city : "Stern" // Level = 2
23 /// } // Level = 1
24 /// } // Level = 0
25 /// </code>
26 /// </remarks>
27 public class JSONParser : Disposable {
28
29 enum MemberContext {
30 MemberName,
31 MemberValue
32 }
33
34 #region Parser rules
35 struct ParserContext {
36 readonly int[,] m_dfa;
37 int m_state;
38
39 readonly JSONElementContext m_elementContext;
40
41 public ParserContext(int[,] dfa, int state, JSONElementContext context) {
42 m_dfa = dfa;
43 m_state = state;
44 m_elementContext = context;
45 }
46
47 public bool Move(JsonTokenType token) {
48 var next = m_dfa[m_state, (int)token];
49 if (next == AutomatonConst.UNREACHABLE_STATE)
50 return false;
51 m_state = next;
52 return true;
53 }
54
55 public JSONElementContext ElementContext {
56 get { return m_elementContext; }
57 }
58 }
59
60 static readonly ParserContext _jsonContext;
61 static readonly ParserContext _objectContext;
62 static readonly ParserContext _arrayContext;
63
64 static JSONParser() {
65
66 var valueExpression = MakeToken(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String);
67 var memberExpression = MakeToken(JsonTokenType.String).Cat(MakeToken(JsonTokenType.NameSeparator)).Cat(valueExpression);
68
69 var objectExpression = memberExpression
70 .Cat(
71 MakeToken(JsonTokenType.ValueSeparator)
72 .Cat(memberExpression)
73 .EClosure()
74 )
75 .Optional()
76 .Cat(MakeToken(JsonTokenType.EndObject))
77 .End();
78
79 var arrayExpression = valueExpression
80 .Cat(
81 MakeToken(JsonTokenType.ValueSeparator)
82 .Cat(valueExpression)
83 .EClosure()
84 )
85 .Optional()
86 .Cat(MakeToken(JsonTokenType.EndArray))
87 .End();
88
89 var jsonExpression = valueExpression.End();
90
91 _jsonContext = CreateParserContext(jsonExpression, JSONElementContext.None);
92 _objectContext = CreateParserContext(objectExpression, JSONElementContext.Object);
93 _arrayContext = CreateParserContext(arrayExpression, JSONElementContext.Array);
94 }
95
96 static Token MakeToken(params JsonTokenType[] input) {
97 return Token.New( input.Select(t => (int)t).ToArray() );
98 }
99
100 static ParserContext CreateParserContext(Token expr, JSONElementContext context) {
101
102 var dfa = new DFATable();
103 var builder = new RegularExpressionVisitor(dfa);
104 expr.Accept(builder);
105 builder.BuildDFA();
106
107 return new ParserContext(dfa.CreateTransitionTable(), dfa.InitialState, context);
108 }
109
110 #endregion
111
112 readonly JSONScanner m_scanner;
113 MemberContext m_memberContext;
114
115 JSONElementType m_elementType;
116 object m_elementValue;
117 string m_memberName = String.Empty;
118
119 Stack<ParserContext> m_stack = new Stack<ParserContext>();
120 ParserContext m_context = _jsonContext;
121
122 /// <summary>
123 /// Создает новый парсер на основе строки, содержащей JSON
124 /// </summary>
125 /// <param name="text"></param>
126 public JSONParser(string text) {
127 Safe.ArgumentNotEmpty(text, "text");
128 m_scanner = new JSONScanner(text);
129 }
130
131 /// <summary>
132 /// Создает новый экземпляр парсера, на основе текстового потока.
133 /// </summary>
134 /// <param name="reader">Текстовый поток.</param>
135 public JSONParser(TextReader reader) {
136 Safe.ArgumentNotNull(reader, "reader");
137 m_scanner = new JSONScanner(reader);
138 }
139
140 public int Level {
141 get { return m_stack.Count; }
142 }
143
144 /// <summary>
145 /// Тип текущего элемента на котором стоит парсер.
146 /// </summary>
147 public JSONElementType ElementType {
148 get { return m_elementType; }
149 }
150
151 /// <summary>
152 /// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда
153 /// пустая строка.
154 /// </summary>
155 public string ElementName {
156 get { return m_memberName; }
157 }
158
159 /// <summary>
160 /// Значение элемента. Только для элементов типа <see cref="JSONElementType.Value"/>, для остальных <c>null</c>
161 /// </summary>
162 public object ElementValue {
163 get { return m_elementValue; }
164 }
165
166 /// <summary>
167 /// Читает слеюудущий объект из потока
168 /// </summary>
169 /// <returns><c>true</c> - операция чтения прошла успешно, <c>false</c> - конец данных</returns>
170 public bool Read() {
171 object tokenValue;
172 JsonTokenType tokenType;
173
174 m_memberName = String.Empty;
175
176 while (m_scanner.ReadToken(out tokenValue, out tokenType)) {
177 if(!m_context.Move(tokenType))
178 UnexpectedToken(tokenValue, tokenType);
179
180 switch (tokenType) {
181 case JsonTokenType.BeginObject:
182 m_stack.Push(m_context);
183 m_context = _objectContext;
184
185 m_elementValue = null;
186 m_memberContext = MemberContext.MemberName;
187 m_elementType = JSONElementType.BeginObject;
188 return true;
189 case JsonTokenType.EndObject:
190 if (m_stack.Count == 0)
191 UnexpectedToken(tokenValue, tokenType);
192 m_context = m_stack.Pop();
193
194 m_elementValue = null;
195 m_elementType = JSONElementType.EndObject;
196 return true;
197 case JsonTokenType.BeginArray:
198 m_stack.Push(m_context);
199 m_context = _arrayContext;
200
201 m_elementValue = null;
202 m_memberContext = MemberContext.MemberValue;
203 m_elementType = JSONElementType.BeginArray;
204 return true;
205 case JsonTokenType.EndArray:
206 if (m_stack.Count == 0)
207 UnexpectedToken(tokenValue, tokenType);
208 m_context = m_stack.Pop();
209
210 m_elementValue = null;
211 m_elementType = JSONElementType.EndArray;
212 return true;
213 case JsonTokenType.String:
214 if (m_memberContext == MemberContext.MemberName) {
215 m_memberName = (string)tokenValue;
216 break;
217 }
218 m_elementType = JSONElementType.Value;
219 m_elementValue = tokenValue;
220 return true;
221 case JsonTokenType.Number:
222 m_elementType = JSONElementType.Value;
223 m_elementValue = tokenValue;
224 return true;
225 case JsonTokenType.Literal:
226 m_elementType = JSONElementType.Value;
227 m_elementValue = ParseLiteral((string)tokenValue);
228 return true;
229 case JsonTokenType.NameSeparator:
230 m_memberContext = MemberContext.MemberValue;
231 break;
232 case JsonTokenType.ValueSeparator:
233 m_memberContext = m_context.ElementContext == JSONElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue;
234 break;
235 default:
236 UnexpectedToken(tokenValue, tokenType);
237 break;
238 }
239 }
240 if (m_context.ElementContext != JSONElementContext.None)
241 throw new ParserException("Unexpedted end of data");
242
243 EOF = true;
244
245 return false;
246 }
247
248 object ParseLiteral(string literal) {
249 switch (literal) {
250 case "null":
251 return null;
252 case "false":
253 return false;
254 case "true":
255 return true;
256 default:
257 UnexpectedToken(literal, JsonTokenType.Literal);
258 return null; // avoid compliler error
259 }
260 }
261
262 void UnexpectedToken(object value, JsonTokenType tokenType) {
263 throw new ParserException(String.Format("Unexpected token {0}: '{1}'", tokenType, value));
264 }
265
266
267 /// <summary>
268 /// Признак конца потока
269 /// </summary>
270 public bool EOF {
271 get;
272 private set;
273 }
274
275 protected override void Dispose(bool disposing) {
276 if (disposing)
277 Safe.Dispose(m_scanner);
278 }
279
280 /// <summary>
281 /// Переходит в конец текущего объекта.
282 /// </summary>
283 public void SeekElementEnd() {
284 var level = Level - 1;
285
286 Debug.Assert(level >= 0);
287
288 while (Level != level)
289 Read();
290 }
291 }
292
293 }
@@ -0,0 +1,109
1 using System;
2 using System.Globalization;
3 using Implab.Automaton;
4 using System.Text;
5 using Implab.Components;
6 using System.IO;
7
8 namespace Implab.Formats.JSON {
9 /// <summary>
10 /// Сканнер (лексер), разбивающий поток символов на токены JSON.
11 /// </summary>
12 public class JSONScanner : Disposable {
13 readonly StringBuilder m_builder = new StringBuilder();
14
15 readonly ScannerContext<JSONGrammar.TokenType> m_jsonContext = JSONGrammar.Instance.JsonExpression;
16 readonly ScannerContext<JSONGrammar.TokenType> m_stringContext = JSONGrammar.Instance.JsonStringExpression;
17
18
19 readonly TextScanner m_scanner;
20
21 /// <summary>
22 /// Создает новый экземпляр сканнера
23 /// </summary>
24 public JSONScanner(string text) {
25 Safe.ArgumentNotEmpty(text, "text");
26
27 m_scanner = new StringScanner(text);
28 }
29
30 public JSONScanner(TextReader reader, int bufferMax, int chunkSize) {
31 Safe.ArgumentNotNull(reader, "reader");
32
33 m_scanner = new ReaderScanner(reader, bufferMax, chunkSize);
34 }
35
36 public JSONScanner(TextReader reader) : this(reader, 1024*1024, 1024){
37 }
38
39 /// <summary>
40 /// Читает следующий лексический элемент из входных данных.
41 /// </summary>
42 /// <param name="tokenValue">Возвращает значение прочитанного токена.</param>
43 /// <param name="tokenType">Возвращает тип прочитанного токена.</param>
44 /// <returns><c>true</c> - чтение произведено успешно. <c>false</c> - достигнут конец входных данных</returns>
45 /// <remarks>В случе если токен не распознается, возникает исключение. Значения токенов обрабатываются, т.е.
46 /// в строках обрабатываются экранированные символы, числа становтся типа double.</remarks>
47 public bool ReadToken(out object tokenValue, out JsonTokenType tokenType) {
48 JSONGrammar.TokenType[] tag;
49 while (m_jsonContext.Execute(m_scanner, out tag)) {
50 switch (tag[0]) {
51 case JSONGrammar.TokenType.StringBound:
52 tokenValue = ReadString();
53 tokenType = JsonTokenType.String;
54 break;
55 case JSONGrammar.TokenType.Number:
56 tokenValue = Double.Parse(m_scanner.GetTokenValue(), CultureInfo.InvariantCulture);
57 tokenType = JsonTokenType.Number;
58 break;
59 case JSONGrammar.TokenType.Whitespace:
60 continue;
61 default:
62 tokenType = (JsonTokenType)tag[0];
63 tokenValue = m_scanner.GetTokenValue();
64 break;
65 }
66 return true;
67 }
68 tokenValue = null;
69 tokenType = JsonTokenType.None;
70 return false;
71 }
72
73 string ReadString() {
74 int pos = 0;
75 var buf = new char[6]; // the buffer for unescaping chars
76
77 JSONGrammar.TokenType[] tag;
78 m_builder.Clear();
79
80 while (m_stringContext.Execute(m_scanner, out tag)) {
81 switch (tag[0]) {
82 case JSONGrammar.TokenType.StringBound:
83 return m_builder.ToString();
84 case JSONGrammar.TokenType.UnescapedChar:
85 m_scanner.CopyTokenTo(m_builder);
86 break;
87 case JSONGrammar.TokenType.EscapedUnicode: // \xXXXX - unicode escape sequence
88 m_scanner.CopyTokenTo(buf, 0);
89 m_builder.Append(StringTranslator.TranslateHexUnicode(buf, 2));
90 pos++;
91 break;
92 case JSONGrammar.TokenType.EscapedChar: // \t - escape sequence
93 m_scanner.CopyTokenTo(buf, 0);
94 m_builder.Append(StringTranslator.TranslateEscapedChar(buf[1]));
95 break;
96 }
97
98 }
99
100 throw new ParserException("Unexpected end of data");
101 }
102
103 protected override void Dispose(bool disposing) {
104 if (disposing)
105 Safe.Dispose(m_scanner);
106 base.Dispose(disposing);
107 }
108 }
109 }
@@ -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.Formats.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,335
1 using Implab;
2 using System;
3 using System.Collections.Generic;
4 using System.Globalization;
5 using System.IO;
6 using System.Xml;
7
8 namespace Implab.Formats.JSON {
9 public class JSONXmlReader : XmlReader {
10
11 enum ValueContext {
12 Undefined,
13 ElementStart,
14 ElementValue,
15 ElementEnd,
16 ElementEmpty
17 }
18
19 struct LocalNameContext {
20 public string localName;
21 public bool isArray;
22 }
23
24 JSONParser m_parser;
25 ValueContext m_valueContext;
26 ReadState m_state = ReadState.Initial;
27 Stack<LocalNameContext> m_localNameStack = new Stack<LocalNameContext>();
28 LocalNameContext m_localName;
29 int m_depthCorrection;
30
31 readonly string m_rootName;
32 readonly string m_prefix;
33 readonly string m_namespaceUri;
34 readonly bool m_flattenArrays;
35 readonly string m_arrayItemName;
36 readonly XmlNameTable m_nameTable;
37
38 JSONXmlReader(JSONParser parser, JSONXmlReaderOptions options) {
39 m_parser = parser;
40
41 if (options != null) {
42 m_prefix = options.NodesPrefix ?? String.Empty;
43 m_namespaceUri = options.NamespaceURI ?? String.Empty;
44 m_rootName = options.RootName ?? "json";
45 m_flattenArrays = options.FlattenArrays;
46 m_arrayItemName = options.ArrayItemName ?? "item";
47 m_nameTable = options.NameTable ?? new NameTable();
48 } else {
49 m_prefix = String.Empty;
50 m_namespaceUri = String.Empty;
51 m_rootName = "json";
52 m_flattenArrays = false;
53 m_arrayItemName = "item";
54 m_nameTable = new NameTable();
55 }
56 }
57
58 /// <summary>
59 /// Always 0, JSON doesn't support attributes
60 /// </summary>
61 public override int AttributeCount {
62 get { return 0; }
63 }
64
65 public override string BaseURI {
66 get { return String.Empty; }
67 }
68
69 public override int Depth {
70 get {
71 return m_localNameStack.Count + m_depthCorrection;
72 }
73 }
74
75 public override bool EOF {
76 get { return m_parser.EOF; }
77 }
78
79 /// <summary>
80 /// Always throws an exception
81 /// </summary>
82 /// <param name="i"></param>
83 /// <returns></returns>
84 public override string GetAttribute(int i) {
85 throw new ArgumentOutOfRangeException();
86 }
87
88 /// <summary>
89 /// Always returns empty string
90 /// </summary>
91 /// <param name="name"></param>
92 /// <param name="namespaceURI"></param>
93 /// <returns></returns>
94 public override string GetAttribute(string name, string namespaceURI) {
95 return String.Empty;
96 }
97
98 /// <summary>
99 /// Always returns empty string
100 /// </summary>
101 /// <param name="name"></param>
102 /// <returns></returns>
103 public override string GetAttribute(string name) {
104 return String.Empty;
105 }
106
107 public override bool IsEmptyElement {
108 get { return m_parser.ElementType == JSONElementType.Value && m_valueContext == ValueContext.ElementEmpty; }
109 }
110
111 public override string LocalName {
112 get { return m_localName.localName; }
113 }
114
115 public override string LookupNamespace(string prefix) {
116 if (String.IsNullOrEmpty(prefix) || prefix == m_prefix)
117 return m_namespaceUri;
118
119 return String.Empty;
120 }
121
122 public override bool MoveToAttribute(string name, string ns) {
123 return false;
124 }
125
126 public override bool MoveToAttribute(string name) {
127 return false;
128 }
129
130 public override bool MoveToElement() {
131 return false;
132 }
133
134 public override bool MoveToFirstAttribute() {
135 return false;
136 }
137
138 public override bool MoveToNextAttribute() {
139 return false;
140 }
141
142 public override XmlNameTable NameTable {
143 get { return m_nameTable; }
144 }
145
146 public override string NamespaceURI {
147 get { return m_namespaceUri; }
148 }
149
150 public override XmlNodeType NodeType {
151 get {
152 switch (m_parser.ElementType) {
153 case JSONElementType.BeginObject:
154 case JSONElementType.BeginArray:
155 return XmlNodeType.Element;
156 case JSONElementType.EndObject:
157 case JSONElementType.EndArray:
158 return XmlNodeType.EndElement;
159 case JSONElementType.Value:
160 switch (m_valueContext) {
161 case ValueContext.ElementStart:
162 case ValueContext.ElementEmpty:
163 return XmlNodeType.Element;
164 case ValueContext.ElementValue:
165 return XmlNodeType.Text;
166 case ValueContext.ElementEnd:
167 return XmlNodeType.EndElement;
168 default:
169 throw new InvalidOperationException();
170 }
171 default:
172 throw new InvalidOperationException();
173 }
174 }
175 }
176
177 public override string Prefix {
178 get { return m_prefix; }
179 }
180
181 public override bool Read() {
182 if (m_state != ReadState.Interactive && m_state != ReadState.Initial)
183 return false;
184
185 if (m_state == ReadState.Initial)
186 m_state = ReadState.Interactive;
187
188 try {
189 switch (m_parser.ElementType) {
190 case JSONElementType.Value:
191 switch (m_valueContext) {
192 case ValueContext.ElementStart:
193 SetLocalName(String.Empty);
194 m_valueContext = ValueContext.ElementValue;
195 return true;
196 case ValueContext.ElementValue:
197 RestoreLocalName();
198 m_valueContext = ValueContext.ElementEnd;
199 return true;
200 case ValueContext.ElementEmpty:
201 case ValueContext.ElementEnd:
202 RestoreLocalName();
203 break;
204 }
205 break;
206 case JSONElementType.EndArray:
207 case JSONElementType.EndObject:
208 RestoreLocalName();
209 break;
210 }
211 string itemName = m_parser.ElementType == JSONElementType.None ? m_rootName : m_flattenArrays ? m_localName.localName : m_arrayItemName;
212 while (m_parser.Read()) {
213 if (!String.IsNullOrEmpty(m_parser.ElementName))
214 itemName = m_parser.ElementName;
215
216 switch (m_parser.ElementType) {
217 case JSONElementType.BeginArray:
218 if (m_flattenArrays && !m_localName.isArray) {
219 m_depthCorrection--;
220 SetLocalName(itemName, true);
221 continue;
222 }
223 SetLocalName(itemName, true);
224 break;
225 case JSONElementType.BeginObject:
226 SetLocalName(itemName);
227 break;
228 case JSONElementType.EndArray:
229 if (m_flattenArrays && !m_localNameStack.Peek().isArray) {
230 RestoreLocalName();
231 m_depthCorrection++;
232 continue;
233 }
234 break;
235 case JSONElementType.EndObject:
236 break;
237 case JSONElementType.Value:
238 SetLocalName(itemName);
239 m_valueContext = m_parser.ElementValue == null ? ValueContext.ElementEmpty : ValueContext.ElementStart;
240 break;
241 }
242 return true;
243 }
244
245 m_state = ReadState.EndOfFile;
246 return false;
247 } catch {
248 m_state = ReadState.Error;
249 throw;
250 }
251 }
252
253 public override bool ReadAttributeValue() {
254 return false;
255 }
256
257 public override ReadState ReadState {
258 get { return m_state; }
259 }
260
261 public override void ResolveEntity() {
262 // do nothing
263 }
264
265 public override string Value {
266 get {
267 if (m_parser.ElementValue == null)
268 return String.Empty;
269 if (Convert.GetTypeCode(m_parser.ElementValue) == TypeCode.Double)
270 return ((double)m_parser.ElementValue).ToString(CultureInfo.InvariantCulture);
271 return m_parser.ElementValue.ToString();
272 }
273 }
274
275 void SetLocalName(string name) {
276 m_localNameStack.Push(m_localName);
277 m_localName.localName = name;
278 m_localName.isArray = false;
279 }
280
281 void SetLocalName(string name, bool isArray) {
282 m_localNameStack.Push(m_localName);
283 m_localName.localName = name;
284 m_localName.isArray = isArray;
285 }
286
287 void RestoreLocalName() {
288 m_localName = m_localNameStack.Pop();
289 }
290
291 public override void Close() {
292
293 }
294
295 protected override void Dispose(bool disposing) {
296 #if MONO
297 disposing = true;
298 #endif
299 if (disposing) {
300 m_parser.Dispose();
301 }
302 base.Dispose(disposing);
303 }
304
305 public static JSONXmlReader Create(string file, JSONXmlReaderOptions options) {
306 return Create(File.OpenText(file), options);
307 }
308
309 /// <summary>
310 /// Creates the XmlReader for the specified text stream with JSON data.
311 /// </summary>
312 /// <param name="reader">Text reader.</param>
313 /// <param name="options">Options.</param>
314 /// <remarks>
315 /// The reader will be disposed when the XmlReader is disposed.
316 /// </remarks>
317 public static JSONXmlReader Create(TextReader reader, JSONXmlReaderOptions options) {
318 return new JSONXmlReader(new JSONParser(reader), options);
319 }
320
321 /// <summary>
322 /// Creates the XmlReader for the specified stream with JSON data.
323 /// </summary>
324 /// <param name="stream">Stream.</param>
325 /// <param name="options">Options.</param>
326 /// <remarks>
327 /// The stream will be disposed when the XmlReader is disposed.
328 /// </remarks>
329 public static JSONXmlReader Create(Stream stream, JSONXmlReaderOptions options) {
330 Safe.ArgumentNotNull(stream, "stream");
331 // HACK don't dispose StreaReader to keep stream opened
332 return Create(new StreamReader(stream), options);
333 }
334 }
335 }
@@ -0,0 +1,62
1
2 using System.Xml;
3
4 namespace Implab.Formats.JSON {
5 /// <summary>
6 /// Набор необязательных параметров для <see cref="JSONXmlReader"/>, позволяющий управлять процессом
7 /// интерпретации <c>JSON</c> документа.
8 /// </summary>
9 public class JSONXmlReaderOptions {
10 /// <summary>
11 /// Пространство имен в котором будут располагаться читаемые элементы документа
12 /// </summary>
13 public string NamespaceURI {
14 get;
15 set;
16 }
17
18 /// <summary>
19 /// Интерпретировать массивы как множественные элементы (убирает один уровень вложенности), иначе массив
20 /// представляется в виде узла, дочерними элементами которого являются элементы массива, имена дочерних элементов
21 /// определяются свойством <see cref="ArrayItemName"/>. По умолчанию <c>false</c>.
22 /// </summary>
23 public bool FlattenArrays {
24 get;
25 set;
26 }
27
28 /// <summary>
29 /// Префикс, для узлов документа
30 /// </summary>
31 public string NodesPrefix {
32 get;
33 set;
34 }
35
36 /// <summary>
37 /// Имя корневого элемента в xml документе
38 /// </summary>
39 public string RootName {
40 get;
41 set;
42 }
43
44 /// <summary>
45 /// Имя элемента для массивов, если не включена опция <see cref="FlattenArrays"/>.
46 /// По умолчанию <c>item</c>.
47 /// </summary>
48 public string ArrayItemName {
49 get;
50 set;
51 }
52
53 /// <summary>
54 /// Таблица атомизированных строк для построения документа.
55 /// </summary>
56 public XmlNameTable NameTable {
57 get;
58 set;
59 }
60
61 }
62 }
@@ -0,0 +1,44
1 namespace Implab.Formats.JSON {
2 /// <summary>
3 /// Тип токенов, возвращаемых <see cref="JSONScanner"/>.
4 /// </summary>
5 public enum JsonTokenType : int {
6 None = 0,
7 /// <summary>
8 /// Начало объекта
9 /// </summary>
10 BeginObject,
11 /// <summary>
12 /// Конец объекта
13 /// </summary>
14 EndObject,
15 /// <summary>
16 /// Начало массива
17 /// </summary>
18 BeginArray,
19 /// <summary>
20 /// Конец массива
21 /// </summary>
22 EndArray,
23 /// <summary>
24 /// Строка
25 /// </summary>
26 String,
27 /// <summary>
28 /// Число
29 /// </summary>
30 Number,
31 /// <summary>
32 /// Литерал
33 /// </summary>
34 Literal,
35 /// <summary>
36 /// Разделитель имени <c>:</c>
37 /// </summary>
38 NameSeparator,
39 /// <summary>
40 /// Разделитель имени <c>,</c>
41 /// </summary>
42 ValueSeparator
43 }
44 }
@@ -0,0 +1,52
1 using Implab;
2 using Implab.Formats;
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.Formats.JSON {
11 /// <summary>
12 /// Класс для преобразования экранированной строки JSON
13 /// </summary>
14 static class StringTranslator {
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 internal static char TranslateEscapedChar(char symbol) {
38 return _escMap[symbol];
39 }
40
41 internal static char TranslateHexUnicode(char[] symbols, int offset) {
42 Debug.Assert(symbols != null);
43 Debug.Assert(symbols.Length - offset >= 4);
44
45 int value = (_hexMap[symbols[offset]] << 12)
46 | (_hexMap[symbols[offset + 1]] << 8)
47 | (_hexMap[symbols[offset + 2]] << 4)
48 | (_hexMap[symbols[offset + 3]]);
49 return (char)value;
50 }
51 }
52 }
@@ -0,0 +1,30
1 using System;
2 using System.IO;
3
4 namespace Implab.Formats {
5 public class ReaderScanner: TextScanner {
6 const int CHUNK_SIZE = 1024*4;
7 const int BUFFER_MAX = CHUNK_SIZE*1024;
8
9 readonly TextReader m_reader;
10
11 public ReaderScanner(TextReader reader, int limit, int chunk) : base(limit, chunk) {
12 Safe.ArgumentNotNull(reader, "reader");
13 m_reader = reader;
14 }
15
16 public ReaderScanner(TextReader reader) : this(reader, BUFFER_MAX, CHUNK_SIZE) {
17 }
18
19 protected override int Read(char[] buffer, int offset, int size) {
20 return m_reader.Read(buffer, offset, size);
21 }
22
23 protected override void Dispose(bool disposing) {
24 if (disposing)
25 Safe.Dispose(m_reader);
26 base.Dispose(disposing);
27 }
28 }
29 }
30
@@ -0,0 +1,30
1 namespace Implab.Formats {
2 /// <summary>
3 /// Represents a scanner configuration usefull to recongnize token, based on the DFA.
4 /// </summary>
5 public class ScannerContext<TTag> {
6
7 public int[,] Dfa { get; private set; }
8
9 public bool[] Final { get; private set; }
10
11 public TTag[][] Tags { get; private set; }
12
13 public int State { get; private set; }
14
15 public int[] Alphabet { get; private set; }
16
17 public ScannerContext(int[,] dfa, bool[] final, TTag[][] tags, int state, int[] alphabet) {
18 Dfa = dfa;
19 Final = final;
20 Tags = tags;
21 State = state;
22 Alphabet = alphabet;
23 }
24
25 public bool Execute(TextScanner scanner, out TTag[] tag) {
26 return scanner.ReadToken(Dfa, Final, Tags, State, Alphabet, out tag);
27 }
28 }
29 }
30
@@ -0,0 +1,18
1 using System;
2
3 namespace Implab.Formats {
4 public class StringScanner: TextScanner {
5 const int CHUNK_SIZE = 1024;
6
7 public StringScanner(string text) : base(null) {
8 Safe.ArgumentNotNull(text, "text");
9 var data = text.ToCharArray();
10 Feed(data, 0, data.Length);
11 }
12
13 protected override int Read(char[] buffer, int offset, int size) {
14 return 0;
15 }
16 }
17 }
18
@@ -0,0 +1,157
1 using System;
2 using Implab.Components;
3 using System.Diagnostics;
4 using Implab.Automaton;
5 using System.Text;
6
7 namespace Implab.Formats {
8 public abstract class TextScanner : Disposable {
9 readonly int m_bufferMax;
10 readonly int m_chunkSize;
11
12 char[] m_buffer;
13 int m_bufferOffset;
14 int m_bufferSize;
15 int m_tokenOffset;
16 int m_tokenLength;
17
18 /// <summary>
19 /// Initializes a new instance of the <see cref="Implab.Formats.TextScanner"/> class.
20 /// </summary>
21 /// <param name="bufferMax">Buffer max.</param>
22 /// <param name="chunkSize">Chunk size.</param>
23 protected TextScanner(int bufferMax, int chunkSize) {
24 Debug.Assert(m_chunkSize <= m_bufferMax);
25
26 m_bufferMax = bufferMax;
27 m_chunkSize = chunkSize;
28 }
29
30 /// <summary>
31 /// Initializes a new instance of the <see cref="Implab.Formats.TextScanner"/> class.
32 /// </summary>
33 /// <param name="buffer">Buffer.</param>
34 protected TextScanner(char[] buffer) {
35 if (buffer != null) {
36 m_buffer = buffer;
37 m_bufferSize = buffer.Length;
38 }
39 }
40
41 /// <summary>
42 /// (hungry) Reads the next token.
43 /// </summary>
44 /// <returns><c>true</c>, if token internal was read, <c>false</c> if there is no more tokens in the stream.</returns>
45 /// <param name="dfa">The transition map for the automaton</param>
46 /// <param name="final">Final states of the automaton.</param>
47 /// <param name="tags">Tags.</param>
48 /// <param name="state">The initial state for the automaton.</param>
49 /// <param name="alphabet"></param>
50 /// <param name = "tag"></param>
51 internal bool ReadToken<TTag>(int[,] dfa, bool[] final, TTag[][] tags, int state, int[] alphabet, out TTag[] tag) {
52 m_tokenLength = 0;
53 tag = null;
54
55 var maxSymbol = alphabet.Length - 1;
56 int next;
57 do {
58 // after the next chunk is read the offset in the buffer may change
59 int pos = m_bufferOffset + m_tokenLength;
60 next = state;
61 while (pos < m_bufferSize) {
62 var ch = m_buffer[pos];
63
64 next = dfa[next, ch > maxSymbol ? AutomatonConst.UNCLASSIFIED_INPUT : alphabet[ch]];
65
66 if (next == AutomatonConst.UNREACHABLE_STATE)
67 break;
68
69 state = next;
70 pos++;
71 }
72 m_tokenLength = pos - m_bufferOffset;
73 } while (next != AutomatonConst.UNREACHABLE_STATE && Feed());
74
75 m_tokenOffset = m_bufferOffset;
76 m_bufferOffset += m_tokenLength;
77
78 if (final[state]) {
79 tag = tags[state];
80 return true;
81 }
82
83 if (m_bufferOffset == m_bufferSize) {
84 if (m_tokenLength == 0) //EOF
85 return false;
86
87 throw new ParserException();
88 }
89
90 throw new ParserException(String.Format("Unexpected symbol '{0}'", m_buffer[m_bufferOffset]));
91
92 }
93
94 protected void Feed(char[] buffer, int offset, int length) {
95 m_buffer = buffer;
96 m_bufferOffset = offset;
97 m_bufferSize = offset + length;
98 }
99
100 protected bool Feed() {
101 if (m_chunkSize <= 0)
102 return false;
103
104 if (m_buffer != null) {
105 var free = m_buffer.Length - m_bufferSize;
106
107 if (free < m_chunkSize) {
108 free += m_chunkSize;
109 var used = m_bufferSize - m_bufferOffset;
110 var size = used + free;
111
112 if (size > m_bufferMax)
113 throw new ParserException(String.Format("The buffer limit ({0} Kb) is reached", m_bufferMax / 1024));
114
115 var temp = new char[size];
116
117 var read = Read(temp, used, m_chunkSize);
118 if (read == 0)
119 return false;
120
121 Array.Copy(m_buffer, m_bufferOffset, temp, 0, used);
122
123 m_bufferOffset = 0;
124 m_bufferSize = used + read;
125 m_buffer = temp;
126 } else {
127 var read = Read(m_buffer, m_bufferSize, m_chunkSize);
128 if (read == 0)
129 return false;
130 m_bufferSize += m_chunkSize;
131 }
132 return true;
133 } else {
134 Debug.Assert(m_bufferOffset == 0);
135 m_buffer = new char[m_chunkSize];
136 m_bufferSize = Read(m_buffer, 0, m_chunkSize);
137 return (m_bufferSize != 0);
138 }
139 }
140
141 protected abstract int Read(char[] buffer, int offset, int size);
142
143 public string GetTokenValue() {
144 return new String(m_buffer, m_tokenOffset, m_tokenLength);
145 }
146
147 public void CopyTokenTo(char[] buffer, int offset) {
148 Array.Copy(m_buffer, m_tokenOffset,buffer, offset, m_tokenLength);
149 }
150
151 public void CopyTokenTo(StringBuilder sb) {
152 sb.Append(m_buffer, m_tokenOffset, m_tokenLength);
153 }
154
155 }
156 }
157
@@ -0,0 +1,4
1 <?xml version="1.0" encoding="utf-8"?>
2 <packages>
3 <package id="System.Text.Json" version="2.0.0.11" targetFramework="net45" />
4 </packages> No newline at end of file
1 NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
@@ -1,17 +1,20
1 1 syntax: glob
2 2 Implab.Test/bin/
3 3 *.user
4 4 Implab.Test/obj/
5 5 *.userprefs
6 6 Implab/bin/
7 7 Implab/obj/
8 8 TestResults/
9 9 Implab.Fx/obj/
10 10 Implab.Fx/bin/
11 11 Implab.Fx.Test/bin/
12 12 Implab.Fx.Test/obj/
13 13 _ReSharper.Implab/
14 14 Implab.Diagnostics.Interactive/bin/
15 15 Implab.Diagnostics.Interactive/obj/
16 16 MonoPlay/bin/
17 17 MonoPlay/obj/
18 Implab.Test/Implab.Format.Test/bin/
19 Implab.Test/Implab.Format.Test/obj/
20 *.suo
@@ -1,45 +1,52
1 1 <?xml version="1.0" encoding="utf-8"?>
2 2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 3 <PropertyGroup>
4 4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 6 <ProductVersion>8.0.30703</ProductVersion>
7 7 <SchemaVersion>2.0</SchemaVersion>
8 8 <ProjectGuid>{4D364996-7ECD-4193-8F90-F223FFEA49DA}</ProjectGuid>
9 9 <OutputType>Library</OutputType>
10 10 <RootNamespace>Implab.Format.Test</RootNamespace>
11 11 <AssemblyName>Implab.Format.Test</AssemblyName>
12 12 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
13 <ReleaseVersion>0.2</ReleaseVersion>
13 14 </PropertyGroup>
14 15 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
15 16 <DebugSymbols>true</DebugSymbols>
16 17 <DebugType>full</DebugType>
17 18 <Optimize>false</Optimize>
18 19 <OutputPath>bin\Debug</OutputPath>
19 20 <DefineConstants>DEBUG;</DefineConstants>
20 21 <ErrorReport>prompt</ErrorReport>
21 22 <WarningLevel>4</WarningLevel>
22 23 <ConsolePause>false</ConsolePause>
23 24 </PropertyGroup>
24 25 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
25 26 <DebugType>full</DebugType>
26 27 <Optimize>true</Optimize>
27 28 <OutputPath>bin\Release</OutputPath>
28 29 <ErrorReport>prompt</ErrorReport>
29 30 <WarningLevel>4</WarningLevel>
30 31 <ConsolePause>false</ConsolePause>
31 32 </PropertyGroup>
32 33 <ItemGroup>
33 34 <Reference Include="System" />
34 35 <Reference Include="nunit.framework">
35 <HintPath>..\..\packages\NUnit.3.0.1\lib\net45\nunit.framework.dll</HintPath>
36 <HintPath>..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
36 37 </Reference>
37 38 </ItemGroup>
38 39 <ItemGroup>
39 40 <Compile Include="JsonTests.cs" />
40 41 </ItemGroup>
41 42 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
42 43 <ItemGroup>
44 <ProjectReference Include="..\..\Implab\Implab.csproj">
45 <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
46 <Name>Implab</Name>
47 </ProjectReference>
48 </ItemGroup>
49 <ItemGroup>
43 50 <None Include="packages.config" />
44 51 </ItemGroup>
45 52 </Project> No newline at end of file
@@ -1,12 +1,88
1 1 using NUnit.Framework;
2 2 using System;
3 using Implab.Formats.JSON;
4 using Implab.Automaton;
3 5
4 6 namespace Implab.Format.Test {
5 [TestFixture()]
7 [TestFixture]
6 8 public class JsonTests {
7 [Test()]
8 public void TestCase() {
9 [Test]
10 public void TestScannerValidTokens() {
11 using (var scanner = new JSONScanner(@"9123, -123, 0, 0.1, -0.2, -0.1e3, 1.3E-3, ""some \t\n\u0020 text"", literal []{}:")) {
12
13 Tuple<JsonTokenType,object>[] expexted = {
14 new Tuple<JsonTokenType,object>(JsonTokenType.Number, 9123d),
15 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
16 new Tuple<JsonTokenType,object>(JsonTokenType.Number, -123d),
17 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
18 new Tuple<JsonTokenType,object>(JsonTokenType.Number, 0d),
19 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
20 new Tuple<JsonTokenType,object>(JsonTokenType.Number, 0.1d),
21 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
22 new Tuple<JsonTokenType,object>(JsonTokenType.Number, -0.2d),
23 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
24 new Tuple<JsonTokenType,object>(JsonTokenType.Number, -0.1e3d),
25 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
26 new Tuple<JsonTokenType,object>(JsonTokenType.Number, 1.3E-3d),
27 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
28 new Tuple<JsonTokenType,object>(JsonTokenType.String, "some \t\n text"),
29 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
30 new Tuple<JsonTokenType,object>(JsonTokenType.Literal, "literal"),
31 new Tuple<JsonTokenType,object>(JsonTokenType.BeginArray, " ["),
32 new Tuple<JsonTokenType,object>(JsonTokenType.EndArray, "]"),
33 new Tuple<JsonTokenType,object>(JsonTokenType.BeginObject, "{"),
34 new Tuple<JsonTokenType,object>(JsonTokenType.EndObject, "}"),
35 new Tuple<JsonTokenType,object>(JsonTokenType.NameSeparator, ":")
36 };
37
38 object value;
39 JsonTokenType tokenType;
40 for (var i = 0; i < expexted.Length; i++) {
41
42 Assert.IsTrue(scanner.ReadToken(out value, out tokenType));
43 Assert.AreEqual(expexted[i].Item1, tokenType);
44 Assert.AreEqual(expexted[i].Item2, value);
45 }
46
47 Assert.IsFalse(scanner.ReadToken(out value, out tokenType));
48 }
49 }
50
51 [Test]
52 public void TestScannerBadTokens() {
53 var bad = new [] {
54 " 1",
55 " literal",
56 " \"",
57 "\"unclosed string",
58 "1.bad",
59 "001", // should be read as three numbers
60 "--10",
61 "+10",
62 "1.0.0",
63 "1e1.0",
64 "l1teral0",
65 ".123",
66 "-.123"
67 };
68
69 foreach (var json in bad)
70 using (var scanner = new JSONScanner(json)) {
71 try {
72 object value;
73 JsonTokenType token;
74 scanner.ReadToken(out value, out token);
75 if (!Object.Equals(value,json)) {
76 Console.WriteLine("'{0}' is read as {1}", json, value is String ? String.Format("'{0}'", value) : value );
77 continue;
78 }
79 Assert.Fail("Token '{0}' shouldn't pass", json);
80 } catch (ParserException e) {
81 Console.WriteLine(e.Message);
82 }
83 }
84
9 85 }
10 86 }
11 87 }
12 88
@@ -1,4 +1,4
1 1 <?xml version="1.0" encoding="utf-8"?>
2 2 <packages>
3 <package id="NUnit" version="3.0.1" targetFramework="net45" />
3 <package id="NUnit" version="2.6.4" targetFramework="net45" />
4 4 </packages> No newline at end of file
@@ -1,82 +1,84
1 1 <?xml version="1.0" encoding="utf-8"?>
2 2 <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 3 <PropertyGroup>
4 4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 6 <ProductVersion>8.0.30703</ProductVersion>
7 7 <SchemaVersion>2.0</SchemaVersion>
8 8 <ProjectGuid>{63F92C0C-61BF-48C0-A377-8D67C3C661D0}</ProjectGuid>
9 9 <OutputType>Library</OutputType>
10 10 <AppDesignerFolder>Properties</AppDesignerFolder>
11 11 <RootNamespace>Implab.Test</RootNamespace>
12 12 <AssemblyName>Implab.Test</AssemblyName>
13 13 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
14 14 <FileAlignment>512</FileAlignment>
15 15 <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
16 16 <TargetFrameworkProfile />
17 17 </PropertyGroup>
18 18 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
19 19 <DebugSymbols>true</DebugSymbols>
20 20 <DebugType>full</DebugType>
21 21 <Optimize>false</Optimize>
22 22 <OutputPath>bin\Debug\</OutputPath>
23 23 <DefineConstants>DEBUG;TRACE</DefineConstants>
24 24 <ErrorReport>prompt</ErrorReport>
25 25 <WarningLevel>4</WarningLevel>
26 26 <Prefer32Bit>false</Prefer32Bit>
27 27 </PropertyGroup>
28 28 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
29 29 <DebugType>pdbonly</DebugType>
30 30 <Optimize>true</Optimize>
31 31 <OutputPath>bin\Release\</OutputPath>
32 32 <DefineConstants>TRACE</DefineConstants>
33 33 <ErrorReport>prompt</ErrorReport>
34 34 <WarningLevel>4</WarningLevel>
35 35 <Prefer32Bit>false</Prefer32Bit>
36 36 </PropertyGroup>
37 37 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
38 38 <DebugSymbols>true</DebugSymbols>
39 39 <DebugType>full</DebugType>
40 40 <Optimize>false</Optimize>
41 41 <OutputPath>bin\Debug\</OutputPath>
42 42 <DefineConstants>DEBUG;TRACE</DefineConstants>
43 43 <ErrorReport>prompt</ErrorReport>
44 44 <WarningLevel>4</WarningLevel>
45 45 <Prefer32Bit>false</Prefer32Bit>
46 46 </PropertyGroup>
47 47 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
48 48 <DebugType>pdbonly</DebugType>
49 49 <Optimize>true</Optimize>
50 50 <OutputPath>bin\Release\</OutputPath>
51 51 <DefineConstants>TRACE</DefineConstants>
52 52 <ErrorReport>prompt</ErrorReport>
53 53 <WarningLevel>4</WarningLevel>
54 54 <Prefer32Bit>false</Prefer32Bit>
55 55 </PropertyGroup>
56 56 <ItemGroup>
57 57 <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
58 58 <Reference Include="System" />
59 59 <Reference Include="System.Core">
60 60 <RequiredTargetFramework>3.5</RequiredTargetFramework>
61 61 </Reference>
62 62 </ItemGroup>
63 63 <ItemGroup>
64 64 <Compile Include="AsyncTests.cs" />
65 <Compile Include="CancelationTests.cs" />
65 66 <Compile Include="PromiseHelper.cs" />
66 67 <Compile Include="Properties\AssemblyInfo.cs" />
68 <Compile Include="RunnableComponentTests.cs" />
67 69 </ItemGroup>
68 70 <ItemGroup>
69 71 <ProjectReference Include="..\Implab\Implab.csproj">
70 72 <Project>{99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}</Project>
71 73 <Name>Implab</Name>
72 74 </ProjectReference>
73 75 </ItemGroup>
74 76 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
75 77 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
76 78 Other similar extension points exist, see Microsoft.Common.targets.
77 79 <Target Name="BeforeBuild">
78 80 </Target>
79 81 <Target Name="AfterBuild">
80 82 </Target>
81 83 -->
82 84 </Project> No newline at end of file
@@ -1,68 +1,69
1 1 <?xml version="1.0" encoding="utf-8"?>
2 2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 3 <PropertyGroup>
4 4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 6 <ProductVersion>8.0.30703</ProductVersion>
7 7 <SchemaVersion>2.0</SchemaVersion>
8 8 <ProjectGuid>{2BD05F84-E067-4B87-9477-FDC2676A21C6}</ProjectGuid>
9 9 <OutputType>Library</OutputType>
10 10 <RootNamespace>Implab.Test</RootNamespace>
11 11 <AssemblyName>Implab.Test</AssemblyName>
12 12 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
13 13 <ReleaseVersion>0.2</ReleaseVersion>
14 14 </PropertyGroup>
15 15 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
16 16 <DebugSymbols>true</DebugSymbols>
17 17 <DebugType>full</DebugType>
18 18 <Optimize>false</Optimize>
19 19 <OutputPath>bin\Debug</OutputPath>
20 20 <DefineConstants>DEBUG;MONO</DefineConstants>
21 21 <ErrorReport>prompt</ErrorReport>
22 22 <WarningLevel>4</WarningLevel>
23 23 <ConsolePause>false</ConsolePause>
24 24 </PropertyGroup>
25 25 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
26 26 <Optimize>true</Optimize>
27 27 <OutputPath>bin\Release</OutputPath>
28 28 <ErrorReport>prompt</ErrorReport>
29 29 <WarningLevel>4</WarningLevel>
30 30 <ConsolePause>false</ConsolePause>
31 31 <DefineConstants>MONO</DefineConstants>
32 32 </PropertyGroup>
33 33 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
34 34 <DebugSymbols>true</DebugSymbols>
35 35 <DebugType>full</DebugType>
36 36 <Optimize>false</Optimize>
37 37 <OutputPath>bin\Debug</OutputPath>
38 38 <DefineConstants>DEBUG;TRACE;NET_4_5;MONO</DefineConstants>
39 39 <ErrorReport>prompt</ErrorReport>
40 40 <WarningLevel>4</WarningLevel>
41 41 <ConsolePause>false</ConsolePause>
42 42 </PropertyGroup>
43 43 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
44 44 <Optimize>true</Optimize>
45 45 <OutputPath>bin\Release</OutputPath>
46 46 <DefineConstants>NET_4_5;MONO</DefineConstants>
47 47 <ErrorReport>prompt</ErrorReport>
48 48 <WarningLevel>4</WarningLevel>
49 49 <ConsolePause>false</ConsolePause>
50 50 </PropertyGroup>
51 51 <ItemGroup>
52 52 <Reference Include="System" />
53 53 <Reference Include="nunit.framework" />
54 54 </ItemGroup>
55 55 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
56 56 <ItemGroup>
57 57 <Compile Include="AsyncTests.cs" />
58 58 <Compile Include="PromiseHelper.cs" />
59 59 <Compile Include="Properties\AssemblyInfo.cs" />
60 60 <Compile Include="CancelationTests.cs" />
61 <Compile Include="RunnableComponentTests.cs" />
61 62 </ItemGroup>
62 63 <ItemGroup>
63 64 <ProjectReference Include="..\Implab\Implab.csproj">
64 65 <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
65 66 <Name>Implab</Name>
66 67 </ProjectReference>
67 68 </ItemGroup>
68 69 </Project> No newline at end of file
@@ -1,304 +1,300
1 1 using System;
2 2 using Implab.Parallels;
3 3 using System.Threading;
4 4 using System.Reflection;
5 5
6 6 namespace Implab {
7 7 public abstract class AbstractEvent<THandler> : ICancellationToken, ICancellable {
8 8
9 9 const int UNRESOLVED_SATE = 0;
10 10 const int TRANSITIONAL_STATE = 1;
11 11 protected const int SUCCEEDED_STATE = 2;
12 12 protected const int REJECTED_STATE = 3;
13 13 protected const int CANCELLED_STATE = 4;
14 14
15 15 const int CANCEL_NOT_REQUESTED = 0;
16 16 const int CANCEL_REQUESTING = 1;
17 17 const int CANCEL_REQUESTED = 2;
18 18
19 19 const int RESERVED_HANDLERS_COUNT = 4;
20 20
21 21 int m_state;
22 22 Exception m_error;
23 23 int m_handlersCount;
24 24
25 25 //readonly THandler[] m_handlers = new THandler[RESERVED_HANDLERS_COUNT];
26 26 THandler[] m_handlers;
27 27 MTQueue<THandler> m_extraHandlers;
28 28 int m_handlerPointer = -1;
29 29 int m_handlersCommited;
30 30
31 31 int m_cancelRequest;
32 32 Exception m_cancelationReason;
33 33 MTQueue<Action<Exception>> m_cancelationHandlers;
34 34
35 35
36 36 #region state managment
37 37 bool BeginTransit() {
38 38 return UNRESOLVED_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, UNRESOLVED_SATE);
39 39 }
40 40
41 41 void CompleteTransit(int state) {
42 42 if (TRANSITIONAL_STATE != Interlocked.CompareExchange(ref m_state, state, TRANSITIONAL_STATE))
43 43 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
44 44 }
45 45
46 46 void WaitTransition() {
47 47 while (m_state == TRANSITIONAL_STATE) {
48 48 Thread.MemoryBarrier();
49 49 }
50 50 }
51 51
52 52 protected bool BeginSetResult() {
53 53 if (!BeginTransit()) {
54 54 WaitTransition();
55 55 if (m_state != CANCELLED_STATE)
56 56 throw new InvalidOperationException("The promise is already resolved");
57 57 return false;
58 58 }
59 59 return true;
60 60 }
61 61
62 62 protected void EndSetResult() {
63 63 CompleteTransit(SUCCEEDED_STATE);
64 64 Signal();
65 65 }
66 66
67 67
68 68
69 69 /// <summary>
70 70 /// Выполняет обещание, сообщая об ошибке
71 71 /// </summary>
72 72 /// <remarks>
73 73 /// Поскольку обещание должно работать в многопточной среде, при его выполнении сразу несколько потоков
74 74 /// могу вернуть ошибку, при этом только первая будет использована в качестве результата, остальные
75 75 /// будут проигнорированы.
76 76 /// </remarks>
77 77 /// <param name="error">Исключение возникшее при выполнении операции</param>
78 78 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
79 79 protected void SetError(Exception error) {
80 80 if (BeginTransit()) {
81 if (error is OperationCanceledException) {
82 m_error = error.InnerException;
83 CompleteTransit(CANCELLED_STATE);
84 } else {
85 m_error = error is PromiseTransientException ? error.InnerException : error;
81 m_error = error;
86 82 CompleteTransit(REJECTED_STATE);
87 }
83
88 84 Signal();
89 85 } else {
90 86 WaitTransition();
91 87 if (m_state == SUCCEEDED_STATE)
92 88 throw new InvalidOperationException("The promise is already resolved");
93 89 }
94 90 }
95 91
96 92 /// <summary>
97 93 /// Отменяет операцию, если это возможно.
98 94 /// </summary>
99 95 /// <remarks>Для определения была ли операция отменена следует использовать свойство <see cref="IsCancelled"/>.</remarks>
100 96 protected void SetCancelled(Exception reason) {
101 97 if (BeginTransit()) {
102 98 m_error = reason;
103 99 CompleteTransit(CANCELLED_STATE);
104 100 Signal();
105 101 }
106 102 }
107 103
108 104 protected abstract void SignalHandler(THandler handler, int signal);
109 105
110 106 void Signal() {
111 107 var hp = m_handlerPointer;
112 108 var slot = hp +1 ;
113 109 while (slot < m_handlersCommited) {
114 110 if (Interlocked.CompareExchange(ref m_handlerPointer, slot, hp) == hp) {
115 111 SignalHandler(m_handlers[slot], m_state);
116 112 }
117 113 hp = m_handlerPointer;
118 114 slot = hp +1 ;
119 115 }
120 116
121 117
122 118 if (m_extraHandlers != null) {
123 119 THandler handler;
124 120 while (m_extraHandlers.TryDequeue(out handler))
125 121 SignalHandler(handler, m_state);
126 122 }
127 123 }
128 124
129 125 #endregion
130 126
131 127 protected abstract Signal GetResolveSignal();
132 128
133 129 #region synchronization traits
134 130 protected void WaitResult(int timeout) {
135 131 if (!(IsResolved || GetResolveSignal().Wait(timeout)))
136 132 throw new TimeoutException();
137 133
138 134 switch (m_state) {
139 135 case SUCCEEDED_STATE:
140 136 return;
141 137 case CANCELLED_STATE:
142 throw new OperationCanceledException();
138 throw new OperationCanceledException("The operation has been cancelled", m_error);
143 139 case REJECTED_STATE:
144 140 throw new TargetInvocationException(m_error);
145 141 default:
146 throw new ApplicationException(String.Format("Invalid promise state {0}", m_state));
142 throw new ApplicationException(String.Format("The promise state {0} is invalid", m_state));
147 143 }
148 144 }
149 145 #endregion
150 146
151 147 #region handlers managment
152 148
153 149 protected void AddHandler(THandler handler) {
154 150
155 151 if (m_state > 1) {
156 152 // the promise is in the resolved state, just invoke the handler
157 153 SignalHandler(handler, m_state);
158 154 } else {
159 155 var slot = Interlocked.Increment(ref m_handlersCount) - 1;
160 156
161 157 if (slot < RESERVED_HANDLERS_COUNT) {
162 158
163 159 if (slot == 0) {
164 160 m_handlers = new THandler[RESERVED_HANDLERS_COUNT];
165 161 } else {
166 162 while (m_handlers == null)
167 163 Thread.MemoryBarrier();
168 164 }
169 165
170 166 m_handlers[slot] = handler;
171 167
172 168 while (slot != Interlocked.CompareExchange(ref m_handlersCommited, slot + 1, slot)) {
173 169 }
174 170
175 171 if (m_state > 1) {
176 172 do {
177 173 var hp = m_handlerPointer;
178 174 slot = hp + 1;
179 175 if (slot < m_handlersCommited) {
180 176 if (Interlocked.CompareExchange(ref m_handlerPointer, slot, hp) != hp)
181 177 continue;
182 178 SignalHandler(m_handlers[slot], m_state);
183 179 }
184 180 break;
185 181 } while(true);
186 182 }
187 183 } else {
188 184 if (slot == RESERVED_HANDLERS_COUNT) {
189 185 m_extraHandlers = new MTQueue<THandler>();
190 186 } else {
191 187 while (m_extraHandlers == null)
192 188 Thread.MemoryBarrier();
193 189 }
194 190
195 191 m_extraHandlers.Enqueue(handler);
196 192
197 193 if (m_state > 1 && m_extraHandlers.TryDequeue(out handler))
198 194 // if the promise have been resolved while we was adding the handler to the queue
199 195 // we can't guarantee that someone is still processing it
200 196 // therefore we need to fetch a handler from the queue and execute it
201 197 // note that fetched handler may be not the one that we have added
202 198 // even we can fetch no handlers at all :)
203 199 SignalHandler(handler, m_state);
204 200 }
205 201 }
206 202 }
207 203
208 204 #endregion
209 205
210 206 #region IPromise implementation
211 207
212 208 public bool IsResolved {
213 209 get {
214 210 Thread.MemoryBarrier();
215 211 return m_state > 1;
216 212 }
217 213 }
218 214
219 215 public bool IsCancelled {
220 216 get {
221 217 Thread.MemoryBarrier();
222 218 return m_state == CANCELLED_STATE;
223 219 }
224 220 }
225 221
226 222 #endregion
227 223
228 224 public Exception Error {
229 225 get {
230 226 return m_error;
231 227 }
232 228 }
233 229
234 230 public bool CancelOperationIfRequested() {
235 231 if (IsCancellationRequested) {
236 232 CancelOperation(CancellationReason);
237 233 return true;
238 234 }
239 235 return false;
240 236 }
241 237
242 238 public virtual void CancelOperation(Exception reason) {
243 239 SetCancelled(reason);
244 240 }
245 241
246 242 public void CancellationRequested(Action<Exception> handler) {
247 243 Safe.ArgumentNotNull(handler, "handler");
248 244 if (IsCancellationRequested)
249 245 handler(CancellationReason);
250 246
251 247 if (m_cancelationHandlers == null)
252 248 Interlocked.CompareExchange(ref m_cancelationHandlers, new MTQueue<Action<Exception>>(), null);
253 249
254 250 m_cancelationHandlers.Enqueue(handler);
255 251
256 252 if (IsCancellationRequested && m_cancelationHandlers.TryDequeue(out handler))
257 253 // TryDeque implies MemoryBarrier()
258 254 handler(m_cancelationReason);
259 255 }
260 256
261 257 public bool IsCancellationRequested {
262 258 get {
263 259 do {
264 260 if (m_cancelRequest == CANCEL_NOT_REQUESTED)
265 261 return false;
266 262 if (m_cancelRequest == CANCEL_REQUESTED)
267 263 return true;
268 264 Thread.MemoryBarrier();
269 265 } while(true);
270 266 }
271 267 }
272 268
273 269 public Exception CancellationReason {
274 270 get {
275 271 do {
276 272 Thread.MemoryBarrier();
277 273 } while(m_cancelRequest == CANCEL_REQUESTING);
278 274
279 275 return m_cancelationReason;
280 276 }
281 277 }
282 278
283 279 #region ICancellable implementation
284 280
285 281 public void Cancel() {
286 282 Cancel(null);
287 283 }
288 284
289 285 public void Cancel(Exception reason) {
290 286 if (CANCEL_NOT_REQUESTED == Interlocked.CompareExchange(ref m_cancelRequest, CANCEL_REQUESTING, CANCEL_NOT_REQUESTED)) {
291 287 m_cancelationReason = reason;
292 288 m_cancelRequest = CANCEL_REQUESTED;
293 289 if (m_cancelationHandlers != null) {
294 290 Action<Exception> handler;
295 291 while (m_cancelationHandlers.TryDequeue(out handler))
296 292 handler(m_cancelationReason);
297 293 }
298 294 }
299 295 }
300 296
301 297 #endregion
302 298 }
303 299 }
304 300
@@ -1,142 +1,142
1 1 using System;
2 2 using Implab.Parallels;
3 3
4 4 namespace Implab {
5 5 public abstract class AbstractPromise : AbstractEvent<AbstractPromise.HandlerDescriptor>, IPromise {
6 6 public struct HandlerDescriptor {
7 7 readonly Action m_handler;
8 8 readonly Action<Exception> m_error;
9 9 readonly Action<Exception> m_cancel;
10 10 readonly PromiseEventType m_mask;
11 11
12 12 public HandlerDescriptor(Action success, Action<Exception> error, Action<Exception> cancel) {
13 13 m_handler = success;
14 14 m_error = error;
15 15 m_cancel = cancel;
16 16 m_mask = PromiseEventType.Success;
17 17 }
18 18
19 19 public HandlerDescriptor(Action handler, PromiseEventType mask) {
20 20 m_handler = handler;
21 21 m_error = null;
22 22 m_cancel = null;
23 23 m_mask = mask;
24 24 }
25 25
26 26 public void SignalSuccess() {
27 27 if ((m_mask & PromiseEventType.Success) != 0 && m_handler != null) {
28 28 try {
29 29 m_handler();
30 30 } catch (Exception err) {
31 31 // avoid calling handler twice in case of error
32 32 if (m_error != null)
33 33 SignalError(err);
34 34 }
35 35 }
36 36 }
37 37
38 38 public void SignalError(Exception err) {
39 39 if (m_error != null) {
40 40 try {
41 41 m_error(err);
42 42 // Analysis disable once EmptyGeneralCatchClause
43 43 } catch {
44 44 }
45 45 } else if ((m_mask & PromiseEventType.Error ) != 0 && m_handler != null) {
46 46 try {
47 47 m_handler();
48 48 // Analysis disable once EmptyGeneralCatchClause
49 49 } catch {
50 50 }
51 51 }
52 52 }
53 53
54 54 public void SignalCancel(Exception reason) {
55 55 if (m_cancel != null) {
56 56 try {
57 57 m_cancel(reason);
58 58 } catch (Exception err) {
59 59 SignalError(err);
60 60 }
61 61 } else if ( (m_mask & PromiseEventType.Cancelled) != 0 && m_handler != null) {
62 62 try {
63 63 m_handler();
64 64 // Analysis disable once EmptyGeneralCatchClause
65 65 } catch {
66 66 }
67 67 }
68 68 }
69 69 }
70 70
71 71
72 72 #region implemented abstract members of AbstractPromise
73 73
74 74 protected override void SignalHandler(HandlerDescriptor handler, int signal) {
75 75 switch (signal) {
76 76 case SUCCEEDED_STATE:
77 77 handler.SignalSuccess();
78 78 break;
79 79 case REJECTED_STATE:
80 80 handler.SignalError(Error);
81 81 break;
82 82 case CANCELLED_STATE:
83 83 handler.SignalCancel(CancellationReason);
84 84 break;
85 85 default:
86 86 throw new InvalidOperationException(String.Format("Invalid promise signal: {0}", signal));
87 87 }
88 88 }
89 89
90 90 protected override Signal GetResolveSignal() {
91 91 var signal = new Signal();
92 92 On(signal.Set, PromiseEventType.All);
93 93 return signal;
94 94 }
95 95
96 96 #endregion
97 97
98 98 public Type PromiseType {
99 99 get {
100 100 return typeof(void);
101 101 }
102 102 }
103 103
104 104 public IPromise On(Action success, Action<Exception> error, Action<Exception> cancel) {
105 105 AddHandler(new HandlerDescriptor(success, error, cancel));
106 106 return this;
107 107 }
108 108
109 109 public IPromise On(Action success, Action<Exception> error) {
110 110 AddHandler(new HandlerDescriptor(success, error, null));
111 111 return this;
112 112 }
113 113
114 114 public IPromise On(Action success) {
115 115 AddHandler(new HandlerDescriptor(success, null, null));
116 116 return this;
117 117 }
118 118
119 119 public IPromise On(Action handler, PromiseEventType events) {
120 120 AddHandler(new HandlerDescriptor(handler,events));
121 121 return this;
122 122 }
123 123
124 124 public IPromise<T> Cast<T>() {
125 125 throw new InvalidCastException();
126 126 }
127 127
128 128 public void Join() {
129 129 WaitResult(-1);
130 130 }
131 131
132 132 public void Join(int timeout) {
133 133 WaitResult(timeout);
134 134 }
135 135
136 136 protected void SetResult() {
137 BeginSetResult();
137 if(BeginSetResult())
138 138 EndSetResult();
139 139 }
140 140 }
141 141 }
142 142
@@ -1,25 +1,36
1 1 using System;
2 2
3 3 namespace Implab {
4 4 public class ActionChainTask : ActionChainTaskBase, IDeferred {
5 5 readonly Func<IPromise> m_task;
6 6
7 /// <summary>
8 /// Initializes a new instance of the <see cref="Implab.ActionChainTask"/> class.
9 /// </summary>
10 /// <param name="task">The operation which will be performed when the <see cref="Resolve()"/> is called.</param>
11 /// <param name="error">The error handler which will invoke when the <see cref="Reject(Exception)"/> is called or when the task fails with an error.</param>
12 /// <param name="cancel">The cancellation handler.</param>
13 /// <param name="autoCancellable">If set to <c>true</c> will automatically accept
14 /// all cancel requests before the task is started with <see cref="Resolve()"/>,
15 /// after that all requests are directed to the task.</param>
7 16 public ActionChainTask(Func<IPromise> task, Func<Exception, IPromise> error, Func<Exception, IPromise> cancel, bool autoCancellable) : base(error,cancel, autoCancellable) {
8 17 m_task = task;
9 18 }
10 19
11 20 public void Resolve() {
12 21 if (m_task != null && LockCancelation()) {
13 22 try {
14 23 var p = m_task();
15 p.On(SetResult, HandleErrorInternal, SetCancelled);
24 p.On(SetResult, HandleErrorInternal, HandleCancelInternal);
16 25 CancellationRequested(p.Cancel);
26 } catch (OperationCanceledException reason){
27 HandleCancelInternal(reason);
17 28 } catch(Exception err) {
18 29 HandleErrorInternal(err);
19 30 }
20 31 }
21 32 }
22 33
23 34 }
24 35 }
25 36
@@ -1,58 +1,62
1 1 using System;
2 2 using System.Threading;
3 3
4 4 namespace Implab {
5 public class ActionChainTaskBase : AbstractPromise {
5 public class ActionChainTaskBase : AbstractTask {
6 6 readonly Func<Exception, IPromise> m_error;
7 7 readonly Func<Exception, IPromise> m_cancel;
8 8
9 int m_cancelationLock;
10
11 9 protected ActionChainTaskBase(Func<Exception, IPromise> error, Func<Exception, IPromise> cancel, bool autoCancellable) {
12 10 m_error = error;
13 11 m_cancel = cancel;
14 12 if (autoCancellable)
15 13 CancellationRequested(CancelOperation);
16 14 }
17 15
18 16 public void Reject(Exception error) {
19 17 if (LockCancelation())
20 18 HandleErrorInternal(error);
21 19 }
22 20
23
21 public override void CancelOperation(Exception reason) {
22 if (LockCancelation())
23 // отмена вызвана до начала выполнения задачи
24 HandleCancelInternal(reason);
25 }
24 26
25 public override void CancelOperation(Exception reason) {
26 if (LockCancelation()) {
27 protected void HandleCancelInternal(Exception reason) {
27 28 if (m_cancel != null) {
28 29 try {
29 m_cancel(reason).On(SetResult, SetError, SetCancelled);
30 // вызываем обработчик отмены
31 var p = m_cancel(reason);
32 p.On(SetResult, HandleErrorInternal, SetCancelledInternal);
33 // сообщаем асинхронной операции, что клиент уже не хочет получать результат
34 // т.е. если он инициировал отмену, задача отменилась, вызвался обрабочик отмены
35 // отбработчику сообщили, что результат уже не нужен и уже сам обработчик решает
36 // отдавать ли результат или подтвердить отмену (или вернуть ошибку).
37 CancellationRequested(p.Cancel);
30 38 } catch (Exception err) {
31 39 HandleErrorInternal(err);
32 40 }
33 41 } else {
34 SetCancelled(reason);
35 }
42 HandleErrorInternal(reason ?? new OperationCanceledException());
36 43 }
37 44 }
38 45
39 46 protected void HandleErrorInternal(Exception error) {
40 47 if (m_error != null) {
41 48 try {
42 49 var p = m_error(error);
43 p.On(SetResult,SetError,SetCancelled);
50 p.On(SetResult, SetErrorInternal, SetCancelledInternal);
44 51 CancellationRequested(p.Cancel);
45 52 } catch (Exception err) {
46 SetError(err);
53 SetErrorInternal(error);
47 54 }
48 55 } else {
49 SetError(error);
56 SetErrorInternal(error);
50 57 }
51 58 }
52 59
53 protected bool LockCancelation() {
54 return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0);
55 }
56 60 }
57 61 }
58 62
@@ -1,25 +1,27
1 1 using System;
2 2
3 3 namespace Implab {
4 4 public class ActionChainTask<T> : ActionChainTaskBase, IDeferred<T> {
5 5 readonly Func<T, IPromise> m_task;
6 6
7 7 public ActionChainTask(Func<T, IPromise> task, Func<Exception, IPromise> error, Func<Exception, IPromise> cancel, bool autoCancellable) : base(error,cancel, autoCancellable) {
8 8 m_task = task;
9 9 }
10 10
11 11 public void Resolve(T value) {
12 12 if (m_task != null && LockCancelation()) {
13 13 try {
14 14 var p = m_task(value);
15 p.On(SetResult, HandleErrorInternal, SetCancelled);
15 p.On(SetResult, HandleErrorInternal, HandleCancelInternal);
16 16 CancellationRequested(p.Cancel);
17 } catch (OperationCanceledException reason) {
18 HandleCancelInternal(reason);
17 19 } catch(Exception err) {
18 20 HandleErrorInternal(err);
19 21 }
20 22 }
21 23 }
22 24
23 25 }
24 26 }
25 27
@@ -1,22 +1,24
1 1 using System;
2 2
3 3 namespace Implab {
4 4 public class ActionTask : ActionTaskBase, IDeferred {
5 5 readonly Action m_task;
6 6 public ActionTask(Action task, Action<Exception> error, Action<Exception> cancel, bool autoCancellable) : base(error,cancel, autoCancellable) {
7 7 m_task = task;
8 8 }
9 9
10 10 public void Resolve() {
11 11 if (m_task != null && LockCancelation()) {
12 12 try {
13 13 m_task();
14 14 SetResult();
15 } catch(OperationCanceledException reason) {
16 HandleCancelInternal(reason);
15 17 } catch(Exception err) {
16 18 HandleErrorInternal(err);
17 19 }
18 20 }
19 21 }
20 22 }
21 23 }
22 24
@@ -1,57 +1,53
1 1 using System;
2 using System.Threading;
3 2
4 3 namespace Implab {
5 public class ActionTaskBase : AbstractPromise {
4 public class ActionTaskBase : AbstractTask {
6 5 readonly Action<Exception> m_cancel;
7 6 readonly Action<Exception> m_error;
8 7
9 int m_cancelationLock;
10
11 8 protected ActionTaskBase( Action<Exception> error, Action<Exception> cancel, bool autoCancellable) {
12 9 m_error = error;
13 10 m_cancel = cancel;
14 11 if (autoCancellable)
15 12 CancellationRequested(CancelOperation);
16 13 }
17 14
18 15 public void Reject(Exception error) {
19 16 Safe.ArgumentNotNull(error, "error");
20 17 if (LockCancelation())
21 18 HandleErrorInternal(error);
22 19 }
23 20
21 public override void CancelOperation(Exception reason) {
22 if (LockCancelation())
23 HandleCancelInternal(reason);
24 }
25
24 26 protected void HandleErrorInternal(Exception error) {
25 27 if (m_error != null) {
26 28 try {
27 29 m_error(error);
28 30 SetResult();
29 31 } catch(Exception err) {
30 SetError(err);
32 SetErrorInternal(err);
31 33 }
32 34 } else {
33 SetError(error);
35 SetErrorInternal(error);
34 36 }
35 37 }
36 38
37 public override void CancelOperation(Exception reason) {
38 if (LockCancelation()) {
39 protected void HandleCancelInternal(Exception error) {
39 40 if (m_cancel != null) {
40 41 try {
41 m_cancel(reason);
42 m_cancel(error);
42 43 SetResult();
43 44 } catch (Exception err) {
44 45 HandleErrorInternal(err);
45 46 }
46 47 } else {
47 SetCancelled(reason);
48 HandleErrorInternal(error ?? new OperationCanceledException());
49 }
48 50 }
49 51 }
50 52 }
51 53
52 protected bool LockCancelation() {
53 return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0);
54 }
55 }
56 }
57
@@ -1,22 +1,24
1 1 using System;
2 2
3 3 namespace Implab {
4 4 public class ActionTask<T> : ActionTaskBase, IDeferred<T> {
5 5 readonly Action<T> m_task;
6 6 public ActionTask(Action<T> task, Action<Exception> error, Action<Exception> cancel, bool autoCancellable) : base(error,cancel, autoCancellable) {
7 7 m_task = task;
8 8 }
9 9
10 10 public void Resolve(T value) {
11 11 if (m_task != null && LockCancelation()) {
12 12 try {
13 13 m_task(value);
14 14 SetResult();
15 } catch(OperationCanceledException reason) {
16 HandleCancelInternal(reason);
15 17 } catch(Exception err) {
16 18 HandleErrorInternal(err);
17 19 }
18 20 }
19 21 }
20 22 }
21 23 }
22 24
@@ -1,14 +1,24
1 1 namespace Implab.Components {
2 2
3 3 public enum ExecutionState {
4 Reserved = 0,
5 Uninitialized,
4 Undefined = 0,
5
6 Created,
7
8 Initializing,
9
6 10 Ready,
11
7 12 Starting,
13
8 14 Running,
15
9 16 Stopping,
10 Stopped,
17
18 Failed,
19
11 20 Disposed,
12 Failed
21
22 Last = Disposed
13 23 }
14 24 } No newline at end of file
@@ -1,21 +1,21
1 1 using System;
2 2
3 3 namespace Implab.Components {
4 4 /// <summary>
5 5 /// Initializable components are created and initialized in two steps, first we have create the component,
6 6 /// then we have to complete it's creation by calling an <see cref="Init()"/> method. All parameters needed
7 7 /// to complete the initialization must be passed before the calling <see cref="Init()"/>
8 8 /// </summary>
9 9 public interface IInitializable {
10 10 /// <summary>
11 11 /// Completes initialization.
12 12 /// </summary>
13 13 /// <remarks>
14 /// Normally virtual shouldn't be called from the constructor, due to the incomplete object state, but
14 /// Normally virtual methods shouldn't be called from the constructor, due to the incomplete object state, but
15 15 /// they can be called from this method. This method is also usefull when we constructing a complex grpah
16 16 /// of components where cyclic references may take place.
17 17 /// </remarks>
18 18 void Init();
19 19 }
20 20 }
21 21
@@ -1,58 +1,257
1 1 using System;
2 using Implab.Parsing;
3 2
4 3 namespace Implab.Components {
5 public class RunnableComponent : Disposable, IRunnable, IInitializable {
4 public abstract class RunnableComponent : IDisposable, IRunnable, IInitializable {
5 enum Commands {
6 Ok = 0,
7 Fail,
8 Init,
9 Start,
10 Stop,
11 Dispose,
12 Last = Dispose
13 }
14
15 class StateMachine {
16 static readonly ExecutionState[,] _transitions;
6 17
18 static StateMachine() {
19 _transitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1];
20
21 Edge(ExecutionState.Created, ExecutionState.Initializing, Commands.Init);
22 Edge(ExecutionState.Created, ExecutionState.Disposed, Commands.Dispose);
23
24 Edge(ExecutionState.Initializing, ExecutionState.Ready, Commands.Ok);
25 Edge(ExecutionState.Initializing, ExecutionState.Failed, Commands.Fail);
26
27 Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start);
28 Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose);
29
30 Edge(ExecutionState.Starting, ExecutionState.Running, Commands.Ok);
31 Edge(ExecutionState.Starting, ExecutionState.Failed, Commands.Fail);
32 Edge(ExecutionState.Starting, ExecutionState.Stopping, Commands.Stop);
33 Edge(ExecutionState.Starting, ExecutionState.Disposed, Commands.Dispose);
7 34
35 Edge(ExecutionState.Running, ExecutionState.Failed, Commands.Fail);
36 Edge(ExecutionState.Running, ExecutionState.Stopping, Commands.Stop);
37 Edge(ExecutionState.Running, ExecutionState.Disposed, Commands.Dispose);
8 38
39 Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail);
40 Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok);
9 41
42 Edge(ExecutionState.Failed, ExecutionState.Disposed, Commands.Dispose);
43 }
44
45 static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) {
46 _transitions[(int)s1, (int)cmd] = s2;
47 }
48
49 public ExecutionState State {
50 get;
51 private set;
52 }
53
54 public StateMachine(ExecutionState initial) {
55 State = initial;
56 }
57
58 public bool Move(Commands cmd) {
59 var next = _transitions[(int)State, (int)cmd];
60 if (next == ExecutionState.Undefined)
61 return false;
62 State = next;
63 return true;
64 }
65 }
10 66
11 67 IPromise m_pending;
12 68 Exception m_lastError;
13 69
70 readonly StateMachine m_stateMachine;
71
14 72 protected RunnableComponent(bool initialized) {
73 m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created);
74 }
75
76 protected virtual int DisposeTimeout {
77 get {
78 return 10000;
79 }
80 }
81
82 void ThrowInvalidCommand(Commands cmd) {
83 if (m_stateMachine.State == ExecutionState.Disposed)
84 throw new ObjectDisposedException(ToString());
85
86 throw new InvalidOperationException(String.Format("Commnd {0} is not allowed in the state {1}", cmd, m_stateMachine.State));
87 }
88
89 void Move(Commands cmd) {
90 if (!m_stateMachine.Move(cmd))
91 ThrowInvalidCommand(cmd);
92 }
93
94 void Invoke(Commands cmd, Action action) {
95 lock (m_stateMachine)
96 Move(cmd);
97
98 try {
99 action();
100 lock(m_stateMachine)
101 Move(Commands.Ok);
102
103 } catch (Exception err) {
104 lock (m_stateMachine) {
105 Move(Commands.Fail);
106 m_lastError = err;
107 }
108 throw;
109 }
110 }
15 111
112 IPromise InvokeAsync(Commands cmd, Func<IPromise> action, Action<IPromise, IDeferred> chain) {
113 IPromise promise = null;
114 IPromise prev;
115
116 var task = new ActionChainTask(action, null, null, true);
117
118 lock (m_stateMachine) {
119 Move(cmd);
120
121 prev = m_pending;
122
123 promise = task.Then(
124 () => {
125 lock(m_stateMachine) {
126 if (m_pending == promise) {
127 Move(Commands.Ok);
128 m_pending = null;
16 129 }
130 }
131 }, e => {
132 lock(m_stateMachine) {
133 if (m_pending == promise) {
134 Move(Commands.Fail);
135 m_pending = null;
136 m_lastError = e;
137 }
138 }
139 throw new PromiseTransientException(e);
140 }
141 );
142
143 m_pending = promise;
144 }
145
146 if (prev == null)
147 task.Resolve();
148 else
149 chain(prev, task);
150
151 return promise;
152 }
153
17 154
18 155 #region IInitializable implementation
19 156
20 157 public void Init() {
158 Invoke(Commands.Init, OnInitialize);
159 }
21 160
161 protected virtual void OnInitialize() {
22 162 }
23 163
24 164 #endregion
25 165
26 166 #region IRunnable implementation
27 167
28 168 public IPromise Start() {
29 throw new NotImplementedException();
169 return InvokeAsync(Commands.Start, OnStart, null);
30 170 }
31 171
32 172 protected virtual IPromise OnStart() {
33 173 return Promise.SUCCESS;
34 174 }
35 175
36 protected virtual void Run() {
176 public IPromise Stop() {
177 return InvokeAsync(Commands.Stop, OnStop, StopPending).Then(Dispose);
178 }
179
180 protected virtual IPromise OnStop() {
181 return Promise.SUCCESS;
37 182 }
38 183
39 public IPromise Stop() {
40 throw new NotImplementedException();
184 /// <summary>
185 /// Stops the current operation if one exists.
186 /// </summary>
187 /// <param name="current">Current.</param>
188 /// <param name="stop">Stop.</param>
189 protected virtual void StopPending(IPromise current, IDeferred stop) {
190 if (current == null) {
191 stop.Resolve();
192 } else {
193 // связваем текущую операцию с операцией остановки
194 current.On(
195 stop.Resolve, // если текущая операция заверщилась, то можно начинать остановку
196 stop.Reject, // если текущая операция дала ошибку - то все плохо, нельзя продолжать
197 e => stop.Resolve() // если текущая отменилась, то можно начинать остановку
198 );
199 // посылаем текущей операции сигнал остановки
200 current.Cancel();
201 }
41 202 }
42 203
43 204 public ExecutionState State {
44 205 get {
45 throw new NotImplementedException();
206 return m_stateMachine.State;
46 207 }
47 208 }
48 209
49 210 public Exception LastError {
50 211 get {
51 throw new NotImplementedException();
212 return m_lastError;
52 213 }
53 214 }
54 215
55 216 #endregion
217
218 #region IDisposable implementation
219
220 public void Dispose() {
221 IPromise pending;
222 lock (m_stateMachine) {
223 if (m_stateMachine.State == ExecutionState.Disposed)
224 return;
225
226 Move(Commands.Dispose);
227
228 GC.SuppressFinalize(this);
229
230 pending = m_pending;
231 m_pending = null;
232 }
233 if (pending != null) {
234 pending.Cancel();
235 pending.Timeout(DisposeTimeout).On(
236 () => Dispose(true, null),
237 err => Dispose(true, err),
238 reason => Dispose(true, new OperationCanceledException("The operation is cancelled", reason))
239 );
240 } else {
241 Dispose(true, m_lastError);
56 242 }
57 243 }
58 244
245 ~RunnableComponent() {
246 Dispose(false, null);
247 }
248
249 #endregion
250
251 protected virtual void Dispose(bool disposing, Exception lastError) {
252
253 }
254
255 }
256 }
257
@@ -1,24 +1,26
1 1 using System;
2 2
3 3 namespace Implab {
4 4 public class FuncChainTask<TResult> : FuncChainTaskBase<TResult>, IDeferred {
5 5 readonly Func<IPromise<TResult>> m_task;
6 6
7 7 public FuncChainTask(Func<IPromise<TResult>> task, Func<Exception, IPromise<TResult>> error, Func<Exception, IPromise<TResult>> cancel, bool autoCancellable)
8 8 : base(error, cancel, autoCancellable) {
9 9 m_task = task;
10 10 }
11 11
12 12 public void Resolve() {
13 13 if (m_task != null && LockCancelation()) {
14 14 try {
15 15 var operation = m_task();
16 operation.On(SetResult, HandleErrorInternal, SetCancelled);
16 operation.On(SetResult, HandleErrorInternal, HandleCancelInternal);
17 17 CancellationRequested(operation.Cancel);
18 } catch (OperationCanceledException reason) {
19 HandleCancelInternal(reason);
18 20 } catch (Exception err) {
19 21 HandleErrorInternal(err);
20 22 }
21 23 }
22 24 }
23 25 }
24 26 } No newline at end of file
@@ -1,58 +1,54
1 1 using System;
2 using System.Threading;
3 2
4 3 namespace Implab {
5 public class FuncChainTaskBase<TResult> : AbstractPromise<TResult> {
4 public class FuncChainTaskBase<TResult> : AbstractTask<TResult> {
6 5 readonly Func<Exception, IPromise<TResult>> m_error;
7 6 readonly Func<Exception, IPromise<TResult>> m_cancel;
8 7
9 int m_cancelationLock;
10
11 8 protected FuncChainTaskBase( Func<Exception, IPromise<TResult>> error, Func<Exception, IPromise<TResult>> cancel, bool autoCancellable) {
12 9 m_error = error;
13 10 m_cancel = cancel;
14 11 if (autoCancellable)
15 12 CancellationRequested(CancelOperation);
16 13 }
17 14
18 15 public void Reject(Exception error) {
19 16 if (LockCancelation())
20 17 HandleErrorInternal(error);
21 18 }
22 19
23 20 public override void CancelOperation(Exception reason) {
24 if (LockCancelation()) {
25 if (m_cancel != null) {
26 try {
27 m_cancel(reason).On(SetResult, HandleErrorInternal, SetCancelled);
28 } catch (Exception err) {
29 HandleErrorInternal(err);
30 }
31 } else {
32 SetCancelled(reason);
33 }
34 }
35
21 if (LockCancelation())
22 HandleCancelInternal(reason);
36 23 }
37 24
38 25 protected void HandleErrorInternal(Exception error) {
39 26 if (m_error != null) {
40 27 try {
41 var operation = m_error(error);
42
43 operation.On(SetResult, SetError, SetCancelled);
44 CancellationRequested(operation.Cancel);
28 var p = m_error(error);
29 p.On(SetResult, SetErrorInternal, SetCancelledInternal);
30 CancellationRequested(p.Cancel);
45 31 } catch(Exception err) {
46 SetError(err);
32 SetErrorInternal(err);
47 33 }
48 34 } else {
49 SetError(error);
35 SetErrorInternal(error);
50 36 }
51 37 }
52 38
53 protected bool LockCancelation() {
54 return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0);
39 protected void HandleCancelInternal(Exception reason) {
40 if (m_cancel != null) {
41 try {
42 var p = m_cancel(reason);
43 p.On(SetResult, HandleErrorInternal, SetCancelledInternal);
44 CancellationRequested(p.Cancel);
45 } catch (Exception err) {
46 HandleErrorInternal(err);
47 }
48 } else {
49 HandleErrorInternal(reason ?? new OperationCanceledException());
50 }
55 51 }
56 52 }
57 53 }
58 54
@@ -1,23 +1,25
1 1 using System;
2 2
3 3 namespace Implab {
4 4 public class FuncChainTask<TArg,TResult> : FuncChainTaskBase<TResult>, IDeferred<TArg> {
5 5 readonly Func<TArg, IPromise<TResult>> m_task;
6 6
7 7 public FuncChainTask(Func<TArg, IPromise<TResult>> task, Func<Exception, IPromise<TResult>> error, Func<Exception, IPromise<TResult>> cancel, bool autoCancellable) : base(error, cancel, autoCancellable){
8 8 m_task = task;
9 9 }
10 10
11 11 public void Resolve(TArg value) {
12 12 if (m_task != null && LockCancelation()) {
13 13 try {
14 14 var operation = m_task(value);
15 15 operation.On(SetResult, HandleErrorInternal, SetCancelled);
16 16 CancellationRequested(operation.Cancel);
17 } catch (OperationCanceledException reason) {
18 HandleCancelInternal(reason);
17 19 } catch (Exception err) {
18 20 HandleErrorInternal(err);
19 21 }
20 22 }
21 23 }
22 24 }
23 25 } No newline at end of file
@@ -1,23 +1,25
1 1 using System;
2 2 using System.Threading;
3 3
4 4 namespace Implab {
5 5 public class FuncTask<T> : FuncTaskBase<T>, IDeferred {
6 6 readonly Func<T> m_task;
7 7
8 8 public FuncTask(Func<T> task, Func<Exception, T> error, Func<Exception, T> cancel, bool autoCancellable) : base(error, cancel, autoCancellable) {
9 9 m_task = task;
10 10 }
11 11
12 12 public void Resolve() {
13 13 if (m_task != null && LockCancelation()) {
14 14 try {
15 15 SetResult(m_task());
16 } catch(OperationCanceledException reason) {
17 HandleCancelInternal(reason);
16 18 } catch(Exception err) {
17 19 HandleErrorInternal(err);
18 20 }
19 21 }
20 22 }
21 23 }
22 24 }
23 25
@@ -1,55 +1,52
1 1 using System;
2 using System.Threading;
3 2
4 3 namespace Implab {
5 public class FuncTaskBase<TResult> : AbstractPromise<TResult> {
4 public class FuncTaskBase<TResult> : AbstractTask<TResult> {
6 5 readonly Func<Exception, TResult> m_cancel;
7 6 readonly Func<Exception, TResult> m_error;
8 7
9 int m_cancelationLock;
10
11 8 protected FuncTaskBase( Func<Exception, TResult> error, Func<Exception, TResult> cancel, bool autoCancellable) {
12 9 m_error = error;
13 10 m_cancel = cancel;
14 11 if (autoCancellable)
15 12 CancellationRequested(CancelOperation);
16 13 }
17 14
18 15 public void Reject(Exception error) {
19 16 Safe.ArgumentNotNull(error, "error");
20 17 if (LockCancelation())
21 18 HandleErrorInternal(error);
22 19 }
23 20
24 21 protected void HandleErrorInternal(Exception error) {
25 22 if (m_error != null) {
26 23 try {
27 24 SetResult(m_error(error));
28 25 } catch(Exception err) {
29 SetError(err);
26 SetErrorInternal(err);
30 27 }
31 28 } else {
32 SetError(error);
29 SetErrorInternal(error);
33 30 }
34 31 }
35 32
36 33 public override void CancelOperation(Exception reason) {
37 if (LockCancelation()) {
34 if (LockCancelation())
35 HandleCancelInternal(reason);
36 }
37
38 protected void HandleCancelInternal(Exception reason) {
38 39 if (m_cancel != null) {
39 40 try {
40 41 SetResult(m_cancel(reason));
41 42 } catch (Exception err) {
42 43 HandleErrorInternal(err);
43 44 }
44 45 } else {
45 SetCancelled(reason);
46 }
46 HandleErrorInternal(reason ?? new OperationCanceledException());
47 47 }
48 48 }
49 49
50 protected bool LockCancelation() {
51 return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0);
52 }
53 50 }
54 51 }
55 52
@@ -1,22 +1,24
1 1 using System;
2 2
3 3 namespace Implab {
4 4 public class FuncTask<TArg, TResult> : FuncTaskBase<TResult>, IDeferred<TArg> {
5 5 readonly Func<TArg, TResult> m_task;
6 6
7 7 public FuncTask(Func<TArg, TResult> task, Func<Exception, TResult> error,Func<Exception, TResult> cancel, bool autoCancellable) : base(error,cancel, autoCancellable) {
8 8 m_task = task;
9 9 }
10 10
11 11 public void Resolve(TArg value) {
12 12 if (m_task != null && LockCancelation()) {
13 13 try {
14 14 SetResult(m_task(value));
15 } catch(OperationCanceledException reason) {
16 HandleCancelInternal(reason);
15 17 } catch (Exception err) {
16 18 HandleErrorInternal(err);
17 19 }
18 20 }
19 21 }
20 22 }
21 23 }
22 24
@@ -1,261 +1,276
1 1 <?xml version="1.0" encoding="utf-8"?>
2 2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 3 <PropertyGroup>
4 4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 6 <ProjectGuid>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</ProjectGuid>
7 7 <OutputType>Library</OutputType>
8 8 <RootNamespace>Implab</RootNamespace>
9 9 <AssemblyName>Implab</AssemblyName>
10 10 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
11 <ReleaseVersion>0.2</ReleaseVersion>
12 <ProductVersion>8.0.30703</ProductVersion>
13 <SchemaVersion>2.0</SchemaVersion>
11 14 </PropertyGroup>
12 15 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
13 16 <DebugSymbols>true</DebugSymbols>
14 17 <DebugType>full</DebugType>
15 18 <Optimize>false</Optimize>
16 19 <OutputPath>bin\Debug</OutputPath>
17 20 <DefineConstants>TRACE;DEBUG;</DefineConstants>
18 21 <ErrorReport>prompt</ErrorReport>
19 22 <WarningLevel>4</WarningLevel>
20 23 <ConsolePause>false</ConsolePause>
21 24 <RunCodeAnalysis>true</RunCodeAnalysis>
22 25 </PropertyGroup>
23 26 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
24 27 <DebugType>full</DebugType>
25 28 <Optimize>true</Optimize>
26 29 <OutputPath>bin\Release</OutputPath>
27 30 <ErrorReport>prompt</ErrorReport>
28 31 <WarningLevel>4</WarningLevel>
29 32 <ConsolePause>false</ConsolePause>
30 33 </PropertyGroup>
31 34 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
32 35 <DebugSymbols>true</DebugSymbols>
33 36 <DebugType>full</DebugType>
34 37 <Optimize>false</Optimize>
35 38 <OutputPath>bin\Debug</OutputPath>
36 39 <DefineConstants>TRACE;DEBUG;NET_4_5</DefineConstants>
37 40 <ErrorReport>prompt</ErrorReport>
38 41 <WarningLevel>4</WarningLevel>
39 42 <RunCodeAnalysis>true</RunCodeAnalysis>
40 43 <ConsolePause>false</ConsolePause>
41 44 </PropertyGroup>
42 45 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
43 46 <Optimize>true</Optimize>
44 47 <OutputPath>bin\Release</OutputPath>
45 48 <ErrorReport>prompt</ErrorReport>
46 49 <WarningLevel>4</WarningLevel>
47 50 <ConsolePause>false</ConsolePause>
48 51 <DefineConstants>NET_4_5</DefineConstants>
49 52 </PropertyGroup>
50 53 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'DebugMono|AnyCPU' ">
51 54 <DebugSymbols>true</DebugSymbols>
52 55 <DebugType>full</DebugType>
53 56 <Optimize>false</Optimize>
54 57 <OutputPath>bin\Debug</OutputPath>
55 58 <DefineConstants>TRACE;DEBUG;NET_4_5;MONO</DefineConstants>
56 59 <ErrorReport>prompt</ErrorReport>
57 60 <WarningLevel>4</WarningLevel>
58 61 <RunCodeAnalysis>true</RunCodeAnalysis>
59 62 <ConsolePause>false</ConsolePause>
60 63 </PropertyGroup>
61 64 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseMono|AnyCPU' ">
62 65 <Optimize>true</Optimize>
63 66 <OutputPath>bin\Release</OutputPath>
64 67 <DefineConstants>NET_4_5;MONO;</DefineConstants>
65 68 <ErrorReport>prompt</ErrorReport>
66 69 <WarningLevel>4</WarningLevel>
67 70 <ConsolePause>false</ConsolePause>
68 71 </PropertyGroup>
69 72 <ItemGroup>
70 73 <Reference Include="System" />
71 74 <Reference Include="System.Xml" />
72 75 <Reference Include="mscorlib" />
73 76 </ItemGroup>
74 77 <ItemGroup>
75 78 <Compile Include="CustomEqualityComparer.cs" />
76 79 <Compile Include="Diagnostics\ConsoleTraceListener.cs" />
77 80 <Compile Include="Diagnostics\EventText.cs" />
78 81 <Compile Include="Diagnostics\LogChannel.cs" />
79 82 <Compile Include="Diagnostics\LogicalOperation.cs" />
80 83 <Compile Include="Diagnostics\TextFileListener.cs" />
81 84 <Compile Include="Diagnostics\TraceLog.cs" />
82 85 <Compile Include="Diagnostics\TraceEvent.cs" />
83 86 <Compile Include="Diagnostics\TraceEventType.cs" />
84 87 <Compile Include="ICancellable.cs" />
85 88 <Compile Include="IProgressHandler.cs" />
86 89 <Compile Include="IProgressNotifier.cs" />
87 90 <Compile Include="IPromiseT.cs" />
88 91 <Compile Include="IPromise.cs" />
89 92 <Compile Include="IServiceLocator.cs" />
90 93 <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 94 <Compile Include="Parallels\DispatchPool.cs" />
102 95 <Compile Include="Parallels\ArrayTraits.cs" />
103 96 <Compile Include="Parallels\MTQueue.cs" />
104 97 <Compile Include="Parallels\WorkerPool.cs" />
105 <Compile Include="Parsing\AltToken.cs" />
106 <Compile Include="Parsing\BinaryToken.cs" />
107 <Compile Include="Parsing\CatToken.cs" />
108 <Compile Include="Parsing\CDFADefinition.cs" />
109 <Compile Include="Parsing\DFABuilder.cs" />
110 <Compile Include="Parsing\DFAStateDescriptor.cs" />
111 <Compile Include="Parsing\DFAutomaton.cs" />
112 <Compile Include="Parsing\EDFADefinition.cs" />
113 <Compile Include="Parsing\EmptyToken.cs" />
114 <Compile Include="Parsing\EndToken.cs" />
115 <Compile Include="Parsing\EnumAlphabet.cs" />
116 <Compile Include="Parsing\Grammar.cs" />
117 <Compile Include="Parsing\IAlphabet.cs" />
118 <Compile Include="Parsing\IDFADefinition.cs" />
119 <Compile Include="Parsing\IVisitor.cs" />
120 <Compile Include="Parsing\ParserException.cs" />
121 <Compile Include="Parsing\Scanner.cs" />
122 <Compile Include="Parsing\StarToken.cs" />
123 <Compile Include="Parsing\SymbolToken.cs" />
124 <Compile Include="Parsing\Token.cs" />
125 98 <Compile Include="ProgressInitEventArgs.cs" />
126 99 <Compile Include="Properties\AssemblyInfo.cs" />
127 100 <Compile Include="Parallels\AsyncPool.cs" />
128 101 <Compile Include="Safe.cs" />
129 102 <Compile Include="ValueEventArgs.cs" />
130 103 <Compile Include="PromiseExtensions.cs" />
131 104 <Compile Include="SyncContextPromise.cs" />
132 105 <Compile Include="Diagnostics\OperationContext.cs" />
133 106 <Compile Include="Diagnostics\TraceContext.cs" />
134 107 <Compile Include="Diagnostics\LogEventArgs.cs" />
135 108 <Compile Include="Diagnostics\LogEventArgsT.cs" />
136 109 <Compile Include="Diagnostics\Extensions.cs" />
137 110 <Compile Include="PromiseEventType.cs" />
138 111 <Compile Include="Parallels\AsyncQueue.cs" />
139 112 <Compile Include="PromiseT.cs" />
140 113 <Compile Include="IDeferred.cs" />
141 114 <Compile Include="IDeferredT.cs" />
142 115 <Compile Include="Promise.cs" />
143 116 <Compile Include="PromiseTransientException.cs" />
144 117 <Compile Include="Parallels\Signal.cs" />
145 118 <Compile Include="Parallels\SharedLock.cs" />
146 119 <Compile Include="Diagnostics\ILogWriter.cs" />
147 120 <Compile Include="Diagnostics\ListenerBase.cs" />
148 121 <Compile Include="Parallels\BlockingQueue.cs" />
149 122 <Compile Include="AbstractEvent.cs" />
150 123 <Compile Include="AbstractPromise.cs" />
151 124 <Compile Include="AbstractPromiseT.cs" />
152 125 <Compile Include="FuncTask.cs" />
153 126 <Compile Include="FuncTaskBase.cs" />
154 127 <Compile Include="FuncTaskT.cs" />
155 128 <Compile Include="ActionChainTaskBase.cs" />
156 129 <Compile Include="ActionChainTask.cs" />
157 130 <Compile Include="ActionChainTaskT.cs" />
158 131 <Compile Include="FuncChainTaskBase.cs" />
159 132 <Compile Include="FuncChainTask.cs" />
160 133 <Compile Include="FuncChainTaskT.cs" />
161 134 <Compile Include="ActionTaskBase.cs" />
162 135 <Compile Include="ActionTask.cs" />
163 136 <Compile Include="ActionTaskT.cs" />
164 137 <Compile Include="ICancellationToken.cs" />
165 138 <Compile Include="SuccessPromise.cs" />
166 139 <Compile Include="SuccessPromiseT.cs" />
167 140 <Compile Include="PromiseAwaiterT.cs" />
168 141 <Compile Include="PromiseAwaiter.cs" />
169 142 <Compile Include="Components\ComponentContainer.cs" />
170 143 <Compile Include="Components\Disposable.cs" />
171 144 <Compile Include="Components\DisposablePool.cs" />
172 145 <Compile Include="Components\ObjectPool.cs" />
173 146 <Compile Include="Components\ServiceLocator.cs" />
174 147 <Compile Include="Components\IInitializable.cs" />
175 148 <Compile Include="TaskController.cs" />
176 149 <Compile Include="Components\App.cs" />
177 150 <Compile Include="Components\IRunnable.cs" />
178 151 <Compile Include="Components\ExecutionState.cs" />
179 152 <Compile Include="Components\RunnableComponent.cs" />
180 153 <Compile Include="Components\IFactory.cs" />
181 <Compile Include="Parsing\DFADefinition.cs" />
182 <Compile Include="Parsing\IndexedAlphabetBase.cs" />
183 <Compile Include="Parsing\CharAlphabet.cs" />
184 <Compile Include="Parsing\IAlphabetBuilder.cs" />
185 <Compile Include="Parsing\IDFADefinitionBuilder.cs" />
154 <Compile Include="Automaton\IAlphabet.cs" />
155 <Compile Include="Automaton\ParserException.cs" />
156 <Compile Include="Automaton\IndexedAlphabetBase.cs" />
157 <Compile Include="Automaton\IAlphabetBuilder.cs" />
158 <Compile Include="Automaton\RegularExpressions\AltToken.cs" />
159 <Compile Include="Automaton\RegularExpressions\BinaryToken.cs" />
160 <Compile Include="Automaton\RegularExpressions\CatToken.cs" />
161 <Compile Include="Automaton\RegularExpressions\StarToken.cs" />
162 <Compile Include="Automaton\RegularExpressions\SymbolToken.cs" />
163 <Compile Include="Automaton\RegularExpressions\EmptyToken.cs" />
164 <Compile Include="Automaton\RegularExpressions\Token.cs" />
165 <Compile Include="Automaton\RegularExpressions\IVisitor.cs" />
166 <Compile Include="Automaton\AutomatonTransition.cs" />
167 <Compile Include="Formats\JSON\JSONElementContext.cs" />
168 <Compile Include="Formats\JSON\JSONElementType.cs" />
169 <Compile Include="Formats\JSON\JSONGrammar.cs" />
170 <Compile Include="Formats\JSON\JSONParser.cs" />
171 <Compile Include="Formats\JSON\JSONScanner.cs" />
172 <Compile Include="Formats\JSON\JsonTokenType.cs" />
173 <Compile Include="Formats\JSON\JSONWriter.cs" />
174 <Compile Include="Formats\JSON\JSONXmlReader.cs" />
175 <Compile Include="Formats\JSON\JSONXmlReaderOptions.cs" />
176 <Compile Include="Formats\JSON\StringTranslator.cs" />
177 <Compile Include="Automaton\MapAlphabet.cs" />
178 <Compile Include="Formats\CharAlphabet.cs" />
179 <Compile Include="Formats\ByteAlphabet.cs" />
180 <Compile Include="Automaton\IDFATable.cs" />
181 <Compile Include="Automaton\IDFATableBuilder.cs" />
182 <Compile Include="Automaton\DFATable.cs" />
183 <Compile Include="Automaton\RegularExpressions\RegularExpressionVisitor.cs" />
184 <Compile Include="Automaton\RegularExpressions\ITaggedDFABuilder.cs" />
185 <Compile Include="Formats\TextScanner.cs" />
186 <Compile Include="Formats\StringScanner.cs" />
187 <Compile Include="Formats\ReaderScanner.cs" />
188 <Compile Include="Formats\ScannerContext.cs" />
189 <Compile Include="Formats\Grammar.cs" />
190 <Compile Include="Automaton\RegularExpressions\EndTokenT.cs" />
191 <Compile Include="Automaton\RegularExpressions\EndToken.cs" />
192 <Compile Include="Automaton\RegularExpressions\RegularExpressionVisitorT.cs" />
193 <Compile Include="Automaton\AutomatonConst.cs" />
194 <Compile Include="Automaton\RegularExpressions\RegularDFA.cs" />
195 <Compile Include="Components\LazyAndWeak.cs" />
196 <Compile Include="AbstractTask.cs" />
197 <Compile Include="AbstractTaskT.cs" />
186 198 </ItemGroup>
187 199 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
188 200 <ItemGroup />
189 201 <ProjectExtensions>
190 202 <MonoDevelop>
191 203 <Properties>
192 204 <Policies>
193 205 <CSharpFormattingPolicy IndentSwitchBody="True" NamespaceBraceStyle="EndOfLine" ClassBraceStyle="EndOfLine" InterfaceBraceStyle="EndOfLine" StructBraceStyle="EndOfLine" EnumBraceStyle="EndOfLine" MethodBraceStyle="EndOfLine" ConstructorBraceStyle="EndOfLine" DestructorBraceStyle="EndOfLine" BeforeMethodDeclarationParentheses="False" BeforeMethodCallParentheses="False" BeforeConstructorDeclarationParentheses="False" NewLineBeforeConstructorInitializerColon="NewLine" NewLineAfterConstructorInitializerColon="SameLine" BeforeIndexerDeclarationBracket="False" BeforeDelegateDeclarationParentheses="False" NewParentheses="False" SpacesBeforeBrackets="False" inheritsSet="Mono" inheritsScope="text/x-csharp" scope="text/x-csharp" />
194 206 <TextStylePolicy FileWidth="120" EolMarker="Unix" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/x-csharp" />
195 207 <DotNetNamingPolicy DirectoryNamespaceAssociation="PrefixedHierarchical" ResourceNamePolicy="MSBuild" />
196 208 <TextStylePolicy FileWidth="120" TabsToSpaces="False" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="application/xml" />
197 209 <XmlFormattingPolicy inheritsSet="Mono" inheritsScope="application/xml" scope="application/xml" />
198 210 <TextStylePolicy FileWidth="120" TabsToSpaces="False" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/plain" />
199 211 <NameConventionPolicy>
200 212 <Rules>
201 213 <NamingRule Name="Namespaces" AffectedEntity="Namespace" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
202 214 <NamingRule Name="Types" AffectedEntity="Class, Struct, Enum, Delegate" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
203 215 <NamingRule Name="Interfaces" AffectedEntity="Interface" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
204 216 <RequiredPrefixes>
205 217 <String>I</String>
206 218 </RequiredPrefixes>
207 219 </NamingRule>
208 220 <NamingRule Name="Attributes" AffectedEntity="CustomAttributes" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
209 221 <RequiredSuffixes>
210 222 <String>Attribute</String>
211 223 </RequiredSuffixes>
212 224 </NamingRule>
213 225 <NamingRule Name="Event Arguments" AffectedEntity="CustomEventArgs" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
214 226 <RequiredSuffixes>
215 227 <String>EventArgs</String>
216 228 </RequiredSuffixes>
217 229 </NamingRule>
218 230 <NamingRule Name="Exceptions" AffectedEntity="CustomExceptions" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
219 231 <RequiredSuffixes>
220 232 <String>Exception</String>
221 233 </RequiredSuffixes>
222 234 </NamingRule>
223 235 <NamingRule Name="Methods" AffectedEntity="Methods" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
224 236 <NamingRule Name="Static Readonly Fields" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Protected, Public" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True" />
225 237 <NamingRule Name="Fields (Non Private)" AffectedEntity="Field" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
226 238 <NamingRule Name="ReadOnly Fields (Non Private)" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False" />
227 239 <NamingRule Name="Fields (Private)" AffectedEntity="Field, ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
228 240 <RequiredPrefixes>
229 241 <String>m_</String>
230 242 </RequiredPrefixes>
231 243 </NamingRule>
232 244 <NamingRule Name="Static Fields (Private)" AffectedEntity="Field" VisibilityMask="Private" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True">
233 245 <RequiredPrefixes>
234 246 <String>_</String>
235 247 </RequiredPrefixes>
236 248 </NamingRule>
237 249 <NamingRule Name="ReadOnly Fields (Private)" AffectedEntity="ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
238 250 <RequiredPrefixes>
239 251 <String>m_</String>
240 252 </RequiredPrefixes>
241 253 </NamingRule>
242 254 <NamingRule Name="Constant Fields" AffectedEntity="ConstantField" VisibilityMask="VisibilityMask" NamingStyle="AllUpper" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
243 255 <NamingRule Name="Properties" AffectedEntity="Property" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
244 256 <NamingRule Name="Events" AffectedEntity="Event" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
245 257 <NamingRule Name="Enum Members" AffectedEntity="EnumMember" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
246 258 <NamingRule Name="Parameters" AffectedEntity="Parameter, LocalVariable" VisibilityMask="VisibilityMask" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
247 259 <NamingRule Name="Type Parameters" AffectedEntity="TypeParameter" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
248 260 <RequiredPrefixes>
249 261 <String>T</String>
250 262 </RequiredPrefixes>
251 263 </NamingRule>
252 264 </Rules>
253 265 </NameConventionPolicy>
254 266 </Policies>
255 267 </Properties>
256 268 </MonoDevelop>
257 269 </ProjectExtensions>
258 270 <ItemGroup>
259 271 <Folder Include="Components\" />
272 <Folder Include="Automaton\RegularExpressions\" />
273 <Folder Include="Formats\" />
274 <Folder Include="Formats\JSON\" />
260 275 </ItemGroup>
261 276 </Project> No newline at end of file
@@ -1,299 +1,289
1 1 using System.Threading;
2 2 using System;
3 3 using Implab.Diagnostics;
4 4 using System.Collections.Generic;
5 5
6
7 #if NET_4_5
8 using System.Threading.Tasks;
9 #endif
10
11 6 namespace Implab {
12 7 public static class PromiseExtensions {
13 8 public static IPromise<T> DispatchToCurrentContext<T>(this IPromise<T> that) {
14 9 Safe.ArgumentNotNull(that, "that");
15 10 var context = SynchronizationContext.Current;
16 11 if (context == null)
17 12 return that;
18 13
19 14 var p = new SyncContextPromise<T>(context);
20 p.On(that.Cancel, PromiseEventType.Cancelled);
15 p.CancellationRequested(that.Cancel);
21 16
22 17 that.On(
23 18 p.Resolve,
24 19 p.Reject,
25 p.Cancel
20 p.CancelOperation
26 21 );
27 22 return p;
28 23 }
29 24
30 25 public static IPromise<T> DispatchToContext<T>(this IPromise<T> that, SynchronizationContext context) {
31 26 Safe.ArgumentNotNull(that, "that");
32 27 Safe.ArgumentNotNull(context, "context");
33 28
34 29 var p = new SyncContextPromise<T>(context);
35 p.On(that.Cancel, PromiseEventType.Cancelled);
36
30 p.CancellationRequested(that.Cancel);
37 31
38 32 that.On(
39 33 p.Resolve,
40 34 p.Reject,
41 p.Cancel
35 p.CancelOperation
42 36 );
43 37 return p;
44 38 }
45 39
46 40 /// <summary>
47 41 /// Ensures the dispatched.
48 42 /// </summary>
49 43 /// <returns>The dispatched.</returns>
50 44 /// <param name="that">That.</param>
51 45 /// <param name="head">Head.</param>
52 46 /// <param name="cleanup">Cleanup.</param>
53 47 /// <typeparam name="TPromise">The 1st type parameter.</typeparam>
54 48 /// <typeparam name="T">The 2nd type parameter.</typeparam>
55 49 public static TPromise EnsureDispatched<TPromise,T>(this TPromise that, IPromise<T> head, Action<T> cleanup) where TPromise : IPromise{
56 50 Safe.ArgumentNotNull(that, "that");
57 51 Safe.ArgumentNotNull(head, "head");
58 52
59 53 that.On(() => head.On(cleanup), PromiseEventType.Cancelled);
60 54
61 55 return that;
62 56 }
63 57
64 58 public static AsyncCallback AsyncCallback<T>(this Promise<T> that, Func<IAsyncResult,T> callback) {
65 59 Safe.ArgumentNotNull(that, "that");
66 60 Safe.ArgumentNotNull(callback, "callback");
67 61 var op = TraceContext.Instance.CurrentOperation;
68 62 return ar => {
69 63 TraceContext.Instance.EnterLogicalOperation(op,false);
70 64 try {
71 65 that.Resolve(callback(ar));
72 66 } catch (Exception err) {
73 67 that.Reject(err);
74 68 } finally {
75 69 TraceContext.Instance.Leave();
76 70 }
77 71 };
78 72 }
79 73
80 static void CancelCallback(object cookie) {
81 ((ICancellable)cookie).Cancel();
74 static void CancelByTimeoutCallback(object cookie) {
75 ((ICancellable)cookie).Cancel(new TimeoutException());
82 76 }
83 77
84 78 /// <summary>
85 79 /// Cancells promise after the specified timeout is elapsed.
86 80 /// </summary>
87 81 /// <param name="that">The promise to cancel on timeout.</param>
88 82 /// <param name="milliseconds">The timeout in milliseconds.</param>
89 83 /// <typeparam name="TPromise">The 1st type parameter.</typeparam>
90 84 public static TPromise Timeout<TPromise>(this TPromise that, int milliseconds) where TPromise : IPromise {
91 85 Safe.ArgumentNotNull(that, "that");
92 var timer = new Timer(CancelCallback, that, milliseconds, -1);
86 var timer = new Timer(CancelByTimeoutCallback, that, milliseconds, -1);
93 87 that.On(timer.Dispose, PromiseEventType.All);
94 88 return that;
95 89 }
96 90
97 91 public static IPromise Bundle(this ICollection<IPromise> that) {
98 92 Safe.ArgumentNotNull(that, "that");
99 93
100 94 int count = that.Count;
101 95 int errors = 0;
102 96 var medium = new Promise();
103 97
104 98 if (count == 0) {
105 99 medium.Resolve();
106 100 return medium;
107 101 }
108 102
109 103 medium.On(() => {
110 104 foreach(var p2 in that)
111 105 p2.Cancel();
112 106 }, PromiseEventType.ErrorOrCancel);
113 107
114 108 foreach (var p in that)
115 109 p.On(
116 110 () => {
117 111 if (Interlocked.Decrement(ref count) == 0)
118 112 medium.Resolve();
119 113 },
120 114 error => {
121 115 if (Interlocked.Increment(ref errors) == 1)
122 116 medium.Reject(
123 117 new Exception("The dependency promise is failed", error)
124 118 );
125 119 },
126 120 reason => {
127 121 if (Interlocked.Increment(ref errors) == 1)
128 122 medium.Cancel(
129 123 new Exception("The dependency promise is cancelled")
130 124 );
131 125 }
132 126 );
133 127
134 128 return medium;
135 129 }
136 130
137 131 public static IPromise<T[]> Bundle<T>(this ICollection<IPromise<T>> that) {
138 132 Safe.ArgumentNotNull(that, "that");
139 133
140 134 int count = that.Count;
141 135 int errors = 0;
142 136 var medium = new Promise<T[]>();
143 137 var results = new T[that.Count];
144 138
145 139 medium.On(() => {
146 140 foreach(var p2 in that)
147 141 p2.Cancel();
148 142 }, PromiseEventType.ErrorOrCancel);
149 143
150 144 int i = 0;
151 145 foreach (var p in that) {
152 146 var idx = i;
153 147 p.On(
154 148 x => {
155 149 results[idx] = x;
156 150 if (Interlocked.Decrement(ref count) == 0)
157 151 medium.Resolve(results);
158 152 },
159 153 error => {
160 154 if (Interlocked.Increment(ref errors) == 1)
161 155 medium.Reject(
162 156 new Exception("The dependency promise is failed", error)
163 157 );
164 158 },
165 159 reason => {
166 160 if (Interlocked.Increment(ref errors) == 1)
167 161 medium.Cancel(
168 162 new Exception("The dependency promise is cancelled", reason)
169 163 );
170 164 }
171 165 );
172 166 i++;
173 167 }
174 168
175 169 return medium;
176 170 }
177 171
178 172 public static IPromise Then(this IPromise that, Action success, Action<Exception> error, Action<Exception> cancel) {
179 173 Safe.ArgumentNotNull(that, "that");
180 174
181 175 var d = new ActionTask(success, error, cancel, false);
182 176 that.On(d.Resolve, d.Reject, d.CancelOperation);
183 if (success != null)
184 177 d.CancellationRequested(that.Cancel);
185 178 return d;
186 179 }
187 180
188 181 public static IPromise Then(this IPromise that, Action success, Action<Exception> error) {
189 182 return Then(that, success, error, null);
190 183 }
191 184
192 185 public static IPromise Then(this IPromise that, Action success) {
193 186 return Then(that, success, null, null);
194 187 }
195 188
196 189 public static IPromise<T> Then<T>(this IPromise that, Func<T> success, Func<Exception, T> error, Func<Exception, T> cancel) {
197 190 Safe.ArgumentNotNull(that, "that");
198 191
199 192 var d = new FuncTask<T>(success, error, cancel, false);
200 193 that.On(d.Resolve, d.Reject, d.CancelOperation);
201 if (success != null)
202 194 d.CancellationRequested(that.Cancel);
203 195 return d;
204 196 }
205 197
206 198 public static IPromise<T> Then<T>(this IPromise that, Func<T> success, Func<Exception, T> error) {
207 199 return Then(that, success, error, null);
208 200 }
209 201
210 202 public static IPromise<T> Then<T>(this IPromise that, Func<T> success) {
211 203 return Then(that, success, null, null);
212 204 }
213 205
214 206 public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success, Func<Exception, T2> error, Func<Exception, T2> cancel) {
215 207 Safe.ArgumentNotNull(that, "that");
216 208 var d = new FuncTask<T,T2>(success, error, cancel, false);
217 209 that.On(d.Resolve, d.Reject, d.CancelOperation);
218 if (success != null)
219 210 d.CancellationRequested(that.Cancel);
220 211 return d;
221 212 }
222 213
223 214 public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success, Func<Exception, T2> error) {
224 215 return Then(that, success, error, null);
225 216 }
226 217
227 218 public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success) {
228 219 return Then(that, success, null, null);
229 220 }
230 221
231 222 #region chain traits
232 223 public static IPromise Chain(this IPromise that, Func<IPromise> success, Func<Exception,IPromise> error, Func<Exception,IPromise> cancel) {
233 224 Safe.ArgumentNotNull(that, "that");
234 225
235 226 var d = new ActionChainTask(success, error, cancel, false);
236 227 that.On(d.Resolve, d.Reject, d.CancelOperation);
237 if (success != null)
238 228 d.CancellationRequested(that.Cancel);
239 229 return d;
240 230 }
241 231
242 232 public static IPromise Chain(this IPromise that, Func<IPromise> success, Func<Exception,IPromise> error) {
243 233 return Chain(that, success, error, null);
244 234 }
245 235
246 236 public static IPromise Chain(this IPromise that, Func<IPromise> success) {
247 237 return Chain(that, success, null, null);
248 238 }
249 239
250 240 public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success, Func<Exception, IPromise<T>> error, Func<Exception, IPromise<T>> cancel) {
251 241 Safe.ArgumentNotNull(that, "that");
252 242
253 243 var d = new FuncChainTask<T>(success, error, cancel, false);
254 244 that.On(d.Resolve, d.Reject, d.CancelOperation);
255 245 if (success != null)
256 246 d.CancellationRequested(that.Cancel);
257 247 return d;
258 248 }
259 249
260 250 public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success, Func<Exception, IPromise<T>> error) {
261 251 return Chain(that, success, error, null);
262 252 }
263 253
264 254 public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success) {
265 255 return Chain(that, success, null, null);
266 256 }
267 257
268 258 public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success, Func<Exception, IPromise<T2>> error, Func<Exception, IPromise<T2>> cancel) {
269 259 Safe.ArgumentNotNull(that, "that");
270 260 var d = new FuncChainTask<T,T2>(success, error, cancel, false);
271 261 that.On(d.Resolve, d.Reject, d.CancelOperation);
272 262 if (success != null)
273 263 d.CancellationRequested(that.Cancel);
274 264 return d;
275 265 }
276 266
277 267 public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success, Func<Exception, IPromise<T2>> error) {
278 268 return Chain(that, success, error, null);
279 269 }
280 270
281 271 public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success) {
282 272 return Chain(that, success, null, null);
283 273 }
284 274
285 275 #endregion
286 276
287 277
288 278 #if NET_4_5
289 279
290 280 public static PromiseAwaiter<T> GetAwaiter<T>(this IPromise<T> that) {
291 281 Safe.ArgumentNotNull(that, "that");
292 282
293 283 return new PromiseAwaiter<T>(that);
294 284 }
295 285
296 286 #endif
297 287 }
298 288 }
299 289
@@ -1,123 +1,128
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Linq;
4 4 using System.Text;
5 5 using System.Text.RegularExpressions;
6 6 using System.Diagnostics;
7 7
8 8 namespace Implab
9 9 {
10 10 public static class Safe
11 11 {
12 12 public static void ArgumentAssert(bool condition, string paramName) {
13 13 if (!condition)
14 14 throw new ArgumentException("The parameter is invalid", paramName);
15 15 }
16 16
17 17 public static void ArgumentMatch(string value, string paramName, Regex rx) {
18 18 if (rx == null)
19 19 throw new ArgumentNullException("rx");
20 20 if (!rx.IsMatch(value))
21 21 throw new ArgumentException(String.Format("The prameter value must match {0}", rx), paramName);
22 22 }
23 23
24 24 public static void ArgumentNotEmpty(string value, string paramName) {
25 25 if (String.IsNullOrEmpty(value))
26 26 throw new ArgumentException("The parameter can't be empty", paramName);
27 27 }
28 28
29 29 public static void ArgumentNotEmpty<T>(T[] value, string paramName) {
30 30 if (value == null || value.Length == 0)
31 31 throw new ArgumentException("The array must be not emty", paramName);
32 32 }
33 33
34 34 public static void ArgumentNotNull(object value, string paramName) {
35 35 if (value == null)
36 36 throw new ArgumentNullException(paramName);
37 37 }
38 38
39 39 public static void ArgumentInRange(int value, int min, int max, string paramName) {
40 40 if (value < min || value > max)
41 41 throw new ArgumentOutOfRangeException(paramName);
42 42 }
43 43
44 public static void ArgumentOfType(object value, Type type, string paramName) {
45 if (!type.IsInstanceOfType(value))
46 throw new ArgumentException(String.Format("The parameter must be of type {0}", type), paramName);
47 }
48
44 49 public static void Dispose(params IDisposable[] objects) {
45 50 foreach (var d in objects)
46 51 if (d != null)
47 52 d.Dispose();
48 53 }
49 54
50 55 public static void Dispose(params object[] objects) {
51 56 foreach (var obj in objects) {
52 57 var d = obj as IDisposable;
53 58 if (d != null)
54 59 d.Dispose();
55 60 }
56 61 }
57 62
58 63 public static void Dispose(object obj) {
59 64 var d = obj as IDisposable;
60 65 if (d != null)
61 66 d.Dispose();
62 67 }
63 68
64 69 [DebuggerStepThrough]
65 70 public static IPromise<T> WrapPromise<T>(Func<T> action) {
66 71 ArgumentNotNull(action, "action");
67 72
68 73 var p = new Promise<T>();
69 74 try {
70 75 p.Resolve(action());
71 76 } catch (Exception err) {
72 77 p.Reject(err);
73 78 }
74 79
75 80 return p;
76 81 }
77 82
78 83 [DebuggerStepThrough]
79 84 public static IPromise WrapPromise(Action action) {
80 85 ArgumentNotNull(action, "action");
81 86
82 87 var p = new Promise();
83 88 try {
84 89 action();
85 90 p.Resolve();
86 91 } catch (Exception err) {
87 92 p.Reject(err);
88 93 }
89 94
90 95 return p;
91 96 }
92 97
93 98 [DebuggerStepThrough]
94 99 public static IPromise InvokePromise(Func<IPromise> action) {
95 100 ArgumentNotNull(action, "action");
96 101
97 102 try {
98 103 var p = action();
99 104 if (p == null) {
100 105 var d = new Promise();
101 106 d.Reject(new Exception("The action returned null"));
102 107 p = d;
103 108 }
104 109 return p;
105 110 } catch (Exception err) {
106 111 var p = new Promise();
107 112 p.Reject(err);
108 113 return p;
109 114 }
110 115 }
111 116
112 117 [DebuggerStepThrough]
113 118 public static IPromise<T> InvokePromise<T>(Func<IPromise<T>> action) {
114 119 ArgumentNotNull(action, "action");
115 120
116 121 try {
117 122 return action() ?? Promise<T>.FromException(new Exception("The action returned null"));
118 123 } catch (Exception err) {
119 124 return Promise<T>.FromException(err);
120 125 }
121 126 }
122 127 }
123 128 }
@@ -1,47 +1,53
1 1 <?xml version="1.0" encoding="utf-8"?>
2 2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 3 <PropertyGroup>
4 4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 6 <ProductVersion>8.0.30703</ProductVersion>
7 7 <SchemaVersion>2.0</SchemaVersion>
8 8 <ProjectGuid>{15DD7123-D504-4627-8B4F-D00C7F04D033}</ProjectGuid>
9 9 <OutputType>Exe</OutputType>
10 10 <RootNamespace>MonoPlay</RootNamespace>
11 11 <AssemblyName>MonoPlay</AssemblyName>
12 12 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
13 13 <ReleaseVersion>0.2</ReleaseVersion>
14 14 </PropertyGroup>
15 15 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
16 16 <DebugSymbols>true</DebugSymbols>
17 17 <DebugType>full</DebugType>
18 18 <Optimize>false</Optimize>
19 19 <OutputPath>bin\Debug</OutputPath>
20 20 <DefineConstants>DEBUG;TRACE;</DefineConstants>
21 21 <ErrorReport>prompt</ErrorReport>
22 22 <WarningLevel>4</WarningLevel>
23 23 <ConsolePause>false</ConsolePause>
24 24 </PropertyGroup>
25 25 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
26 26 <DebugType>full</DebugType>
27 27 <Optimize>true</Optimize>
28 28 <OutputPath>bin\Release</OutputPath>
29 29 <ErrorReport>prompt</ErrorReport>
30 30 <WarningLevel>4</WarningLevel>
31 31 <ConsolePause>false</ConsolePause>
32 32 </PropertyGroup>
33 33 <ItemGroup>
34 34 <Reference Include="System" />
35 <Reference Include="System.Text.Json">
36 <HintPath>..\packages\System.Text.Json.2.0.0.11\lib\net40\System.Text.Json.dll</HintPath>
37 </Reference>
35 38 </ItemGroup>
36 39 <ItemGroup>
37 40 <Compile Include="Program.cs" />
38 41 <Compile Include="Properties\AssemblyInfo.cs" />
39 42 </ItemGroup>
40 43 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
41 44 <ItemGroup>
42 45 <ProjectReference Include="..\Implab\Implab.csproj">
43 46 <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
44 47 <Name>Implab</Name>
45 48 </ProjectReference>
46 49 </ItemGroup>
50 <ItemGroup>
51 <None Include="packages.config" />
52 </ItemGroup>
47 53 </Project> No newline at end of file
@@ -1,37 +1,45
1 1 using System;
2 2 using Implab;
3 3 using System.Threading.Tasks;
4 using Implab.Formats.JSON;
5 using System.IO;
6 using System.Text.Json;
4 7
5 8 namespace MonoPlay {
6 9 class MainClass {
7 10
8 11
9 12 public static void Main(string[] args) {
10 13 if (args == null)
11 14 throw new ArgumentNullException("args");
12
13 var t1 = Environment.TickCount;
15 int t1, t2;
14 16
15 DoWork().GetAwaiter().GetResult();
17 for (int i = 0; i < 2; i++) {
18 t1 = Environment.TickCount;
19 int elements =0;
20 using (var reader = new JSONParser(File.OpenText("/home/sergey/temp/citylots.json"))) {
21 while (reader.Read())
22 elements++;
23 }
16 24
17 var t2 = Environment.TickCount;
18 Console.WriteLine("done: {0} ms, {1:.00} Mb, {2} GC", t2 - t1, GC.GetTotalMemory(false) / (1024*1024), GC.CollectionCount(0) );
19
25 t2 = Environment.TickCount;
26 Console.WriteLine("attempt {0} done: {1} ms, {2:.00} Mb, {3} GC, Elements: {4}",i+1, t2 - t1, GC.GetTotalMemory(false) / (1024*1024), GC.CollectionCount(0), elements );
20 27 }
21 28
22 static IPromise<int> DoItem(int x) {
23 //return Promise<int>.FromResult(x + 1);
24 var p = new Promise<int>();
25 p.Resolve(x+1);
26 return p;
29 Console.WriteLine("Syste.Text.Json");
30 var paraser = new JsonParser();
31 for (int i = 0; i < 2; i++) {
32 t1 = Environment.TickCount;
33 using (var reader = File.OpenText("/home/sergey/temp/citylots.json")) {
34 paraser.Parse(reader);
27 35 }
28 36
29 static async Task<int> DoWork() {
30 var c = 0;
31 for (int i = 0; i < 10000000; i++)
32 c = await DoItem(c);
33 return c;
37 t2 = Environment.TickCount;
38 Console.WriteLine("attempt {0} done: {1} ms, {2:.00} Mb, {3} GC, ",i+1, t2 - t1, GC.GetTotalMemory(false) / (1024*1024), GC.CollectionCount(0));
39 }
40
41
34 42 }
35 43
36 44 }
37 45 }
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now