Spectre 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|