iparser 1.1.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 74b3def90b2fb587ba964156f44d311a931f9978
4
+ data.tar.gz: b6fa824918d59bcce6ec0867ef778c4b097b3475
5
+ SHA512:
6
+ metadata.gz: 8a56f7c33f4582f05e655ea0d1106d4e6f54ead2d57c274d6fb018ff942160ae2594df1b346b16ff5307ae0bb48adee902ee1b501f6e6589dd9c8f002f11a26b
7
+ data.tar.gz: d3426a1e514372f664b8801caab325ebd66c3063c4c7732efe868daf3f5abec4d1285264dbaf9122a943d9d8ec016f2fdddb97178265e5d18ac0fc6808d0a309
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in iparser.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 anton
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,227 @@
1
+ # Iparser
2
+
3
+ Universal parser machine to generate your specific parsers.
4
+ Used for simple and fast create your specific parsers.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'parser'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install parser
21
+
22
+ ## Usage
23
+
24
+ For example usage, present here very simple parser for automatically generate documentation from source code.
25
+
26
+ *source № 1*:
27
+ ```ruby
28
+ #
29
+ # Create parser-machine object.
30
+ #
31
+ parser = Iparser::Machine.new
32
+
33
+ #
34
+ # Create startup state for this parser-machine.
35
+ #
36
+ ps_idle = Iparser::State.new('idle')
37
+
38
+ #
39
+ # Add branch indexes to 'comment-line' and 'comment-block' state.
40
+ #
41
+ ps_idle.branches << 1
42
+ ps_idle.branches << 2
43
+
44
+ #
45
+ # Create single line comment state for this parser-machine.
46
+ #
47
+ ps_cline = Iparser::State.new('comment-line')
48
+ ps_cline.entry << /\//
49
+ ps_cline.entry << /\//
50
+ ps_cline.entry << /\//
51
+ ps_cline.leave << /[\n\r]/
52
+
53
+ #
54
+ # Create multiline comment state for this parser-machine.
55
+ #
56
+ ps_cblock = Iparser::State.new('comment-block')
57
+ ps_cblock.entry << /\//
58
+ ps_cblock.entry << /\*/
59
+ ps_cblock.entry << /\*/
60
+ ps_cblock.leave << /\*/
61
+ ps_cblock.leave << /\//
62
+ ps_cblock.ignore << '*'
63
+
64
+ #
65
+ # Add all states to parser-machine.
66
+ #
67
+ parser.addstate ps_idle
68
+ parser.addstate ps_cline
69
+ parser.addstate ps_cblock
70
+
71
+ #
72
+ # Call parser startup method.
73
+ #
74
+ parser.prestart
75
+
76
+ #
77
+ # Call interactive mode for check state-machine.
78
+ #
79
+ parser.interactive_parser
80
+ ```
81
+
82
+ Run this script and typing `'///'` for branch to 'comment-line' state. Then type `'\n'` or `'\r'` for leave this state.
83
+ **NOTE**: Type `'\\'` for input `'\'`. Check each state.
84
+ After successfully check, add the following code to the beginning of the file:
85
+
86
+ *source № 2*:
87
+ ```ruby
88
+ #
89
+ # Simple check startup arguments.
90
+ #
91
+ if( ARGV.size != 1 || !File.exist?(ARGV[0]) )
92
+ puts
93
+ puts "ERROR: unable to open file #{ARGV[0]}"
94
+ puts
95
+ exit
96
+ end
97
+ #
98
+ # Create output file.
99
+ #
100
+ $fout = File.new( 'index.html', 'w' )
101
+
102
+ #
103
+ # Create initializer method for parser-states.
104
+ #
105
+ def doc_init ( str )
106
+ $fout.print "<p>"
107
+ end
108
+ #
109
+ # Create handler method for parser-states.
110
+ #
111
+ def doc_handler ( c )
112
+ $fout.print c
113
+ end
114
+ #
115
+ # Create finalizer method for parser-states.
116
+ #
117
+ def doc_fini ( str )
118
+ $fout.puts "</p>"
119
+ end
120
+ ```
121
+ Method `doc_init` is state contructor.
122
+ Method `doc_handler` is state handler and call in `comment-line` or `comment-block` for each input char.
123
+ Method `doc_fini` is state destructor.
124
+
125
+ Handler may be return following data types: `Fixnum` - index to branch (>=0),
126
+ `NilClass` - hold current state (nil) and any data types for break parse (error, method `parse`
127
+ return `false`).
128
+
129
+ For `comment-block` state set ignore char - '*', and handler don't called to this chars.
130
+
131
+ Add the following code instead `parser.interactive_parser`:
132
+
133
+ *source № 3*:
134
+ ```ruby
135
+ $fout.puts "<html>"
136
+ $fout.puts "<body>"
137
+
138
+ File.open( ARGV[0], 'r' ).each do |line|
139
+ line.each_char do |c|
140
+ parser.parse(c)
141
+ end
142
+ end
143
+
144
+ $fout.puts "</body>"
145
+ $fout.puts "</html>"
146
+ $fout.close
147
+ ```
148
+ Now developing of the simple parser has been finished. You can create test file, for example 'test.c':
149
+
150
+ *source № 4*:
151
+ ```
152
+ #include <stdlib.h>
153
+
154
+ ///Test function - 1.
155
+ void test1 ( void )
156
+ {
157
+ }
158
+ /**
159
+ * Test function - 2.
160
+ */
161
+ void test2 ( void )
162
+ {
163
+ }
164
+ ```
165
+
166
+ and do folow command in command line as:
167
+
168
+ $ ruby <you parser script name>.rb test.c
169
+
170
+ After work, we should see a file named 'index.html'.
171
+
172
+ ###### Своих не бросаем!
173
+ ------------------------
174
+
175
+ Для примера использования данного gem, напишем простой парсер файла исходного кода
176
+ для автоматической генерации документации, отдаленно напоминающего doxygen.
177
+ Создадим ruby скрипт c именем 'parser_example.rb' и наполним его содержимым из *source № 1*.
178
+
179
+ Далее запустим скрипт на выполнение:
180
+
181
+ $ ruby parser_example.rb
182
+
183
+ Теперь в интерактивном режиме проверим корректность описанных нами состояний: `comment-line` и `comment-block`.
184
+ Для чего введем строку символов `///` и увидим что наш парсер вывел `branch to <comment-line>`, это говорит
185
+ о том, что парсер перешол в состояние для обработки однострочных комментариев, т.е. в состояние `comment-line`.
186
+ Теперь введем `\n` или `\r` и увидим что парсер покинул состояние `comment-line` и вернулся в состояние `idle`.
187
+ Аналогично проведем проверку для всех оставшихся состояний.
188
+
189
+ **NOTE**: для ввода символа `'\'` необходимо набрать `'\\'`.
190
+
191
+ Если все переходы работают как мы и ожидали, то
192
+ можно перейти к написанию обработчиков наших состояний. Для этого допишем в наш скрипт код из *source № 2*
193
+ в начало файла и вместо строки `parser.interactive_parser` добавим код из *source № 3*.
194
+
195
+ Метод `doc_init` будет вызываться при входе в состояние, т.е. является конструктором состояния.
196
+ Метод `doc_handler` будет вызываться каждый раз, до тех пор пока парсер находится в состоянии `comment-line` или `comment-block`.
197
+ Метод `doc_fini` будет вызываться при выходе из состояния, т.е. является деструктором состояния.
198
+
199
+ Обработчик состояния должен возвращать следующие типы данных: `Fixnum` - индекс состояния на которое надо перейти (>=0),
200
+ `NilClass` - оставаться в текущем состоянии (nil) и остальные типы данных которые будут расценены как ошибка
201
+ обработки и метод `parse` вернет `false` (error). Во всех остальных случаях `parse` возвращает `true`.
202
+
203
+ Дополнительно для состояния `comment-block` мы указали символы, которые надо игнорировать,
204
+ а именно `'*'` и `doc_handler` не будет вызываться при наличия данного символа во входном потоке.
205
+
206
+ И наконец создадим тестовый файл с именем 'test.c' и наполним его содержимым из *source № 4*.
207
+ Наш простой парсер готов. Теперь запустим его набрав следующую команду:
208
+
209
+ $ ruby parser_example.rb test.c
210
+
211
+ По окончанию работы мы должны увидеть файл с именем 'index.html'.
212
+
213
+ ## Development
214
+
215
+ 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.
216
+
217
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
218
+
219
+ ## Contributing
220
+
221
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/parser. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
222
+
223
+
224
+ ## License
225
+
226
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
227
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "iparser"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
data/iparser.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'iparser/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "iparser"
8
+ spec.version = Iparser::VERSION
9
+ spec.authors = ["gera-gas"]
10
+ spec.email = ["gera_box@mail.ru"]
11
+
12
+ spec.summary = %q{Universal parser machine to generate your specific parsers.}
13
+ spec.description = %q{Universal parser machine implementation with interactive mode.}
14
+ spec.homepage = "https://github.com/gera-gas/iparser"
15
+ spec.license = "MIT"
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.10"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ end
@@ -0,0 +1,3 @@
1
+ module Iparser
2
+ VERSION = "1.1.1"
3
+ end
data/lib/iparser.rb ADDED
@@ -0,0 +1,569 @@
1
+ #--
2
+ # iparser.rb - Universal parser machine to generate your specific parsers.
3
+ #++
4
+ # Parser::State.new - Create single state for parser.
5
+ # Parser::Machine.new - Create parser machine.
6
+ #
7
+ require "iparser/version"
8
+
9
+ module Iparser
10
+ ##
11
+ # Used for describe single state
12
+ # of parser machine.
13
+ #
14
+ class State
15
+
16
+ attr_reader :statename
17
+ attr_accessor :branches, :entry, :leave, :ientry, :ileave, :ignore
18
+ #
19
+ # call-seq:
20
+ # State.new( String )
21
+ #
22
+ # Class constructor.
23
+ #
24
+ def initialize ( sname )
25
+
26
+ raise TypeError, 'Incorrectly types for <ParserState> constructor.' unless
27
+ sname.instance_of? String
28
+
29
+ @statename = sname
30
+ @init = nil # method called after entry (state constructor).
31
+ @fini = nil # method called after leave (state destructor).
32
+ @handler = nil # state machine for handler current state.
33
+ @ignore = []
34
+ @ientry = 0 # use to save compred index of this state.
35
+ @ileave = 0 # use to save compred index of this state.
36
+ @entry = [] # template chars for entry state.
37
+ @leave = [] # template chars for leave state.
38
+ @branches = [] # indexes of any states to branch.
39
+ end
40
+
41
+ #
42
+ # call-seq:
43
+ # init( method(:some_init_method) )
44
+ #
45
+ # 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
52
+ end
53
+
54
+ #
55
+ # call-seq:
56
+ # fini( method(:some_fini_method) )
57
+ #
58
+ # 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
65
+ end
66
+
67
+ def run_init( *args ) # :nodoc:
68
+ return @init.call( *args ) if @init != nil
69
+ return nil
70
+ end
71
+
72
+ def run_fini( *args ) # :nodoc:
73
+ return @fini.call( *args ) if @fini != nil
74
+ return nil
75
+ end
76
+
77
+ #
78
+ # call-seq:
79
+ # handler( method(:some_handler_method) )
80
+ #
81
+ # 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
88
+ end
89
+
90
+ def run_handler( *args ) # :nodoc:
91
+ return @handler.call( *args ) if @handler != nil
92
+ return nil
93
+ end
94
+
95
+ end # class State
96
+
97
+
98
+ ##
99
+ # Used for create parser machine.
100
+ #
101
+ class Machine
102
+
103
+ attr_reader :parserstate
104
+ #
105
+ # call-seq:
106
+ # Machine.new( )
107
+ #
108
+ # Class constructor.
109
+ #
110
+ def initialize ( )
111
+ @buffer = [] # буфер для символов входного потока
112
+ @states = [] # массив с состояниями парсера, <ParserState> объекты
113
+ @chain = [] # цепочка работающих состояний
114
+ #
115
+ # Машина состояний для метода classify.
116
+ #
117
+ @matchstate = {
118
+ :state => 0,
119
+ :index => 0
120
+ }
121
+ @parserstate = ''
122
+ end
123
+
124
+ #
125
+ # Сбрасывает чувствительные переменные.
126
+ #
127
+ def reset ( ) # :nodoc:
128
+ @buffer = []
129
+ @states.each do |s|
130
+ s.ientry = 0
131
+ s.ileave = 0
132
+ end
133
+ end
134
+
135
+ #
136
+ # Initialize parser object,
137
+ # should be called before call other methods.
138
+ #
139
+ def prestart ( )
140
+ reset( )
141
+ @matchstate[:state] = 0
142
+ @chain = [ @states[0], ]
143
+ end
144
+
145
+ #
146
+ # Display information about of each state of parser.
147
+ #
148
+ def display ( )
149
+ puts 'Parser states: ' + @states.size.to_s
150
+
151
+ @states.each do |st|
152
+ puts
153
+ puts '*' * 80
154
+ puts 'state: ' + st.statename
155
+ puts 'branches: '
156
+ st.branches.each do |br|
157
+ puts ' ' + @states[br].statename
158
+ end
159
+ end
160
+ end
161
+
162
+ #
163
+ # Обработка ввода для интерактивных режимов работы.
164
+ #
165
+ def interactive_input ( ) # :nodoc:
166
+ state = 0
167
+ rv = ""
168
+ str = gets
169
+ #
170
+ # Сразу нажата <Enter> => exit.
171
+ return rv if str[0] == '\n'
172
+ #
173
+ # Выполняем разбор посимвольно.
174
+ str.each_char do |c|
175
+ break if c == ?\n
176
+ case state
177
+ #
178
+ # Сборка символов и проверка на наличие
179
+ # экранирующего символа, значит это ESC-символы.
180
+ when 0
181
+ if c == '\\' then
182
+ state = 1
183
+ else
184
+ rv += c
185
+ end
186
+ #
187
+ # Анализ ESC символа.
188
+ when 1
189
+ case c
190
+ when '0'
191
+ rv += "\0"
192
+ when 'n'
193
+ rv += "\n"
194
+ when 'r'
195
+ rv += "\r"
196
+ when '\\'
197
+ rv += "\\"
198
+ when 'a'
199
+ rv += "\a"
200
+ when 'b'
201
+ rv += "\b"
202
+ when 't'
203
+ rv += "\t"
204
+ when 'v'
205
+ rv += "\v"
206
+ when 'f'
207
+ rv += "\f"
208
+ else
209
+ puts
210
+ puts 'ERROR: unrecognized esc-symbols.'
211
+ puts
212
+ exit
213
+ end
214
+ state = 0
215
+ end
216
+ end
217
+ return rv
218
+ end
219
+
220
+ #
221
+ # Обработка вывода для интерактивных режимов работы.
222
+ #
223
+ def interactive_output( istr ) # :nodoc:
224
+ str = []
225
+ istr.bytes.each do |c|
226
+ case c
227
+ when 0
228
+ str << c.to_s + ":\\0"
229
+ when 10
230
+ str << c.to_s + ":\\n"
231
+ when 13
232
+ str << c.to_s + ":\\r"
233
+ when 7
234
+ str << c.to_s + ":\\a"
235
+ when 8
236
+ str << c.to_s + ":\\b"
237
+ when 9
238
+ str << c.to_s + ":\\t"
239
+ when 11
240
+ str << c.to_s + ":\\v"
241
+ when 12
242
+ str << c.to_s + ":\\f"
243
+ else
244
+ str << c.to_s + ":" + c.chr
245
+ end
246
+ end
247
+ return str
248
+ end
249
+
250
+ #
251
+ # Run parser machine for check in interactive mode.
252
+ #
253
+ def interactive_parser ( )
254
+ puts 'Press <Enter> to exit...'
255
+ #
256
+ # Цикл обработки ввода.
257
+ loop {
258
+ str = interactive_input( )
259
+ break if str == ""
260
+ #
261
+ # Цикл посимвольной классификаци.
262
+ str.bytes.each do |c|
263
+ parse( c.chr )
264
+ puts 'parser: ' + @parserstate
265
+ puts 'symbol: ' + interactive_output( c.chr ).to_s
266
+ puts 'buffer: ' + @buffer.to_s
267
+ puts 'state: ' + @chain.last.statename
268
+ puts
269
+ end
270
+ }
271
+ end
272
+
273
+ #
274
+ # call-seq:
275
+ # s = Parser::State.new('idle')
276
+ # p = Parser::Machine.new
277
+ # p << s
278
+ #
279
+ # Add any parser-state to current parser.
280
+ #
281
+ def addstate ( ps )
282
+ raise TypeError, ps.class.to_s + ': Incorrectly types for \'<<\' method of <Parser>.' unless
283
+ ps.instance_of? State
284
+
285
+ @states << ps
286
+ end
287
+
288
+ #
289
+ # Сравнивает символы входного потока
290
+ # с символами из указанного шаблона.
291
+ # В качестве шаблона выступают поля <entry> или <leave>
292
+ # объектов типа <ParserState>.
293
+ #
294
+ def cmp ( tmp, idx ) # :nodoc:
295
+ #
296
+ # проверка на случай если шаблон не задан,
297
+ # т.е. проинициализирован в [].
298
+ #
299
+ if tmp.size > 0 then
300
+ if idx < tmp.size then
301
+ return true if @buffer.last =~ tmp[ idx ]
302
+ end
303
+ end
304
+ return false
305
+ end
306
+
307
+ #
308
+ # Поиск в массиве указанного диапазона,
309
+ # указанного в параметрах символа.
310
+ #
311
+ # >=0 : индекс совпавшего элемента.
312
+ # -1 : нет совпадений.
313
+ #
314
+ def checkback ( tmp, len ) # :nodoc:
315
+ if len > 0 then
316
+ i = len
317
+ len.times {
318
+ i = i - 1
319
+ return i if cmp( tmp, i )
320
+ }
321
+ end
322
+ return -1
323
+ end
324
+
325
+ #
326
+ # Находит соответствие между символами входного потока
327
+ # и возможными переходами.
328
+ #
329
+ # При совпадени возвращает индекс состояния
330
+ # в массиве состояний, иначе:
331
+ #
332
+ # >0 : прыжок в новое состояние
333
+ # -1 : возврат в предыдущее состояние
334
+ # -2 : еще идет проверка.
335
+ # -3 : нет cовпадений (промах).
336
+ #
337
+ def classify ( state ) # :nodoc:
338
+ case @matchstate[:state]
339
+ #
340
+ # Состояние еще не определено.
341
+ # :state = 0
342
+ #
343
+ when 0
344
+ mcount = 0
345
+ mindex = 0
346
+ backtag = 0
347
+ #
348
+ # Проверка условия выхода из состояния.
349
+ #
350
+ if cmp( state.leave, state.ileave ) then
351
+ state.ileave = state.ileave.next
352
+ #
353
+ # Возврат в предыдущее состояние.
354
+ if state.ileave >= state.leave.size then
355
+ return -1
356
+ end
357
+ backtag = 1
358
+ else
359
+ #
360
+ # Нет совпадения, но если уже часть сравнений
361
+ # успешна, то возможно входной символ совпадает
362
+ # с предыдущими, уже совпавшими символами,
363
+ # т.е. как откат в режиме <wait>.
364
+ #
365
+ i = checkback( state.leave, state.ileave )
366
+
367
+ if i != -1 then
368
+ state.ileave = i.next
369
+ backtag = 1
370
+ else
371
+ state.ileave = 0
372
+ backtag = 0
373
+ end
374
+ end
375
+ #
376
+ # Проверка возможных переходов для
377
+ # указанного в параметрах состояния.
378
+ #
379
+ state.branches.each do |b|
380
+ if cmp( @states[b].entry, @states[b].ientry ) then
381
+ mcount = mcount + 1
382
+ mindex = b
383
+ @states[b].ientry = @states[b].ientry.next
384
+ #
385
+ # состояние полностью пройдено.
386
+ #
387
+ if @states[ b ].ientry >= @states[ b ].entry.size then
388
+ return b
389
+ end
390
+ else
391
+ #
392
+ # Нет совпадения, но если уже часть сравнений
393
+ # успешна, то возможно входной символ совпадает
394
+ # с предыдущими, уже совпавшими символами,
395
+ # т.е. как откат в режиме <wait>.
396
+ #
397
+ i = checkback( @states[b].entry, @states[b].ientry )
398
+
399
+ if i != -1 then
400
+ mcount = mcount + 1
401
+ mindex = b
402
+ @states[b].ientry = i.next
403
+ else
404
+ @states[b].ientry = 0
405
+ end
406
+ end
407
+ end
408
+ #
409
+ # Анализ количества совпадений.
410
+ #
411
+ case mcount
412
+ #
413
+ # нет совпадений.
414
+ when 0
415
+ return (-3 + backtag)
416
+ #
417
+ # однозначное совпадение, но весь массив шаблонов
418
+ # еще не пройден.
419
+ #
420
+ when 1
421
+ @matchstate[:state] = 1
422
+ @matchstate[:index] = mindex
423
+ return -2
424
+ #
425
+ # нет однозначного соответствия.
426
+ else
427
+ return -2
428
+ end
429
+ ##
430
+ # Состояние точно определено.
431
+ # :state = 1
432
+ #
433
+ when 1
434
+ i = @matchstate[:index]
435
+ if cmp( @states[ i ].entry, @states[ i ].ientry ) then
436
+ #
437
+ # Инкремент счетчика (индекса) вхождений.
438
+ @states[ i ].ientry = @states[ i ].ientry.next
439
+ #
440
+ # Массив шаблонов совпадает полностью.
441
+ # можно считать, что 100% совпадение.
442
+ #
443
+ if @states[ i ].ientry >= @states[ i ].entry.size then
444
+ @matchstate[:state] = 0
445
+ return i
446
+ end
447
+ return -2
448
+ end
449
+ #
450
+ # Нет совпадения, но если уже часть сравнений
451
+ # успешна, то возможно входной символ совпадает
452
+ # с предыдущими, уже совпавшими символами,
453
+ # т.е. как откат в режиме <wait>.
454
+ #
455
+ idx = checkback( @states[i].entry, @states[i].ientry )
456
+
457
+ if idx != -1 then
458
+ @states[i].ientry = idx.next
459
+ return -2
460
+ end
461
+ @states[i].ientry = 0
462
+ @matchstate[:state] = 0
463
+ #return (-3 + backtag)
464
+ #return -4
465
+ return -3
466
+ end # case @matchstate
467
+ end
468
+
469
+ #
470
+ # Main method, used for parse input stream.
471
+ # Parse will be starting in unit with nil index (0).
472
+ #
473
+ # Return true if parsing process is successful, else return false.
474
+ #
475
+ def parse ( c )
476
+ @parserstate = 'wait'
477
+ retval = true
478
+ #
479
+ # * Фиксированное состояние (определенное): -1.
480
+ # * Не фиксированное состояние (неопределенное): -2.
481
+ # * Переход (смена состояний): >0.
482
+ #
483
+ @buffer << c
484
+
485
+ #
486
+ # Проверка переходов в другие состояния.
487
+ #
488
+ r = classify( @chain.last )
489
+
490
+ #
491
+ # Переход (прыжок) в другое состояние.
492
+ # <branch>:
493
+ #
494
+ if r >= 0 then
495
+ @chain << @states[r]
496
+ @chain.last.run_init( @buffer )
497
+ reset( )
498
+ @parserstate = 'branch'
499
+ #
500
+ # Возврат из текущего состояния.
501
+ # <back>:
502
+ #
503
+ 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 )
511
+ end
512
+ reset( )
513
+ @parserstate = 'back'
514
+ #
515
+ # Нет совпадений.
516
+ # <miss>:
517
+ #
518
+ elsif r == -3 then
519
+ #
520
+ # если в процессе состояния <wait>
521
+ # мы попали в <miss>, то накопленный
522
+ # буфер надо обработать.
523
+ #
524
+ @buffer.each do |ch|
525
+ @parserstate = 'miss'
526
+ tag = true
527
+ if @chain.last.ignore.size > 0 then
528
+ tag = false if @chain.last.ignore.include?(ch)
529
+ end
530
+ if tag == true then
531
+ r = @chain.last.run_handler( ch )
532
+ #
533
+ # Анализ результата обработки состояния.
534
+ #
535
+ case r.class.to_s
536
+ #
537
+ # Fixnum - переход на любое состояние (индекс).
538
+ when 'Fixnum'
539
+ if( (r >= 0) && (r < @states.size) ) then
540
+ @chain << @states[r]
541
+ reset( )
542
+ @parserstate = 'hardset'
543
+ else
544
+ raise TypeError, "Method <#{@chain.last.statename}> return incorrectly index."
545
+ end
546
+ #
547
+ # nil - ничего не возвращает.
548
+ when 'NilClass'
549
+ #
550
+ # else - расценивается как ошибка обработки.
551
+ # обработка ложится на плечи разработчика.
552
+ #
553
+ else
554
+ @parserstate = 'error'
555
+ retval = false
556
+ break
557
+ end
558
+ end
559
+ end
560
+ @buffer = []
561
+ end
562
+ return retval
563
+ end
564
+
565
+ private :reset, :cmp, :checkback, :interactive_input, :interactive_output
566
+
567
+ end # class Machine
568
+
569
+ end # Module Iparser
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: iparser
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.1
5
+ platform: ruby
6
+ authors:
7
+ - gera-gas
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-11-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: Universal parser machine implementation with interactive mode.
42
+ email:
43
+ - gera_box@mail.ru
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - CODE_OF_CONDUCT.md
50
+ - Gemfile
51
+ - LICENSE.txt
52
+ - README.md
53
+ - Rakefile
54
+ - bin/console
55
+ - bin/setup
56
+ - iparser.gemspec
57
+ - lib/iparser.rb
58
+ - lib/iparser/version.rb
59
+ homepage: https://github.com/gera-gas/iparser
60
+ licenses:
61
+ - MIT
62
+ metadata: {}
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubyforge_project:
79
+ rubygems_version: 2.2.2
80
+ signing_key:
81
+ specification_version: 4
82
+ summary: Universal parser machine to generate your specific parsers.
83
+ test_files: []
84
+ has_rdoc: