Mercurial > pub > ImplabNet
changeset 176:0c3c69fe225b ref20160224
rewritten the text scanner
author | cin |
---|---|
date | Tue, 22 Mar 2016 18:58:40 +0300 |
parents | 96a89dcb4060 |
children | a0ff6a0e9c44 |
files | Implab/Automaton/DFAStateDescriptor.cs Implab/Automaton/DFATable.cs Implab/Automaton/IndexedAlphabetBase.cs Implab/Automaton/MapAlphabet.cs Implab/Automaton/RegularExpressions/DFAStateDescriptorT.cs Implab/Automaton/RegularExpressions/Grammar.cs Implab/Automaton/RegularExpressions/RegularDFA.cs Implab/Automaton/Scanner.cs Implab/Formats/BufferScanner.cs Implab/Formats/ByteAlphabet.cs Implab/Formats/CharAlphabet.cs Implab/Formats/JSON/JSONGrammar.cs Implab/Formats/JSON/JSONScanner.cs Implab/Formats/JSON/StringTranslator.cs Implab/Formats/ReaderScanner.cs Implab/Formats/ScannerContext.cs Implab/Formats/StringScanner.cs Implab/Formats/TextScanner.cs Implab/Implab.csproj |
diffstat | 19 files changed, 614 insertions(+), 842 deletions(-) [+] |
line wrap: on
line diff
--- a/Implab/Automaton/DFAStateDescriptor.cs Mon Mar 21 18:41:45 2016 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -namespace Implab.Automaton { - public struct DFAStateDescriptor { - public readonly bool final; - public readonly int[] transitions; - - - public DFAStateDescriptor(int[] transitions, bool final) { - this.transitions = transitions; - this.final = final; - } - - public DFAStateDescriptor(int[] transitions) : this(transitions, false) { - } - - public DFAStateDescriptor(int size, bool final) { - Safe.ArgumentInRange(size, 0, int.MaxValue, "size"); - - this.final = final; - - transitions = new int[size]; - - for (int i = 0; i < size; i++) - transitions[i] = DFAConst.UNREACHABLE_STATE; - } - } -}
--- a/Implab/Automaton/DFATable.cs Mon Mar 21 18:41:45 2016 +0300 +++ b/Implab/Automaton/DFATable.cs Tue Mar 22 18:58:40 2016 +0300 @@ -1,305 +1,313 @@ -using Implab; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Implab.Automaton { - public class DFATable : IDFATableBuilder { - int m_stateCount; - int m_symbolCount; - int m_initialState; - - readonly HashSet<int> m_finalStates = new HashSet<int>(); - readonly HashSet<AutomatonTransition> m_transitions = new HashSet<AutomatonTransition>(); - - - #region IDFADefinition implementation - - public bool IsFinalState(int s) { - Safe.ArgumentInRange(s, 0, m_stateCount, "s"); - - return m_finalStates.Contains(s); - } - - public IEnumerable<int> FinalStates { - get { - return m_finalStates; - } - } - - public int StateCount { - get { return m_stateCount; } - } - - public int AlphabetSize { - get { return m_symbolCount; } - } - - public int InitialState { - get { return m_initialState; } - } - - #endregion - - public void SetInitialState(int s) { - Safe.ArgumentAssert(s >= 0, "s"); - m_initialState = s; - } - - public void MarkFinalState(int state) { - m_finalStates.Add(state); - } - - public void Add(AutomatonTransition item) { - Safe.ArgumentAssert(item.s1 >= 0, "item"); - Safe.ArgumentAssert(item.s2 >= 0, "item"); - Safe.ArgumentAssert(item.edge >= 0, "item"); - - m_stateCount = Math.Max(m_stateCount, Math.Max(item.s1, item.s2) + 1); - m_symbolCount = Math.Max(m_symbolCount, item.edge); - - m_transitions.Add(item); - } - - public void Clear() { - m_stateCount = 0; - m_symbolCount = 0; - m_finalStates.Clear(); - m_transitions.Clear(); - } - - public bool Contains(AutomatonTransition item) { - return m_transitions.Contains(item); - } - - public void CopyTo(AutomatonTransition[] array, int arrayIndex) { - m_transitions.CopyTo(array, arrayIndex); - } - - public bool Remove(AutomatonTransition item) { - m_transitions.Remove(item); - } - - public int Count { - get { - return m_transitions.Count; - } - } - - public bool IsReadOnly { - get { - return false; - } - } - - public IEnumerator<AutomatonTransition> GetEnumerator() { - return m_transitions.GetEnumerator(); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return GetEnumerator(); - } - - public DFAStateDescriptor[] CreateTransitionTable() { - var table = new DFAStateDescriptor[StateCount]; - - foreach (var t in this) { - if (table[t.s1].transitions == null) - table[t.s1] = new DFAStateDescriptor(AlphabetSize, IsFinalState(t.s1)); - if (table[t.s2].transitions == null) - table[t.s2] = new DFAStateDescriptor(AlphabetSize, IsFinalState(t.s2)); - table[t.s1].transitions[t.edge] = t.s2; - } - - return table; - } - - /// <summary>Формирует множества конечных состояний перед началом работы алгоритма минимизации.</summary> - /// <remarks> - /// В процессе построения минимального автомата требуется разделить множество состояний, - /// на два подмножества - конечные состояния и все остальные, после чего эти подмножества - /// будут резделены на более мелкие. Иногда требуется гарантировать различия конечных сосотяний, - /// для этого необходимо переопределить даннцю фукнцию, для получения множеств конечных состояний. - /// </remarks> - /// <returns>The final states.</returns> - protected virtual IEnumerable<HashSet<int>> GroupFinalStates() { - return new HashSet<int>[] { m_finalStates }; - } - - protected void Optimize( - IDFATableBuilder optimalDFA, - IDictionary<int,int> alphabetMap, - IDictionary<int,int> stateMap - ) { - Safe.ArgumentNotNull(optimalDFA, "dfa"); - Safe.ArgumentNotNull(alphabetMap, "alphabetMap"); - Safe.ArgumentNotNull(stateMap, "stateMap"); - - - var setComparer = new CustomEqualityComparer<HashSet<int>>( - (x, y) => x.SetEquals(y), - s => s.Sum(x => x.GetHashCode()) - ); - - var optimalStates = new HashSet<HashSet<int>>(setComparer); - var queue = new HashSet<HashSet<int>>(setComparer); - - // получаем конечные состояния, сгруппированные по маркерам - optimalStates.UnionWith( - GroupFinalStates() - ); - - var state = new HashSet<int>( - Enumerable - .Range(0, m_stateCount - 1) - .Where(i => !m_finalStates.Contains(i)) - ); - - optimalStates.Add(state); - queue.Add(state); - - var rmap = m_transitions - .GroupBy(t => t.s2) - .ToLookup( - g => g.Key, // s2 - g => g.ToLookup(t => t.edge, t => t.s1) - ); - - while (queue.Count > 0) { - var stateA = queue.First(); - queue.Remove(stateA); - - for (int c = 0; c < m_symbolCount; c++) { - var stateX = new HashSet<int>(); - foreach(var a in stateA) - stateX.UnionWith(rmap[a][c]); // all states from wich 'c' leads to 'a' - - foreach (var stateY in optimalStates.ToArray()) { - if (stateX.Overlaps(stateY) && !stateY.IsSubsetOf(stateX)) { - var stateR1 = new HashSet<int>(stateY); - var stateR2 = new HashSet<int>(stateY); - - stateR1.IntersectWith(stateX); - stateR2.ExceptWith(stateX); - - optimalStates.Remove(stateY); - optimalStates.Add(stateR1); - optimalStates.Add(stateR2); - - if (queue.Contains(stateY)) { - queue.Remove(stateY); - queue.Add(stateR1); - queue.Add(stateR2); - } else { - queue.Add(stateR1.Count <= stateR2.Count ? stateR1 : stateR2); - } - } - } - } - } - - // карта получения оптимального состояния по соотвествующему ему простому состоянию - var nextState = 0; - foreach (var item in optimalStates) { - var id = nextState++; - foreach (var s in item) - stateMap[s] = id; - } - - // получаем минимальный алфавит - // входные символы не различимы, если Move(s,a1) == Move(s,a2), для любого s - // для этого используем алгоритм кластеризации, сначала - // считаем, что все символы не различимы - - var minClasses = new HashSet<HashSet<int>>(setComparer); - var alphaQueue = new Queue<HashSet<int>>(); - alphaQueue.Enqueue(new HashSet<int>(Enumerable.Range(0,AlphabetSize))); - - // для всех состояний, будем проверять каждый класс на различимость, - // т.е. символы различимы, если они приводят к разным состояниям - for (int s = 0 ; s < optimalStates.Count; s++) { - var newQueue = new Queue<HashSet<int>>(); - - foreach (var A in alphaQueue) { - // классы из одного символа делить бесполезно, переводим их сразу в - // результирующий алфавит - if (A.Count == 1) { - minClasses.Add(A); - continue; - } - - // различаем классы символов, которые переводят в различные оптимальные состояния - // optimalState -> alphaClass - var classes = new Dictionary<int, HashSet<int>>(); - - foreach (var term in A) { - // ищем все переходы класса по символу term - var res = m_transitions.Where(t => stateMap[t.s1] == s && t.edge == term).Select(t => stateMap[t.s2]).ToArray(); - - var s2 = res.Length > 0 ? res[0] : -1; - - HashSet<int> a2; - if (!classes.TryGetValue(s2, out a2)) { - a2 = new HashSet<int>(); - newQueue.Enqueue(a2); - classes[s2] = a2; - } - a2.Add(term); - } - } - - if (newQueue.Count == 0) - break; - alphaQueue = newQueue; - } - - // после окончания работы алгоритма в очереди останутся минимальные различимые классы - // входных символов - foreach (var A in alphaQueue) - minClasses.Add(A); - - // построение отображения алфавитов входных символов. - // поскольку символ DFAConst.UNCLASSIFIED_INPUT может иметь - // специальное значение, тогда сохраним минимальный класс, - // содержащий этот символ на томже месте. - - var nextCls = 0; - foreach (var item in minClasses) { - if (nextCls == DFAConst.UNCLASSIFIED_INPUT) - nextCls++; - - // сохраняем DFAConst.UNCLASSIFIED_INPUT - var cls = item.Contains(DFAConst.UNCLASSIFIED_INPUT) ? DFAConst.UNCLASSIFIED_INPUT : nextCls; - - foreach (var a in item) - alphabetMap[a] = cls; - - nextCls++; - } - - // построение автомата - optimalDFA.SetInitialState(stateMap[m_initialState]); - - foreach (var sf in m_finalStates.Select(s => stateMap[s]).Distinct()) - optimalDFA.MarkFinalState(sf); - - foreach (var t in m_transitions.Select(t => new AutomatonTransition(stateMap[t.s1],stateMap[t.s2],alphabetMap[t.edge])).Distinct()) - optimalDFA.Add(t); - } - - protected void PrintDFA<TInput, TState>(IAlphabet<TInput> inputAlphabet, IAlphabet<TState> stateAlphabet) { - Safe.ArgumentNotNull(inputAlphabet, "inputAlphabet"); - Safe.ArgumentNotNull(stateAlphabet, "stateAlphabet"); - - foreach(var t in m_transitions) - Console.WriteLine( - "[{0}] -{{{1}}}-> [{2}]{3}", - String.Join(",", stateAlphabet.GetSymbols(t.s1)), - String.Join("", inputAlphabet.GetSymbols(t.edge)), - String.Join(",", stateAlphabet.GetSymbols(t.s2)), - m_finalStates.Contains(t.s2) ? "$" : "" - ); - } - - } -} +using Implab; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Implab.Automaton { + public class DFATable : IDFATableBuilder { + int m_stateCount; + int m_symbolCount; + int m_initialState; + + readonly HashSet<int> m_finalStates = new HashSet<int>(); + readonly HashSet<AutomatonTransition> m_transitions = new HashSet<AutomatonTransition>(); + + + #region IDFADefinition implementation + + public bool IsFinalState(int s) { + Safe.ArgumentInRange(s, 0, m_stateCount, "s"); + + return m_finalStates.Contains(s); + } + + public IEnumerable<int> FinalStates { + get { + return m_finalStates; + } + } + + public int StateCount { + get { return m_stateCount; } + } + + public int AlphabetSize { + get { return m_symbolCount; } + } + + public int InitialState { + get { return m_initialState; } + } + + #endregion + + public void SetInitialState(int s) { + Safe.ArgumentAssert(s >= 0, "s"); + m_initialState = s; + } + + public void MarkFinalState(int state) { + m_finalStates.Add(state); + } + + public void Add(AutomatonTransition item) { + Safe.ArgumentAssert(item.s1 >= 0, "item"); + Safe.ArgumentAssert(item.s2 >= 0, "item"); + Safe.ArgumentAssert(item.edge >= 0, "item"); + + m_stateCount = Math.Max(m_stateCount, Math.Max(item.s1, item.s2) + 1); + m_symbolCount = Math.Max(m_symbolCount, item.edge); + + m_transitions.Add(item); + } + + public void Clear() { + m_stateCount = 0; + m_symbolCount = 0; + m_finalStates.Clear(); + m_transitions.Clear(); + } + + public bool Contains(AutomatonTransition item) { + return m_transitions.Contains(item); + } + + public void CopyTo(AutomatonTransition[] array, int arrayIndex) { + m_transitions.CopyTo(array, arrayIndex); + } + + public bool Remove(AutomatonTransition item) { + m_transitions.Remove(item); + } + + public int Count { + get { + return m_transitions.Count; + } + } + + public bool IsReadOnly { + get { + return false; + } + } + + public IEnumerator<AutomatonTransition> GetEnumerator() { + return m_transitions.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + public int[,] CreateTransitionTable() { + var table = new int[StateCount,AlphabetSize]; + + for (int i = 0; i < StateCount; i++) + for (int j = 0; i < AlphabetSize; j++) + table[i, j] = DFAConst.UNREACHABLE_STATE; + + foreach (var t in this) + table[t.s1,t.edge] = t.s2; + + return table; + } + + public bool[] CreateFinalStateTable() { + var table = new bool[StateCount]; + + foreach (var s in FinalStates) + table[s] = true; + + return table; + } + + /// <summary>Формирует множества конечных состояний перед началом работы алгоритма минимизации.</summary> + /// <remarks> + /// В процессе построения минимального автомата требуется разделить множество состояний, + /// на два подмножества - конечные состояния и все остальные, после чего эти подмножества + /// будут резделены на более мелкие. Иногда требуется гарантировать различия конечных сосотяний, + /// для этого необходимо переопределить даннцю фукнцию, для получения множеств конечных состояний. + /// </remarks> + /// <returns>The final states.</returns> + protected virtual IEnumerable<HashSet<int>> GroupFinalStates() { + return new HashSet<int>[] { m_finalStates }; + } + + protected void Optimize( + IDFATableBuilder optimalDFA, + IDictionary<int,int> alphabetMap, + IDictionary<int,int> stateMap + ) { + Safe.ArgumentNotNull(optimalDFA, "dfa"); + Safe.ArgumentNotNull(alphabetMap, "alphabetMap"); + Safe.ArgumentNotNull(stateMap, "stateMap"); + + + var setComparer = new CustomEqualityComparer<HashSet<int>>( + (x, y) => x.SetEquals(y), + s => s.Sum(x => x.GetHashCode()) + ); + + var optimalStates = new HashSet<HashSet<int>>(setComparer); + var queue = new HashSet<HashSet<int>>(setComparer); + + // получаем конечные состояния, сгруппированные по маркерам + optimalStates.UnionWith( + GroupFinalStates() + ); + + var state = new HashSet<int>( + Enumerable + .Range(0, m_stateCount - 1) + .Where(i => !m_finalStates.Contains(i)) + ); + + optimalStates.Add(state); + queue.Add(state); + + var rmap = m_transitions + .GroupBy(t => t.s2) + .ToLookup( + g => g.Key, // s2 + g => g.ToLookup(t => t.edge, t => t.s1) + ); + + while (queue.Count > 0) { + var stateA = queue.First(); + queue.Remove(stateA); + + for (int c = 0; c < m_symbolCount; c++) { + var stateX = new HashSet<int>(); + foreach(var a in stateA) + stateX.UnionWith(rmap[a][c]); // all states from wich 'c' leads to 'a' + + foreach (var stateY in optimalStates.ToArray()) { + if (stateX.Overlaps(stateY) && !stateY.IsSubsetOf(stateX)) { + var stateR1 = new HashSet<int>(stateY); + var stateR2 = new HashSet<int>(stateY); + + stateR1.IntersectWith(stateX); + stateR2.ExceptWith(stateX); + + optimalStates.Remove(stateY); + optimalStates.Add(stateR1); + optimalStates.Add(stateR2); + + if (queue.Contains(stateY)) { + queue.Remove(stateY); + queue.Add(stateR1); + queue.Add(stateR2); + } else { + queue.Add(stateR1.Count <= stateR2.Count ? stateR1 : stateR2); + } + } + } + } + } + + // карта получения оптимального состояния по соотвествующему ему простому состоянию + var nextState = 0; + foreach (var item in optimalStates) { + var id = nextState++; + foreach (var s in item) + stateMap[s] = id; + } + + // получаем минимальный алфавит + // входные символы не различимы, если Move(s,a1) == Move(s,a2), для любого s + // для этого используем алгоритм кластеризации, сначала + // считаем, что все символы не различимы + + var minClasses = new HashSet<HashSet<int>>(setComparer); + var alphaQueue = new Queue<HashSet<int>>(); + alphaQueue.Enqueue(new HashSet<int>(Enumerable.Range(0,AlphabetSize))); + + // для всех состояний, будем проверять каждый класс на различимость, + // т.е. символы различимы, если они приводят к разным состояниям + for (int s = 0 ; s < optimalStates.Count; s++) { + var newQueue = new Queue<HashSet<int>>(); + + foreach (var A in alphaQueue) { + // классы из одного символа делить бесполезно, переводим их сразу в + // результирующий алфавит + if (A.Count == 1) { + minClasses.Add(A); + continue; + } + + // различаем классы символов, которые переводят в различные оптимальные состояния + // optimalState -> alphaClass + var classes = new Dictionary<int, HashSet<int>>(); + + foreach (var term in A) { + // ищем все переходы класса по символу term + var res = m_transitions.Where(t => stateMap[t.s1] == s && t.edge == term).Select(t => stateMap[t.s2]).ToArray(); + + var s2 = res.Length > 0 ? res[0] : -1; + + HashSet<int> a2; + if (!classes.TryGetValue(s2, out a2)) { + a2 = new HashSet<int>(); + newQueue.Enqueue(a2); + classes[s2] = a2; + } + a2.Add(term); + } + } + + if (newQueue.Count == 0) + break; + alphaQueue = newQueue; + } + + // после окончания работы алгоритма в очереди останутся минимальные различимые классы + // входных символов + foreach (var A in alphaQueue) + minClasses.Add(A); + + // построение отображения алфавитов входных символов. + // поскольку символ DFAConst.UNCLASSIFIED_INPUT может иметь + // специальное значение, тогда сохраним минимальный класс, + // содержащий этот символ на томже месте. + + var nextCls = 0; + foreach (var item in minClasses) { + if (nextCls == DFAConst.UNCLASSIFIED_INPUT) + nextCls++; + + // сохраняем DFAConst.UNCLASSIFIED_INPUT + var cls = item.Contains(DFAConst.UNCLASSIFIED_INPUT) ? DFAConst.UNCLASSIFIED_INPUT : nextCls; + + foreach (var a in item) + alphabetMap[a] = cls; + + nextCls++; + } + + // построение автомата + optimalDFA.SetInitialState(stateMap[m_initialState]); + + foreach (var sf in m_finalStates.Select(s => stateMap[s]).Distinct()) + optimalDFA.MarkFinalState(sf); + + foreach (var t in m_transitions.Select(t => new AutomatonTransition(stateMap[t.s1],stateMap[t.s2],alphabetMap[t.edge])).Distinct()) + optimalDFA.Add(t); + } + + protected void PrintDFA<TInput, TState>(IAlphabet<TInput> inputAlphabet, IAlphabet<TState> stateAlphabet) { + Safe.ArgumentNotNull(inputAlphabet, "inputAlphabet"); + Safe.ArgumentNotNull(stateAlphabet, "stateAlphabet"); + + foreach(var t in m_transitions) + Console.WriteLine( + "[{0}] -{{{1}}}-> [{2}]{3}", + String.Join(",", stateAlphabet.GetSymbols(t.s1)), + String.Join("", inputAlphabet.GetSymbols(t.edge)), + String.Join(",", stateAlphabet.GetSymbols(t.s2)), + m_finalStates.Contains(t.s2) ? "$" : "" + ); + } + + } +}
--- a/Implab/Automaton/IndexedAlphabetBase.cs Mon Mar 21 18:41:45 2016 +0300 +++ b/Implab/Automaton/IndexedAlphabetBase.cs Tue Mar 22 18:58:40 2016 +0300 @@ -13,82 +13,38 @@ /// to the input alphabet of the automaton. It's assumed that the index to the symbol match /// is well known and documented. /// </remarks> - public abstract class IndexedAlphabetBase<T> : IAlphabetBuilder<T> { - int m_nextId = 1; - readonly int[] m_map; - - protected IndexedAlphabetBase(int mapSize) { - m_map = new int[mapSize]; - } - - protected IndexedAlphabetBase(int[] map) { - Debug.Assert(map != null && map.Length > 0); - Debug.Assert(map.All(x => x >= 0)); - - m_map = map; - m_nextId = map.Max() + 1; - } - - public int DefineSymbol(T symbol) { - var index = GetSymbolIndex(symbol); - if (m_map[index] == DFAConst.UNCLASSIFIED_INPUT) - m_map[index] = m_nextId++; - return m_map[index]; - } - - public int DefineSymbol(T symbol, int cls) { - var index = GetSymbolIndex(symbol); - m_map[index] = cls; - m_nextId = Math.Max(cls + 1, m_nextId); - return cls; - } + public abstract class IndexedAlphabetBase<T> : MapAlphabet<T> { - public int DefineClass(IEnumerable<T> symbols) { - return DefineClass(symbols, m_nextId); - } - - public int DefineClass(IEnumerable<T> symbols, int cls) { - Safe.ArgumentNotNull(symbols, "symbols"); - symbols = symbols.Distinct(); - - foreach (var symbol in symbols) - m_map[GetSymbolIndex(symbol)] = cls; - - m_nextId = Math.Max(cls + 1, m_nextId); - - return cls; - } - - public virtual int Translate(T symbol) { - return m_map[GetSymbolIndex(symbol)]; - } - - public int Count { - get { return m_nextId; } - } - - public bool Contains(T symbol) { - return true; - } - - public IEnumerable<T> GetSymbols(int cls) { - for (var i = 0; i < m_map.Length; i++) - if (m_map[i] == cls) - yield return GetSymbolByIndex(i); + protected IndexedAlphabetBase() :base(true, null) { } public abstract int GetSymbolIndex(T symbol); - public abstract T GetSymbolByIndex(int index); - - public abstract IEnumerable<T> InputSymbols { get; } - /// <summary> /// Gets the translation map from the index of the symbol to it's class this is usefull for the optimized input symbols transtaion. /// </summary> + /// <remarks> + /// The map is continous and start from the symbol with zero code. The last symbol + /// in the map is the last classified symbol in the alphabet, i.e. the map can be + /// shorter then the whole alphabet. + /// </remarks> /// <returns>The translation map.</returns> public int[] GetTranslationMap() { - return m_map; + Dictionary<int,int> map = new Dictionary<int, int>(); + + int max; + foreach (var p in Mappings) { + var index = GetSymbolIndex(p.Key); + max = Math.Max(max, index); + map[index] = p.Value; + } + + var result = new int[max + 1]; + + for (int i = 0; i < result.Length; i++) + map.TryGetValue(i, out result[i]); + + return result; } } }
--- a/Implab/Automaton/MapAlphabet.cs Mon Mar 21 18:41:45 2016 +0300 +++ b/Implab/Automaton/MapAlphabet.cs Tue Mar 22 18:58:40 2016 +0300 @@ -69,9 +69,16 @@ public IEnumerable<T> GetSymbols(int cls) { + Safe.ArgumentAssert(cls > 0, "cls"); return m_map.Where(p => p.Value == cls).Select(p => p.Key); } #endregion + + public IEnumerable<KeyValuePair<T,int>> Mappings { + get { + return m_map; + } + } } }
--- a/Implab/Automaton/RegularExpressions/DFAStateDescriptorT.cs Mon Mar 21 18:41:45 2016 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -using System; - -namespace Implab.Automaton.RegularExpressions { - public struct DFAStateDescriptor<T> { - public readonly bool final; - - public readonly int[] transitions; - - public readonly T[] tags; - - public DFAStateDescriptor(int size, bool final, T[] tags) { - Safe.ArgumentAssert(size >= 0, "size"); - this.final = final; - this.tags = tags; - - transitions = new int[size]; - - for (int i = 0; i < size; i++) - transitions[i] = DFAConst.UNREACHABLE_STATE; - } - } -} -
--- a/Implab/Automaton/RegularExpressions/Grammar.cs Mon Mar 21 18:41:45 2016 +0300 +++ b/Implab/Automaton/RegularExpressions/Grammar.cs Tue Mar 22 18:58:40 2016 +0300 @@ -66,9 +66,9 @@ return Token<TTag>.New( Enumerable.Range(0, AlphabetBuilder.Count).Except(TranslateOrDie(symbols)).ToArray() ); } - protected abstract IAlphabetBuilder<TSymbol> CreateAlphabet(); + protected abstract IndexedAlphabetBase<TSymbol> CreateAlphabet(); - protected RegularDFA<TSymbol, TTag> BuildDFA(Token<TTag> regexp) { + protected ScannerContext<TTag> BuildScannerContext(Token<TTag> regexp) { var dfa = new RegularDFA<TSymbol, TTag>(AlphabetBuilder); @@ -80,7 +80,16 @@ if (dfa.IsFinalState(dfa.InitialState)) throw new ApplicationException("The specified language contains empty token"); - return dfa.Optimize(CreateAlphabet()); + var ab = CreateAlphabet(); + var optimal = dfa.Optimize(ab); + + return new ScannerContext<TTag>( + optimal.CreateTransitionTable(), + optimal.CreateFinalStateTable(), + optimal.CreateTagTable(), + optimal.InitialState, + ab.GetTranslationMap() + ); } }
--- a/Implab/Automaton/RegularExpressions/RegularDFA.cs Mon Mar 21 18:41:45 2016 +0300 +++ b/Implab/Automaton/RegularExpressions/RegularDFA.cs Tue Mar 22 18:58:40 2016 +0300 @@ -36,16 +36,11 @@ return m_tags.TryGetValue(s, out tags) ? tags : new TTag[0]; } - public new DFAStateDescriptor<TTag>[] CreateTransitionTable() { - var table = new DFAStateDescriptor<TTag>[StateCount]; + public TTag[][] CreateTagTable() { + var table = new TTag[StateCount][]; - foreach (var t in this) { - if (table[t.s1].transitions == null) - table[t.s1] = new DFAStateDescriptor<TTag>(AlphabetSize, IsFinalState(t.s1), GetStateTag(t.s1)); - if (table[t.s2].transitions == null) - table[t.s2] = new DFAStateDescriptor<TTag>(AlphabetSize, IsFinalState(t.s2), GetStateTag(t.s2)); - table[t.s1].transitions[t.edge] = t.s2; - } + foreach (var pair in m_tags) + table[pair.Key] = pair.Value; return table; }
--- a/Implab/Automaton/Scanner.cs Mon Mar 21 18:41:45 2016 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,255 +0,0 @@ -using Implab; -using System; -using System.Collections.Generic; -using System.IO; -using Implab.Components; -using Implab.Automaton.RegularExpressions; - -namespace Implab.Automaton { - /// <summary> - /// Базовый класс для разбора потока входных символов на токены. - /// </summary> - /// <remarks> - /// Сканнер имеет внутри буффер с симолами входного текста, по которому перемещаются два - /// указателя, начала и конца токена, при перемещении искользуется ДКА для определения - /// конца токена и допустимости текущего символа. - /// </remarks> - public abstract class Scanner<TTag> : Disposable { - protected struct ScannerConfig { - public readonly DFAStateDescriptor<TTag>[] states; - public readonly int[] alphabet; - public readonly int initialState; - - public ScannerConfig(DFAStateDescriptor<TTag>[] states, int[] alphabet, int initialState) { - this.initialState = initialState; - this.alphabet = alphabet; - this.states = states; - } - } - - Stack<ScannerConfig> m_defs = new Stack<ScannerConfig>(); - - ScannerConfig m_config; - - protected DFAStateDescriptor<TTag> m_currentState; - int m_previewCode; - - protected int m_tokenLen; - protected int m_tokenOffset; - - protected char[] m_buffer; - protected int m_bufferSize; - protected int m_pointer; - - TextReader m_reader; - bool m_disposeReader; - int m_chunkSize = 1024; // 1k - int m_limit = 10 * 1024 * 1024; // 10Mb - - protected Scanner(ScannerConfig config) { - Safe.ArgumentNotEmpty(config.states, "config.states"); - Safe.ArgumentNotNull(config.alphabet, "config.alphabet"); - - m_config = config; - } - - /// <summary> - /// Заполняет входными данными буффер. - /// </summary> - /// <param name="data">Данные для обработки.</param> - /// <remarks>Копирование данных не происходит, переданный массив используется в - /// качестве входного буффера.</remarks> - public void Feed(char[] data) { - Safe.ArgumentNotNull(data, "data"); - - Feed(data, data.Length); - } - - /// <summary> - /// Заполняет буффур чтения входными данными. - /// </summary> - /// <param name="data">Данные для обработки.</param> - /// <param name="length">Длина данных для обработки.</param> - /// <remarks>Копирование данных не происходит, переданный массив используется в - /// качестве входного буффера.</remarks> - public void Feed(char[] data, int length) { - Safe.ArgumentNotNull(data, "data"); - Safe.ArgumentInRange(length, 0, data.Length, "length"); - AssertNotDisposed(); - - m_pointer = -1; - m_buffer = data; - m_bufferSize = length; - Shift(); - } - - public void Feed(TextReader reader, bool dispose) { - Safe.ArgumentNotNull(reader, "reader"); - AssertNotDisposed(); - - if (m_reader != null && m_disposeReader) - m_reader.Dispose(); - - m_reader = reader; - m_disposeReader = dispose; - m_pointer = -1; - m_buffer = new char[m_chunkSize]; - m_bufferSize = 0; - Shift(); - } - - /// <summary> - /// Получает текущий токен в виде строки. - /// </summary> - /// <returns></returns> - protected string GetTokenValue() { - return new String(m_buffer, m_tokenOffset, m_tokenLen); - } - - /// <summary> - /// Метки текущего токена, которые были назначены в регулярном выражении. - /// </summary> - protected TTag[] TokenTags { - get { - return m_currentState.tags; - } - } - - /// <summary> - /// Признак конца данных - /// </summary> - public bool EOF { - get { - return m_pointer >= m_bufferSize; - } - } - - /// <summary> - /// Читает следующий токен, при этом <see cref="m_tokenOffset"/> указывает на начало токена, - /// <see cref="m_tokenLen"/> на длину токена, <see cref="m_buffer"/> - массив символов, в - /// котором находится токен. - /// </summary> - /// <returns><c>false</c> - достигнут конец данных, токен не прочитан.</returns> - protected bool ReadTokenInternal() { - if (m_pointer >= m_bufferSize) - return false; - - m_currentState = m_config.states[m_config.initialState]; - m_tokenLen = 0; - m_tokenOffset = m_pointer; - int nextState; - do { - nextState = m_currentState.transitions[m_previewCode]; - if (nextState == DFAConst.UNREACHABLE_STATE) { - if (m_currentState.final) - return true; - - throw new ParserException( - String.Format( - "Unexpected symbol '{0}', at pos {1}", - m_buffer[m_pointer], - Position - ) - ); - } - m_currentState = m_config.states[nextState]; - m_tokenLen++; - - } while (Shift()); - - // END OF DATA - if (!m_currentState.final) - throw new ParserException("Unexpected end of data"); - - return true; - } - - - bool Shift() { - m_pointer++; - - if (m_pointer >= m_bufferSize) { - if (!ReadNextChunk()) - return false; - } - - m_previewCode = m_config.alphabet[m_buffer[m_pointer]]; - - return true; - } - - bool ReadNextChunk() { - if (m_reader == null) - return false; - - // extend buffer if nesessary - if (m_pointer + m_chunkSize > m_buffer.Length) { - // trim unused buffer head - var size = m_tokenLen + m_chunkSize; - if (size >= m_limit) - throw new ParserException(String.Format("Input buffer {0} bytes limit exceeded", m_limit)); - var temp = new char[size]; - Array.Copy(m_buffer, m_tokenOffset, temp, 0, m_tokenLen); - m_pointer -= m_tokenOffset; - m_bufferSize -= m_tokenOffset; - m_tokenOffset = 0; - m_buffer = temp; - } - - var read = m_reader.Read(m_buffer, m_tokenLen, m_chunkSize); - if (read == 0) - return false; - - m_bufferSize += read; - - return true; - } - - /// <summary> - /// Позиция сканнера во входном буфере - /// </summary> - public int Position { - get { - return m_pointer + 1; - } - } - - /// <summary> - /// Преключает внутренний ДКА на указанный, позволяет реализовать подобие захватывающей - /// группировки. - /// </summary> - /// <param name = "config"></param> - protected void Switch(ScannerConfig config) { - Safe.ArgumentNotNull(config.states, "config.states"); - - m_defs.Push(m_config); - m_config = config; - - m_previewCode = m_config.alphabet[m_buffer[m_pointer]]; - } - - /// <summary> - /// Восстанавливает предыдущей ДКА сканнера. - /// </summary> - protected void Restore() { - if (m_defs.Count == 0) - throw new InvalidOperationException(); - m_config = m_defs.Pop(); - - m_previewCode = m_config.alphabet[m_buffer[m_pointer]]; - } - - protected override void Dispose(bool disposing) { - if (disposing) { - if (m_reader != null && m_disposeReader) - m_reader.Dispose(); - m_buffer = null; - m_bufferSize = 0; - m_pointer = 0; - m_tokenLen = 0; - m_tokenOffset = 0; - } - base.Dispose(disposing); - } - } -}
--- a/Implab/Formats/BufferScanner.cs Mon Mar 21 18:41:45 2016 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -using System; -using Implab.Automaton.RegularExpressions; -using Implab.Automaton; -using System.Diagnostics; - -namespace Implab.Formats { - public struct BufferScanner<TTag> { - readonly DFAStateDescriptor<TTag>[] m_dfa; - int m_state; - int m_pos; - - public BufferScanner(DFAStateDescriptor<TTag>[] dfa, int initialState) { - m_dfa = dfa; - m_state = initialState; - } - - public int Position { - get { return m_pos; } - } - - /// <summary> - /// Scan this instance. - /// </summary> - /// <returns><c>true</c> - additional data required</returns> - public bool Scan(int[] buffer, int position, int length) { - var hi = position + length; - m_pos = position; - - while (position < hi) { - var next = m_dfa[m_state].transitions[buffer[position]]; - if (next == DFAConst.UNREACHABLE_STATE) { - if (m_dfa[m_state].final) - return false; - - throw new ParserException( - String.Format( - "Unexpected symbol" - ) - ); - } - m_pos++; - m_state = next; - } - - return true; - } - - public void Eof() { - if (!m_dfa[m_state].final) - throw new ParserException( - String.Format( - "Unexpected EOF" - ) - ); - } - - public TTag[] GetTokenTags() { - return m_dfa[m_state].tags; - } - } -} -
--- a/Implab/Formats/ByteAlphabet.cs Mon Mar 21 18:41:45 2016 +0300 +++ b/Implab/Formats/ByteAlphabet.cs Tue Mar 22 18:58:40 2016 +0300 @@ -4,7 +4,7 @@ namespace Implab.Formats { public class ByteAlphabet : IndexedAlphabetBase<byte> { - public ByteAlphabet() : base(byte.MaxValue + 1){ + public ByteAlphabet() { } #region implemented abstract members of IndexedAlphabetBase @@ -13,10 +13,6 @@ return (int)symbol; } - public override byte GetSymbolByIndex(int index) { - return (byte)index; - } - public IEnumerable<byte> InputSymbols { get { return Enumerable.Range(byte.MinValue, byte.MaxValue).Cast<byte>();
--- a/Implab/Formats/CharAlphabet.cs Mon Mar 21 18:41:45 2016 +0300 +++ b/Implab/Formats/CharAlphabet.cs Tue Mar 22 18:58:40 2016 +0300 @@ -5,19 +5,14 @@ namespace Implab.Formats { public class CharAlphabet: IndexedAlphabetBase<char> { - public CharAlphabet() - : base(char.MaxValue + 1) { + public CharAlphabet() { } public override int GetSymbolIndex(char symbol) { return symbol; } - public override char GetSymbolByIndex(int index) { - return (char)index; - } - - public override IEnumerable<char> InputSymbols { + public IEnumerable<char> InputSymbols { get { return Enumerable.Range(char.MinValue, char.MaxValue).Cast<char>(); } } }
--- a/Implab/Formats/JSON/JSONGrammar.cs Mon Mar 21 18:41:45 2016 +0300 +++ b/Implab/Formats/JSON/JSONGrammar.cs Tue Mar 22 18:58:40 2016 +0300 @@ -20,14 +20,7 @@ StringBound, EscapedChar, UnescapedChar, - EscapedUnicode, - - Minus, - Plus, - Sign, - Integer, - Dot, - Exp + EscapedUnicode } static Lazy<JSONGrammar> _instance = new Lazy<JSONGrammar>(); @@ -36,8 +29,8 @@ get { return _instance.Value; } } - readonly RegularDFA<char, TokenType> m_jsonDFA; - readonly RegularDFA<char, TokenType> m_stringDFA; + readonly ScannerContext<TokenType> m_jsonDFA; + readonly ScannerContext<TokenType> m_stringDFA; public JSONGrammar() { DefineAlphabet(Enumerable.Range(0, 0x20).Select(x => (char)x)); @@ -88,17 +81,17 @@ .Or(unescaped.Closure().Tag(TokenType.UnescapedChar)); - m_jsonDFA = BuildDFA(jsonExpression); - m_stringDFA = BuildDFA(jsonStringExpression); + m_jsonDFA = BuildScannerContext(jsonExpression); + m_stringDFA = BuildScannerContext(jsonStringExpression); } - public RegularDFA<char, TokenType> JsonDFA { + public ScannerContext<TokenType> JsonDFA { get { return m_jsonDFA; } } - public RegularDFA<char,TokenType> JsonStringDFA { + public ScannerContext<TokenType> JsonStringDFA { get { return m_stringDFA; }
--- a/Implab/Formats/JSON/JSONScanner.cs Mon Mar 21 18:41:45 2016 +0300 +++ b/Implab/Formats/JSON/JSONScanner.cs Tue Mar 22 18:58:40 2016 +0300 @@ -1,25 +1,37 @@ using System; using System.Globalization; using Implab.Automaton; +using System.Text; +using Implab.Components; +using System.IO; +using Implab.Automaton.RegularExpressions; namespace Implab.Formats.JSON { /// <summary> /// Сканнер (лексер), разбивающий поток символов на токены JSON. /// </summary> - public class JSONScanner : Scanner<object> { - char[] m_stringBuffer; - DFAStateDescriptior<>[] m_stringDFA; - int[] m_stringAlphabet; + public class JSONScanner : Disposable { + readonly StringBuilder m_builder = new StringBuilder(); + + readonly ScannerContext<JSONGrammar.TokenType> m_jsonScanner = JSONGrammar.Instance.JsonDFA; + readonly ScannerContext<JSONGrammar.TokenType> m_stringScanner = JSONGrammar.Instance.JsonStringDFA; + + + readonly TextScanner m_scanner; /// <summary> /// Создает новый экземпляр сканнера /// </summary> - public JSONScanner() - : base(JSONGrammar.Instance.JsonDFA.GetTransitionTable(), JSONGrammar.Instance.JsonDFA.Alphabet.GetTranslationMap()) { - m_stringBuffer = new char[1024]; - var dfa = JSONGrammar.Instance.JsonStringDFA; - m_stringAlphabet = dfa.Alphabet.GetTranslationMap(); - m_stringDFA = dfa.States; + public JSONScanner(string text) { + Safe.ArgumentNotEmpty(text, "text"); + + m_scanner = new StringScanner(text); + } + + public JSONScanner(TextReader reader, int bufferMax, int chunkSize) { + Safe.ArgumentNotNull(reader, "reader"); + + m_scanner = new ReaderScanner(reader); } /// <summary> @@ -31,19 +43,20 @@ /// <remarks>В случе если токен не распознается, возникает исключение. Значения токенов обрабатываются, т.е. /// в строках обрабатываются экранированные символы, числа становтся типа double.</remarks> public bool ReadToken(out object tokenValue, out JsonTokenType tokenType) { - if (ReadTokenInternal()) { - switch ((JSONGrammar.TokenType)m_currentState.tag[0]) { + JSONGrammar.TokenType[] tag; + if (m_jsonScanner.Execute(m_scanner, out tag)) { + switch (tag[0]) { case JSONGrammar.TokenType.StringBound: tokenValue = ReadString(); tokenType = JsonTokenType.String; break; case JSONGrammar.TokenType.Number: - tokenValue = Double.Parse(new String(m_buffer, m_tokenOffset, m_tokenLen), CultureInfo.InvariantCulture); + tokenValue = Double.Parse(m_scanner.GetTokenValue(), CultureInfo.InvariantCulture); tokenType = JsonTokenType.Number; break; default: - tokenType = (JsonTokenType)m_currentState.tag[0]; - tokenValue = new String(m_buffer, m_tokenOffset, m_tokenLen); + tokenType = (JsonTokenType)tag[0]; + tokenValue = m_scanner.GetTokenValue(); break; } return true; @@ -55,26 +68,26 @@ string ReadString() { int pos = 0; - Switch(m_stringDFA, m_stringAlphabet); - while (ReadTokenInternal()) { - switch ((JSONGrammar.TokenType)m_currentState.tag[0]) { + char[] buf = new char[6]; // the buffer for unescaping chars + + JSONGrammar.TokenType[] tag; + m_builder.Clear(); + + while (m_stringScanner.Execute(m_scanner, out tag)) { + switch (tag[0]) { case JSONGrammar.TokenType.StringBound: - Restore(); - return new String(m_stringBuffer, 0, pos); + return m_builder.ToString(); case JSONGrammar.TokenType.UnescapedChar: - EnsureStringBufferSize(pos + m_tokenLen); - Array.Copy(m_buffer, m_tokenOffset, m_stringBuffer, pos, m_tokenLen); - pos += m_tokenLen; + m_scanner.CopyTokenTo(m_builder); break; - case JSONGrammar.TokenType.EscapedUnicode: - EnsureStringBufferSize(pos + 1); - m_stringBuffer[pos] = StringTranslator.TranslateHexUnicode(m_buffer, m_tokenOffset + 2); + case JSONGrammar.TokenType.EscapedUnicode: // \xXXXX - unicode escape sequence + m_scanner.CopyTokenTo(buf, 0); + m_builder.Append(StringTranslator.TranslateHexUnicode(buf, 2)); pos++; break; - case JSONGrammar.TokenType.EscapedChar: - EnsureStringBufferSize(pos + 1); - m_stringBuffer[pos] = StringTranslator.TranslateEscapedChar(m_buffer[m_tokenOffset + 1]); - pos++; + case JSONGrammar.TokenType.EscapedChar: // \t - escape sequence + m_scanner.CopyTokenTo(buf, 0); + m_builder.Append(StringTranslator.TranslateEscapedChar(buf[1])); break; default: break; @@ -84,13 +97,5 @@ throw new ParserException("Unexpected end of data"); } - - void EnsureStringBufferSize(int size) { - if (size > m_stringBuffer.Length) { - var newBuffer = new char[size]; - m_stringBuffer.CopyTo(newBuffer, 0); - m_stringBuffer = newBuffer; - } - } } }
--- a/Implab/Formats/JSON/StringTranslator.cs Mon Mar 21 18:41:45 2016 +0300 +++ b/Implab/Formats/JSON/StringTranslator.cs Tue Mar 22 18:58:40 2016 +0300 @@ -1,5 +1,5 @@ using Implab; -using Implab.Parsing; +using Implab.Formats; using System; using System.Collections.Generic; using System.Diagnostics; @@ -7,11 +7,11 @@ using System.Text; using System.Threading.Tasks; -namespace Implab.JSON { +namespace Implab.Formats.JSON { /// <summary> /// Класс для преобразования экранированной строки JSON /// </summary> - public class StringTranslator : Scanner { + public class StringTranslator : TextScanner<JSONGrammar.TokenType> { static readonly char[] _escMap; static readonly int[] _hexMap; @@ -34,8 +34,7 @@ } - public StringTranslator() - : base(JSONGrammar.Instance.JsonStringDFA.States, JSONGrammar.Instance.JsonStringDFA.Alphabet.GetTranslationMap()) { + public StringTranslator() { } public string Translate(string data) { @@ -59,7 +58,7 @@ int pos = 0; while (ReadTokenInternal()) { - switch ((JSONGrammar.TokenType)TokenTags[0]) { + switch ((JSONGrammar.TokenType)Tags[0]) { case JSONGrammar.TokenType.UnescapedChar: Array.Copy(m_buffer,m_tokenOffset,translated,pos,m_tokenLen); pos += m_tokenLen;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Formats/ReaderScanner.cs Tue Mar 22 18:58:40 2016 +0300 @@ -0,0 +1,30 @@ +using System; +using System.IO; + +namespace Implab.Formats { + public class ReaderScanner: TextScanner { + const int CHUNK_SIZE = 1024; + const int BUFFER_MAX = CHUNK_SIZE*1024; + + readonly TextReader m_reader; + + public ReaderScanner(TextReader reader, int limit, int chunk) : base(limit, chunk) { + Safe.ArgumentNotNull(reader, "reader"); + m_reader = reader; + } + + public ReaderScanner(TextReader reader) : this(reader, BUFFER_MAX, CHUNK_SIZE) { + } + + protected override int Read(char[] buffer, int offset, int size) { + return m_reader.Read(buffer, offset, size); + } + + protected override void Dispose(bool disposing) { + if (disposing) + Safe.Dispose(m_reader); + base.Dispose(disposing); + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Formats/ScannerContext.cs Tue Mar 22 18:58:40 2016 +0300 @@ -0,0 +1,24 @@ +using System; + +namespace Implab.Formats { + public class ScannerContext<TTag> { + public int[,] Dfa { get; private set; } + public bool[] Final { get; private set; } + public TTag[][] Tags { get; private set; } + public int State { get; private set; } + public int[] Alphabet { get; private set; } + + public ScannerContext(int[,] dfa, bool[] final, TTag[][] tags, int state, int[] alphabet) { + Dfa = dfa; + Final = final; + Tags = tags; + State = state; + Alphabet = alphabet; + } + + public bool Execute(TextScanner scanner, out TTag[] tag) { + return scanner.ReadToken(Dfa, Final, Tags, State, Alphabet, out tag); + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Formats/StringScanner.cs Tue Mar 22 18:58:40 2016 +0300 @@ -0,0 +1,26 @@ +using System; + +namespace Implab.Formats { + public class StringScanner: TextScanner { + const int CHUNK_SIZE = 1024; + + readonly string m_text; + int m_pos; + + public StringScanner(string text) : base(text.Length, text.Length < CHUNK_SIZE ? text.Length : CHUNK_SIZE) { + m_text = text; + Feed(); + } + + protected override int Read(char[] buffer, int offset, int size) { + var actual = size + m_pos > m_text.Length ? m_text.Length - m_pos : size; + + m_text.CopyTo(m_pos,buffer,offset, actual); + + m_pos += actual; + + return actual; + } + } +} +
--- a/Implab/Formats/TextScanner.cs Mon Mar 21 18:41:45 2016 +0300 +++ b/Implab/Formats/TextScanner.cs Tue Mar 22 18:58:40 2016 +0300 @@ -3,50 +3,146 @@ using Implab.Automaton.RegularExpressions; using System.Diagnostics; using Implab.Automaton; +using System.IO; +using System.Text; namespace Implab.Formats { - public abstract class TextScanner<TTag> : Disposable { + public abstract class TextScanner : Disposable { + readonly int m_bufferMax; + readonly int m_chunkSize; - int m_maxSymbol; - int[] m_symbolMap; - - readonly char[] m_buffer; + char[] m_buffer; int m_bufferOffset; int m_bufferSize; + int m_tokenOffset; int m_tokenLength; - TTag[] m_tags; + /// <summary> + /// Initializes a new instance of the <see cref="Implab.Formats.TextScanner`1"/> class. + /// </summary> + /// <param name="bufferMax">Buffer max.</param> + /// <param name="chunkSize">Chunk size.</param> + protected TextScanner(int bufferMax, int chunkSize) { + Debug.Assert(m_chunkSize <= m_bufferMax); + + m_bufferMax = bufferMax; + m_chunkSize = chunkSize; + } - protected bool ReadTokenInternal(DFAStateDescriptor<TTag>[] dfa, int state) { - Debug.Assert(dfa != null); + /// <summary> + /// Initializes a new instance of the <see cref="Implab.Formats.TextScanner`1"/> class. + /// </summary> + /// <param name="buffer">Buffer.</param> + protected TextScanner(char[] buffer) { + if (buffer != null) { + m_buffer = buffer; + m_bufferSize = buffer.Length; + } + } + + /// <summary> + /// (hungry) Reads the next token. + /// </summary> + /// <returns><c>true</c>, if token internal was read, <c>false</c> if there is no more tokens in the stream.</returns> + /// <param name="dfa">The transition map for the automaton</param> + /// <param name="final">Final states of the automaton.</param> + /// <param name="tags">Tags.</param> + /// <param name="state">The initial state for the automaton.</param> + internal bool ReadToken<TTag>(int[,] dfa, int[] final, TTag[][] tags, int state, int[] alphabet, out TTag[] tag) { + Safe.ArgumentNotNull(); + m_tokenLength = 0; + + var maxSymbol = alphabet.Length - 1; do { - for (var pos = m_bufferOffset; pos < m_bufferSize; pos++) { + // after the next chunk is read the offset in the buffer may change + int pos = m_bufferOffset + m_tokenLength; + + while(pos < m_bufferSize) { var ch = m_buffer[pos]; - state = dfa[state].transitions[m_symbolMap[ch > m_maxSymbol ? m_maxSymbol : ch]]; + + state = dfa[state,ch > maxSymbol ? DFAConst.UNCLASSIFIED_INPUT : alphabet[ch]]; if (state == DFAConst.UNREACHABLE_STATE) break; + + pos++; } - } while (Feed()); + + m_tokenLength = pos - m_bufferOffset; + } while (state != DFAConst.UNREACHABLE_STATE && Feed()); + + m_tokenOffset = m_bufferOffset; + m_bufferOffset += m_tokenLength; - if (dfa[state].final) { + if (final[state]) { + tag = tags[state]; + return true; + } else { + if (m_bufferOffset == m_bufferSize) { + if (m_tokenLength == 0) //EOF + return false; + + throw new ParserException(); + } + throw new ParserException(String.Format("Unexpected symbol '{0}'", m_buffer[m_bufferOffset])); + + } + } - } - + protected void Feed(char[] buffer, int offset, int length) { + m_buffer = buffer; + m_bufferOffset = offset; + m_bufferSize = offset + length; } - bool Feed() { + protected bool Feed() { + if (m_chunkSize <= 0) + return false; + + if (m_buffer != null) { + var free = m_buffer.Length - m_bufferSize; + + if (free < m_chunkSize) { + free += m_chunkSize; + var used = m_bufferSize - m_bufferOffset; + var size = used + free; + + if (size > m_bufferMax) + throw new ParserException(String.Format("The buffer limit ({0} Kb) is reached"), m_bufferMax/1024); + + var temp = new char[size]; + var read = Read(temp, used, m_chunkSize); + if (read == 0) + return false; + + Array.Copy(m_buffer, m_bufferOffset, temp, 0, used); + + m_bufferOffset = 0; + m_bufferSize = used + read; + m_buffer = temp; + } + } else { + Debug.Assert(m_bufferOffset == 0); + m_buffer = new char[m_chunkSize]; + m_bufferSize = Read(m_buffer, 0, m_chunkSize); + return (m_bufferSize != 0); + } } protected abstract int Read(char[] buffer, int offset, int size); - protected TTag[] Tags { - get { - return m_tags; - } + public string GetTokenValue() { + return new String(m_buffer, m_tokenOffset, m_tokenLength); } + public void CopyTokenTo(char[] buffer, int offset) { + m_buffer.CopyTo(buffer, offset); + } + + public void CopyTokenTo(StringBuilder sb) { + sb.Append(m_buffer, m_tokenOffset, m_tokenLength); + } } }
--- a/Implab/Implab.csproj Mon Mar 21 18:41:45 2016 +0300 +++ b/Implab/Implab.csproj Tue Mar 22 18:58:40 2016 +0300 @@ -151,11 +151,9 @@ <Compile Include="Components\ExecutionState.cs" /> <Compile Include="Components\RunnableComponent.cs" /> <Compile Include="Components\IFactory.cs" /> - <Compile Include="Automaton\DFAStateDescriptor.cs" /> <Compile Include="Automaton\EnumAlphabet.cs" /> <Compile Include="Automaton\IAlphabet.cs" /> <Compile Include="Automaton\ParserException.cs" /> - <Compile Include="Automaton\Scanner.cs" /> <Compile Include="Automaton\IndexedAlphabetBase.cs" /> <Compile Include="Automaton\IAlphabetBuilder.cs" /> <Compile Include="Automaton\RegularExpressions\AltToken.cs" /> @@ -190,9 +188,10 @@ <Compile Include="Automaton\RegularExpressions\RegularDFA.cs" /> <Compile Include="Automaton\RegularExpressions\RegularExpressionVisitor.cs" /> <Compile Include="Automaton\RegularExpressions\ITaggedDFABuilder.cs" /> - <Compile Include="Automaton\RegularExpressions\DFAStateDescriptorT.cs" /> - <Compile Include="Formats\BufferScanner.cs" /> <Compile Include="Formats\TextScanner.cs" /> + <Compile Include="Formats\StringScanner.cs" /> + <Compile Include="Formats\ReaderScanner.cs" /> + <Compile Include="Formats\ScannerContext.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup />