iparser 1.1.3 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0106d75e819c43af7da5c282d661c29a5c90e8ea
4
- data.tar.gz: 1cebe402197c60e390f477b5d9331c70ea8f520b
3
+ metadata.gz: 9a12b304b0cfd0fc94f9043f03321648f95f2a60
4
+ data.tar.gz: 48f5b42bf26ec9cc78ba8ec443085325693a9173
5
5
  SHA512:
6
- metadata.gz: 1464526b0c4670428996b7dde12b96366dba8a27c9fa7a701265313904d4f0c97193338cf6f058b0a65dddae73eeea8e11cc4534db8c249c372d4978d15537a0
7
- data.tar.gz: 5ecbdd4e9ad9f5a494ff109b816f2bf5290e5dc6d8b3882b18ce74a4304d3f856d524ff0075e0ca7032114bbba5ad90d1a71b4f2dc86064b07a00d6699bd6e01
6
+ metadata.gz: c0d50e2ccdd4cc0393a302869fffe3d10ece69ffb828f761fda020c06bbf5edd56a8aa3ea0e57ca4b3079b077b38b38e9058451a59e4fa1e0e2c499b56692bbf
7
+ data.tar.gz: 42e50dd5f5a159452909a9aeaf52428c203fac762872221d2a308fcd273bd7313ca4a00f855b9611784b6b3e9f91829607c855f74bec6ea710ae5ea45a8a9ff9
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Iparser
2
2
 
3
- Universal parser machine to generate your specific parsers.
3
+ Universal parser machine to generate your specific parsers (Parser engine).
4
4
  Used for simple and fast create your specific parsers.
5
5
 
6
6
  ## Installation
@@ -22,249 +22,174 @@ Or install it yourself as:
22
22
  ## Usage
23
23
 
24
24
  For example usage, present here very simple parser for automatically generate documentation from source code.
25
+ Create file 'parser_example.rb' and copy the contents from the file *source № 1*.
25
26
 
26
27
  *source № 1*:
27
28
  ```ruby
28
29
  require 'iparser'
29
30
 
30
- #
31
31
  # Create parser-machine object.
32
- #
33
32
  parser = Iparser::Machine.new
34
-
35
- #
36
33
  # Create startup state for this parser-machine.
37
- #
38
34
  ps_idle = Iparser::State.new('idle')
39
-
40
- #
41
- # Add branch indexes to 'comment-line' and 'comment-block' state.
42
- #
43
- ps_idle.branches << 1
44
- ps_idle.branches << 2
45
-
46
- #
47
35
  # Create single line comment state for this parser-machine.
48
- #
49
36
  ps_cline = Iparser::State.new('comment-line')
50
- ps_cline.entry << /\//
51
- ps_cline.entry << /\//
52
- ps_cline.entry << /\//
53
- ps_cline.leave << /[\n\r]/
54
-
55
- #
56
37
  # Create multiline comment state for this parser-machine.
57
- #
58
38
  ps_cblock = Iparser::State.new('comment-block')
59
- ps_cblock.entry << /\//
60
- ps_cblock.entry << /\*/
61
- ps_cblock.entry << /\*/
62
- ps_cblock.leave << /\*/
63
- ps_cblock.leave << /\//
64
- ps_cblock.ignore << '*'
65
39
 
66
- #
67
40
  # Add all states to parser-machine.
68
- #
69
41
  parser.addstate ps_idle
70
42
  parser.addstate ps_cline
71
43
  parser.addstate ps_cblock
72
44
 
73
- #
45
+ # Describe 'comment-line' state.
46
+ # Set template for entry this state (String or Regexp).
47
+ ps_cline.entry << '/'
48
+ ps_cline.entry << '/'
49
+ ps_cline.entry << '/'
50
+ # Set template for leave this state (String or Regexp).
51
+ ps_cline.leave << /[\n\r]/
52
+ # Add handler to 'commaent-line' state.
53
+ ps_cline.init( method(:doc_init) )
54
+ ps_cline.handler( method(:doc_handler) )
55
+ ps_cline.fini( method(:doc_fini) )
56
+ # Add branch indexes to 'comment-line' and 'comment-block' state.
57
+ # From 'idle' we can branch to 'comment-line' or 'comment-block' states.
58
+ ps_idle.branches << parser.state_index( ps_cline )
59
+ ps_idle.branches << parser.state_index( ps_cblock )
60
+
61
+ # Describe 'comment-block' state.
62
+ # Set template for entry this state (String or Regexp).
63
+ ps_cblock.entry << '/'
64
+ ps_cblock.entry << '*'
65
+ ps_cblock.entry << '*'
66
+ # Set template for leave this state (String or Regexp).
67
+ ps_cblock.leave << '*'
68
+ ps_cblock.leave << '/'
69
+ # Set template for ignore this state (String format only).
70
+ ps_cblock.ignore << '*'
71
+
74
72
  # Call parser startup method.
75
- #
76
73
  parser.prestart
77
-
78
- #
79
74
  # Call interactive mode for check state-machine.
80
- #
81
75
  parser.interactive_parser
82
76
  ```
83
77
 
84
- Run this script and typing `'///'` for branch to 'comment-line' state. Then type `'\n'` or `'\r'` for leave this state.
85
- **NOTE**: Type `'\\'` for input `'\'`. Check each state. Press `enter` (input empty string) to leave interactive mode.
86
- After successfully check, add the following code to the beginning of the file:
87
-
88
- *source № 2*:
78
+ Run this script `ruby parser_example.rb` and typing `'///'` for branch to 'comment-line' state.
79
+ Then type `'\n'` or `'\r'` for leave this state.
80
+ Press `enter` (input empty string) to leave interactive mode.
81
+ Check each state.
82
+
83
+ **NOTE**: Type `'\\'` for input `'\'`.
84
+
85
+ Each state has the following templates:
86
+ 1 __entry__ - used for set condition (template) to entry state.
87
+ 2 __leave__ - used for set condition (template) to leave state (return to previous state).
88
+ 3 __ignore__ - used for set symbols to ignoring.
89
+
90
+ After successfully check, you can add handler to each state.
91
+ Each state has the following handlers:
92
+ 1 __init__ - is state contructor (called when entry to state).
93
+ 2 __fini__ - is state destructor (called when leave state).
94
+ 3 __handler__ - is state handler (called every time, is still in state).
95
+
96
+ Each handler can return the following values: __nil__ - nothing is done (method `.parse` return `true`)
97
+ and __any__ values for break parsing (method `.parse` return `false`). For break parsing process you
98
+ should check return value of `.parse` method. For example:
89
99
  ```ruby
90
- #
91
- # Simple check startup arguments.
92
- #
93
- if( ARGV.size != 1 || !File.exist?(ARGV[0]) )
94
- puts
95
- puts "ERROR: unable to open file #{ARGV[0]}"
96
- puts
97
- exit
98
- end
99
- #
100
- # Create output file.
101
- #
102
- $fout = File.new( 'index.html', 'w' )
103
-
104
- #
105
- # Create initializer method for parser-states.
106
- #
107
- def doc_init ( str )
108
- $fout.print "<p>"
109
- end
110
- #
111
- # Create handler method for parser-states.
112
- #
113
- def doc_handler ( c )
114
- $fout.print c
115
- end
116
- #
117
- # Create finalizer method for parser-states.
118
- #
119
- def doc_fini ( str )
120
- $fout.puts "</p>"
121
- end
122
- ```
123
- Method `doc_init` is state contructor.
124
- Method `doc_handler` is state handler and call in `comment-line` or `comment-block` for each input char.
125
- Method `doc_fini` is state destructor.
126
-
127
- Handler may be return following data types: `Fixnum` - index to branch (>=0),
128
- `NilClass` - hold current state (nil) and any data types for break parse (error, method `parse`
129
- return `false`).
130
-
131
- For `comment-block` state set ignore char - `*`, and handler don't called to this chars.
132
-
133
- Add the following code instead `parser.interactive_parser`:
134
-
135
- *source № 3*:
136
- ```ruby
137
- $fout.puts "<html>"
138
- $fout.puts "<body>"
139
-
140
- File.open( ARGV[0], 'r' ).each do |line|
141
- line.each_char do |c|
142
- parser.parse(c)
143
- end
100
+ parser = Iparser::Machine.new
101
+ '123asd'.each_char do |c|
102
+ exit if !parser.parse(c)
144
103
  end
145
-
146
- $fout.puts "</body>"
147
- $fout.puts "</html>"
148
- $fout.close
149
104
  ```
105
+ Also each state have a `branches` field. Use the `branches` to add the index state,
106
+ which is a possible jump.
150
107
 
151
- And add this code (*source № 4*) before `addstate` method call.
152
-
153
- *source № 4*:
154
- ```ruby
155
- #
156
- # Add handlers for states.
157
- #
158
- ps_cline.init( method(:doc_init) )
159
- ps_cline.handler( method(:doc_handler) )
160
- ps_cline.fini( method(:doc_fini) )
108
+ We create the following handlers:
161
109
 
162
- ps_cblock.init( method(:doc_init) )
163
- ps_cblock.handler( method(:doc_handler) )
164
- ps_cblock.fini( method(:doc_fini) )
165
- ```
110
+ * Method `doc_init` is state destructor.
111
+ * Method `doc_handler` is state handler and call in `comment-line` or `comment-block` for each input char.
112
+ * Method `doc_fini` is state destructor.
166
113
 
167
- The result is a file with the following content.
114
+ For `comment-block` state set ignore char - `*`, and handler don't called to this chars.
115
+ The result is a file with the following content of 'parser_example.rb':
168
116
 
169
- *source № 5*:
117
+ *source № 2*:
170
118
  ```ruby
171
119
  require 'iparser'
172
120
 
173
- #
174
121
  # Simple check startup arguments.
175
- #
176
122
  if( ARGV.size != 1 || !File.exist?(ARGV[0]) )
177
123
  puts
178
124
  puts "ERROR: unable to open file #{ARGV[0]}"
179
125
  puts
180
126
  exit
181
127
  end
182
- #
128
+
183
129
  # Create output file.
184
- #
185
130
  $fout = File.new( 'index.html', 'w' )
186
131
 
187
- #
188
132
  # Create initializer method for parser-states.
189
- #
190
133
  def doc_init ( str )
191
134
  $fout.print "<p>"
192
135
  end
193
- #
194
136
  # Create handler method for parser-states.
195
- #
196
137
  def doc_handler ( c )
197
138
  $fout.print c
198
139
  end
199
- #
200
140
  # Create finalizer method for parser-states.
201
- #
202
141
  def doc_fini ( str )
203
142
  $fout.puts "</p>"
204
143
  end
205
144
 
206
- #
207
145
  # Create parser-machine object.
208
- #
209
146
  parser = Iparser::Machine.new
210
-
211
- #
212
147
  # Create startup state for this parser-machine.
213
- #
214
148
  ps_idle = Iparser::State.new('idle')
215
-
216
- #
217
- # Add branch indexes to 'comment-line' and 'comment-block' state.
218
- #
219
- ps_idle.branches << 1
220
- ps_idle.branches << 2
221
-
222
- #
223
149
  # Create single line comment state for this parser-machine.
224
- #
225
150
  ps_cline = Iparser::State.new('comment-line')
226
- ps_cline.entry << /\//
227
- ps_cline.entry << /\//
228
- ps_cline.entry << /\//
229
- ps_cline.leave << /[\n\r]/
230
-
231
- #
232
151
  # Create multiline comment state for this parser-machine.
233
- #
234
152
  ps_cblock = Iparser::State.new('comment-block')
235
- ps_cblock.entry << /\//
236
- ps_cblock.entry << /\*/
237
- ps_cblock.entry << /\*/
238
- ps_cblock.leave << /\*/
239
- ps_cblock.leave << /\//
240
- ps_cblock.ignore << '*'
241
153
 
242
- #
243
- # Add handlers for states.
244
- #
154
+ # Add all states to parser-machine.
155
+ parser.addstate ps_idle
156
+ parser.addstate ps_cline
157
+ parser.addstate ps_cblock
158
+
159
+ # Describe 'comment-line' state.
160
+ # Set template for entry this state (String or Regexp).
161
+ ps_cline.entry << '/'
162
+ ps_cline.entry << '/'
163
+ ps_cline.entry << '/'
164
+ # Set template for leave this state (String or Regexp).
165
+ ps_cline.leave << /[\n\r]/
166
+ # Add handler to 'commaent-line' state.
245
167
  ps_cline.init( method(:doc_init) )
246
168
  ps_cline.handler( method(:doc_handler) )
247
169
  ps_cline.fini( method(:doc_fini) )
248
-
170
+ # Add branch indexes to 'comment-line' and 'comment-block' state.
171
+ # From 'idle' we can branch to 'comment-line' or 'comment-block' states.
172
+ ps_idle.branches << parser.state_index( ps_cline )
173
+ ps_idle.branches << parser.state_index( ps_cblock )
174
+
175
+ # Describe 'comment-block' state.
176
+ # Set template for entry this state (String or Regexp).
177
+ ps_cblock.entry << '/'
178
+ ps_cblock.entry << '*'
179
+ ps_cblock.entry << '*'
180
+ # Set template for leave this state (String or Regexp).
181
+ ps_cblock.leave << '*'
182
+ ps_cblock.leave << '/'
183
+ # Set template for ignore this state (String format only).
184
+ ps_cblock.ignore << '*'
185
+ # Add handler to 'commaent-block' state.
249
186
  ps_cblock.init( method(:doc_init) )
250
187
  ps_cblock.handler( method(:doc_handler) )
251
188
  ps_cblock.fini( method(:doc_fini) )
252
189
 
253
- #
254
- # Add all states to parser-machine.
255
- #
256
- parser.addstate ps_idle
257
- parser.addstate ps_cline
258
- parser.addstate ps_cblock
259
-
260
- #
261
190
  # Call parser startup method.
262
- #
263
191
  parser.prestart
264
192
 
265
- #
266
- # Call interactive mode for check state-machine.
267
- #
268
193
  $fout.puts "<html>"
269
194
  $fout.puts "<body>"
270
195
 
@@ -281,7 +206,7 @@ $fout.close
281
206
 
282
207
  Now developing of the simple parser has been finished. You can create test file, for example 'test.c':
283
208
 
284
- *source № 6*:
209
+ *source № 3*:
285
210
  ```
286
211
  #include <stdlib.h>
287
212
 
@@ -297,9 +222,9 @@ void test2 ( void )
297
222
  }
298
223
  ```
299
224
 
300
- and do folow command in command line as:
225
+ and execute folow command in command line as:
301
226
 
302
- $ ruby <you parser script name>.rb test.c
227
+ $ ruby parser_example.rb test.c
303
228
 
304
229
  After work, we should see a file named 'index.html'.
305
230
 
@@ -323,29 +248,46 @@ After work, we should see a file named 'index.html'.
323
248
 
324
249
  **NOTE**: для ввода символа `'\'` необходимо набрать `'\\'`.
325
250
 
326
- Если все переходы работают как мы и ожидали, то
327
- можно перейти к написанию обработчиков наших состояний. Для этого допишем в наш скрипт код из *source № 2*
328
- в начало файла и вместо строки `parser.interactive_parser` добавим код из *source № 3* и *source № 4*.
329
- В результате должен получится код как на *source № 5*.
251
+ Если все переходы работают как мы и ожидали, то можно перейти к написанию обработчиков наших состояний.
330
252
 
331
253
  Метод `doc_init` будет вызываться при входе в состояние, т.е. является конструктором состояния.
332
254
  Метод `doc_handler` будет вызываться каждый раз, до тех пор пока парсер находится в состоянии `comment-line` или `comment-block`.
333
255
  Метод `doc_fini` будет вызываться при выходе из состояния, т.е. является деструктором состояния.
334
256
 
335
- Обработчик состояния должен возвращать следующие типы данных: `Fixnum` - индекс состояния на которое надо перейти (>=0),
336
- `NilClass` - оставаться в текущем состоянии (nil) и остальные типы данных которые будут расценены как ошибка
337
- обработки и метод `parse` вернет `false` (error). Во всех остальных случаях `parse` возвращает `true`.
257
+ Обработчики состояния должены возвращать следующие значения: __nil__ - говорит парсер-машине о том,
258
+ что процесс разбора продолжается и метод `.parse` вернет `true` и любое отличное от __nil__ значение
259
+ будет разценено парсером как ошибка обработки и метод `.parse` вернет `false`. Для того, чтобы
260
+ прервать процесс процесс парсинга, следует анализировать возвращаемое значение метода `,parse`,
261
+ например так:
262
+ ```ruby
263
+ parser = Iparser::Machine.new
264
+ '123asd'.each_char do |c|
265
+ exit if !parser.parse(c)
266
+ end
267
+ ```
268
+ Также, каждое состояние имеет поле `branches`, для добавление индекса состояний на которые
269
+ возможен переход из текущего состояния.
338
270
 
339
271
  Дополнительно для состояния `comment-block` мы указали символы, которые надо игнорировать,
340
272
  а именно `'*'` и `doc_handler` не будет вызываться при наличия данного символа во входном потоке.
273
+ По окончанию мы получим файл с содержимым из *source № 2*.
341
274
 
342
- И наконец создадим тестовый файл с именем 'test.c' и наполним его содержимым из *source № 6*.
275
+ И наконец создадим тестовый файл с именем 'test.c' и наполним его содержимым из *source № 3*.
343
276
  Наш простой парсер готов. Теперь запустим его набрав следующую команду:
344
277
 
345
278
  $ ruby parser_example.rb test.c
346
279
 
347
280
  По окончанию работы мы должны увидеть файл с именем 'index.html'.
348
281
 
282
+ ## Patch
283
+
284
+ Details information for each patch.
285
+
286
+ ##### 1.1.4
287
+ * Add method `.state_index` for return index of parser-state.
288
+ * Add analyes result of any handler (init, fini, handler).
289
+ * Now `entry` and `leave` templates you can set a string or regular expression.
290
+
349
291
  ## Development
350
292
 
351
293
  After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/iparser.gemspec CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["gera-gas"]
10
10
  spec.email = ["gera_box@mail.ru"]
11
11
 
12
- spec.summary = %q{Universal parser machine to generate your specific parsers.}
13
- spec.description = %q{Universal parser machine implementation with interactive mode.}
12
+ spec.summary = %q{Universal parser machine implementation with interactive mode. Can be used as a parser engine.}
13
+ spec.description = %q{}
14
14
  spec.homepage = "https://github.com/gera-gas/iparser"
15
15
  spec.license = "MIT"
16
16
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
data/lib/iparser.rb CHANGED
@@ -1,30 +1,22 @@
1
1
  #--
2
2
  # iparser.rb - Universal parser machine to generate your specific parsers.
3
3
  #++
4
- # Parser::State.new - Create single state for parser.
5
- # Parser::Machine.new - Create parser machine.
6
- #
7
- require "iparser/version"
4
+ require 'iparser/version'
8
5
 
9
- module Iparser
10
- ##
6
+ module Iparser
11
7
  # Used for describe single state
12
8
  # of parser machine.
13
- #
14
9
  class State
15
10
 
16
11
  attr_reader :statename
17
12
  attr_accessor :branches, :entry, :leave, :ientry, :ileave, :ignore
18
- #
13
+
19
14
  # call-seq:
20
15
  # State.new( String )
21
- #
22
- # Class constructor.
23
- #
24
16
  def initialize ( sname )
25
-
26
- raise TypeError, 'Incorrectly types for <ParserState> constructor.' unless
27
- sname.instance_of? String
17
+ unless sname.instance_of? String
18
+ raise TypeError, 'Incorrectly types for <Parser-State> constructor.'
19
+ end
28
20
 
29
21
  @statename = sname
30
22
  @init = nil # method called after entry (state constructor).
@@ -38,30 +30,22 @@ module Iparser
38
30
  @branches = [] # indexes of any states to branch.
39
31
  end
40
32
 
41
- #
42
33
  # call-seq:
43
34
  # init( method(:some_init_method) )
44
35
  #
45
36
  # Set initializer method for current state.
46
- #
47
- def init ( m )
48
- raise TypeError, m.class.to_s + ': Incorrectly types for <init> method of <ParserState>.' unless
49
- m.instance_of? Method
50
-
51
- @init = m
37
+ def init ( method )
38
+ raise TypeError, error_message(method, __method__) unless method.instance_of? Method
39
+ @init = method
52
40
  end
53
41
 
54
- #
55
42
  # call-seq:
56
43
  # fini( method(:some_fini_method) )
57
44
  #
58
45
  # Set finalizer method for current state.
59
- #
60
- def fini ( m )
61
- raise TypeError, m.class.to_s + ': Incorrectly types for <fini> method of <ParserState>.' unless
62
- m.instance_of? Method
63
-
64
- @fini = m
46
+ def fini ( method )
47
+ raise TypeError, error_message(method, __method__) unless method.instance_of? Method
48
+ @fini = method
65
49
  end
66
50
 
67
51
  def run_init( *args ) # :nodoc:
@@ -74,39 +58,35 @@ module Iparser
74
58
  return nil
75
59
  end
76
60
 
77
- #
78
61
  # call-seq:
79
62
  # handler( method(:some_handler_method) )
80
63
  #
81
64
  # Set handler method for current state.
82
- #
83
- def handler ( h )
84
- raise TypeError, h.class.to_s + ': Incorrectly types for <handler> method of <ParserState>.' unless
85
- h.instance_of? Method
86
-
87
- @handler = h
65
+ def handler ( method )
66
+ raise TypeError, error_message(method, __method__) unless method.instance_of? Method
67
+ @handler = method
88
68
  end
89
69
 
90
70
  def run_handler( *args ) # :nodoc:
91
71
  return @handler.call( *args ) if @handler != nil
92
72
  return nil
93
73
  end
94
-
74
+
75
+ private
76
+ def error_message(object, method)
77
+ "#{object.class}: Incorrectly types for <#{method}> method of <Parser-State>."
78
+ end
95
79
  end # class State
80
+
96
81
 
97
-
98
- ##
99
82
  # Used for create parser machine.
100
- #
101
83
  class Machine
84
+ INITIAL_STATE = 'wait'
102
85
 
103
86
  attr_reader :parserstate
104
- #
87
+
105
88
  # call-seq:
106
89
  # Machine.new( )
107
- #
108
- # Class constructor.
109
- #
110
90
  def initialize ( )
111
91
  @buffer = [] # буфер для символов входного потока
112
92
  @states = [] # массив с состояниями парсера, <ParserState> объекты
@@ -121,9 +101,7 @@ module Iparser
121
101
  @parserstate = ''
122
102
  end
123
103
 
124
- #
125
104
  # Сбрасывает чувствительные переменные.
126
- #
127
105
  def reset ( ) # :nodoc:
128
106
  @buffer = []
129
107
  @states.each do |s|
@@ -132,26 +110,21 @@ module Iparser
132
110
  end
133
111
  end
134
112
 
135
- #
136
113
  # Initialize parser object,
137
114
  # should be called before call other methods.
138
- #
139
115
  def prestart ( )
140
116
  reset( )
141
117
  @matchstate[:state] = 0
142
118
  @chain = [ @states[0], ]
143
119
  end
144
120
 
145
- #
146
121
  # Display information about of each state of parser.
147
- #
148
122
  def display ( )
149
123
  puts 'Parser states: ' + @states.size.to_s
150
124
 
151
125
  @states.each do |st|
152
126
  puts
153
- puts '*' * 80
154
- puts 'state: ' + st.statename
127
+ puts '** state: ' + st.statename
155
128
  puts 'branches: '
156
129
  st.branches.each do |br|
157
130
  puts ' ' + @states[br].statename
@@ -159,17 +132,15 @@ module Iparser
159
132
  end
160
133
  end
161
134
 
162
- #
163
135
  # Обработка ввода для интерактивных режимов работы.
164
- #
165
136
  def interactive_input ( ) # :nodoc:
166
137
  state = 0
167
138
  rv = ""
168
139
  str = gets
169
- #
140
+
170
141
  # Сразу нажата <Enter> => exit.
171
142
  return rv if str[0] == '\n'
172
- #
143
+
173
144
  # Выполняем разбор посимвольно.
174
145
  str.each_char do |c|
175
146
  break if c == ?\n
@@ -206,9 +177,7 @@ module Iparser
206
177
  when 'f'
207
178
  rv += "\f"
208
179
  else
209
- puts
210
- puts 'ERROR: unrecognized esc-symbols.'
211
- puts
180
+ puts "\nERROR: unrecognized esc-symbols.\n"
212
181
  exit
213
182
  end
214
183
  state = 0
@@ -217,9 +186,7 @@ module Iparser
217
186
  return rv
218
187
  end
219
188
 
220
- #
221
189
  # Обработка вывода для интерактивных режимов работы.
222
- #
223
190
  def interactive_output( istr ) # :nodoc:
224
191
  str = []
225
192
  istr.bytes.each do |c|
@@ -247,82 +214,88 @@ module Iparser
247
214
  return str
248
215
  end
249
216
 
250
- #
251
217
  # Run parser machine for check in interactive mode.
252
- #
253
218
  def interactive_parser ( )
254
219
  puts 'Press <Enter> to exit...'
255
- #
220
+
256
221
  # Цикл обработки ввода.
257
- loop {
222
+ loop do
258
223
  str = interactive_input( )
259
224
  break if str == ""
260
- #
225
+
261
226
  # Цикл посимвольной классификаци.
262
227
  str.bytes.each do |c|
263
228
  parse( c.chr )
264
229
  puts 'parser: ' + @parserstate
265
230
  puts 'symbol: ' + interactive_output( c.chr ).to_s
266
- puts 'buffer: ' + @buffer.to_s
267
231
  puts 'state: ' + @chain.last.statename
268
232
  puts
269
233
  end
270
- }
234
+ end
271
235
  end
272
236
 
273
- #
274
237
  # call-seq:
275
238
  # s = Parser::State.new('idle')
276
239
  # p = Parser::Machine.new
277
240
  # p << s
278
241
  #
279
242
  # Add any parser-state to current parser.
280
- #
281
243
  def addstate ( ps )
282
244
  raise TypeError, ps.class.to_s + ': Incorrectly types for \'<<\' method of <Parser>.' unless
283
245
  ps.instance_of? State
284
-
285
246
  @states << ps
286
247
  end
248
+
249
+ # call-seq:
250
+ # some_state1.branches << parser.state_index(some_state2).
251
+ # Return index
252
+ def state_index ( state )
253
+ raise TypeError, ps.class.to_s + ': Incorrectly types for \'state_index\' method of <Parser>.' unless
254
+ state.instance_of? State
255
+
256
+ @states.each_with_index do |st,i|
257
+ return i if state == st
258
+ end
259
+ raise "State <#{state.statename}> is not exist in Parser."
260
+ end
287
261
 
288
- #
289
262
  # Сравнивает символы входного потока
290
263
  # с символами из указанного шаблона.
291
264
  # В качестве шаблона выступают поля <entry> или <leave>
292
265
  # объектов типа <ParserState>.
293
- #
294
266
  def cmp ( tmp, idx ) # :nodoc:
295
- #
267
+
296
268
  # проверка на случай если шаблон не задан,
297
269
  # т.е. проинициализирован в [].
298
- #
299
270
  if tmp.size > 0 then
300
271
  if idx < tmp.size then
301
- return true if @buffer.last =~ tmp[ idx ]
272
+ case tmp[idx].class.to_s
273
+ when 'Regexp'
274
+ return true if @buffer.last =~ tmp[ idx ]
275
+ when 'String'
276
+ return true if @buffer.last == tmp[ idx ]
277
+ end
302
278
  end
303
279
  end
304
280
  return false
305
281
  end
306
282
 
307
- #
308
283
  # Поиск в массиве указанного диапазона,
309
284
  # указанного в параметрах символа.
310
285
  #
311
286
  # >=0 : индекс совпавшего элемента.
312
287
  # -1 : нет совпадений.
313
- #
314
288
  def checkback ( tmp, len ) # :nodoc:
315
289
  if len > 0 then
316
290
  i = len
317
- len.times {
291
+ len.times do
318
292
  i = i - 1
319
293
  return i if cmp( tmp, i )
320
- }
294
+ end
321
295
  end
322
296
  return -1
323
297
  end
324
298
 
325
- #
326
299
  # Находит соответствие между символами входного потока
327
300
  # и возможными переходами.
328
301
  #
@@ -333,20 +306,17 @@ module Iparser
333
306
  # -1 : возврат в предыдущее состояние
334
307
  # -2 : еще идет проверка.
335
308
  # -3 : нет cовпадений (промах).
336
- #
337
309
  def classify ( state ) # :nodoc:
338
310
  case @matchstate[:state]
339
- #
311
+
340
312
  # Состояние еще не определено.
341
313
  # :state = 0
342
- #
343
314
  when 0
344
315
  mcount = 0
345
316
  mindex = 0
346
317
  backtag = 0
347
318
  #
348
319
  # Проверка условия выхода из состояния.
349
- #
350
320
  if cmp( state.leave, state.ileave ) then
351
321
  state.ileave = state.ileave.next
352
322
  #
@@ -375,7 +345,6 @@ module Iparser
375
345
  #
376
346
  # Проверка возможных переходов для
377
347
  # указанного в параметрах состояния.
378
- #
379
348
  state.branches.each do |b|
380
349
  if cmp( @states[b].entry, @states[b].ientry ) then
381
350
  mcount = mcount + 1
@@ -383,7 +352,6 @@ module Iparser
383
352
  @states[b].ientry = @states[b].ientry.next
384
353
  #
385
354
  # состояние полностью пройдено.
386
- #
387
355
  if @states[ b ].ientry >= @states[ b ].entry.size then
388
356
  return b
389
357
  end
@@ -393,7 +361,6 @@ module Iparser
393
361
  # успешна, то возможно входной символ совпадает
394
362
  # с предыдущими, уже совпавшими символами,
395
363
  # т.е. как откат в режиме <wait>.
396
- #
397
364
  i = checkback( @states[b].entry, @states[b].ientry )
398
365
 
399
366
  if i != -1 then
@@ -407,7 +374,6 @@ module Iparser
407
374
  end
408
375
  #
409
376
  # Анализ количества совпадений.
410
- #
411
377
  case mcount
412
378
  #
413
379
  # нет совпадений.
@@ -416,7 +382,6 @@ module Iparser
416
382
  #
417
383
  # однозначное совпадение, но весь массив шаблонов
418
384
  # еще не пройден.
419
- #
420
385
  when 1
421
386
  @matchstate[:state] = 1
422
387
  @matchstate[:index] = mindex
@@ -426,10 +391,9 @@ module Iparser
426
391
  else
427
392
  return -2
428
393
  end
429
- ##
394
+
430
395
  # Состояние точно определено.
431
396
  # :state = 1
432
- #
433
397
  when 1
434
398
  i = @matchstate[:index]
435
399
  if cmp( @states[ i ].entry, @states[ i ].ientry ) then
@@ -439,7 +403,6 @@ module Iparser
439
403
  #
440
404
  # Массив шаблонов совпадает полностью.
441
405
  # можно считать, что 100% совпадение.
442
- #
443
406
  if @states[ i ].ientry >= @states[ i ].entry.size then
444
407
  @matchstate[:state] = 0
445
408
  return i
@@ -451,7 +414,6 @@ module Iparser
451
414
  # успешна, то возможно входной символ совпадает
452
415
  # с предыдущими, уже совпавшими символами,
453
416
  # т.е. как откат в режиме <wait>.
454
- #
455
417
  idx = checkback( @states[i].entry, @states[i].ientry )
456
418
 
457
419
  if idx != -1 then
@@ -460,20 +422,16 @@ module Iparser
460
422
  end
461
423
  @states[i].ientry = 0
462
424
  @matchstate[:state] = 0
463
- #return (-3 + backtag)
464
- #return -4
465
425
  return -3
466
426
  end # case @matchstate
467
427
  end
468
428
 
469
- #
470
429
  # Main method, used for parse input stream.
471
430
  # Parse will be starting in unit with nil index (0).
472
431
  #
473
432
  # Return true if parsing process is successful, else return false.
474
- #
475
433
  def parse ( c )
476
- @parserstate = 'wait'
434
+ @parserstate = INITIAL_STATE
477
435
  retval = true
478
436
  #
479
437
  # * Фиксированное состояние (определенное): -1.
@@ -481,46 +439,46 @@ module Iparser
481
439
  # * Переход (смена состояний): >0.
482
440
  #
483
441
  @buffer << c
484
-
485
- #
442
+
486
443
  # Проверка переходов в другие состояния.
487
- #
488
444
  r = classify( @chain.last )
489
445
 
490
- #
491
446
  # Переход (прыжок) в другое состояние.
492
447
  # <branch>:
493
- #
494
448
  if r >= 0 then
495
449
  @chain << @states[r]
496
- @chain.last.run_init( @buffer )
497
- reset( )
498
- @parserstate = 'branch'
499
- #
450
+ if @chain.last.run_init( @buffer ) == nil then
451
+ reset( )
452
+ @parserstate = 'branch'
453
+ else
454
+ @parserstate = 'error'
455
+ retval = false
456
+ end
457
+
500
458
  # Возврат из текущего состояния.
501
459
  # <back>:
502
- #
503
460
  elsif r == -1 then
504
- @chain.last.run_fini( @buffer )
505
- #
506
- # если это состояние не первое в цепочке
507
- # тогда откатываемся назад.
508
- #
509
- if @chain.size > 1 then
510
- @chain.delete_at( @chain.size - 1 )
461
+ if @chain.last.run_fini( @buffer ) == nil then
462
+ #
463
+ # если это состояние не первое в цепочке
464
+ # тогда откатываемся назад.
465
+ if @chain.size > 1 then
466
+ @chain.delete_at( @chain.size - 1 )
467
+ end
468
+ reset( )
469
+ @parserstate = 'back'
470
+ else
471
+ @parserstate = 'error'
472
+ retval = false
511
473
  end
512
- reset( )
513
- @parserstate = 'back'
514
- #
474
+
515
475
  # Нет совпадений.
516
476
  # <miss>:
517
- #
518
477
  elsif r == -3 then
519
478
  #
520
479
  # если в процессе состояния <wait>
521
480
  # мы попали в <miss>, то накопленный
522
481
  # буфер надо обработать.
523
- #
524
482
  @buffer.each do |ch|
525
483
  @parserstate = 'miss'
526
484
  tag = true
@@ -531,7 +489,6 @@ module Iparser
531
489
  r = @chain.last.run_handler( ch )
532
490
  #
533
491
  # Анализ результата обработки состояния.
534
- #
535
492
  case r.class.to_s
536
493
  #
537
494
  # Fixnum - переход на любое состояние (индекс).
@@ -549,7 +506,6 @@ module Iparser
549
506
  #
550
507
  # else - расценивается как ошибка обработки.
551
508
  # обработка ложится на плечи разработчика.
552
- #
553
509
  else
554
510
  @parserstate = 'error'
555
511
  retval = false
@@ -563,7 +519,6 @@ module Iparser
563
519
  end
564
520
 
565
521
  private :reset, :cmp, :checkback, :interactive_input, :interactive_output
566
-
567
522
  end # class Machine
568
523
 
569
- end # Module Iparser
524
+ end
@@ -1,3 +1,3 @@
1
1
  module Iparser
2
- VERSION = "1.1.3"
2
+ VERSION = "1.1.4"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iparser
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.3
4
+ version: 1.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - gera-gas
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-11-29 00:00:00.000000000 Z
11
+ date: 2015-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -38,7 +38,7 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.0'
41
- description: Universal parser machine implementation with interactive mode.
41
+ description: ''
42
42
  email:
43
43
  - gera_box@mail.ru
44
44
  executables: []
@@ -79,6 +79,7 @@ rubyforge_project:
79
79
  rubygems_version: 2.2.2
80
80
  signing_key:
81
81
  specification_version: 4
82
- summary: Universal parser machine to generate your specific parsers.
82
+ summary: Universal parser machine implementation with interactive mode. Can be used
83
+ as a parser engine.
83
84
  test_files: []
84
85
  has_rdoc: