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