-
Notifications
You must be signed in to change notification settings - Fork 95
/
regression_spec.rb
183 lines (163 loc) · 5.38 KB
/
regression_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# Encoding: UTF-8
require File.dirname(__FILE__) + '/../spec_helper'
require 'parslet'
describe "Regressions from real examples" do
# This parser piece produces on the left a subtree that is keyed (a hash)
# and on the right a subtree that is a repetition of such subtrees. I've
# for now decided that these would merge into the repetition such that the
# return value is an array. This avoids maybe loosing keys/values in a
# hash merge.
#
class ArgumentListParser
include Parslet
rule :argument_list do
expression.as(:argument) >>
(comma >> expression.as(:argument)).repeat
end
rule :expression do
string
end
rule :string do
str('"') >>
(
str('\\') >> any |
str('"').absent? >> any
).repeat.as(:string) >>
str('"') >> space?
end
rule :comma do
str(',') >> space?
end
rule :space? do
space.maybe
end
rule :space do
match("[ \t]").repeat(1)
end
def parse(str)
argument_list.parse(str)
end
end
describe ArgumentListParser do
let(:instance) { ArgumentListParser.new }
it "should have method expression" do
instance.should respond_to(:expression)
end
it 'should parse "arg1", "arg2"' do
result = ArgumentListParser.new.parse('"arg1", "arg2"')
result.should have(2).elements
result.each do |r|
r[:argument]
end
end
it 'should parse "arg1", "arg2", "arg3"' do
result = ArgumentListParser.new.parse('"arg1", "arg2", "arg3"')
result.should have(3).elements
result.each do |r|
r[:argument]
end
end
end
class ParensParser < Parslet::Parser
rule(:balanced) {
str('(').as(:l) >> balanced.maybe.as(:m) >> str(')').as(:r)
}
root(:balanced)
end
describe ParensParser do
let(:instance) { ParensParser.new }
context "statefulness: trying several expressions in sequence" do
it "should not be stateful" do
# NOTE: Since you've come here to read this, I'll explain why
# this is broken and not fixed: You're looking at the tuning branch,
# which rewrites a bunch of stuff - so I have failing tests to
# remind me of what is left to be done. And to remind you not to
# trust this code.
instance.parse('(())')
lambda {
instance.parse('((()))')
instance.parse('(((())))')
}.should_not raise_error(Parslet::ParseFailed)
end
end
context "expression '(())'" do
let(:result) { instance.parse('(())') }
it "should yield a doubly nested hash" do
result.should be_a(Hash)
result.should have_key(:m)
result[:m].should be_a(Hash) # This was an array earlier
end
context "inner hash" do
let(:inner) { result[:m] }
it "should have nil as :m" do
inner[:m].should be_nil
end
end
end
end
class ALanguage < Parslet::Parser
root(:expressions)
rule(:expressions) { (line >> eol).repeat(1) | line }
rule(:line) { space? >> an_expression.as(:exp).repeat }
rule(:an_expression) { str('a').as(:a) >> space? }
rule(:eol) { space? >> match["\n\r"].repeat(1) >> space? }
rule(:space?) { space.repeat }
rule(:space) { multiline_comment.as(:multi) | line_comment.as(:line) | str(' ') }
rule(:line_comment) { str('//') >> (match["\n\r"].absent? >> any).repeat }
rule(:multiline_comment) { str('/*') >> (str('*/').absent? >> any).repeat >> str('*/') }
end
describe ALanguage do
def remove_indent(s)
s.to_s.lines.map { |l| l.chomp.strip }.join("\n")
end
it "should count lines correctly" do
begin
subject.parse('
a
a a a
aaa // ff
/*
a
*/
b
')
rescue => ex
end
remove_indent(subject.error_tree).should == remove_indent(%q(
`- Unknown error in (LINE EOL){1, } / LINE
|- Failed to match sequence (LINE EOL) at line 8 char 11.
| `- Failed to match sequence (SPACE? [\n\r]{1, }) at line 8 char 11.
| `- Expected at least 1 of [\n\r] at line 8 char 11.
| `- Failed to match [\n\r] at line 8 char 11.
`- Unknown error in SPACE? exp:AN_EXPRESSION{0, }
`- Failed to match sequence (a:'a' SPACE?) at line 8 char 11.
`- Expected "a", but got "b" at line 8 char 11.).strip)
end
end
class BLanguage < Parslet::Parser
root :expression
rule(:expression) { b.as(:one) >> b.as(:two) }
rule(:b) { str('b') }
end
describe BLanguage do
it "should parse 'bb'" do
subject.should parse('bb').as(:one => 'b', :two => 'b')
end
it "should transform with binding constraint" do
transform = Parslet::Transform.new do |t|
t.rule(:one => simple(:b), :two => simple(:b)) { :ok }
end
transform.apply(subject.parse('bb')).should == :ok
end
end
class UnicodeLanguage < Parslet::Parser
root :gobble
rule(:gobble) { any.repeat }
end
describe UnicodeLanguage do
it "should parse UTF-8 strings" do
subject.should parse('éèäöü').as('éèäöü')
subject.should parse('RubyKaigi2009のテーマは、「変わる/変える」です。 前回の').as('RubyKaigi2009のテーマは、「変わる/変える」です。 前回の')
end
end
end