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,68 @@
|
|
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 generic Negation classes.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'spectre/base/parser'
|
21
|
+
require 'spectre/base/operators'
|
22
|
+
|
23
|
+
module Spectre
|
24
|
+
|
25
|
+
##
|
26
|
+
# Keeps all the generic Negation classes.
|
27
|
+
#
|
28
|
+
module Negations
|
29
|
+
|
30
|
+
##
|
31
|
+
# Negates a Parser that will consume a single token only.
|
32
|
+
#
|
33
|
+
class NegatedSingleTokenParser < Operators::Negation
|
34
|
+
include Parser
|
35
|
+
|
36
|
+
def scan iter
|
37
|
+
token = iter.get
|
38
|
+
ret = @node.left.parse iter
|
39
|
+
backtrack iter
|
40
|
+
|
41
|
+
if ret
|
42
|
+
nil
|
43
|
+
else
|
44
|
+
+iter
|
45
|
+
create_match iter, token
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Negates a Paser that returns only 0 length matches.
|
52
|
+
#
|
53
|
+
class NegatedZeroTokenParser < Operators::Negation
|
54
|
+
include Parser
|
55
|
+
|
56
|
+
def scan iter
|
57
|
+
ret = @node.left.parse iter
|
58
|
+
backtrack iter
|
59
|
+
|
60
|
+
if ret then nil
|
61
|
+
else Match.new 0, iter.empty
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
@@ -0,0 +1,172 @@
|
|
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 generic Primitives.
|
18
|
+
#
|
19
|
+
# Registered shortcuts for generic primitive Parsers:
|
20
|
+
#
|
21
|
+
#
|
22
|
+
# RangeParser :range
|
23
|
+
# SequenceParser :seq
|
24
|
+
# NothingParser :nothing
|
25
|
+
# EOLParser :eol
|
26
|
+
# EOIParser :eoi
|
27
|
+
#
|
28
|
+
#
|
29
|
+
# See also ShortcutsMixin.
|
30
|
+
#
|
31
|
+
|
32
|
+
require 'spectre/base/parser'
|
33
|
+
require 'spectre/base/grammar'
|
34
|
+
|
35
|
+
module Spectre
|
36
|
+
|
37
|
+
##
|
38
|
+
# Matches a range of tokens.
|
39
|
+
# Can be supplied with anything that implements the methods +#include?+,
|
40
|
+
# +#first+ and +#last+.
|
41
|
+
#
|
42
|
+
# Shortcut: +range+.
|
43
|
+
#
|
44
|
+
class RangeParser
|
45
|
+
include Parser
|
46
|
+
|
47
|
+
def initialize range
|
48
|
+
[:include?, :first, :last].each { |meth|
|
49
|
+
raise "#{range.inspect} does not respond to ##{meth}, which is required for RangeParser" unless
|
50
|
+
range.respond_to? meth
|
51
|
+
}
|
52
|
+
|
53
|
+
super()
|
54
|
+
@range = range
|
55
|
+
end
|
56
|
+
|
57
|
+
def negation; Negations::NegatedSingleTokenParser.new; end
|
58
|
+
|
59
|
+
def scan iter
|
60
|
+
return nil unless iter.valid?
|
61
|
+
token = +iter
|
62
|
+
|
63
|
+
if @range.include? token
|
64
|
+
create_match iter, token
|
65
|
+
else
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def inspect
|
71
|
+
"[range:#{@range.first}..#{@range.last}]"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# Matches a sequence of tokens.
|
77
|
+
#
|
78
|
+
# Can be used on anything that is convertible to an array of tokens, i.e. the given
|
79
|
+
# object must either respond to +#split(Regexp)+ or +#to_a+.
|
80
|
+
#
|
81
|
+
# NOTE: It's negation is a zero token parser, i.e. +~seq([1,2,3])+ will only
|
82
|
+
# test if the input at that location contains the sequence and return a failure if so
|
83
|
+
# and a successful match of length 0 if not.
|
84
|
+
#
|
85
|
+
# Shortcut: +seq+.
|
86
|
+
#
|
87
|
+
class SequenceParser
|
88
|
+
include Parser
|
89
|
+
|
90
|
+
def initialize seq
|
91
|
+
raise "given object neither responds to #to_a nor #split, one of which is required for SequenceParser" unless
|
92
|
+
seq.respond_to? :to_a or seq.respond_to? :split
|
93
|
+
|
94
|
+
super()
|
95
|
+
@seq = seq
|
96
|
+
@seq = seq.respond_to?(:split) ? seq.split(//) : seq.to_a
|
97
|
+
end
|
98
|
+
|
99
|
+
def negation; Negations::NegatedZeroTokenParser.new; end
|
100
|
+
|
101
|
+
def scan iter
|
102
|
+
return nil unless iter.valid?
|
103
|
+
buf = iter.empty
|
104
|
+
|
105
|
+
@seq.each { |s|
|
106
|
+
token = iter.get
|
107
|
+
return nil if not iter.valid? or token != s
|
108
|
+
+iter
|
109
|
+
buf = iter.concat buf, token
|
110
|
+
}
|
111
|
+
|
112
|
+
create_match iter, buf
|
113
|
+
end
|
114
|
+
|
115
|
+
def inspect
|
116
|
+
"[seq:#{@seq.inject(''){ |memo,i| memo + i + ', ' }[0..-3] }]"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
##
|
121
|
+
# Matches nothing, i.e. it always fails.
|
122
|
+
# Shortcut: +nothing+.
|
123
|
+
#
|
124
|
+
class NothingParser
|
125
|
+
include Parser
|
126
|
+
|
127
|
+
def negation; Negations::NegatedZeroTokenParser.new; end
|
128
|
+
|
129
|
+
def scan iter
|
130
|
+
nil
|
131
|
+
end
|
132
|
+
|
133
|
+
def inspect
|
134
|
+
"[nothing]"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
# Matches the end of the input.
|
140
|
+
# Shortcut: +eoi+.
|
141
|
+
#
|
142
|
+
class EOIParser
|
143
|
+
include Parser
|
144
|
+
|
145
|
+
def negation; Negations::NegatedZeroTokenParser.new; end
|
146
|
+
|
147
|
+
def scan iter
|
148
|
+
iter.valid? ? nil : Match.new(0, iter.empty)
|
149
|
+
end
|
150
|
+
|
151
|
+
def inspect
|
152
|
+
"[EOI]"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
ShortcutsMixin.register_shortcut( {
|
157
|
+
:range => RangeParser,
|
158
|
+
:seq => SequenceParser,
|
159
|
+
:nothing => NothingParser,
|
160
|
+
:eoi => EOIParser,
|
161
|
+
} )
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
##
|
166
|
+
# Register Array as a POD with the RangeParser.
|
167
|
+
Spectre::register_POD(Array) { |arr| Spectre::RangeParser.new arr }
|
168
|
+
|
169
|
+
##
|
170
|
+
# Register Range as a POD with the RangeParser.
|
171
|
+
Spectre::register_POD(Range) { |arr| Spectre::RangeParser.new arr }
|
172
|
+
|
@@ -0,0 +1,43 @@
|
|
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 pre-defined semantic action classes.
|
18
|
+
#
|
19
|
+
|
20
|
+
module Spectre
|
21
|
+
|
22
|
+
##
|
23
|
+
# This semantic action will be instantiated everytime a symbol is given to
|
24
|
+
# +Parser#[]+.
|
25
|
+
# It stores the value it receives inside the closure.
|
26
|
+
#
|
27
|
+
class ClosureAction
|
28
|
+
|
29
|
+
##
|
30
|
+
# +sym+ is the Symbol under which the parsed value will be stored in the Closure.
|
31
|
+
#
|
32
|
+
def initialize sym
|
33
|
+
@sym = sym
|
34
|
+
end
|
35
|
+
|
36
|
+
def call match, closure
|
37
|
+
raise "no closure set for ClosureAction" unless closure
|
38
|
+
closure[@sym] = match.value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
@@ -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 Parsers and InputIterators for String parsing.
|
18
|
+
# See Spectre::StringParsing.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'spectre/base'
|
22
|
+
require 'spectre/generic'
|
23
|
+
require 'spectre/string/inputiterator'
|
24
|
+
require 'spectre/string/primitives'
|
25
|
+
require 'spectre/string/additionals'
|
26
|
+
require 'spectre/string/directives'
|
27
|
+
|
28
|
+
module Spectre
|
29
|
+
|
30
|
+
module StringParsing
|
31
|
+
|
32
|
+
include Spectre
|
33
|
+
|
34
|
+
##
|
35
|
+
# Shortcut that parses the +string+ using the +parser+ and a StringInputIterator.
|
36
|
+
# +pre_skip+ will be passed on to +Node#parse+.
|
37
|
+
# If you mix the StringParsing module into your class, it will gain this method.
|
38
|
+
#
|
39
|
+
def parse string, parser, pre_skip = true
|
40
|
+
parser.to_p.parse StringInputIterator.new(string), pre_skip
|
41
|
+
end
|
42
|
+
|
43
|
+
class << self
|
44
|
+
##
|
45
|
+
# Shortcut that parses the +string+ using the +parser+ and a StringInputIterator.
|
46
|
+
# +pre_skip+ will be passed on to +Node#parse+.
|
47
|
+
# If you mix the StringParsing module into your class, it will gain this method.
|
48
|
+
#
|
49
|
+
def parse string, parser, pre_skip = true
|
50
|
+
parser.to_p.parse StringInputIterator.new(string), pre_skip
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
@@ -0,0 +1,80 @@
|
|
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 Parsers for StringParsing that are used less commonly and are
|
18
|
+
# more complex than the primitives.
|
19
|
+
# Registered shortcuts for additional String Parsers:
|
20
|
+
#
|
21
|
+
# RegExpParser :rex
|
22
|
+
#
|
23
|
+
# See also ShortcutsMixin.
|
24
|
+
#
|
25
|
+
|
26
|
+
require 'spectre/base/parser'
|
27
|
+
require 'spectre/base/grammar'
|
28
|
+
|
29
|
+
module Spectre
|
30
|
+
|
31
|
+
module StringParsing
|
32
|
+
|
33
|
+
##
|
34
|
+
# Matches a regular expression.
|
35
|
+
# Naturally this Parser is an implicit lexeme, i.e. it will ignore any skippers set
|
36
|
+
# on the InputIterator.
|
37
|
+
#
|
38
|
+
# NOTE: In order to calculate the Match, the regular expression will be applied to
|
39
|
+
# the whole rest of the InputIterator.
|
40
|
+
#
|
41
|
+
# Shortcut: +rex+.
|
42
|
+
#
|
43
|
+
class RegExpParser
|
44
|
+
include Parser
|
45
|
+
|
46
|
+
def initialize rex
|
47
|
+
super()
|
48
|
+
@rex = rex
|
49
|
+
end
|
50
|
+
|
51
|
+
def scan iter
|
52
|
+
m = @rex.match iter.rest
|
53
|
+
|
54
|
+
if m and m.pre_match.empty?
|
55
|
+
backtrack iter
|
56
|
+
data = nil
|
57
|
+
iter.ignore_skipper { |i| data = i + m[0].length }
|
58
|
+
create_match iter, data
|
59
|
+
else
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def inspect
|
65
|
+
"[regexp:#{@rex.inspect}]"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
ShortcutsMixin.register_shortcut :rex => RegExpParser
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# Register regular expressions as a POD with the RegExpParser.
|
77
|
+
Spectre::register_POD(Regexp) do |rex|
|
78
|
+
Spectre::StringParsing::RegExpParser.new rex
|
79
|
+
end
|
80
|
+
|
@@ -0,0 +1,51 @@
|
|
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 Directives for String parsing.
|
18
|
+
# Registered shortcuts for standard String Directives:
|
19
|
+
#
|
20
|
+
# CharParser :char
|
21
|
+
#
|
22
|
+
# See also ShortcutsMixin.
|
23
|
+
#
|
24
|
+
|
25
|
+
require 'spectre/base/directive'
|
26
|
+
require 'spectre/base/grammar'
|
27
|
+
require 'spectre/generic/directives'
|
28
|
+
|
29
|
+
module Spectre
|
30
|
+
|
31
|
+
module StringParsing
|
32
|
+
|
33
|
+
##
|
34
|
+
# Converts the input to lower-case.
|
35
|
+
#
|
36
|
+
# Shortcut: +lower_d+
|
37
|
+
#
|
38
|
+
class LowerDirective < Directive
|
39
|
+
transformation! lambda { |token,is| token.downcase }
|
40
|
+
|
41
|
+
def inspect
|
42
|
+
"[lower_d:#{@left.inspect}]"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
ShortcutsMixin.register_shortcut :lower_d => LowerDirective
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|