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,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
|
+
|