Mercurial > pub > ImplabNet
comparison Implab/Automaton/Scanner.cs @ 162:0526412bbb26 ref20160224
DFA refactoring
| author | cin |
|---|---|
| date | Wed, 24 Feb 2016 08:39:53 +0300 |
| parents | |
| children | ec35731ae299 |
comparison
equal
deleted
inserted
replaced
| 161:2a8466f0cb8a | 162:0526412bbb26 |
|---|---|
| 1 using Implab; | |
| 2 using System; | |
| 3 using System.Collections.Generic; | |
| 4 using System.IO; | |
| 5 using Implab.Components; | |
| 6 | |
| 7 namespace Implab.Automaton { | |
| 8 /// <summary> | |
| 9 /// Базовый класс для разбора потока входных символов на токены. | |
| 10 /// </summary> | |
| 11 /// <remarks> | |
| 12 /// Сканнер имеет внутри буффер с симолами входного текста, по которому перемещаются два | |
| 13 /// указателя, начала и конца токена, при перемещении искользуется ДКА для определения | |
| 14 /// конца токена и допустимости текущего символа. | |
| 15 /// </remarks> | |
| 16 public abstract class Scanner<TTag> : Disposable { | |
| 17 struct ScannerConfig { | |
| 18 public DFAStateDescriptior<TTag>[] states; | |
| 19 public int[] alphabetMap; | |
| 20 } | |
| 21 | |
| 22 Stack<ScannerConfig> m_defs = new Stack<ScannerConfig>(); | |
| 23 | |
| 24 DFAStateDescriptior<TTag>[] m_states; | |
| 25 int[] m_alphabetMap; | |
| 26 | |
| 27 protected DFAStateDescriptior<TTag> m_currentState; | |
| 28 int m_previewCode; | |
| 29 | |
| 30 protected int m_tokenLen = 0; | |
| 31 protected int m_tokenOffset; | |
| 32 | |
| 33 protected char[] m_buffer; | |
| 34 protected int m_bufferSize; | |
| 35 protected int m_pointer; | |
| 36 | |
| 37 TextReader m_reader; | |
| 38 bool m_disposeReader; | |
| 39 int m_chunkSize = 1024; // 1k | |
| 40 int m_limit = 10 * 1024 * 1024; // 10Mb | |
| 41 | |
| 42 protected Scanner(DFAStateDescriptior<TTag>[] states, int[] alphabet) { | |
| 43 Safe.ArgumentNotEmpty(states, "states"); | |
| 44 Safe.ArgumentNotNull(alphabet, "alphabet"); | |
| 45 | |
| 46 m_states = states; | |
| 47 m_alphabetMap = alphabet; | |
| 48 | |
| 49 Feed(new char[0]); | |
| 50 } | |
| 51 | |
| 52 /// <summary> | |
| 53 /// Заполняет входными данными буффер. | |
| 54 /// </summary> | |
| 55 /// <param name="data">Данные для обработки.</param> | |
| 56 /// <remarks>Копирование данных не происходит, переданный массив используется в | |
| 57 /// качестве входного буффера.</remarks> | |
| 58 public void Feed(char[] data) { | |
| 59 Safe.ArgumentNotNull(data, "data"); | |
| 60 | |
| 61 Feed(data, data.Length); | |
| 62 } | |
| 63 | |
| 64 /// <summary> | |
| 65 /// Заполняет буффур чтения входными данными. | |
| 66 /// </summary> | |
| 67 /// <param name="data">Данные для обработки.</param> | |
| 68 /// <param name="length">Длина данных для обработки.</param> | |
| 69 /// <remarks>Копирование данных не происходит, переданный массив используется в | |
| 70 /// качестве входного буффера.</remarks> | |
| 71 public void Feed(char[] data, int length) { | |
| 72 Safe.ArgumentNotNull(data, "data"); | |
| 73 Safe.ArgumentInRange(length, 0, data.Length, "length"); | |
| 74 AssertNotDisposed(); | |
| 75 | |
| 76 m_pointer = -1; | |
| 77 m_buffer = data; | |
| 78 m_bufferSize = length; | |
| 79 Shift(); | |
| 80 } | |
| 81 | |
| 82 public void Feed(TextReader reader, bool dispose) { | |
| 83 Safe.ArgumentNotNull(reader, "reader"); | |
| 84 AssertNotDisposed(); | |
| 85 | |
| 86 if (m_reader != null && m_disposeReader) | |
| 87 m_reader.Dispose(); | |
| 88 | |
| 89 m_reader = reader; | |
| 90 m_disposeReader = dispose; | |
| 91 m_pointer = -1; | |
| 92 m_buffer = new char[m_chunkSize]; | |
| 93 m_bufferSize = 0; | |
| 94 Shift(); | |
| 95 } | |
| 96 | |
| 97 /// <summary> | |
| 98 /// Получает текущий токен в виде строки. | |
| 99 /// </summary> | |
| 100 /// <returns></returns> | |
| 101 protected string GetTokenValue() { | |
| 102 return new String(m_buffer, m_tokenOffset, m_tokenLen); | |
| 103 } | |
| 104 | |
| 105 /// <summary> | |
| 106 /// Метки текущего токена, которые были назначены в регулярном выражении. | |
| 107 /// </summary> | |
| 108 protected TTag[] TokenTags { | |
| 109 get { | |
| 110 return m_currentState.tag; | |
| 111 } | |
| 112 } | |
| 113 | |
| 114 /// <summary> | |
| 115 /// Признак конца данных | |
| 116 /// </summary> | |
| 117 public bool EOF { | |
| 118 get { | |
| 119 return m_pointer >= m_bufferSize; | |
| 120 } | |
| 121 } | |
| 122 | |
| 123 /// <summary> | |
| 124 /// Читает следующий токен, при этом <see cref="m_tokenOffset"/> указывает на начало токена, | |
| 125 /// <see cref="m_tokenLen"/> на длину токена, <see cref="m_buffer"/> - массив символов, в | |
| 126 /// котором находится токен. | |
| 127 /// </summary> | |
| 128 /// <returns><c>false</c> - достигнут конец данных, токен не прочитан.</returns> | |
| 129 protected bool ReadTokenInternal() { | |
| 130 if (m_pointer >= m_bufferSize) | |
| 131 return false; | |
| 132 | |
| 133 m_currentState = m_states[DFADefinition.INITIAL_STATE]; | |
| 134 m_tokenLen = 0; | |
| 135 m_tokenOffset = m_pointer; | |
| 136 int nextState; | |
| 137 do { | |
| 138 nextState = m_currentState.transitions[m_previewCode]; | |
| 139 if (nextState == DFAConst.UNREACHABLE_STATE) { | |
| 140 if (m_currentState.final) | |
| 141 return true; | |
| 142 else | |
| 143 throw new ParserException( | |
| 144 String.Format( | |
| 145 "Unexpected symbol '{0}', at pos {1}", | |
| 146 m_buffer[m_pointer], | |
| 147 Position | |
| 148 ) | |
| 149 ); | |
| 150 } else { | |
| 151 m_currentState = m_states[nextState]; | |
| 152 m_tokenLen++; | |
| 153 } | |
| 154 | |
| 155 } while (Shift()); | |
| 156 | |
| 157 // END OF DATA | |
| 158 if (!m_currentState.final) | |
| 159 throw new ParserException("Unexpected end of data"); | |
| 160 | |
| 161 return true; | |
| 162 } | |
| 163 | |
| 164 | |
| 165 bool Shift() { | |
| 166 m_pointer++; | |
| 167 | |
| 168 if (m_pointer >= m_bufferSize) { | |
| 169 if (!ReadNextChunk()) | |
| 170 return false; | |
| 171 } | |
| 172 | |
| 173 m_previewCode = m_alphabetMap[m_buffer[m_pointer]]; | |
| 174 | |
| 175 return true; | |
| 176 } | |
| 177 | |
| 178 bool ReadNextChunk() { | |
| 179 if (m_reader == null) | |
| 180 return false; | |
| 181 | |
| 182 // extend buffer if nesessary | |
| 183 if (m_pointer + m_chunkSize > m_buffer.Length) { | |
| 184 // trim unused buffer head | |
| 185 var size = m_tokenLen + m_chunkSize; | |
| 186 if (size >= m_limit) | |
| 187 throw new ParserException(String.Format("Input buffer {0} bytes limit exceeded", m_limit)); | |
| 188 var temp = new char[size]; | |
| 189 Array.Copy(m_buffer, m_tokenOffset, temp, 0, m_tokenLen); | |
| 190 m_pointer -= m_tokenOffset; | |
| 191 m_bufferSize -= m_tokenOffset; | |
| 192 m_tokenOffset = 0; | |
| 193 m_buffer = temp; | |
| 194 } | |
| 195 | |
| 196 var read = m_reader.Read(m_buffer, m_tokenLen, m_chunkSize); | |
| 197 if (read == 0) | |
| 198 return false; | |
| 199 | |
| 200 m_bufferSize += read; | |
| 201 | |
| 202 return true; | |
| 203 } | |
| 204 | |
| 205 /// <summary> | |
| 206 /// Позиция сканнера во входном буфере | |
| 207 /// </summary> | |
| 208 public int Position { | |
| 209 get { | |
| 210 return m_pointer + 1; | |
| 211 } | |
| 212 } | |
| 213 | |
| 214 /// <summary> | |
| 215 /// Преключает внутренний ДКА на указанный, позволяет реализовать подобие захватывающей | |
| 216 /// группировки. | |
| 217 /// </summary> | |
| 218 /// <param name="states">Таблица состояний нового ДКА</param> | |
| 219 /// <param name="alphabet">Таблица входных символов для нового ДКА</param> | |
| 220 protected void Switch(DFAStateDescriptior<TTag>[] states, int[] alphabet) { | |
| 221 Safe.ArgumentNotNull(states, "dfa"); | |
| 222 | |
| 223 m_defs.Push(new ScannerConfig { | |
| 224 states = m_states, | |
| 225 alphabetMap = m_alphabetMap | |
| 226 }); | |
| 227 | |
| 228 m_states = states; | |
| 229 m_alphabetMap = alphabet; | |
| 230 | |
| 231 m_previewCode = m_alphabetMap[m_buffer[m_pointer]]; | |
| 232 } | |
| 233 | |
| 234 /// <summary> | |
| 235 /// Восстанавливает предыдущей ДКА сканнера. | |
| 236 /// </summary> | |
| 237 protected void Restore() { | |
| 238 if (m_defs.Count == 0) | |
| 239 throw new InvalidOperationException(); | |
| 240 var prev = m_defs.Pop(); | |
| 241 m_states = prev.states; | |
| 242 m_alphabetMap = prev.alphabetMap; | |
| 243 m_previewCode = m_alphabetMap[m_buffer[m_pointer]]; | |
| 244 } | |
| 245 | |
| 246 protected override void Dispose(bool disposing) { | |
| 247 if (disposing) { | |
| 248 if (m_reader != null && m_disposeReader) | |
| 249 m_reader.Dispose(); | |
| 250 m_buffer = null; | |
| 251 m_bufferSize = 0; | |
| 252 m_pointer = 0; | |
| 253 m_tokenLen = 0; | |
| 254 m_tokenOffset = 0; | |
| 255 } | |
| 256 base.Dispose(disposing); | |
| 257 } | |
| 258 } | |
| 259 } |
