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