Spectre 0.0.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.
- data/CHANGELOG +1 -0
- data/LICENSE +23 -0
- data/README +20 -0
- data/Rakefile +112 -0
- data/lib/spectre/base.rb +44 -0
- data/lib/spectre/base/closure.rb +96 -0
- data/lib/spectre/base/directive.rb +148 -0
- data/lib/spectre/base/grammar.rb +269 -0
- data/lib/spectre/base/inputiterator.rb +276 -0
- data/lib/spectre/base/node.rb +393 -0
- data/lib/spectre/base/operators.rb +342 -0
- data/lib/spectre/base/parser.rb +110 -0
- data/lib/spectre/generic.rb +115 -0
- data/lib/spectre/generic/directives.rb +246 -0
- data/lib/spectre/generic/negations.rb +68 -0
- data/lib/spectre/generic/primitives.rb +172 -0
- data/lib/spectre/generic/semanticaction.rb +43 -0
- data/lib/spectre/string.rb +57 -0
- data/lib/spectre/string/additionals.rb +80 -0
- data/lib/spectre/string/directives.rb +51 -0
- data/lib/spectre/string/inputiterator.rb +57 -0
- data/lib/spectre/string/primitives.rb +400 -0
- data/test/base/closure_tests.rb +108 -0
- data/test/base/grammar_tests.rb +97 -0
- data/test/base/operator_tests.rb +335 -0
- data/test/base/semanticaction_tests.rb +53 -0
- data/test/generic/directive_tests.rb +224 -0
- data/test/generic/negation_tests.rb +146 -0
- data/test/generic/primitive_tests.rb +99 -0
- data/test/string/POD2Parser_tests.rb +93 -0
- data/test/string/additional_tests.rb +43 -0
- data/test/string/directive_tests.rb +32 -0
- data/test/string/primitive_tests.rb +173 -0
- data/test/tests.rb +33 -0
- data/test/tutorial/funnymath_tests.rb +57 -0
- data/test/tutorial/html_tests.rb +171 -0
- data/test/tutorial/skipping_tests.rb +60 -0
- metadata +109 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
# This is Spectre, a parser framework inspired by Boost.Spirit,
|
2
|
+
# which can be found at http://spirit.sourceforge.net/.
|
3
|
+
#
|
4
|
+
# If you want to find out more or need a tutorial, go to
|
5
|
+
# http://spectre.rubyforge.org/
|
6
|
+
# You'll find a nice wiki there!
|
7
|
+
#
|
8
|
+
# Author:: Fabian Streitel (karottenreibe)
|
9
|
+
# Copyright:: Copyright (c) 2009 Fabian Streitel
|
10
|
+
# License:: Boost Software License 1.0
|
11
|
+
# For further information regarding this license, you can go to
|
12
|
+
# http://www.boost.org/LICENSE_1_0.txt
|
13
|
+
# or read the file LICENSE distributed with this software.
|
14
|
+
# Homepage:: http://spectre.rubyforge.org/
|
15
|
+
# Git repo:: http://rubyforge.org/scm/?group_id=7618
|
16
|
+
#
|
17
|
+
# Keeps the standard InputIterator for String parsing.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'spectre/base/inputiterator'
|
21
|
+
|
22
|
+
module Spectre
|
23
|
+
|
24
|
+
module StringParsing
|
25
|
+
|
26
|
+
##
|
27
|
+
# The standard input provider for Strings.
|
28
|
+
# The default transformation set will skip white-space (\s, \n, \t, \r, \f).
|
29
|
+
# This can be undone by wrapping the Parser(s) inside a +lexeme_d+.
|
30
|
+
#
|
31
|
+
class StringInputIterator < InputIterator
|
32
|
+
|
33
|
+
##
|
34
|
+
# The default skipper for this InputIterator class.
|
35
|
+
# Skips white space.
|
36
|
+
#
|
37
|
+
def default_skipper
|
38
|
+
lambda { |token,is|
|
39
|
+
case token
|
40
|
+
when " ", "\n", "\r", "\t", "\f" then 1
|
41
|
+
else nil
|
42
|
+
end
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def concat val1, val2
|
47
|
+
( val1 || '' ) + ( val2 || '' )
|
48
|
+
end
|
49
|
+
|
50
|
+
def empty
|
51
|
+
''
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
@@ -0,0 +1,400 @@
|
|
1
|
+
# This is Spectre, a parser framework inspired by Boost.Spirit,
|
2
|
+
# which can be found at http://spirit.sourceforge.net/.
|
3
|
+
#
|
4
|
+
# If you want to find out more or need a tutorial, go to
|
5
|
+
# http://spectre.rubyforge.org/
|
6
|
+
# You'll find a nice wiki there!
|
7
|
+
#
|
8
|
+
# Author:: Fabian Streitel (karottenreibe)
|
9
|
+
# Copyright:: Copyright (c) 2009 Fabian Streitel
|
10
|
+
# License:: Boost Software License 1.0
|
11
|
+
# For further information regarding this license, you can go to
|
12
|
+
# http://www.boost.org/LICENSE_1_0.txt
|
13
|
+
# or read the file LICENSE distributed with this software.
|
14
|
+
# Homepage:: http://spectre.rubyforge.org/
|
15
|
+
# Git repo:: http://rubyforge.org/scm/?group_id=7618
|
16
|
+
#
|
17
|
+
# Keeps the Standard Parsers for String parsing.
|
18
|
+
# Registered shortcuts for standard String Parsers:
|
19
|
+
#
|
20
|
+
# CharParser :char
|
21
|
+
# StringParser :string
|
22
|
+
# AnycharParser :any_char
|
23
|
+
# AlnumParser :alnum_char
|
24
|
+
# AlphaParser :alpha_char
|
25
|
+
# BlankParser :blank_char
|
26
|
+
# ControlParser :control_char
|
27
|
+
# DigitParser :digit
|
28
|
+
# GraphParser :graph_char
|
29
|
+
# LowerParser :lower_char
|
30
|
+
# PrintParser :print_char
|
31
|
+
# PunctParser :punct_char
|
32
|
+
# SpaceParser :space_char
|
33
|
+
# UpperParser :upper_char
|
34
|
+
# HexDigitParser :hex_digit
|
35
|
+
# EOLParser :eol
|
36
|
+
#
|
37
|
+
# See also ShortcutsMixin.
|
38
|
+
#
|
39
|
+
|
40
|
+
require 'spectre/base/parser'
|
41
|
+
require 'spectre/base/grammar'
|
42
|
+
|
43
|
+
module Spectre
|
44
|
+
|
45
|
+
module StringParsing
|
46
|
+
|
47
|
+
##
|
48
|
+
# Matches one single character.
|
49
|
+
# Shortcut: +char+.
|
50
|
+
#
|
51
|
+
class CharParser
|
52
|
+
include Parser
|
53
|
+
|
54
|
+
def initialize char
|
55
|
+
super()
|
56
|
+
@char = char
|
57
|
+
end
|
58
|
+
|
59
|
+
def negation; Negations::NegatedSingleTokenParser.new; end
|
60
|
+
|
61
|
+
def scan iter
|
62
|
+
if iter.valid? and +iter == @char
|
63
|
+
create_match iter, @char
|
64
|
+
else
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def inspect
|
70
|
+
"[char:'#{@char}']"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Matches a String.
|
76
|
+
# Is an implicit lexeme, i.e. works on character level only, i.e. it will ignore any skippers set on
|
77
|
+
# the InputIterator
|
78
|
+
#
|
79
|
+
# NOTE: It's negation is a zero token parser, i.e. +~string('foo')+ will only
|
80
|
+
# test if the input at that location contains the string and return a failure if so
|
81
|
+
# and a successful match of length 0 if not.
|
82
|
+
#
|
83
|
+
# Shortcut: +string+.
|
84
|
+
#
|
85
|
+
class StringParser
|
86
|
+
include Parser
|
87
|
+
|
88
|
+
def initialize string
|
89
|
+
super()
|
90
|
+
@string = string
|
91
|
+
end
|
92
|
+
|
93
|
+
def negation; Negations::NegatedZeroTokenParser.new; end
|
94
|
+
|
95
|
+
def scan iter
|
96
|
+
return Match.new 0, @string if @string.length == 0
|
97
|
+
return nil unless iter.valid?
|
98
|
+
|
99
|
+
iter.ignore_skipper do
|
100
|
+
return create_match iter, @string if
|
101
|
+
(iter + @string.length) == @string
|
102
|
+
end
|
103
|
+
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
|
107
|
+
def inspect
|
108
|
+
"[string:\"#{@string}\"]"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Matches any character, like the Regexp +/./+.
|
114
|
+
# Shortcut: +any_char+.
|
115
|
+
#
|
116
|
+
class AnycharParser
|
117
|
+
include Parser
|
118
|
+
|
119
|
+
def negation; Negations::NegatedSingleTokenParser.new; end
|
120
|
+
|
121
|
+
def scan iter
|
122
|
+
unless iter.valid?
|
123
|
+
nil
|
124
|
+
else
|
125
|
+
char = +iter
|
126
|
+
create_match iter, char
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def inspect
|
131
|
+
"[any_char]"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
##
|
136
|
+
# Matches any alpha-numeric character.
|
137
|
+
# Shortcut: +alnum_char+.
|
138
|
+
#
|
139
|
+
class AlnumParser < RangeParser
|
140
|
+
def initialize
|
141
|
+
super( ('0'..'9').to_a + ('a'..'z').to_a + ('A'..'Z').to_a )
|
142
|
+
end
|
143
|
+
|
144
|
+
def inspect
|
145
|
+
"[alnum_char]"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
##
|
150
|
+
# Matches any alphabetic character.
|
151
|
+
# Shortcut: +alpha_char+.
|
152
|
+
#
|
153
|
+
class AlphaParser < RangeParser
|
154
|
+
def initialize
|
155
|
+
super( ('a'..'z').to_a + ('A'..'Z').to_a )
|
156
|
+
end
|
157
|
+
|
158
|
+
def inspect
|
159
|
+
"[alpha_char]"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# Matches spaces and tabs.
|
165
|
+
# Shortcut: +blank_char+.
|
166
|
+
#
|
167
|
+
class BlankParser < RangeParser
|
168
|
+
def initialize
|
169
|
+
super([" ", "\t"])
|
170
|
+
end
|
171
|
+
|
172
|
+
def inspect
|
173
|
+
"[blank_char]"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
##
|
178
|
+
# Matches control characters.
|
179
|
+
# Shortcut: +control_char+.
|
180
|
+
#
|
181
|
+
class ControlParser < RangeParser
|
182
|
+
def initialize
|
183
|
+
super(["\0", "\a", "\b", "\t", "\n", "\f", "\r", "\e", "\s"])
|
184
|
+
end
|
185
|
+
|
186
|
+
def inspect
|
187
|
+
"[control_char]"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
##
|
192
|
+
# Matches numeric digits.
|
193
|
+
# Shortcut: +digit+.
|
194
|
+
#
|
195
|
+
class DigitParser < RangeParser
|
196
|
+
def initialize
|
197
|
+
super( ('0'..'9').to_a )
|
198
|
+
end
|
199
|
+
|
200
|
+
def inspect
|
201
|
+
"[digit]"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
##
|
206
|
+
# Matches non-space printing characters.
|
207
|
+
# Shortcut: +graph_char+.
|
208
|
+
#
|
209
|
+
class GraphParser
|
210
|
+
include Parser
|
211
|
+
|
212
|
+
def negation; Negations::NegatedSingleTokenParser.new; end
|
213
|
+
|
214
|
+
def scan iter
|
215
|
+
return nil unless iter.valid?
|
216
|
+
char = +iter
|
217
|
+
|
218
|
+
if char and char[0].between? 33, 126
|
219
|
+
create_match iter, char
|
220
|
+
else
|
221
|
+
nil
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def inspect
|
226
|
+
"[graph_char]"
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
##
|
231
|
+
# Matches lowercase letters.
|
232
|
+
# Shortcut: +lower_char+.
|
233
|
+
#
|
234
|
+
class LowerParser < RangeParser
|
235
|
+
def initialize
|
236
|
+
super( ('a'..'z').to_a )
|
237
|
+
end
|
238
|
+
|
239
|
+
def inspect
|
240
|
+
"[lower_char]"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
##
|
245
|
+
# Matches printable characters.
|
246
|
+
# Shortcut: +print_char+.
|
247
|
+
#
|
248
|
+
class PrintParser
|
249
|
+
include Parser
|
250
|
+
|
251
|
+
class << self
|
252
|
+
def negation; Negations::NegatedSingleTokenParser.new; end
|
253
|
+
end
|
254
|
+
|
255
|
+
def scan iter
|
256
|
+
return nil unless iter.valid?
|
257
|
+
char = +iter
|
258
|
+
|
259
|
+
if char and char[0].between? 32, 126
|
260
|
+
create_match iter, char
|
261
|
+
else
|
262
|
+
nil
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def inspect
|
267
|
+
"[print_char]"
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
##
|
272
|
+
# Matches punctuation symbols.
|
273
|
+
# Shortcut: +punct_char+.
|
274
|
+
#
|
275
|
+
class PunctParser
|
276
|
+
include Parser
|
277
|
+
|
278
|
+
class << self
|
279
|
+
def negation; Negations::NegatedSingleTokenParser.new; end
|
280
|
+
end
|
281
|
+
|
282
|
+
def scan iter
|
283
|
+
return nil unless iter.valid?
|
284
|
+
char = +iter
|
285
|
+
|
286
|
+
if char and ( char[0].between? 33, 126 ) and ( not ('a'..'z').include? char ) and
|
287
|
+
( not ('A'..'Z').include? char ) and ( not ('0'..'9').include? char )
|
288
|
+
create_match iter, char
|
289
|
+
else
|
290
|
+
nil
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
def inspect
|
295
|
+
"[punct_char]"
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
##
|
300
|
+
# Matches spaces, tabs, returns and newlines.
|
301
|
+
# Shortcut: +space_char+.
|
302
|
+
#
|
303
|
+
class SpaceParser < RangeParser
|
304
|
+
def initialize
|
305
|
+
super([" ", "\t", "\n", "\r"])
|
306
|
+
end
|
307
|
+
|
308
|
+
def inspect
|
309
|
+
"[space_char]"
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
##
|
314
|
+
# Matches uppercase letters.
|
315
|
+
# Shortcut: +upper_char+.
|
316
|
+
#
|
317
|
+
class UpperParser < RangeParser
|
318
|
+
def initialize
|
319
|
+
super( ('A'..'Z').to_a )
|
320
|
+
end
|
321
|
+
|
322
|
+
def inspect
|
323
|
+
"[upper_char]"
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
##
|
328
|
+
# Matches hexadecimal digits.
|
329
|
+
# Shortcut: +hex_digit+.
|
330
|
+
#
|
331
|
+
class HexDigitParser < RangeParser
|
332
|
+
def initialize
|
333
|
+
super( ('0'..'9').to_a + ('a'..'f').to_a + ('A'..'F').to_a )
|
334
|
+
end
|
335
|
+
|
336
|
+
def inspect
|
337
|
+
"[hex_digit]"
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
##
|
342
|
+
# Matches any end-of-line characters (CR/LF) and combinations thereof.
|
343
|
+
# Shortcut: +eol+.
|
344
|
+
#
|
345
|
+
class EOLParser
|
346
|
+
include Parser
|
347
|
+
|
348
|
+
class << self
|
349
|
+
def negation; Negations::NegatedZeroTokenParser.new; end
|
350
|
+
end
|
351
|
+
|
352
|
+
def scan iter
|
353
|
+
return nil unless iter.valid?
|
354
|
+
buf = nil
|
355
|
+
|
356
|
+
while iter.valid? and ["\n", "\r"].include? iter.get
|
357
|
+
buf = iter.concat(buf, +iter)
|
358
|
+
end
|
359
|
+
|
360
|
+
if buf then create_match iter, buf
|
361
|
+
else nil
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
def inspect
|
366
|
+
"[EOL]"
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
ShortcutsMixin.register_shortcut( {
|
371
|
+
:char => CharParser,
|
372
|
+
:string => StringParser,
|
373
|
+
:any_char => AnycharParser,
|
374
|
+
:alnum_char => AlnumParser,
|
375
|
+
:alpha_char => AlphaParser,
|
376
|
+
:blank_char => BlankParser,
|
377
|
+
:control_char => ControlParser,
|
378
|
+
:digit => DigitParser,
|
379
|
+
:graph_char => GraphParser,
|
380
|
+
:lower_char => LowerParser,
|
381
|
+
:print_char => PrintParser,
|
382
|
+
:punct_char => PunctParser,
|
383
|
+
:space_char => SpaceParser,
|
384
|
+
:upper_char => UpperParser,
|
385
|
+
:hex_digit => HexDigitParser,
|
386
|
+
:eol => EOLParser,
|
387
|
+
} )
|
388
|
+
|
389
|
+
end
|
390
|
+
|
391
|
+
end
|
392
|
+
|
393
|
+
##
|
394
|
+
# Register String as a POD with the CharParser and StringParser.
|
395
|
+
Spectre::register_POD(String) do |str|
|
396
|
+
str.length == 1 ?
|
397
|
+
Spectre::StringParsing::CharParser.new(str) :
|
398
|
+
Spectre::StringParsing::StringParser.new(str)
|
399
|
+
end
|
400
|
+
|