iparser 1.1.6 → 1.1.7
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 +4 -4
- data/lib/iparser.rb +2 -558
- data/lib/iparser/machine.rb +490 -0
- data/lib/iparser/state.rb +76 -0
- data/lib/iparser/version.rb +1 -1
- metadata +4 -11
- data/.gitignore +0 -9
- data/CODE_OF_CONDUCT.md +0 -13
- data/Gemfile +0 -4
- data/LICENSE.txt +0 -21
- data/README.md +0 -313
- data/Rakefile +0 -1
- data/bin/console +0 -14
- data/bin/setup +0 -7
- data/iparser.gemspec +0 -23
@@ -0,0 +1,76 @@
|
|
1
|
+
module Iparser
|
2
|
+
# Used for describe single state
|
3
|
+
# of parser machine.
|
4
|
+
class State
|
5
|
+
|
6
|
+
attr_reader :statename
|
7
|
+
attr_accessor :branches, :entry, :leave, :ientry, :ileave, :ignore
|
8
|
+
|
9
|
+
# call-seq:
|
10
|
+
# State.new( String )
|
11
|
+
def initialize ( sname )
|
12
|
+
unless sname.instance_of? String
|
13
|
+
raise TypeError, 'Incorrectly types for <Parser-State> constructor.'
|
14
|
+
end
|
15
|
+
|
16
|
+
@statename = sname
|
17
|
+
@init = nil # method called after entry (state constructor).
|
18
|
+
@fini = nil # method called after leave (state destructor).
|
19
|
+
@handler = nil # state machine for handler current state.
|
20
|
+
@ignore = { :all => [], :handler => [] }
|
21
|
+
@ientry = 0 # use to save compred index of this state.
|
22
|
+
@ileave = 0 # use to save compred index of this state.
|
23
|
+
@entry = [] # template chars for entry state.
|
24
|
+
@leave = [] # template chars for leave state.
|
25
|
+
@branches = [] # indexes of any states to branch.
|
26
|
+
end
|
27
|
+
|
28
|
+
# call-seq:
|
29
|
+
# init( method(:some_init_method) )
|
30
|
+
#
|
31
|
+
# Set initializer method for current state.
|
32
|
+
def init ( method )
|
33
|
+
raise TypeError, error_message(method, __method__) unless method.instance_of? Method
|
34
|
+
@init = method
|
35
|
+
end
|
36
|
+
|
37
|
+
# call-seq:
|
38
|
+
# fini( method(:some_fini_method) )
|
39
|
+
#
|
40
|
+
# Set finalizer method for current state.
|
41
|
+
def fini ( method )
|
42
|
+
raise TypeError, error_message(method, __method__) unless method.instance_of? Method
|
43
|
+
@fini = method
|
44
|
+
end
|
45
|
+
|
46
|
+
def run_init( *args ) # :nodoc:
|
47
|
+
return @init.call( *args ) if @init != nil
|
48
|
+
return nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def run_fini( *args ) # :nodoc:
|
52
|
+
return @fini.call( *args ) if @fini != nil
|
53
|
+
return nil
|
54
|
+
end
|
55
|
+
|
56
|
+
# call-seq:
|
57
|
+
# handler( method(:some_handler_method) )
|
58
|
+
#
|
59
|
+
# Set handler method for current state.
|
60
|
+
def handler ( method )
|
61
|
+
raise TypeError, error_message(method, __method__) unless method.instance_of? Method
|
62
|
+
@handler = method
|
63
|
+
end
|
64
|
+
|
65
|
+
def run_handler( *args ) # :nodoc:
|
66
|
+
return @handler.call( *args ) if @handler != nil
|
67
|
+
return nil
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
def error_message(object, method)
|
72
|
+
"#{object.class}: Incorrectly types for <#{method}> method of <Parser-State>."
|
73
|
+
end
|
74
|
+
end # class State
|
75
|
+
|
76
|
+
end
|
data/lib/iparser/version.rb
CHANGED
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.
|
4
|
+
version: 1.1.7
|
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-12-
|
11
|
+
date: 2015-12-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -45,16 +45,9 @@ executables: []
|
|
45
45
|
extensions: []
|
46
46
|
extra_rdoc_files: []
|
47
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
48
|
- lib/iparser.rb
|
49
|
+
- lib/iparser/machine.rb
|
50
|
+
- lib/iparser/state.rb
|
58
51
|
- lib/iparser/version.rb
|
59
52
|
homepage: https://github.com/gera-gas/iparser
|
60
53
|
licenses:
|
data/.gitignore
DELETED
data/CODE_OF_CONDUCT.md
DELETED
@@ -1,13 +0,0 @@
|
|
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
DELETED
data/LICENSE.txt
DELETED
@@ -1,21 +0,0 @@
|
|
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
DELETED
@@ -1,313 +0,0 @@
|
|
1
|
-
# Iparser
|
2
|
-
|
3
|
-
Universal parser machine to generate your specific parsers (Parser engine).
|
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 'Iparser'
|
12
|
-
```
|
13
|
-
|
14
|
-
And then execute:
|
15
|
-
|
16
|
-
$ bundle
|
17
|
-
|
18
|
-
Or install it yourself as:
|
19
|
-
|
20
|
-
$ gem install iparser
|
21
|
-
|
22
|
-
## Usage
|
23
|
-
|
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*.
|
26
|
-
|
27
|
-
*source № 1*:
|
28
|
-
```ruby
|
29
|
-
require 'iparser'
|
30
|
-
|
31
|
-
# Create parser-machine object.
|
32
|
-
parser = Iparser::Machine.new
|
33
|
-
# Create startup state for this parser-machine.
|
34
|
-
ps_idle = Iparser::State.new('idle')
|
35
|
-
# Create single line comment state for this parser-machine.
|
36
|
-
ps_cline = Iparser::State.new('comment-line')
|
37
|
-
# Create multiline comment state for this parser-machine.
|
38
|
-
ps_cblock = Iparser::State.new('comment-block')
|
39
|
-
|
40
|
-
# Add all states to parser-machine.
|
41
|
-
parser.addstate ps_idle
|
42
|
-
parser.addstate ps_cline
|
43
|
-
parser.addstate ps_cblock
|
44
|
-
|
45
|
-
# Add branch indexes to 'comment-line' and 'comment-block' state.
|
46
|
-
# From 'idle' we can branch to 'comment-line' or 'comment-block' states.
|
47
|
-
ps_idle.branches << parser.state_index( ps_cline )
|
48
|
-
ps_idle.branches << parser.state_index( ps_cblock )
|
49
|
-
|
50
|
-
# Describe 'comment-line' state.
|
51
|
-
# Set template for entry this state (String or Regexp).
|
52
|
-
ps_cline.entry << '/'
|
53
|
-
ps_cline.entry << '/'
|
54
|
-
ps_cline.entry << '/'
|
55
|
-
# Set template for leave this state (String or Regexp).
|
56
|
-
ps_cline.leave << /[\n\r]/
|
57
|
-
|
58
|
-
# Describe 'comment-block' state.
|
59
|
-
# Set template for entry this state (String or Regexp).
|
60
|
-
ps_cblock.entry << '/'
|
61
|
-
ps_cblock.entry << '*'
|
62
|
-
ps_cblock.entry << '*'
|
63
|
-
# Set template for leave this state (String or Regexp).
|
64
|
-
ps_cblock.leave << '*'
|
65
|
-
ps_cblock.leave << '/'
|
66
|
-
# Set template for ignore this state (String format only).
|
67
|
-
ps_cblock.ignore << '*'
|
68
|
-
|
69
|
-
# Call parser startup method.
|
70
|
-
parser.prestart
|
71
|
-
# Call interactive mode for check state-machine.
|
72
|
-
parser.interactive_parser
|
73
|
-
```
|
74
|
-
|
75
|
-
Run this script `ruby parser_example.rb` and typing `'///'` for branch to 'comment-line' state.
|
76
|
-
Then type `'\n'` or `'\r'` for leave this state.
|
77
|
-
Press `enter` (input empty string) to leave interactive mode.
|
78
|
-
Check each state.
|
79
|
-
|
80
|
-
**NOTE**: Type `'\\'` for input `'\'`.
|
81
|
-
|
82
|
-
Each state has the following templates:
|
83
|
-
* `entry` - used for set condition (template) to entry state.
|
84
|
-
* `leave` - used for set condition (template) to leave state (return to previous state).
|
85
|
-
* `ignore` - used for set symbols to ignoring.
|
86
|
-
|
87
|
-
After successfully check, you can add handler to each state.
|
88
|
-
Each state has the following handlers:
|
89
|
-
* `init` - is state contructor (called when entry to state).
|
90
|
-
* `fini` - is state destructor (called when leave state).
|
91
|
-
* `handler` - is state handler (called every time, is still in state).
|
92
|
-
|
93
|
-
Each handler can return the following values: __nil__ - nothing is done (method `.parse` return `true`)
|
94
|
-
and __any__ values for break parsing (method `.parse` return `false`). For break parsing process you
|
95
|
-
should check return value of `.parse` method. For example:
|
96
|
-
```ruby
|
97
|
-
parser = Iparser::Machine.new
|
98
|
-
'123asd'.each_char do |c|
|
99
|
-
exit if !parser.parse(c)
|
100
|
-
end
|
101
|
-
```
|
102
|
-
Also each state have a `branches` field. Use the `branches` to add the index state,
|
103
|
-
which is a possible jump.
|
104
|
-
|
105
|
-
We create the following handlers:
|
106
|
-
|
107
|
-
* Method `doc_init` is state destructor.
|
108
|
-
* Method `doc_handler` is state handler and call in `comment-line` or `comment-block` for each input char.
|
109
|
-
* Method `doc_fini` is state destructor.
|
110
|
-
|
111
|
-
For `comment-block` state set ignore char - `*`, and handler don't called to this chars.
|
112
|
-
The result is a file with the following content of 'parser_example.rb':
|
113
|
-
|
114
|
-
Constructors and destructors handlers will be getting array chars with own templates.
|
115
|
-
For example `doc_init` getting follow array chars: `['/', '/', '/']`
|
116
|
-
|
117
|
-
*source № 2*:
|
118
|
-
```ruby
|
119
|
-
require 'iparser'
|
120
|
-
|
121
|
-
# Simple check startup arguments.
|
122
|
-
if( ARGV.size != 1 || !File.exist?(ARGV[0]) )
|
123
|
-
puts
|
124
|
-
puts "ERROR: unable to open file #{ARGV[0]}"
|
125
|
-
puts
|
126
|
-
exit
|
127
|
-
end
|
128
|
-
|
129
|
-
# Create output file.
|
130
|
-
$fout = File.new( 'index.html', 'w' )
|
131
|
-
|
132
|
-
# Create initializer method for parser-states.
|
133
|
-
def doc_init ( str )
|
134
|
-
$fout.print "<p>"; return nil
|
135
|
-
end
|
136
|
-
# Create handler method for parser-states.
|
137
|
-
def doc_handler ( c )
|
138
|
-
$fout.print c; return nil
|
139
|
-
end
|
140
|
-
# Create finalizer method for parser-states.
|
141
|
-
def doc_fini ( str )
|
142
|
-
$fout.puts "</p>"; return nil
|
143
|
-
end
|
144
|
-
|
145
|
-
# Create parser-machine object.
|
146
|
-
parser = Iparser::Machine.new
|
147
|
-
# Create startup state for this parser-machine.
|
148
|
-
ps_idle = Iparser::State.new('idle')
|
149
|
-
# Create single line comment state for this parser-machine.
|
150
|
-
ps_cline = Iparser::State.new('comment-line')
|
151
|
-
# Create multiline comment state for this parser-machine.
|
152
|
-
ps_cblock = Iparser::State.new('comment-block')
|
153
|
-
|
154
|
-
# Add all states to parser-machine.
|
155
|
-
parser.addstate ps_idle
|
156
|
-
parser.addstate ps_cline
|
157
|
-
parser.addstate ps_cblock
|
158
|
-
|
159
|
-
# Add branch indexes to 'comment-line' and 'comment-block' state.
|
160
|
-
# From 'idle' we can branch to 'comment-line' or 'comment-block' states.
|
161
|
-
ps_idle.branches << parser.state_index( ps_cline )
|
162
|
-
ps_idle.branches << parser.state_index( ps_cblock )
|
163
|
-
|
164
|
-
# Describe 'comment-line' state.
|
165
|
-
# Set template for entry this state (String or Regexp).
|
166
|
-
ps_cline.entry << '/'
|
167
|
-
ps_cline.entry << '/'
|
168
|
-
ps_cline.entry << '/'
|
169
|
-
# Set template for leave this state (String or Regexp).
|
170
|
-
ps_cline.leave << /[\n\r]/
|
171
|
-
# Add handler to 'commaent-line' state.
|
172
|
-
ps_cline.init( method(:doc_init) )
|
173
|
-
ps_cline.handler( method(:doc_handler) )
|
174
|
-
ps_cline.fini( method(:doc_fini) )
|
175
|
-
|
176
|
-
# Describe 'comment-block' state.
|
177
|
-
# Set template for entry this state (String or Regexp).
|
178
|
-
ps_cblock.entry << '/'
|
179
|
-
ps_cblock.entry << '*'
|
180
|
-
ps_cblock.entry << '*'
|
181
|
-
# Set template for leave this state (String or Regexp).
|
182
|
-
ps_cblock.leave << '*'
|
183
|
-
ps_cblock.leave << '/'
|
184
|
-
# Set template for ignore this state (String format only).
|
185
|
-
ps_cblock.ignore << '*'
|
186
|
-
# Add handler to 'commaent-block' state.
|
187
|
-
ps_cblock.init( method(:doc_init) )
|
188
|
-
ps_cblock.handler( method(:doc_handler) )
|
189
|
-
ps_cblock.fini( method(:doc_fini) )
|
190
|
-
|
191
|
-
# Call parser startup method.
|
192
|
-
parser.prestart
|
193
|
-
|
194
|
-
$fout.puts "<html>"
|
195
|
-
$fout.puts "<body>"
|
196
|
-
|
197
|
-
File.open( ARGV[0], 'r' ).each do |line|
|
198
|
-
line.each_char do |c|
|
199
|
-
parser.parse(c)
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
$fout.puts "</body>"
|
204
|
-
$fout.puts "</html>"
|
205
|
-
$fout.close
|
206
|
-
```
|
207
|
-
|
208
|
-
Now developing of the simple parser has been finished. You can create test file, for example 'test.c':
|
209
|
-
|
210
|
-
*source № 3*:
|
211
|
-
```
|
212
|
-
#include <stdlib.h>
|
213
|
-
|
214
|
-
///Test function - 1.
|
215
|
-
void test1 ( void )
|
216
|
-
{
|
217
|
-
}
|
218
|
-
/**
|
219
|
-
* Test function - 2.
|
220
|
-
*/
|
221
|
-
void test2 ( void )
|
222
|
-
{
|
223
|
-
}
|
224
|
-
```
|
225
|
-
|
226
|
-
and execute folow command in command line as:
|
227
|
-
|
228
|
-
$ ruby parser_example.rb test.c
|
229
|
-
|
230
|
-
After work, we should see a file named 'index.html'.
|
231
|
-
|
232
|
-
###### Своих не бросаем!
|
233
|
-
------------------------
|
234
|
-
|
235
|
-
Для примера использования данного gem, напишем простой парсер файла исходного кода
|
236
|
-
для автоматической генерации документации, отдаленно напоминающего doxygen.
|
237
|
-
Создадим ruby скрипт c именем 'parser_example.rb' и наполним его содержимым из *source № 1*.
|
238
|
-
|
239
|
-
Далее запустим скрипт на выполнение:
|
240
|
-
|
241
|
-
$ ruby parser_example.rb
|
242
|
-
|
243
|
-
Теперь в интерактивном режиме проверим корректность описанных нами состояний: `comment-line` и `comment-block`.
|
244
|
-
Для чего введем строку символов `///` и увидим что наш парсер вывел `branch to <comment-line>`, это говорит
|
245
|
-
о том, что парсер перешол в состояние для обработки однострочных комментариев, т.е. в состояние `comment-line`.
|
246
|
-
Теперь введем `\n` или `\r` и увидим что парсер покинул состояние `comment-line` и вернулся в состояние `idle`.
|
247
|
-
Аналогично проведем проверку для всех оставшихся состояний. Для выхода из интерактивного режима просто введите
|
248
|
-
`Enter`, т.е. пустую строку.
|
249
|
-
|
250
|
-
**NOTE**: для ввода символа `'\'` необходимо набрать `'\\'`.
|
251
|
-
|
252
|
-
Если все переходы работают как мы и ожидали, то можно перейти к написанию обработчиков наших состояний.
|
253
|
-
|
254
|
-
* Метод `doc_init` будет вызываться при входе в состояние, т.е. является конструктором состояния.
|
255
|
-
* Метод `doc_handler` будет вызываться каждый раз, до тех пор пока парсер находится в состоянии `comment-line` или `comment-block`.
|
256
|
-
* Метод `doc_fini` будет вызываться при выходе из состояния, т.е. является деструктором состояния.
|
257
|
-
|
258
|
-
Обработчики состояния должены возвращать следующие значения: __nil__ - говорит парсер-машине о том,
|
259
|
-
что процесс разбора продолжается и метод `.parse` вернет `true` и любое отличное от __nil__ значение
|
260
|
-
будет разценено парсером как ошибка обработки и метод `.parse` вернет `false`. Для того, чтобы
|
261
|
-
прервать процесс процесс парсинга, следует анализировать возвращаемое значение метода `,parse`,
|
262
|
-
например так:
|
263
|
-
```ruby
|
264
|
-
parser = Iparser::Machine.new
|
265
|
-
'123asd'.each_char do |c|
|
266
|
-
exit if !parser.parse(c)
|
267
|
-
end
|
268
|
-
```
|
269
|
-
Также, каждое состояние имеет поле `branches`, для добавление индекса состояний на которые
|
270
|
-
возможен переход из текущего состояния.
|
271
|
-
|
272
|
-
Дополнительно для состояния `comment-block` мы указали символы, которые надо игнорировать,
|
273
|
-
а именно `'*'` и `doc_handler` не будет вызываться при наличия данного символа во входном потоке.
|
274
|
-
По окончанию мы получим файл с содержимым из *source № 2*.
|
275
|
-
|
276
|
-
И наконец создадим тестовый файл с именем 'test.c' и наполним его содержимым из *source № 3*.
|
277
|
-
Наш простой парсер готов. Теперь запустим его набрав следующую команду:
|
278
|
-
|
279
|
-
$ ruby parser_example.rb test.c
|
280
|
-
|
281
|
-
По окончанию работы мы должны увидеть файл с именем 'index.html'.
|
282
|
-
|
283
|
-
## Patch
|
284
|
-
|
285
|
-
Details information for each patch.
|
286
|
-
|
287
|
-
##### 1.1.6
|
288
|
-
* Fixed bug in analyse `leave` template (incorrectly handling, if `entry` and `leave` is begin with identical symbol).
|
289
|
-
* Now `ignore` template enabled for all handlers (init, fini, handler).
|
290
|
-
|
291
|
-
##### 1.1.5
|
292
|
-
* Add method `.current_state` for return name (String) of current state in parser-machine.
|
293
|
-
|
294
|
-
##### 1.1.4
|
295
|
-
* Add method `.state_index` for return index of parser-state.
|
296
|
-
* Add analyse result of any handler (init, fini, handler).
|
297
|
-
* Now `entry` and `leave` templates you can set a string or regular expression.
|
298
|
-
|
299
|
-
## Development
|
300
|
-
|
301
|
-
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.
|
302
|
-
|
303
|
-
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).
|
304
|
-
|
305
|
-
## Contributing
|
306
|
-
|
307
|
-
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.
|
308
|
-
|
309
|
-
|
310
|
-
## License
|
311
|
-
|
312
|
-
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
313
|
-
|