rattler 0.2.0 → 0.2.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/README.rdoc +2 -0
- data/bin/rtlr +8 -5
- data/bin/rtlr.bat +0 -0
- data/features/command_line/dest_option.feature +34 -0
- data/features/command_line/output_option.feature +31 -0
- data/features/command_line/parser_generator.feature +80 -0
- data/features/grammar/word_literal.feature +3 -3
- data/features/step_definitions/cli_steps.rb +3 -0
- data/features/step_definitions/{rattler_steps.rb → grammar_steps.rb} +0 -0
- data/features/support/env.rb +1 -0
- data/lib/rattler.rb +2 -2
- data/lib/rattler/back_end/parser_generator.rb +1 -0
- data/lib/rattler/back_end/parser_generator/one_or_more_generator.rb +17 -28
- data/lib/rattler/back_end/parser_generator/repeat_generating.rb +69 -0
- data/lib/rattler/back_end/parser_generator/rule_generator.rb +1 -1
- data/lib/rattler/back_end/parser_generator/zero_or_more_generator.rb +9 -63
- data/lib/rattler/grammar/grammar_parser.rb +1 -1
- data/lib/rattler/grammar/metagrammar.rb +63 -73
- data/lib/rattler/grammar/rattler.rtlr +5 -13
- data/lib/rattler/parsers/combining.rb +7 -0
- data/lib/rattler/parsers/match_joining.rb +7 -0
- data/lib/rattler/parsers/predicate.rb +7 -0
- data/lib/rattler/parsers/sequence.rb +1 -1
- data/lib/rattler/parsers/token.rb +1 -1
- data/lib/rattler/runner.rb +6 -0
- data/lib/rattler/runtime.rb +1 -1
- data/lib/rattler/runtime/extended_packrat_parser.rb +62 -0
- data/lib/rattler/runtime/packrat_parser.rb +46 -12
- data/lib/rattler/runtime/parse_failure.rb +1 -1
- data/lib/rattler/util/parser_spec_helper.rb +1 -0
- data/spec/rattler/back_end/parser_generator/one_or_more_generator_spec.rb +14 -4
- data/spec/rattler/back_end/parser_generator/zero_or_more_generator_spec.rb +12 -2
- data/spec/rattler/runtime/extended_packrat_parser_spec.rb +36 -0
- data/spec/rattler/runtime/packrat_parser_spec.rb +1 -1
- data/spec/rattler/runtime/shared_parser_examples.rb +29 -0
- metadata +462 -79
- data/lib/rattler/runtime/wdm_parser.rb +0 -83
- data/spec/rattler/runtime/wdm_parser_spec.rb +0 -21
@@ -53,7 +53,18 @@ module Rattler
|
|
53
53
|
# @private
|
54
54
|
def match_requires #:nodoc:
|
55
55
|
a0 = []
|
56
|
-
while r =
|
56
|
+
while r = begin
|
57
|
+
p0 = @scanner.pos
|
58
|
+
begin
|
59
|
+
@scanner.skip(/(?>(?>(?>[[:space:]])+|(?>\#)(?>(?>[^\n])*))*)(?>(?>require)(?>(?![[:alnum:]_])))/) &&
|
60
|
+
(r0_0 = match(:literal)) &&
|
61
|
+
match(:eol) &&
|
62
|
+
r0_0
|
63
|
+
end || begin
|
64
|
+
@scanner.pos = p0
|
65
|
+
false
|
66
|
+
end
|
67
|
+
end
|
57
68
|
a0 << r
|
58
69
|
end
|
59
70
|
({:requires => select_captures(a0)})
|
@@ -61,91 +72,70 @@ module Rattler
|
|
61
72
|
|
62
73
|
# @private
|
63
74
|
def match_module_decl #:nodoc:
|
64
|
-
match(:parser_decl) ||
|
65
|
-
match(:grammar_decl)
|
66
|
-
end
|
67
|
-
|
68
|
-
# @private
|
69
|
-
def match_includes #:nodoc:
|
70
|
-
a0 = []
|
71
|
-
while r = match(:include_decl)
|
72
|
-
a0 << r
|
73
|
-
end
|
74
|
-
({:includes => select_captures(a0)})
|
75
|
-
end
|
76
|
-
|
77
|
-
# @private
|
78
|
-
def match_require_decl #:nodoc:
|
79
|
-
p0 = @scanner.pos
|
80
75
|
begin
|
81
|
-
@scanner.
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
76
|
+
p0 = @scanner.pos
|
77
|
+
begin
|
78
|
+
@scanner.skip(/(?>(?>(?>[[:space:]])+|(?>\#)(?>(?>[^\n])*))*)(?>(?>parser)(?>(?![[:alnum:]_])))/) &&
|
79
|
+
(r0_0 = match(:constant)) &&
|
80
|
+
(r0_1 = ((r = begin
|
81
|
+
p1 = @scanner.pos
|
82
|
+
begin
|
83
|
+
@scanner.skip(/(?>(?>(?>[[:space:]])+|(?>\#)(?>(?>[^\n])*))*)(?><)/) &&
|
84
|
+
match(:constant)
|
85
|
+
end || begin
|
86
|
+
@scanner.pos = p1
|
87
|
+
false
|
88
|
+
end
|
89
|
+
end) ? [r] : [])) &&
|
90
|
+
match(:eol) &&
|
91
|
+
(parser_decl r0_0, r0_1)
|
92
|
+
end || begin
|
93
|
+
@scanner.pos = p0
|
94
|
+
false
|
95
|
+
end
|
96
|
+
end ||
|
94
97
|
begin
|
95
|
-
@scanner.
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
end
|
106
|
-
end) ? [r] : [])) &&
|
107
|
-
match(:eol) &&
|
108
|
-
(parser_decl r0_0, r0_1)
|
109
|
-
end || begin
|
110
|
-
@scanner.pos = p0
|
111
|
-
false
|
98
|
+
p0 = @scanner.pos
|
99
|
+
begin
|
100
|
+
@scanner.skip(/(?>(?>(?>[[:space:]])+|(?>\#)(?>(?>[^\n])*))*)(?>(?>grammar)(?>(?![[:alnum:]_])))/) &&
|
101
|
+
(r0_0 = match(:constant)) &&
|
102
|
+
match(:eol) &&
|
103
|
+
({:grammar_name => r0_0})
|
104
|
+
end || begin
|
105
|
+
@scanner.pos = p0
|
106
|
+
false
|
107
|
+
end
|
112
108
|
end
|
113
109
|
end
|
114
110
|
|
115
111
|
# @private
|
116
|
-
def
|
117
|
-
|
118
|
-
begin
|
119
|
-
@scanner.
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
112
|
+
def match_includes #:nodoc:
|
113
|
+
a0 = []
|
114
|
+
while r = begin
|
115
|
+
p0 = @scanner.pos
|
116
|
+
begin
|
117
|
+
@scanner.skip(/(?>(?>(?>[[:space:]])+|(?>\#)(?>(?>[^\n])*))*)(?>(?>include)(?>(?![[:alnum:]_])))/) &&
|
118
|
+
(r0_0 = match(:constant)) &&
|
119
|
+
match(:eol) &&
|
120
|
+
r0_0
|
121
|
+
end || begin
|
122
|
+
@scanner.pos = p0
|
123
|
+
false
|
124
|
+
end
|
126
125
|
end
|
127
|
-
|
128
|
-
|
129
|
-
# @private
|
130
|
-
def match_include_decl #:nodoc:
|
131
|
-
p0 = @scanner.pos
|
132
|
-
begin
|
133
|
-
@scanner.skip(/(?>(?>(?>[[:space:]])+|(?>\#)(?>(?>[^\n])*))*)(?>(?>include)(?>(?![[:alnum:]_])))/) &&
|
134
|
-
(r0_0 = match(:constant)) &&
|
135
|
-
match(:eol) &&
|
136
|
-
r0_0
|
137
|
-
end || begin
|
138
|
-
@scanner.pos = p0
|
139
|
-
false
|
126
|
+
a0 << r
|
140
127
|
end
|
128
|
+
({:includes => select_captures(a0)})
|
141
129
|
end
|
142
130
|
|
143
131
|
# @private
|
144
132
|
def match_rules #:nodoc:
|
145
133
|
a0 = []
|
146
|
-
while r =
|
147
|
-
|
148
|
-
|
134
|
+
while r = begin
|
135
|
+
match(:directive) ||
|
136
|
+
match(:rule) ||
|
137
|
+
match(:block_close)
|
138
|
+
end
|
149
139
|
a0 << r
|
150
140
|
end
|
151
141
|
Rules.parsed(select_captures(a0)) unless a0.empty?
|
@@ -10,24 +10,16 @@ include Rattler::Parsers
|
|
10
10
|
|
11
11
|
heading <- requires module_decl? includes {|r,m,i| heading r, m, i }
|
12
12
|
|
13
|
-
requires <-
|
13
|
+
requires <- (~`require` literal ~eol)* {|r| {:requires => r} }
|
14
14
|
|
15
|
-
module_decl <-
|
15
|
+
module_decl <- ~`parser` constant (~'<' constant)? ~eol {|p,b| parser_decl p, b }
|
16
|
+
| ~`grammar` constant ~eol {|g| {:grammar_name => g} }
|
16
17
|
|
17
|
-
includes <-
|
18
|
-
|
19
|
-
require_decl <- ~`require` literal ~eol
|
20
|
-
|
21
|
-
parser_decl <- ~`parser` constant (~'<' constant)? ~eol {|p,b| parser_decl p, b }
|
22
|
-
|
23
|
-
grammar_decl <- ~`grammar` constant ~eol {|g| {:grammar_name => g} }
|
24
|
-
|
25
|
-
include_decl <- ~`include` constant ~eol
|
18
|
+
includes <- (~`include` constant ~eol)* {|i| {:includes => i} }
|
26
19
|
|
27
20
|
rules <- (directive | rule | block_close)+ <Rules>
|
28
21
|
|
29
|
-
directive <- ws_directive
|
30
|
-
| wc_directive
|
22
|
+
directive <- ws_directive | wc_directive
|
31
23
|
|
32
24
|
ws_directive <- ws_decl ~'{' {|e| start_ws e }
|
33
25
|
| ws_decl {|e| set_ws e }
|
data/lib/rattler/runner.rb
CHANGED
data/lib/rattler/runtime.rb
CHANGED
@@ -16,7 +16,7 @@ module Rattler
|
|
16
16
|
autoload :Parser, 'rattler/runtime/parser'
|
17
17
|
autoload :RecursiveDescentParser, 'rattler/runtime/recursive_descent_parser'
|
18
18
|
autoload :PackratParser, 'rattler/runtime/packrat_parser'
|
19
|
-
autoload :
|
19
|
+
autoload :ExtendedPackratParser, 'rattler/runtime/extended_packrat_parser'
|
20
20
|
autoload :ParseNode, 'rattler/runtime/parse_node'
|
21
21
|
autoload :ParserHelper, 'rattler/runtime/parser_helper'
|
22
22
|
autoload :ParseFailure, 'rattler/runtime/parse_failure'
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#
|
2
|
+
# = rattler/runtime/extended_packrat_parser.rb
|
3
|
+
#
|
4
|
+
# Author:: Jason Arhart
|
5
|
+
# Documentation:: Author
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'rattler/runtime'
|
9
|
+
|
10
|
+
module Rattler::Runtime
|
11
|
+
#
|
12
|
+
# +ExtendedPackratParser+ implements the algorithm described by Alessandro
|
13
|
+
# Warth, James R. Douglass, and Todd Millstein for extending packrat parsing
|
14
|
+
# to support left-recursive grammars. It currently only implements the first
|
15
|
+
# part to support direct left recursion.
|
16
|
+
#
|
17
|
+
# @author Jason Arhart
|
18
|
+
#
|
19
|
+
class ExtendedPackratParser < PackratParser
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# @private
|
24
|
+
def apply!(rule_name, key, start_pos) #:nodoc:
|
25
|
+
lr = LR.new
|
26
|
+
m = @memo[key] = MemoEntry.new(lr, start_pos, nil, nil)
|
27
|
+
result = eval_rule rule_name
|
28
|
+
memorize m, result
|
29
|
+
result = grow_lr(rule_name, start_pos, m) if result and lr.detected
|
30
|
+
result
|
31
|
+
end
|
32
|
+
|
33
|
+
def recall(m)
|
34
|
+
if (result = m.result).is_a? LR
|
35
|
+
result.detected = true
|
36
|
+
false
|
37
|
+
else
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# @private
|
43
|
+
def grow_lr(rule_name, start_pos, m) #:nodoc:
|
44
|
+
loop do
|
45
|
+
@scanner.pos = start_pos
|
46
|
+
result = eval_rule(rule_name)
|
47
|
+
return recall(m) if !result or @scanner.pos <= m.end_pos
|
48
|
+
memorize m, result
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# @private
|
53
|
+
class LR
|
54
|
+
def initialize(detected = false)
|
55
|
+
@detected = detected
|
56
|
+
end
|
57
|
+
attr_accessor :detected
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# = rattler/runtime/
|
2
|
+
# = rattler/runtime/packrat_parser.rb
|
3
3
|
#
|
4
4
|
# Author:: Jason Arhart
|
5
5
|
# Documentation:: Author
|
@@ -24,10 +24,13 @@ module Rattler::Runtime
|
|
24
24
|
#
|
25
25
|
def initialize(source, options={})
|
26
26
|
super
|
27
|
-
@
|
28
|
-
@pos_memo = {}
|
27
|
+
@memo = {}
|
29
28
|
end
|
30
29
|
|
30
|
+
# @private
|
31
|
+
alias_method :eval_rule, :apply
|
32
|
+
private :eval_rule
|
33
|
+
|
31
34
|
protected
|
32
35
|
|
33
36
|
# Apply a rule by dispatching to the method associated with the given rule
|
@@ -40,18 +43,49 @@ module Rattler::Runtime
|
|
40
43
|
# @return (see RecursiveDescentParser#apply)
|
41
44
|
#
|
42
45
|
def apply(rule_name)
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
46
|
+
start_pos = @scanner.pos
|
47
|
+
key = [rule_name, start_pos]
|
48
|
+
if @memo.has_key? key
|
49
|
+
recall @memo[key]
|
47
50
|
else
|
48
|
-
|
49
|
-
@pos_memo[key] = @scanner.pos
|
50
|
-
@result_memo[key] = result = super
|
51
|
-
@pos_memo[key] = @scanner.pos
|
51
|
+
apply! rule_name, key, start_pos
|
52
52
|
end
|
53
|
-
result
|
54
53
|
end
|
55
54
|
|
55
|
+
private
|
56
|
+
|
57
|
+
# @private
|
58
|
+
def apply!(rule_name, key, start_pos) #:nodoc:
|
59
|
+
m = @memo[key] = MemoEntry.new(false, start_pos, start_pos, 'left-recursion detected')
|
60
|
+
memorize m, eval_rule(rule_name)
|
61
|
+
end
|
62
|
+
|
63
|
+
# @private
|
64
|
+
def memorize(m, result) #:nodoc:
|
65
|
+
m.end_pos = @scanner.pos
|
66
|
+
m.failure_pos = @failure_pos
|
67
|
+
m.failure_msg = @failure_msg
|
68
|
+
m.result = result
|
69
|
+
end
|
70
|
+
|
71
|
+
# @private
|
72
|
+
def recall(m) #:nodoc:
|
73
|
+
@scanner.pos = m.end_pos
|
74
|
+
@failure_pos = m.failure_pos
|
75
|
+
@failure_msg = m.failure_msg
|
76
|
+
m.result
|
77
|
+
end
|
78
|
+
|
79
|
+
# @private
|
80
|
+
class MemoEntry
|
81
|
+
def initialize(result, end_pos, failure_pos, failure_msg)
|
82
|
+
@result = result
|
83
|
+
@end_pos = end_pos
|
84
|
+
@failure_pos = failure_pos
|
85
|
+
@failure_msg = failure_msg
|
86
|
+
end
|
87
|
+
attr_accessor :result, :end_pos, :failure_pos, :failure_msg
|
88
|
+
end
|
89
|
+
|
56
90
|
end
|
57
91
|
end
|
@@ -9,6 +9,10 @@ describe Rattler::BackEnd::ParserGenerator::OneOrMoreGenerator do
|
|
9
9
|
let(:one_or_more) { OneOrMore[Match[/w+/]] }
|
10
10
|
|
11
11
|
describe '#gen_basic' do
|
12
|
+
|
13
|
+
let :one_or_more do
|
14
|
+
OneOrMore[Choice[Match[/[[:alpha:]]/], Match[/[[:digit:]]/]]]
|
15
|
+
end
|
12
16
|
|
13
17
|
context 'when nested' do
|
14
18
|
it 'generates nested one-or-more matching code' do
|
@@ -16,7 +20,10 @@ describe Rattler::BackEnd::ParserGenerator::OneOrMoreGenerator do
|
|
16
20
|
should == (<<-CODE).strip
|
17
21
|
begin
|
18
22
|
a = []
|
19
|
-
while r =
|
23
|
+
while r = begin
|
24
|
+
@scanner.scan(/[[:alpha:]]/) ||
|
25
|
+
@scanner.scan(/[[:digit:]]/)
|
26
|
+
end
|
20
27
|
a << r
|
21
28
|
end
|
22
29
|
a unless a.empty?
|
@@ -30,7 +37,10 @@ end
|
|
30
37
|
top_level_code {|g| g.gen_basic one_or_more }.
|
31
38
|
should == (<<-CODE).strip
|
32
39
|
a = []
|
33
|
-
while r =
|
40
|
+
while r = begin
|
41
|
+
@scanner.scan(/[[:alpha:]]/) ||
|
42
|
+
@scanner.scan(/[[:digit:]]/)
|
43
|
+
end
|
34
44
|
a << r
|
35
45
|
end
|
36
46
|
a unless a.empty?
|
@@ -49,7 +59,7 @@ a unless a.empty?
|
|
49
59
|
end
|
50
60
|
|
51
61
|
context 'when top-level' do
|
52
|
-
it 'generates
|
62
|
+
it 'generates nested one-or-more positive lookahead code' do
|
53
63
|
top_level_code {|g| g.gen_assert one_or_more }.
|
54
64
|
should == '@scanner.skip(/(?=w+)/) && true'
|
55
65
|
end
|
@@ -66,7 +76,7 @@ a unless a.empty?
|
|
66
76
|
end
|
67
77
|
|
68
78
|
context 'when top-level' do
|
69
|
-
it 'generates
|
79
|
+
it 'generates nested one-or-more negative lookahead code' do
|
70
80
|
top_level_code {|g| g.gen_disallow one_or_more }.
|
71
81
|
should == '@scanner.skip(/(?!w+)/) && true'
|
72
82
|
end
|