fa 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +145 -8
- data/Rakefile +6 -0
- data/fa.gemspec +1 -0
- data/lib/fa.rb +133 -7
- data/lib/fa/version.rb +1 -1
- metadata +15 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f916d972953f8fa49a4b31c3523e0a360424333
|
4
|
+
data.tar.gz: b751b82025a64eccf35c143f01b43ddc35105daa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eaac55dbfc978116be6cddff230df876dd0263b9a795d47b2032d669707081f17a49c98feaabac21c6fd1a3bf8be2c509431c494e279d199d71311d98d95a54a
|
7
|
+
data.tar.gz: 11e957f7da8b07f41337c80e5dc6aeb2583d660010ae705b57d8a59eb70ea711868beb4d5f7b7ea7268275e404f3309fd03355159607c47e9122380454db8819
|
data/README.md
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
# Fa
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
This gem provides bindings to [libfa](http://augeas.net/libfa/index.html),
|
4
|
+
a library for doing algebra on [regular expressions](#syntax). If you've
|
5
|
+
ever asked yourself questions like "Are these two regular expressions
|
6
|
+
matching the same set of strings ?" or wanted to determine a regular
|
7
|
+
expression that matches all strings that match one regular expression, but
|
8
|
+
not a second one, this is the library that can answer these questions for
|
9
|
+
you.
|
6
10
|
|
7
11
|
## Installation
|
8
12
|
|
@@ -20,17 +24,150 @@ Or install it yourself as:
|
|
20
24
|
|
21
25
|
$ gem install fa
|
22
26
|
|
27
|
+
For things to work out, you will also have to have `libfa` installed; the
|
28
|
+
library is distributed as part of [augeas](http://augeas.net/). On Red
|
29
|
+
Hat-derived distros like Fedora, CentOS, or RHEL, you will need to `yum
|
30
|
+
install augeas-libs`, on Debian-derived distros, run `apt-get install
|
31
|
+
libaugeas0`.
|
32
|
+
|
23
33
|
## Usage
|
24
34
|
|
25
|
-
|
35
|
+
To perform computations on regular expressions, `libfa` needs to first
|
36
|
+
convert your regular expression into a finite automaton. This is done by
|
37
|
+
compiling your regular expression:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
fa1 = Fa.compile("(a|b)") # can also be written as Fa["(a|b)"]
|
41
|
+
fa2 = fa1.plus
|
42
|
+
```
|
43
|
+
|
44
|
+
Notice that the regular expression needs to be given as a
|
45
|
+
string. Unfortunately, Ruby regular expressions allow constructs that go
|
46
|
+
beyond the mathematical notion of a regular expression and can therefore
|
47
|
+
not be used to do the kinds of computation that `libfa` performs. The
|
48
|
+
regular expressions that `libfa` deals in must be written using a (subset
|
49
|
+
of) the notation for
|
50
|
+
[extended POSIX regular expressions](https://en.wikibooks.org/wiki/Regular_Expressions/POSIX-Extended_Regular_Expressions). The
|
51
|
+
biggest difference between POSIX ERE and the syntax that `libfa`
|
52
|
+
understands is that `libfa` does not allow backreferences, does not support
|
53
|
+
anchors like `^` and `$`, and does not support named character classes like
|
54
|
+
`[[:space:]]`.
|
55
|
+
|
56
|
+
You can always turn a finite automaton back into a regular expression using
|
57
|
+
`Fa#to_s`:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
|
61
|
+
puts fa1
|
62
|
+
# "b|a"
|
63
|
+
puts fa1.minimize
|
64
|
+
# "[ab]"
|
65
|
+
puts fa1.union(fa2).minimize
|
66
|
+
# "[ab][ab]*"
|
67
|
+
puts fa1.concat(fa2).minimize
|
68
|
+
# "[ab][ab][ab]*"
|
69
|
+
puts fa2.intersect(fa1).minimize
|
70
|
+
# "b|a"
|
71
|
+
puts fa2.intersect(Fa["a*"]).minimize
|
72
|
+
# "aa*"
|
73
|
+
puts fa1.intersect(Fa["a*"]).minimize
|
74
|
+
# "a"
|
75
|
+
puts Fa["a+"].minus(Fa["a{2,}"])
|
76
|
+
# "a"
|
77
|
+
```
|
78
|
+
|
79
|
+
You can also compare finite automata, and therefore learn things on how
|
80
|
+
they behave on _all_ strings, for example if they match the same exact set
|
81
|
+
of strings, or if one matches strictly more strings than another:
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
fa = Fa["[a-z]"].intersect(Fa["a*"])
|
85
|
+
puts Fa["a"].equals(fa)
|
86
|
+
# true
|
87
|
+
fa = Fa["a"].union(Fa["b"]).star.concat(Fa["c"].plus)
|
88
|
+
puts Fa["(a|b)*c+"].equals(fa)
|
89
|
+
# true
|
90
|
+
puts Fa["[ab]*"].contains(Fa["a*"])
|
91
|
+
# true
|
92
|
+
puts Fa["a+"].minus(Fa["a*"]).empty?
|
93
|
+
# true
|
94
|
+
```
|
95
|
+
|
96
|
+
### Syntax
|
97
|
+
|
98
|
+
The regular expressions that `libfa` understand are a subset of the POSIX
|
99
|
+
extended regular expression syntax. If you are a regular expression
|
100
|
+
aficionado, you should note that `libfa` does not support some popular
|
101
|
+
syntax extensions. Most importantly, it does not support backreferences,
|
102
|
+
anchors such as `^` and `$`, and named character classes such as
|
103
|
+
`[[:upper:]]`. The first two are not supported since they take the notation
|
104
|
+
out of the realm of finite automata and actual regular expressions. Named
|
105
|
+
character classes are not implemented because there's a lot of work to
|
106
|
+
support them, even though there is no objection from theory to them.
|
107
|
+
|
108
|
+
The character set that `libfa` operates on is simple 8 bit ASCII. In other
|
109
|
+
words, to `libfa`, a character is a byte, and it does not support larger
|
110
|
+
character sets such as UTF-8.
|
111
|
+
|
112
|
+
The following characters have special meaning for `libfa`. The symbols `R`,
|
113
|
+
`S`, `T`, etc. in the list below can be regular expressions themselves. The
|
114
|
+
list is ordered by increasing precendence of the operators.
|
115
|
+
|
116
|
+
* `R|S`: matches anything that matches either `R` or `S`
|
117
|
+
* `R*`: matches any number of `R`, including none
|
118
|
+
* `R+`: matches any number of `R`, but at least one
|
119
|
+
* `R?`: matches no or one occurence of `R`
|
120
|
+
* `R{n,m}`: matches at least `n` but no more than `m` occurences of
|
121
|
+
`R`. `n` must be nonnegative. If `m` is missing or equals `-1`, matches
|
122
|
+
an unlimited number of `R`.
|
123
|
+
* `(R)`: the parentheses are solely there for grouping, and this expression
|
124
|
+
matches the same strings as `R` alone
|
125
|
+
* `[C]`: matches the characters in the character set `C`; see below for the
|
126
|
+
syntax of character sets
|
127
|
+
* `.`: matches any single character except for newline
|
128
|
+
* `\c`: the literal character `c`, even if it would otherwise have special
|
129
|
+
meaning; the expression `\(` matches an opening parenthesis.
|
130
|
+
|
131
|
+
Character classes `[C]` use the following notation:
|
132
|
+
|
133
|
+
* `[^C]`: matches all characters not in `[C]`. `[^a-zA-Z]` matches
|
134
|
+
everything that is not a letter.
|
135
|
+
* `[a-z]`: matches all characters between `a` and `z`, inclusive. Multiple
|
136
|
+
ranges can be specified in the same character set. `[a-zA-Z0-9]` is a
|
137
|
+
perfectly valid character set.
|
138
|
+
* if a character set should include `]`, it must be listed as the first
|
139
|
+
character. `[][]` matches the opening and closing bracket.
|
140
|
+
* if a character set should include `-`, it must be listed as the last
|
141
|
+
character. `[-]` matches solely a dash.
|
142
|
+
* no characters in character sets are special, and there is no backslash
|
143
|
+
escaping of characters in character classes. `[.]` matches a literal dot.
|
144
|
+
|
145
|
+
The regular expression syntax has no notation for control characters: when
|
146
|
+
`libfa` sees `\n` in a string you are compiling, it will match that against
|
147
|
+
the character `n`, not a newline. That's not a problem as the strings you
|
148
|
+
write in Ruby code go through Ruby's backslash interpretation first. When
|
149
|
+
you write `Fa.compile("[\n]")`, `libfa` never sees the backslash as Ruby
|
150
|
+
replaces `\n` with a newline character before that string ever makes it to
|
151
|
+
`libfa`. That has the funny consequence that if you want to use a literal
|
152
|
+
backslash in your regular expression, your input string must have _four_
|
153
|
+
backslashes in it: when you write `Fa.compile("\\\\")`, Ruby first turns
|
154
|
+
that into a string with two backslashes, which `libfa` then interprets as a
|
155
|
+
single
|
156
|
+
backslash. \[_If you are reading this in YARD documentation and only saw two backslashes in the `Fa.compile`, it's because YARD reduced them from the markdown source. Github does not, and so this example will always be wrong in one of them._\]
|
26
157
|
|
27
158
|
## Development
|
28
159
|
|
29
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then,
|
160
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then,
|
161
|
+
run `rake test` to run the tests. You can also run `bin/console` for an
|
162
|
+
interactive prompt that will allow you to experiment.
|
30
163
|
|
31
|
-
To install this gem onto your local machine, run `bundle exec rake
|
164
|
+
To install this gem onto your local machine, run `bundle exec rake
|
165
|
+
install`. To release a new version, update the version number in
|
166
|
+
`version.rb`, and then run `bundle exec rake release`, which will create a
|
167
|
+
git tag for the version, push git commits and tags, and push the `.gem`
|
168
|
+
file to [rubygems.org](https://rubygems.org).
|
32
169
|
|
33
170
|
## Contributing
|
34
171
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at
|
36
|
-
|
172
|
+
Bug reports and pull requests are welcome on GitHub at
|
173
|
+
https://github.com/lutter/ruby-fa.
|
data/Rakefile
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require "rake/testtask"
|
3
|
+
require 'yard'
|
3
4
|
|
4
5
|
Rake::TestTask.new(:test) do |t|
|
5
6
|
t.libs << "test"
|
@@ -7,4 +8,9 @@ Rake::TestTask.new(:test) do |t|
|
|
7
8
|
t.test_files = FileList['test/**/*_test.rb']
|
8
9
|
end
|
9
10
|
|
11
|
+
YARD::Rake::YardocTask.new do |t|
|
12
|
+
#t.options = ['--any', '--extra', '--opts']
|
13
|
+
t.stats_options = ['--list-undoc']
|
14
|
+
end
|
15
|
+
|
10
16
|
task :default => :test
|
data/fa.gemspec
CHANGED
data/lib/fa.rb
CHANGED
@@ -1,11 +1,18 @@
|
|
1
1
|
require "fa/version"
|
2
2
|
require "fa/ffi"
|
3
3
|
|
4
|
+
# Namespace for the libfa bindings
|
4
5
|
module Fa
|
6
|
+
# A generic error encountered during an fa operation
|
5
7
|
class Error < StandardError; end
|
6
8
|
|
9
|
+
# An operation in libfa failed because it could not allocate enough
|
10
|
+
# memory
|
7
11
|
class OutOfMemoryError < Error; end
|
8
12
|
|
13
|
+
# The class representing a finite automaton. It contains a pointer to a
|
14
|
+
# +struct fa+ from +libfa+ and provides Ruby wrappers for the various
|
15
|
+
# +libfa+ operations.
|
9
16
|
class Automaton < ::FFI::AutoPointer
|
10
17
|
attr_reader :faptr
|
11
18
|
|
@@ -13,54 +20,130 @@ module Fa
|
|
13
20
|
@faptr = faptr
|
14
21
|
end
|
15
22
|
|
23
|
+
# Minimizes this automaton in place. Uses either Hopcroft's or
|
24
|
+
# Brzozowski's algorithm. Due to a stupid design mistake in +libfa+,
|
25
|
+
# the algorithm is selected through a global variable. It defaults to
|
26
|
+
# Hopcroft's algorithm though.
|
27
|
+
#
|
28
|
+
# @return [Fa::Automaton] this automaton
|
16
29
|
def minimize
|
17
30
|
r = FFI::minimize(faptr)
|
18
31
|
raise Error if r < 0
|
19
32
|
self
|
20
33
|
end
|
21
34
|
|
35
|
+
# Concatenates +self+ with +other+, corresponding to +SO+. Neither
|
36
|
+
# +self+ nor +other+ will be modified.
|
37
|
+
#
|
38
|
+
# @param [Fa::Automaton] other
|
39
|
+
# @raise OutOfMemoryError if +libfa+ fails to allocate memory
|
40
|
+
# @return [Fa::Automaton] the concatenation of +self+ and +other+
|
22
41
|
def concat(other)
|
23
42
|
from_ptr( FFI::concat(faptr, other.faptr) )
|
24
43
|
end
|
25
44
|
|
45
|
+
# Produces the union of +self+ with +other+, corresponding to
|
46
|
+
# +S|O+. Neither +self+ nor +other+ will be modified.
|
47
|
+
#
|
48
|
+
# @param [Fa::Automaton] other
|
49
|
+
# @raise OutOfMemoryError if +libfa+ fails to allocate memory
|
50
|
+
# @return [Fa::Automaton] the union of +self+ and +other+
|
26
51
|
def union(other)
|
27
52
|
from_ptr( FFI::union(faptr, other.faptr) )
|
28
53
|
end
|
29
54
|
|
55
|
+
# Produces an iteration of +self+, corresponding to
|
56
|
+
# +S{min,max}+. +self+ will not be modified.
|
57
|
+
#
|
58
|
+
# @param [Int] min the minimum number of matches
|
59
|
+
# @param [Int] max the maximum number of matches, use -1 for an
|
60
|
+
# unlimited number of matches
|
61
|
+
# @raise OutOfMemoryError if +libfa+ fails to allocate memory
|
62
|
+
# @return [Fa::Automaton] the iterated automaton
|
30
63
|
def iter(min, max)
|
31
64
|
from_ptr( FFI::iter(faptr, min, max) )
|
32
65
|
end
|
33
66
|
|
67
|
+
# Produces an iteration of any number of +self+, corresponding to
|
68
|
+
# +S*+. +self+ will not be modified.
|
69
|
+
#
|
70
|
+
# @raise OutOfMemoryError if +libfa+ fails to allocate memory
|
71
|
+
# @return [Fa::Automaton] the iterated automaton
|
34
72
|
def star
|
35
73
|
iter(0, -1)
|
36
74
|
end
|
37
75
|
|
76
|
+
# Produces an iteration of at least one +self+, corresponding to
|
77
|
+
# +S\++. +self+ will not be modified.
|
78
|
+
#
|
79
|
+
# @raise OutOfMemoryError if +libfa+ fails to allocate memory
|
80
|
+
# @return [Fa::Automaton] the iterated automaton
|
38
81
|
def plus
|
39
82
|
iter(1, -1)
|
40
83
|
end
|
41
84
|
|
85
|
+
# Produces an iteration of zero or one +self+, corresponding to
|
86
|
+
# +S?+. +self+ will not be modified.
|
87
|
+
#
|
88
|
+
# @raise OutOfMemoryError if +libfa+ fails to allocate memory
|
89
|
+
# @return [Fa::Automaton] the iterated automaton
|
42
90
|
def maybe
|
43
91
|
iter(0, 1)
|
44
92
|
end
|
45
93
|
|
94
|
+
# Produces the intersection of +self+ and +other+. Neither +self+ nor
|
95
|
+
# +other+ will be modified. The resulting automaton will match all
|
96
|
+
# strings that simultaneously match +self+ and +other+,
|
97
|
+
#
|
98
|
+
# @param [Fa::Automaton] other
|
99
|
+
# @raise OutOfMemoryError if +libfa+ fails to allocate memory
|
100
|
+
# @return [Fa::Automaton] the iterated automaton
|
46
101
|
def intersect(other)
|
47
102
|
from_ptr( FFI::intersect(faptr, other.faptr) )
|
48
103
|
end
|
49
104
|
|
105
|
+
# Produces the difference of +self+ and +other+. Neither +self+ nor
|
106
|
+
# +other+ will be modified. The resulting automaton will match all
|
107
|
+
# strings that match +self+ but not +other+,
|
108
|
+
#
|
109
|
+
# @param [Fa::Automaton] other
|
110
|
+
# @raise OutOfMemoryError if +libfa+ fails to allocate memory
|
111
|
+
# @return [Fa::Automaton] the iterated automaton
|
50
112
|
def minus(other)
|
51
113
|
from_ptr( FFI::minus(faptr, other.faptr) )
|
52
114
|
end
|
53
115
|
|
116
|
+
# Produces the complement of +self+. +self+ will not be modified. The
|
117
|
+
# resulting automaton will match all strings that do _not_ match
|
118
|
+
# +self+.
|
119
|
+
#
|
120
|
+
# @raise OutOfMemoryError if +libfa+ fails to allocate memory
|
121
|
+
# @return [Fa::Automaton] the iterated automaton
|
54
122
|
def complement
|
55
123
|
from_ptr( FFI::complement(faptr) )
|
56
124
|
end
|
57
125
|
|
126
|
+
# Returns whether +self+ and +other+ match the same set of strings
|
127
|
+
#
|
128
|
+
# @param [Fa::Automaton] other
|
129
|
+
# @return [Boolean]
|
58
130
|
def equals(other)
|
59
131
|
r = FFI::equals(faptr, other.faptr)
|
60
132
|
raise Error if r < 0
|
61
133
|
return r == 1
|
62
134
|
end
|
63
135
|
|
136
|
+
# Returns whether +self+ and +other+ match the same set of strings
|
137
|
+
#
|
138
|
+
# @param [Fa::Automaton] other
|
139
|
+
# @return [Boolean]
|
140
|
+
def ==(other); equals(other); end
|
141
|
+
|
142
|
+
# Returns whether +self+ matches all the strings that +other+
|
143
|
+
# matches. +self+ may match more strings than that.
|
144
|
+
#
|
145
|
+
# @param [Fa::Automaton] other
|
146
|
+
# @return [Boolean]
|
64
147
|
def contains(other)
|
65
148
|
# The C function works like fa1 <= fa2, and not how the
|
66
149
|
# Ruby nomenclature would suggest it, so swap the arguments
|
@@ -69,20 +152,48 @@ module Fa
|
|
69
152
|
return r == 1
|
70
153
|
end
|
71
154
|
|
72
|
-
|
155
|
+
# Returns whether +other+ matches all the strings that +self+
|
156
|
+
# matches. +other+ may match more strings than that.
|
157
|
+
#
|
158
|
+
# @param [Fa::Automaton] other
|
159
|
+
# @return [Boolean]
|
160
|
+
def <=(other); other.contains(self); end
|
161
|
+
|
162
|
+
# Returns whether +self+ is the empty, epsilon or total automaton
|
163
|
+
#
|
164
|
+
# @param [:empty, :epsilon, :total] kind
|
165
|
+
# @return [Boolean]
|
166
|
+
def is_basic(kind)
|
73
167
|
# FFI::is_basic checks if the automaton is structurally the same as
|
74
168
|
# +basic+; we just want to check here if they accept the same
|
75
169
|
# language. If is_basic fails, we therefore check for equality
|
76
|
-
r = FFI::is_basic(faptr,
|
170
|
+
r = FFI::is_basic(faptr, kind)
|
77
171
|
return true if r == 1
|
78
|
-
return equals(Fa::make_basic(
|
172
|
+
return equals(Fa::make_basic(kind))
|
79
173
|
end
|
80
174
|
|
175
|
+
# Returns whether +self+ is the empty automaton, i.e., matches no words
|
176
|
+
# at all
|
177
|
+
# @return [Boolean]
|
81
178
|
def empty?; is_basic(:empty); end
|
179
|
+
|
180
|
+
# Returns whether +self+ is the epsilon automaton, i.e., matches only
|
181
|
+
# the empty string
|
182
|
+
# @return [Boolean]
|
82
183
|
def epsilon?; is_basic(:epsilon); end
|
184
|
+
|
185
|
+
# Returns whether +self+ is the total automaton, i.e., matches all
|
186
|
+
# possible words.
|
187
|
+
# @return [Boolean]
|
83
188
|
def total?; is_basic(:total); end
|
84
189
|
|
85
|
-
|
190
|
+
# Return the representation of +self+ as a regular expression. Note
|
191
|
+
# that that regular expression can look pretty complicated, even for
|
192
|
+
# seemingly simple automata. Sometimes, minimizing the automaton before
|
193
|
+
# turning it into a string helps; sometimes it doesn't.
|
194
|
+
#
|
195
|
+
# @return [String] the regular expression for +self+
|
196
|
+
def to_s
|
86
197
|
rx = ::FFI::MemoryPointer.new :string
|
87
198
|
rx_len = ::FFI::MemoryPointer.new :size_t
|
88
199
|
r = FFI::as_regexp(faptr, rx, rx_len)
|
@@ -97,21 +208,36 @@ module Fa
|
|
97
208
|
|
98
209
|
:private
|
99
210
|
def from_ptr(ptr)
|
100
|
-
raise OutOfMemoryError if ptr.
|
211
|
+
raise OutOfMemoryError if ptr.null?
|
101
212
|
Automaton.new(ptr)
|
102
213
|
end
|
103
214
|
end
|
104
215
|
|
216
|
+
# Compiles +rx+ into a finite automaton
|
217
|
+
# @param [String] rx a regular expression
|
218
|
+
# @return [Fa::Automaton] the finite automaton
|
105
219
|
def self.compile(rx)
|
106
220
|
faptr = ::FFI::MemoryPointer.new :pointer
|
107
221
|
r = FFI::compile(rx, rx.size, faptr)
|
108
|
-
raise Error if r
|
222
|
+
raise Error if r != 0 # REG_NOERROR is 0, at least for glibc
|
109
223
|
Automaton.new(faptr.get_pointer(0))
|
110
224
|
end
|
111
225
|
|
226
|
+
# Compiles +rx+ into a finite automaton
|
227
|
+
# @param [String] rx a regular expression
|
228
|
+
# @return [Fa::Automaton] the finite automaton
|
229
|
+
def self.[](rx)
|
230
|
+
compile(rx)
|
231
|
+
end
|
232
|
+
|
233
|
+
# Makes a basic finite automaton, either an empty, epsilon, or total
|
234
|
+
# finite automaton. Those match no words, only the empty word, or all
|
235
|
+
# words.
|
236
|
+
# @param [:empty, :epsilon, :total] kind
|
237
|
+
# @return [Fa::Automaton] the finite automaton
|
112
238
|
def self.make_basic(kind)
|
113
239
|
faptr = FFI::make_basic(kind)
|
114
|
-
raise OutOfMemoryError if faptr.
|
240
|
+
raise OutOfMemoryError if faptr.null?
|
115
241
|
Automaton.new(faptr)
|
116
242
|
end
|
117
243
|
end
|
data/lib/fa/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- 'David Lutterkort
|
@@ -68,6 +68,20 @@ dependencies:
|
|
68
68
|
- - "~>"
|
69
69
|
- !ruby/object:Gem::Version
|
70
70
|
version: '5.0'
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: yard
|
73
|
+
requirement: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
type: :development
|
79
|
+
prerelease: false
|
80
|
+
version_requirements: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
71
85
|
description: |
|
72
86
|
Bindings for libfa, a library to manipulate finite automata. Automata are
|
73
87
|
constructed from regular expressions, using extended POSIX syntax, and make
|