citrus 1.6.0 → 1.7.0
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.
- data/README +30 -7
- data/examples/calc.citrus +2 -0
- data/examples/calc.rb +3 -0
- data/examples/ip.citrus +82 -0
- data/examples/ip.rb +75 -0
- data/extras/citrus.vim +1 -1
- data/lib/citrus.rb +16 -8
- data/lib/citrus/file.rb +24 -26
- data/test/file_test.rb +11 -4
- data/test/rule_test.rb +7 -0
- metadata +5 -3
data/README
CHANGED
@@ -10,7 +10,7 @@ elegance and expressiveness of the language with the simplicity and power of
|
|
10
10
|
parsing expressions.
|
11
11
|
|
12
12
|
|
13
|
-
|
13
|
+
= Installation
|
14
14
|
|
15
15
|
|
16
16
|
Via RubyGems:
|
@@ -24,7 +24,7 @@ From a local copy:
|
|
24
24
|
$ rake package && sudo rake install
|
25
25
|
|
26
26
|
|
27
|
-
|
27
|
+
= Background
|
28
28
|
|
29
29
|
|
30
30
|
In order to be able to use Citrus effectively, you must first understand the
|
@@ -99,7 +99,7 @@ available to them including the text of the match, its position in the input,
|
|
99
99
|
and any submatches.
|
100
100
|
|
101
101
|
|
102
|
-
|
102
|
+
= Syntax
|
103
103
|
|
104
104
|
|
105
105
|
The most straightforward way to compose a Citrus grammar is to use Citrus' own
|
@@ -168,7 +168,7 @@ Note that any operator binds more tightly than the bar.
|
|
168
168
|
== Super
|
169
169
|
|
170
170
|
When including a grammar inside another, all rules in the child that have the
|
171
|
-
same name as a rule in the parent also have access to the super keyword to
|
171
|
+
same name as a rule in the parent also have access to the "super" keyword to
|
172
172
|
invoke the parent rule.
|
173
173
|
|
174
174
|
== Labels
|
@@ -181,8 +181,31 @@ immediately preceding any expression.
|
|
181
181
|
# expression may be referred to as "chars"
|
182
182
|
# in a block method
|
183
183
|
|
184
|
+
== Precedence
|
184
185
|
|
185
|
-
|
186
|
+
The following table contains a list of all operators and their precedence. A
|
187
|
+
higher level of precedence indicates tighter binding.
|
188
|
+
|
189
|
+
Operator | Level of Precedence | Name
|
190
|
+
----------------------------------------------------------------
|
191
|
+
'' | 6 | Literal string
|
192
|
+
"" | 6 | Literal string
|
193
|
+
[] | 6 | Character class
|
194
|
+
. | 6 | Any character (dot)
|
195
|
+
// | 6 | Regular expression
|
196
|
+
() | 6 | Grouping
|
197
|
+
* | 5 | Repetition (arbitrary)
|
198
|
+
+ | 5 | Repetition (one or more)
|
199
|
+
? | 5 | Repetition (zero or one)
|
200
|
+
& | 4 | And predicate
|
201
|
+
! | 4 | Not predicate
|
202
|
+
: | 4 | Label
|
203
|
+
<>, {} | 3 | Extension
|
204
|
+
e1 e2 | 2 | Sequence
|
205
|
+
e1 | e2 | 1 | Ordered choice
|
206
|
+
|
207
|
+
|
208
|
+
= Example
|
186
209
|
|
187
210
|
|
188
211
|
Below is an example of a simple grammar that is able to parse strings of
|
@@ -300,7 +323,7 @@ Take a look at examples/calc.citrus for an example of a calculator that is able
|
|
300
323
|
to parse and evaluate more complex mathematical expressions.
|
301
324
|
|
302
325
|
|
303
|
-
|
326
|
+
= Links
|
304
327
|
|
305
328
|
|
306
329
|
http://mjijackson.com/citrus
|
@@ -309,7 +332,7 @@ http://en.wikipedia.org/wiki/Parsing_expression_grammar
|
|
309
332
|
http://treetop.rubyforge.org/index.html
|
310
333
|
|
311
334
|
|
312
|
-
|
335
|
+
= License
|
313
336
|
|
314
337
|
|
315
338
|
Copyright 2010 Michael Jackson
|
data/examples/calc.citrus
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# A grammar for mathematical formulas that apply the basic four operations to
|
2
2
|
# non-negative numbers (integers and floats), respecting operator precedence and
|
3
3
|
# ignoring whitespace.
|
4
|
+
#
|
5
|
+
# An identical grammar that is written using pure Ruby can be found in calc.rb.
|
4
6
|
grammar Calc
|
5
7
|
rule term
|
6
8
|
additive | factor
|
data/examples/calc.rb
CHANGED
@@ -3,6 +3,9 @@ require 'citrus'
|
|
3
3
|
# A grammar for mathematical formulas that apply the basic four operations to
|
4
4
|
# non-negative numbers (integers and floats), respecting operator precedence and
|
5
5
|
# ignoring whitespace.
|
6
|
+
#
|
7
|
+
# An identical grammar that is written using Citrus' own grammar syntax can be
|
8
|
+
# found in calc.citrus.
|
6
9
|
grammar :Calc do
|
7
10
|
rule :term do
|
8
11
|
any(:additive, :factor)
|
data/examples/ip.citrus
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# The grammars in this file conform to the ABNF given in Appendix A of RFC 3986
|
2
|
+
# Uniform Resource Identifier (URI): Generic Syntax.
|
3
|
+
#
|
4
|
+
# See http://tools.ietf.org/html/rfc3986#appendix-A for more information.
|
5
|
+
|
6
|
+
grammar IPv4Address
|
7
|
+
# A host identified by an IPv4 literal address is represented in
|
8
|
+
# dotted-decimal notation (a sequence of four decimal numbers in the
|
9
|
+
# range 0 to 255, separated by "."), as described in [RFC1123] by
|
10
|
+
# reference to [RFC0952]. Note that other forms of dotted notation may
|
11
|
+
# be interpreted on some platforms, as described in Section 7.4, but
|
12
|
+
# only the dotted-decimal form of four octets is allowed by this
|
13
|
+
# grammar.
|
14
|
+
rule IPv4address
|
15
|
+
(dec-octet '.' dec-octet '.' dec-octet '.' dec-octet) {
|
16
|
+
def version; 4 end
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
rule dec-octet
|
21
|
+
'25' [0-5] # 250-255
|
22
|
+
| '2' [0-4] DIGIT # 200-249
|
23
|
+
| '1' DIGIT DIGIT # 100-199
|
24
|
+
| [1-9] DIGIT # 10-99
|
25
|
+
| DIGIT # 0-9
|
26
|
+
end
|
27
|
+
|
28
|
+
rule DIGIT
|
29
|
+
[0-9]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
grammar IPv6Address
|
34
|
+
include IPv4Address
|
35
|
+
|
36
|
+
# A 128-bit IPv6 address is divided into eight 16-bit pieces. Each
|
37
|
+
# piece is represented numerically in case-insensitive hexadecimal,
|
38
|
+
# using one to four hexadecimal digits (leading zeroes are permitted).
|
39
|
+
# The eight encoded pieces are given most-significant first, separated
|
40
|
+
# by colon characters. Optionally, the least-significant two pieces
|
41
|
+
# may instead be represented in IPv4 address textual format. A
|
42
|
+
# sequence of one or more consecutive zero-valued 16-bit pieces within
|
43
|
+
# the address may be elided, omitting all their digits and leaving
|
44
|
+
# exactly two consecutive colons in their place to mark the elision.
|
45
|
+
rule IPv6address
|
46
|
+
( (h16 ":")6*6 ls32
|
47
|
+
| "::" (h16 ":")5*5 ls32
|
48
|
+
| h16 ? "::" (h16 ":")4*4 ls32
|
49
|
+
| (h16 (":" h16)*1)? "::" (h16 ":")3*3 ls32
|
50
|
+
| (h16 (":" h16)*2)? "::" (h16 ":")2*2 ls32
|
51
|
+
| (h16 (":" h16)*3)? "::" h16 ":" ls32
|
52
|
+
| (h16 (":" h16)*4)? "::" ls32
|
53
|
+
| (h16 (":" h16)*5)? "::" h16
|
54
|
+
| (h16 (":" h16)*6)? "::"
|
55
|
+
) {
|
56
|
+
def version; 6 end
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
# 16-bit address represented in hexadecimal.
|
61
|
+
rule h16
|
62
|
+
HEXDIG 1*4
|
63
|
+
end
|
64
|
+
|
65
|
+
# Least-significant 32 bits of address.
|
66
|
+
rule ls32
|
67
|
+
(h16 ":" h16) | IPv4address
|
68
|
+
end
|
69
|
+
|
70
|
+
rule HEXDIG
|
71
|
+
DIGIT | [a-fA-F] # Hexadecimal should be case-insensitive.
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
grammar IPAddress
|
76
|
+
include IPv4Address
|
77
|
+
include IPv6Address
|
78
|
+
|
79
|
+
rule IPaddress
|
80
|
+
IPv4address | IPv6address
|
81
|
+
end
|
82
|
+
end
|
data/examples/ip.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'citrus'
|
2
|
+
|
3
|
+
# This file contains a small suite of tests for the grammars found in ip.citrus.
|
4
|
+
# If this file is run directly (i.e. using `ruby ip.rb') the tests will run.
|
5
|
+
# Otherwise, this file may be required by another that needs access to the IP
|
6
|
+
# address grammars just as any other file would be.
|
7
|
+
|
8
|
+
# Load and evaluate the grammars contained in ip.citrus into the global
|
9
|
+
# namespace.
|
10
|
+
Citrus.load(File.expand_path('../ip.citrus', __FILE__))
|
11
|
+
|
12
|
+
if $0 == __FILE__
|
13
|
+
require 'test/unit'
|
14
|
+
|
15
|
+
class IPAddressTest < Test::Unit::TestCase
|
16
|
+
def test_dec_octet
|
17
|
+
match = IPv4Address.parse('0', :root => :'dec-octet')
|
18
|
+
assert(match)
|
19
|
+
|
20
|
+
match = IPv4Address.parse('255', :root => :'dec-octet')
|
21
|
+
assert(match)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_hexdig
|
25
|
+
match = IPv6Address.parse('0', :root => :HEXDIG)
|
26
|
+
assert(match)
|
27
|
+
|
28
|
+
match = IPv6Address.parse('A', :root => :HEXDIG)
|
29
|
+
assert(match)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_v4
|
33
|
+
match = IPv4Address.parse('0.0.0.0')
|
34
|
+
assert(match)
|
35
|
+
|
36
|
+
match = IPv4Address.parse('255.255.255.255')
|
37
|
+
assert(match)
|
38
|
+
|
39
|
+
assert_raise Citrus::ParseError do
|
40
|
+
IPv4Address.parse('255.255.255')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_v6
|
45
|
+
match = IPv6Address.parse('1:2:3:4:5:6:7:8')
|
46
|
+
assert(match)
|
47
|
+
|
48
|
+
match = IPv6Address.parse('12AD:34FC:A453:1922::')
|
49
|
+
assert(match)
|
50
|
+
|
51
|
+
match = IPv6Address.parse('12AD::34FC')
|
52
|
+
assert(match)
|
53
|
+
|
54
|
+
match = IPv6Address.parse('12AD::')
|
55
|
+
assert(match)
|
56
|
+
|
57
|
+
match = IPv6Address.parse('::')
|
58
|
+
assert(match)
|
59
|
+
|
60
|
+
assert_raise Citrus::ParseError do
|
61
|
+
IPv6Address.parse('1:2')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_all
|
66
|
+
match = IPAddress.parse('1.2.3.4')
|
67
|
+
assert(match)
|
68
|
+
assert_equal(4, match.version)
|
69
|
+
|
70
|
+
match = IPAddress.parse('1:2:3:4::')
|
71
|
+
assert(match)
|
72
|
+
assert_equal(6, match.version)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/extras/citrus.vim
CHANGED
@@ -16,7 +16,7 @@ syn case match
|
|
16
16
|
syn match ctDoubleColon "::" contained
|
17
17
|
syn match ctConstant "\u\w*" contained
|
18
18
|
syn match ctModule "\(\(::\)\?\u\w*\)\+" contains=ctDoubleColon,ctConstant contained
|
19
|
-
syn match ctVariable "[a-zA-
|
19
|
+
syn match ctVariable "\a[a-zA-Z0-9_-]*" contained
|
20
20
|
|
21
21
|
" Comments
|
22
22
|
syn match ctComment "#.*" contains=@Spell
|
data/lib/citrus.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
#
|
5
5
|
# http://mjijackson.com/citrus
|
6
6
|
module Citrus
|
7
|
-
VERSION = [1,
|
7
|
+
VERSION = [1, 7, 0]
|
8
8
|
|
9
9
|
Infinity = 1.0 / 0
|
10
10
|
|
@@ -68,7 +68,7 @@ module Citrus
|
|
68
68
|
# created with this method may be assigned a name by being assigned to some
|
69
69
|
# constant, e.g.:
|
70
70
|
#
|
71
|
-
# Calc = Grammar.new {}
|
71
|
+
# Calc = Citrus::Grammar.new {}
|
72
72
|
#
|
73
73
|
def self.new(&block)
|
74
74
|
mod = Module.new { include Grammar }
|
@@ -143,8 +143,8 @@ module Citrus
|
|
143
143
|
end
|
144
144
|
|
145
145
|
# Gets/sets the rule with the given +name+. If +obj+ is given the rule
|
146
|
-
# will be set to the value of +obj+ passed through Rule#
|
147
|
-
#
|
146
|
+
# will be set to the value of +obj+ passed through Rule#new. If a block is
|
147
|
+
# given, its return value will be used for the value of +obj+.
|
148
148
|
#
|
149
149
|
# It is important to note that this method will also check any included
|
150
150
|
# grammars for a rule with the given +name+ if one cannot be found in this
|
@@ -156,7 +156,7 @@ module Citrus
|
|
156
156
|
if obj
|
157
157
|
rule_names << sym unless has_rule?(sym)
|
158
158
|
|
159
|
-
rule = Rule.
|
159
|
+
rule = Rule.new(obj)
|
160
160
|
rule.name = name
|
161
161
|
setup_super(rule, name)
|
162
162
|
rule.grammar = self
|
@@ -239,7 +239,7 @@ module Citrus
|
|
239
239
|
# the given +rule+. A block may also be given that will be used to create
|
240
240
|
# an anonymous module. See Rule#ext=.
|
241
241
|
def ext(rule, mod=nil)
|
242
|
-
rule = Rule.
|
242
|
+
rule = Rule.new(rule)
|
243
243
|
mod = Proc.new if block_given?
|
244
244
|
rule.extension = mod if mod
|
245
245
|
rule
|
@@ -342,7 +342,7 @@ module Citrus
|
|
342
342
|
# Input during parsing.
|
343
343
|
module Rule
|
344
344
|
# Returns a new Rule object depending on the type of object given.
|
345
|
-
def self.
|
345
|
+
def self.new(obj)
|
346
346
|
case obj
|
347
347
|
when Rule then obj
|
348
348
|
when Symbol then Alias.new(obj)
|
@@ -356,6 +356,14 @@ module Citrus
|
|
356
356
|
end
|
357
357
|
end
|
358
358
|
|
359
|
+
# Creates a new rule object from the given expression.
|
360
|
+
#
|
361
|
+
# Citrus::Rule.create('"a" | "b"')
|
362
|
+
#
|
363
|
+
def self.create(expr)
|
364
|
+
File.parse(expr, :root => :rule_body).value
|
365
|
+
end
|
366
|
+
|
359
367
|
@unique_id = 0
|
360
368
|
|
361
369
|
# Generates a new rule id.
|
@@ -587,7 +595,7 @@ module Citrus
|
|
587
595
|
include Rule
|
588
596
|
|
589
597
|
def initialize(rules=[])
|
590
|
-
@rules = rules.map {|r| Rule.
|
598
|
+
@rules = rules.map {|r| Rule.new(r) }
|
591
599
|
end
|
592
600
|
|
593
601
|
# An array of the actual Rule objects this rule uses to match.
|
data/lib/citrus/file.rb
CHANGED
@@ -75,7 +75,7 @@ module Citrus
|
|
75
75
|
end
|
76
76
|
|
77
77
|
rule :sequence do
|
78
|
-
zero_or_more(:
|
78
|
+
zero_or_more(:appendix) {
|
79
79
|
def values
|
80
80
|
matches.map {|m| m.value }
|
81
81
|
end
|
@@ -86,44 +86,44 @@ module Citrus
|
|
86
86
|
}
|
87
87
|
end
|
88
88
|
|
89
|
-
rule :
|
90
|
-
all(
|
89
|
+
rule :appendix do
|
90
|
+
all(:prefix, zero_or_one(:extension)) {
|
91
91
|
def value
|
92
|
-
rule =
|
93
|
-
|
94
|
-
rule =
|
92
|
+
rule = prefix.value
|
93
|
+
extension = matches[1].first
|
94
|
+
rule.extension = extension.value if extension
|
95
95
|
rule
|
96
96
|
end
|
97
97
|
}
|
98
98
|
end
|
99
99
|
|
100
|
-
rule :
|
101
|
-
all(:
|
100
|
+
rule :prefix do
|
101
|
+
all(zero_or_one(:predicate), :suffix) {
|
102
102
|
def value
|
103
103
|
rule = suffix.value
|
104
|
-
|
105
|
-
|
104
|
+
predicate = matches[0].first
|
105
|
+
rule = predicate.wrap(rule) if predicate
|
106
106
|
rule
|
107
107
|
end
|
108
108
|
}
|
109
109
|
end
|
110
110
|
|
111
111
|
rule :suffix do
|
112
|
-
all(:primary, zero_or_one(:
|
112
|
+
all(:primary, zero_or_one(:repeat)) {
|
113
113
|
def value
|
114
114
|
rule = primary.value
|
115
|
-
|
116
|
-
rule =
|
115
|
+
repeat = matches[1].first
|
116
|
+
rule = repeat.wrap(rule) if repeat
|
117
117
|
rule
|
118
118
|
end
|
119
119
|
}
|
120
120
|
end
|
121
121
|
|
122
122
|
rule :primary do
|
123
|
-
any(:super, :alias, :
|
123
|
+
any(:super, :alias, :grouping, :terminal)
|
124
124
|
end
|
125
125
|
|
126
|
-
rule :
|
126
|
+
rule :grouping do
|
127
127
|
all(:lparen, :rule_body, :rparen) {
|
128
128
|
def value
|
129
129
|
rule_body.value
|
@@ -157,8 +157,10 @@ module Citrus
|
|
157
157
|
}
|
158
158
|
end
|
159
159
|
|
160
|
+
# Rule names may contain letters, numbers, underscores, and dashes. They
|
161
|
+
# MUST start with a letter.
|
160
162
|
rule :rule_name do
|
161
|
-
all(/[a-zA-
|
163
|
+
all(/[a-zA-Z][a-zA-Z0-9_-]*/, :space) {
|
162
164
|
def value
|
163
165
|
first.text
|
164
166
|
end
|
@@ -184,7 +186,7 @@ module Citrus
|
|
184
186
|
rule :terminal do
|
185
187
|
any(:quoted_string, :character_class, :anything_symbol, :regular_expression) {
|
186
188
|
def value
|
187
|
-
Rule.
|
189
|
+
Rule.new(super)
|
188
190
|
end
|
189
191
|
}
|
190
192
|
end
|
@@ -221,7 +223,7 @@ module Citrus
|
|
221
223
|
}
|
222
224
|
end
|
223
225
|
|
224
|
-
rule :
|
226
|
+
rule :predicate do
|
225
227
|
any(:and, :not, :label)
|
226
228
|
end
|
227
229
|
|
@@ -254,11 +256,7 @@ module Citrus
|
|
254
256
|
end
|
255
257
|
|
256
258
|
rule :extension do
|
257
|
-
any(:tag, :block)
|
258
|
-
def apply(rule)
|
259
|
-
rule.extension = value
|
260
|
-
end
|
261
|
-
}
|
259
|
+
any(:tag, :block)
|
262
260
|
end
|
263
261
|
|
264
262
|
rule :tag do
|
@@ -277,8 +275,8 @@ module Citrus
|
|
277
275
|
}
|
278
276
|
end
|
279
277
|
|
280
|
-
rule :
|
281
|
-
any(:question, :plus, :
|
278
|
+
rule :repeat do
|
279
|
+
any(:question, :plus, :star_quantity) {
|
282
280
|
def wrap(rule)
|
283
281
|
Repeat.new(min, max, rule)
|
284
282
|
end
|
@@ -299,7 +297,7 @@ module Citrus
|
|
299
297
|
}
|
300
298
|
end
|
301
299
|
|
302
|
-
rule :
|
300
|
+
rule :star_quantity do
|
303
301
|
all(/[0-9]*/, '*', /[0-9]*/, :space) {
|
304
302
|
def min
|
305
303
|
matches[0] == '' ? 0 : matches[0].text.to_i
|
data/test/file_test.rb
CHANGED
@@ -105,6 +105,13 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
105
105
|
assert_kind_of(Rule, match.value)
|
106
106
|
assert_instance_of(Repeat, match.value)
|
107
107
|
|
108
|
+
match = grammar.parse('"a"*')
|
109
|
+
assert(match)
|
110
|
+
assert_kind_of(Rule, match.value)
|
111
|
+
assert_instance_of(Repeat, match.value)
|
112
|
+
assert_equal(0, match.value.min)
|
113
|
+
assert_equal(Infinity, match.value.max)
|
114
|
+
|
108
115
|
match = grammar.parse('("a" "b")*')
|
109
116
|
assert(match)
|
110
117
|
assert_kind_of(Rule, match.value)
|
@@ -469,8 +476,8 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
469
476
|
assert_equal(/a/i, match.value)
|
470
477
|
end
|
471
478
|
|
472
|
-
def
|
473
|
-
grammar = file(:
|
479
|
+
def test_predicate
|
480
|
+
grammar = file(:predicate)
|
474
481
|
|
475
482
|
match = grammar.parse('&')
|
476
483
|
assert(match)
|
@@ -570,8 +577,8 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
570
577
|
assert(match.value)
|
571
578
|
end
|
572
579
|
|
573
|
-
def
|
574
|
-
grammar = file(:
|
580
|
+
def test_repeat
|
581
|
+
grammar = file(:repeat)
|
575
582
|
|
576
583
|
match = grammar.parse('?')
|
577
584
|
assert(match)
|
data/test/rule_test.rb
CHANGED
@@ -20,6 +20,13 @@ class RuleTest < Test::Unit::TestCase
|
|
20
20
|
|
21
21
|
NumericModule = Module.new(&NumericProc)
|
22
22
|
|
23
|
+
def test_create
|
24
|
+
rule = Rule.create('"a"')
|
25
|
+
assert(rule)
|
26
|
+
match = rule.match(input('a'))
|
27
|
+
assert(match)
|
28
|
+
end
|
29
|
+
|
23
30
|
def test_match_module
|
24
31
|
rule = EqualRule.new('a')
|
25
32
|
rule.extension = MatchModule
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 1
|
7
|
-
-
|
7
|
+
- 7
|
8
8
|
- 0
|
9
|
-
version: 1.
|
9
|
+
version: 1.7.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Michael Jackson
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-08-17 00:00:00 -
|
17
|
+
date: 2010-08-17 00:00:00 -05:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -63,6 +63,8 @@ files:
|
|
63
63
|
- doc/syntax.rdoc
|
64
64
|
- examples/calc.citrus
|
65
65
|
- examples/calc.rb
|
66
|
+
- examples/ip.citrus
|
67
|
+
- examples/ip.rb
|
66
68
|
- extras/citrus.vim
|
67
69
|
- lib/citrus/debug.rb
|
68
70
|
- lib/citrus/file.rb
|