rip-parser 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +15 -0
- data/.gitignore +9 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +3 -0
- data/LICENSE.md +9 -0
- data/README.md +13 -0
- data/Rakefile +1 -0
- data/bin/console +8 -0
- data/bin/setup +8 -0
- data/legacy/normalizer.rb +279 -0
- data/legacy/parser_spec.rb +999 -0
- data/legacy/rules.rb +250 -0
- data/legacy/rules_spec.rb +1700 -0
- data/rip-parser.gemspec +20 -0
- data/source/rip/parser/about.rb +9 -0
- data/source/rip/parser/error.rb +36 -0
- data/source/rip/parser/grammar.rb +23 -0
- data/source/rip/parser/keywords.rb +84 -0
- data/source/rip/parser/location.rb +47 -0
- data/source/rip/parser/node.rb +115 -0
- data/source/rip/parser/rules/assignment.rb +23 -0
- data/source/rip/parser/rules/binary_condition.rb +24 -0
- data/source/rip/parser/rules/character.rb +40 -0
- data/source/rip/parser/rules/class.rb +60 -0
- data/source/rip/parser/rules/common.rb +47 -0
- data/source/rip/parser/rules/date_time.rb +31 -0
- data/source/rip/parser/rules/expression.rb +122 -0
- data/source/rip/parser/rules/import.rb +23 -0
- data/source/rip/parser/rules/invocation.rb +15 -0
- data/source/rip/parser/rules/invocation_index.rb +15 -0
- data/source/rip/parser/rules/keyword.rb +12 -0
- data/source/rip/parser/rules/lambda.rb +45 -0
- data/source/rip/parser/rules/list.rb +18 -0
- data/source/rip/parser/rules/map.rb +15 -0
- data/source/rip/parser/rules/module.rb +29 -0
- data/source/rip/parser/rules/number.rb +21 -0
- data/source/rip/parser/rules/pair.rb +13 -0
- data/source/rip/parser/rules/property.rb +33 -0
- data/source/rip/parser/rules/range.rb +15 -0
- data/source/rip/parser/rules/reference.rb +16 -0
- data/source/rip/parser/rules/string.rb +41 -0
- data/source/rip/parser/rules/unit.rb +17 -0
- data/source/rip/parser/rules.rb +7 -0
- data/source/rip/parser/utilities/normalizer.rb +638 -0
- data/source/rip/parser.rb +24 -0
- data/source/rip-parser.rb +1 -0
- data/spec/fixtures/syntax_sample.rip +96 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/helpers.rb +57 -0
- data/spec/support/parslet.rb +1 -0
- data/spec/support/shared_examples.rb +9 -0
- data/spec/unit/rip/parser/grammar_spec.rb +8 -0
- data/spec/unit/rip/parser/location_spec.rb +89 -0
- data/spec/unit/rip/parser/node_spec.rb +157 -0
- data/spec/unit/rip/parser/rules/assignment_spec.rb +59 -0
- data/spec/unit/rip/parser/rules/binary_condition_spec.rb +41 -0
- data/spec/unit/rip/parser/rules/character_spec.rb +29 -0
- data/spec/unit/rip/parser/rules/class_spec.rb +181 -0
- data/spec/unit/rip/parser/rules/common_spec.rb +88 -0
- data/spec/unit/rip/parser/rules/date_time_spec.rb +61 -0
- data/spec/unit/rip/parser/rules/expression_spec.rb +95 -0
- data/spec/unit/rip/parser/rules/import_spec.rb +64 -0
- data/spec/unit/rip/parser/rules/invocation_index_spec.rb +46 -0
- data/spec/unit/rip/parser/rules/invocation_spec.rb +46 -0
- data/spec/unit/rip/parser/rules/keyword_spec.rb +17 -0
- data/spec/unit/rip/parser/rules/lambda_spec.rb +174 -0
- data/spec/unit/rip/parser/rules/list_spec.rb +45 -0
- data/spec/unit/rip/parser/rules/map_spec.rb +36 -0
- data/spec/unit/rip/parser/rules/module_spec.rb +63 -0
- data/spec/unit/rip/parser/rules/number_spec.rb +40 -0
- data/spec/unit/rip/parser/rules/pair_spec.rb +25 -0
- data/spec/unit/rip/parser/rules/property_spec.rb +27 -0
- data/spec/unit/rip/parser/rules/range_spec.rb +37 -0
- data/spec/unit/rip/parser/rules/reference_spec.rb +43 -0
- data/spec/unit/rip/parser/rules/string_spec.rb +166 -0
- data/spec/unit/rip/parser/rules/unit_spec.rb +17 -0
- data/spec/unit/rip/parser_spec.rb +106 -0
- metadata +192 -0
@@ -0,0 +1,96 @@
|
|
1
|
+
# start with a comment
|
2
|
+
|
3
|
+
import "something"
|
4
|
+
import(:other)
|
5
|
+
|
6
|
+
answers = [ 42, +42, -42 ]; deserts = [ 3.14, +3.14, -3.14 ]
|
7
|
+
|
8
|
+
CAT = System.String.upper_case(:cat)
|
9
|
+
|
10
|
+
# heredoc
|
11
|
+
<<FOO
|
12
|
+
|
13
|
+
multi-line
|
14
|
+
|
15
|
+
\ttry one today!
|
16
|
+
|
17
|
+
FOO
|
18
|
+
|
19
|
+
|
20
|
+
/reg-ex/
|
21
|
+
|
22
|
+
|
23
|
+
[
|
24
|
+
`v,
|
25
|
+
`\n,
|
26
|
+
`\u0123
|
27
|
+
]
|
28
|
+
|
29
|
+
|
30
|
+
please-dont-ever-do-this = (((((foo).bar()).baz)))
|
31
|
+
|
32
|
+
factorial = (n, m = 0) -> {
|
33
|
+
if (n.==(0)) {
|
34
|
+
m
|
35
|
+
} else {
|
36
|
+
self(n.-(1), n.+(m))
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
answer = 42
|
41
|
+
super-answer = factorial(answer)
|
42
|
+
|
43
|
+
:range: 0..9.map
|
44
|
+
|
45
|
+
map = { :answer: 42 }
|
46
|
+
|
47
|
+
=> { -> { 42 } }
|
48
|
+
-> { :cat }
|
49
|
+
|
50
|
+
lambda = => {
|
51
|
+
(x) -> { x }
|
52
|
+
}
|
53
|
+
|
54
|
+
overload = (n) -> {
|
55
|
+
lambda(n)
|
56
|
+
}
|
57
|
+
|
58
|
+
Person = class {
|
59
|
+
aaa = 3.14
|
60
|
+
|
61
|
+
self.bbb = ~> {
|
62
|
+
@.aaa
|
63
|
+
}
|
64
|
+
|
65
|
+
@.ccc = -> {
|
66
|
+
@.name
|
67
|
+
}
|
68
|
+
|
69
|
+
@.initialize = => {
|
70
|
+
(name<String>) -> {
|
71
|
+
@.name = name
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
Nested = class { }
|
76
|
+
}
|
77
|
+
|
78
|
+
System = class {
|
79
|
+
String = import "./string"
|
80
|
+
}
|
81
|
+
|
82
|
+
"a#{b.c}d"
|
83
|
+
/a#{b.c}d/
|
84
|
+
|
85
|
+
rip_initial_commit = 2012-02-12T00:24:00-0500
|
86
|
+
|
87
|
+
lunch-time = 12:30:00.today()
|
88
|
+
|
89
|
+
space = 100_000km
|
90
|
+
mariana = -10_994km
|
91
|
+
|
92
|
+
foo()
|
93
|
+
list[42]
|
94
|
+
a.b.c
|
95
|
+
|
96
|
+
1.a.+(2.b).-(3.c).*(4.d)
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'pry'
|
2
|
+
|
3
|
+
require_relative '../source/rip-parser'
|
4
|
+
|
5
|
+
pattern = Pathname.new(__dir__).join('support/**/*.rb')
|
6
|
+
Pathname.glob(pattern).each(&method(:require))
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
config.run_all_when_everything_filtered = true
|
10
|
+
config.filter_run :focus => true
|
11
|
+
config.filter_run_excluding :blur => true
|
12
|
+
|
13
|
+
config.expect_with :rspec do |c|
|
14
|
+
c.syntax = :expect
|
15
|
+
end
|
16
|
+
|
17
|
+
config.order = 'random'
|
18
|
+
|
19
|
+
config.color = true
|
20
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module RSpecHelpers
|
2
|
+
def profile_parslet(rip, parslet = :lines)
|
3
|
+
binding.pry
|
4
|
+
|
5
|
+
result = RubyProf.profile do
|
6
|
+
parser(rip).send(parslet).parse_tree
|
7
|
+
end
|
8
|
+
|
9
|
+
result.eliminate_methods!([
|
10
|
+
/Array/,
|
11
|
+
/Class/,
|
12
|
+
/Enumerable/,
|
13
|
+
/Fixnum/,
|
14
|
+
/Hash/,
|
15
|
+
/Kernel/,
|
16
|
+
/Module/,
|
17
|
+
/Object/,
|
18
|
+
/Proc/,
|
19
|
+
/Regexp/,
|
20
|
+
/String/,
|
21
|
+
/Symbol/
|
22
|
+
])
|
23
|
+
|
24
|
+
tree = RubyProf::CallInfoPrinter.new(result)
|
25
|
+
tree.print(STDOUT)
|
26
|
+
end
|
27
|
+
|
28
|
+
def location_for(options = {})
|
29
|
+
origin = options[:origin] || Pathname.pwd
|
30
|
+
offset = options[:offset] || 0
|
31
|
+
line = options[:line] || 1
|
32
|
+
column = options[:column] || 1
|
33
|
+
Rip::Parser::Location.new(origin, offset, line, column)
|
34
|
+
end
|
35
|
+
|
36
|
+
# http://apidock.com/rails/String/strip_heredoc
|
37
|
+
def strip_heredoc(string)
|
38
|
+
indent = string.scan(/^[ \t]*(?=\S)/).min.size
|
39
|
+
string.gsub(/^[ \t]{#{indent}}/, '')
|
40
|
+
end
|
41
|
+
|
42
|
+
def clean_inspect(ast)
|
43
|
+
ast.inspect
|
44
|
+
.gsub(/@\d+/, '')
|
45
|
+
.gsub('\\"', '\'')
|
46
|
+
.gsub(/:0x[0-9a-f]+/, '')
|
47
|
+
.gsub('Rip::Nodes::', '')
|
48
|
+
.gsub('Rip::Utilities::Location ', '')
|
49
|
+
.gsub(/ @location=\#\<([^>]+)>/, '@\1')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
RSpec.configure do |config|
|
54
|
+
config.include RSpecHelpers
|
55
|
+
|
56
|
+
config.extend RSpecHelpers
|
57
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'parslet/rig/rspec'
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Rip::Parser::Location do
|
4
|
+
subject { location }
|
5
|
+
let(:location) { Rip::Parser::Location.new(:rspec, 0, 1, 0) }
|
6
|
+
|
7
|
+
describe '#==' do
|
8
|
+
it 'glosses over superficial differences' do
|
9
|
+
expect(location).to eq(Rip::Parser::Location.new(:rspec, 0, 1, 0))
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'notices important difference in source' do
|
13
|
+
expect(location).not_to eq(location_for(:origin => :cucumber))
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'notices important differences in position' do
|
17
|
+
expect(location).not_to eq(location_for(:offset => 3))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#to_s' do
|
22
|
+
specify { expect(subject.to_s).to eq('rspec:1:0(0)') }
|
23
|
+
|
24
|
+
context 'in another file' do
|
25
|
+
let(:location) { Rip::Parser::Location.new('lib/rip.rip', 47, 8, 3, 5) }
|
26
|
+
|
27
|
+
specify { expect(subject.to_s).to eq('lib/rip.rip:8:3(47..51)') }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#add_character' do
|
32
|
+
let(:new_location) { Rip::Parser::Location.new(:rspec, 5, 1, 5) }
|
33
|
+
|
34
|
+
it 'returns a new location offset by specified characters' do
|
35
|
+
expect(location.add_character(5)).to eq(new_location)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#add_line' do
|
40
|
+
let(:new_location) { Rip::Parser::Location.new(:rspec, 2, 3, 2) }
|
41
|
+
|
42
|
+
it 'returns a new location offset by specified lines' do
|
43
|
+
expect(location.add_line(2)).to eq(new_location)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '.from_slice' do
|
48
|
+
let(:location) { Rip::Parser::Location.from_slice(:rspec, slice) }
|
49
|
+
|
50
|
+
let(:parser) do
|
51
|
+
Class.new(Parslet::Parser) do
|
52
|
+
root :lines
|
53
|
+
|
54
|
+
rule(:lines) { line.repeat }
|
55
|
+
rule(:line) { (as | bs).as(:line) >> eol }
|
56
|
+
|
57
|
+
rule(:as) { a.repeat(3) }
|
58
|
+
rule(:bs) { b.repeat(3) }
|
59
|
+
|
60
|
+
rule(:a) { str('a').as(:a) }
|
61
|
+
rule(:b) { str('b').as(:b) }
|
62
|
+
|
63
|
+
rule(:eol) { str("\n") }
|
64
|
+
end.new
|
65
|
+
end
|
66
|
+
|
67
|
+
let(:slice) { slices[4] }
|
68
|
+
let(:slices) { tree.map(&:values).flatten.map(&:values).flatten }
|
69
|
+
|
70
|
+
let(:source) do
|
71
|
+
strip_heredoc(<<-SOURCE)
|
72
|
+
aaa
|
73
|
+
bbb
|
74
|
+
SOURCE
|
75
|
+
end
|
76
|
+
|
77
|
+
let(:tree) { parser.parse(source) }
|
78
|
+
|
79
|
+
specify { expect(location.origin).to eq(:rspec) }
|
80
|
+
|
81
|
+
specify { expect(location.offset).to eq(5) }
|
82
|
+
|
83
|
+
specify { expect(location.line).to eq(2) }
|
84
|
+
|
85
|
+
specify { expect(location.column).to eq(2) }
|
86
|
+
|
87
|
+
specify { expect(location.length).to eq(1) }
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Rip::Parser::Node do
|
4
|
+
let(:location) { Rip::Parser::Location.new(:rspec, 0, 1, 0) }
|
5
|
+
let(:node) { Rip::Parser::Node.new(location: location, type: :test, answer: 42) }
|
6
|
+
|
7
|
+
describe '#==' do
|
8
|
+
specify { expect(node).to eq(Rip::Parser::Node.new(location: location, type: :test, answer: 42)) }
|
9
|
+
specify { expect(node).to eq(location: location, type: :test, answer: 42) }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#[]' do
|
13
|
+
context 'responding to location' do
|
14
|
+
specify { expect(node[:location]).to eq(location) }
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'symbol key' do
|
18
|
+
specify { expect(node[:answer]).to eq(42) }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'string key' do
|
22
|
+
specify { expect(node['answer']).to eq(42) }
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'missing key' do
|
26
|
+
specify { expect(node[:other]).to be(nil) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#each' do
|
31
|
+
specify do
|
32
|
+
expect do |x|
|
33
|
+
node.each(&x)
|
34
|
+
end.to yield_successive_args([ :location, location ], [ :type, :test ], [ :answer, 42 ])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#key?' do
|
39
|
+
specify { expect(node.key?(:answer)).to be(true) }
|
40
|
+
specify { expect(node.key?(:foo)).to be(false) }
|
41
|
+
|
42
|
+
specify { expect(node.key?('answer')).to be(true) }
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#keys' do
|
46
|
+
specify { expect(node.keys).to match_array([ :answer ]) }
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '#length' do
|
50
|
+
specify { expect(node.length).to eq(node.location.length) }
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#merge' do
|
54
|
+
let(:other_location) { Rip::Parser::Location.new(:rspec, 1, 2, 1) }
|
55
|
+
let(:other) { Rip::Parser::Node.new(location: other_location, type: :other_test, foo: :bar) }
|
56
|
+
|
57
|
+
specify { expect(node.merge(foo: :bar)).to be_a(Rip::Parser::Node) }
|
58
|
+
|
59
|
+
specify { expect(node.merge(answer: :bar).to_h).to eq(location: location, type: :test, answer: :bar) }
|
60
|
+
specify { expect(node.merge(type: :baz, foo: :bar).to_h).to eq(location: location, type: :baz, answer: 42, foo: :bar) }
|
61
|
+
|
62
|
+
specify { expect(node.merge(other).to_h).to eq(location: location, type: :other_test, answer: 42, foo: :bar) }
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#s_expression' do
|
66
|
+
let(:tree) { Rip::Parser::Node.new(location: location, type: :root, children: [ node ]) }
|
67
|
+
|
68
|
+
let(:expected) do
|
69
|
+
{
|
70
|
+
type: :root,
|
71
|
+
children: [
|
72
|
+
{
|
73
|
+
type: :test,
|
74
|
+
answer: 42
|
75
|
+
}
|
76
|
+
]
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
specify { expect(tree.s_expression).to eq(expected) }
|
81
|
+
specify { expect(tree.s_expression).to be_a(Hash) }
|
82
|
+
|
83
|
+
specify { expect(tree.s_expression.keys).to eq([ :type, :children ]) }
|
84
|
+
|
85
|
+
specify { expect(tree.s_expression[:children].first).to be_a(Hash) }
|
86
|
+
end
|
87
|
+
|
88
|
+
describe '#to_h' do
|
89
|
+
specify { expect(node.to_h.keys).to eq([ :location, :type, :answer ]) }
|
90
|
+
|
91
|
+
context 'nested tree' do
|
92
|
+
let(:tree) { Rip::Parser::Node.new(location: location, type: :root, other: node) }
|
93
|
+
|
94
|
+
let(:expected) do
|
95
|
+
{
|
96
|
+
location: location,
|
97
|
+
type: :root,
|
98
|
+
other: {
|
99
|
+
location: location,
|
100
|
+
type: :test,
|
101
|
+
answer: 42
|
102
|
+
}
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
specify { expect(tree.to_h).to eq(expected) }
|
107
|
+
specify { expect(tree.to_h[:other]).to be_a(Hash) }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe '.new' do
|
112
|
+
let(:tree) { Rip::Parser::Node.new(location: location, type: :root, nested: { location: location, type: :nested, foo: :bar }) }
|
113
|
+
|
114
|
+
specify { expect(tree.nested).to be_a(Rip::Parser::Node) }
|
115
|
+
specify { expect(tree.nested.foo).to eq(:bar) }
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'dynamic message lookup' do
|
119
|
+
specify { expect(node).to respond_to(:answer) }
|
120
|
+
specify { expect(node).to_not respond_to(:foo) }
|
121
|
+
|
122
|
+
specify { expect(node.answer).to eq(42) }
|
123
|
+
specify { expect { node.foo }.to raise_error(NoMethodError) }
|
124
|
+
end
|
125
|
+
|
126
|
+
context 'nesting' do
|
127
|
+
let(:root) { Rip::Parser::Node.new(location: location, type: :root, other: node) }
|
128
|
+
|
129
|
+
specify { expect(root.other).to eq(node) }
|
130
|
+
specify { expect(root.other.answer).to eq(42) }
|
131
|
+
|
132
|
+
specify { expect(root).to be_root }
|
133
|
+
specify { expect(root.type).to eq(:root) }
|
134
|
+
|
135
|
+
specify { expect(root).to respond_to(:root?) }
|
136
|
+
specify { expect(root).to respond_to(:test?) }
|
137
|
+
|
138
|
+
context 'nested collection' do
|
139
|
+
let(:nodes) do
|
140
|
+
[
|
141
|
+
Rip::Parser::Node.new(location: location, type: :nested, aaa: 111),
|
142
|
+
Rip::Parser::Node.new(location: location, type: :special, bbb: 222),
|
143
|
+
Rip::Parser::Node.new(location: location, type: :nested, ccc: 333)
|
144
|
+
]
|
145
|
+
end
|
146
|
+
|
147
|
+
let(:root) { Rip::Parser::Node.new(location: location, type: :root, others: nodes) }
|
148
|
+
|
149
|
+
let(:filtered) { nodes.select(&:special?) }
|
150
|
+
|
151
|
+
specify { expect(root.others.sample).to be_a(Rip::Parser::Node) }
|
152
|
+
|
153
|
+
specify { expect(filtered.count).to eq(1) }
|
154
|
+
specify { expect(filtered.first.type).to eq(:special) }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Rip::Parser::Rules::Assignment do
|
4
|
+
class AssignmentParser
|
5
|
+
include Rip::Parser::Rules::Assignment
|
6
|
+
include Rip::Parser::Rules::Module
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:parser) { AssignmentParser.new }
|
10
|
+
|
11
|
+
describe '#reference_assignment' do
|
12
|
+
subject { parser.reference_assignment }
|
13
|
+
|
14
|
+
it { should_not parse('a.b = 42') }
|
15
|
+
|
16
|
+
it do
|
17
|
+
should parse('answer = 42').as(
|
18
|
+
lhs: { reference: 'answer' },
|
19
|
+
location: '=',
|
20
|
+
rhs: {
|
21
|
+
expression_chain: { integer: '42' }
|
22
|
+
}
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
it do
|
27
|
+
should parse('answer = foo.bar').as(
|
28
|
+
lhs: { reference: 'answer' },
|
29
|
+
location: '=',
|
30
|
+
rhs: {
|
31
|
+
expression_chain: [
|
32
|
+
{ reference: 'foo' },
|
33
|
+
{ location: '.', property_name: 'bar' }
|
34
|
+
]
|
35
|
+
}
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#property_assignment' do
|
41
|
+
subject { parser.property_assignment }
|
42
|
+
|
43
|
+
it { should_not parse('a = 42') }
|
44
|
+
|
45
|
+
it do
|
46
|
+
should parse('a.b = 42').as(
|
47
|
+
lhs: {
|
48
|
+
object: { reference: 'a' },
|
49
|
+
location: '.',
|
50
|
+
property_name: 'b'
|
51
|
+
},
|
52
|
+
location: '=',
|
53
|
+
rhs: {
|
54
|
+
expression_chain: { integer: '42' }
|
55
|
+
}
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Rip::Parser::Rules::BinaryCondition do
|
4
|
+
class BinaryConditionParser
|
5
|
+
include Rip::Parser::Rules::BinaryCondition
|
6
|
+
include Rip::Parser::Rules::Module
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:parser) { BinaryConditionParser.new }
|
10
|
+
|
11
|
+
describe '#binary_condition' do
|
12
|
+
subject { parser.binary_condition }
|
13
|
+
|
14
|
+
it do
|
15
|
+
should parse('if (result) { :yes } else { :no }').as(
|
16
|
+
if: 'if',
|
17
|
+
condition: { expression_chain: { reference: 'result' } },
|
18
|
+
consequence: {
|
19
|
+
expression_chain: {
|
20
|
+
location: ':',
|
21
|
+
string: [
|
22
|
+
{ character: 'y' },
|
23
|
+
{ character: 'e' },
|
24
|
+
{ character: 's' }
|
25
|
+
]
|
26
|
+
}
|
27
|
+
},
|
28
|
+
else: 'else',
|
29
|
+
alternative: {
|
30
|
+
expression_chain: {
|
31
|
+
location: ':',
|
32
|
+
string: [
|
33
|
+
{ character: 'n' },
|
34
|
+
{ character: 'o' }
|
35
|
+
]
|
36
|
+
}
|
37
|
+
}
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Rip::Parser::Rules::Character do
|
4
|
+
class CharacterParser
|
5
|
+
include Rip::Parser::Rules::Character
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:parser) { CharacterParser.new }
|
9
|
+
|
10
|
+
describe '#character' do
|
11
|
+
subject { parser.character }
|
12
|
+
|
13
|
+
it { should parse('`c').as(location: '`', character: 'c') }
|
14
|
+
it { should parse('`\n').as(location: '`', escape_location: '\\', escape_special: 'n') }
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#escape_sequence' do
|
18
|
+
subject { parser.escape_sequence }
|
19
|
+
|
20
|
+
it { should parse('\u1234').as(escape_location: '\\', escape_unicode: '1234') }
|
21
|
+
|
22
|
+
described_class::SPECIAL_ESCAPES.each do |_, sequence|
|
23
|
+
it { should parse("\\#{sequence}").as(escape_location: '\\', escape_special: sequence) }
|
24
|
+
end
|
25
|
+
|
26
|
+
it { should parse('\w').as(escape_location: '\\', escape_any: 'w') }
|
27
|
+
it { should_not parse('\ ') }
|
28
|
+
end
|
29
|
+
end
|