opal 1.2.0.beta1 → 1.2.0
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -1
- data/CHANGELOG.md +53 -0
- data/UNRELEASED.md +5 -1
- data/lib/opal/parser/patch.rb +10 -0
- data/lib/opal/rewriter.rb +2 -0
- data/lib/opal/rewriters/pattern_matching.rb +287 -0
- data/lib/opal/version.rb +1 -1
- data/opal/corelib/constants.rb +2 -2
- data/opal/corelib/hash.rb +9 -0
- data/opal/corelib/pattern_matching.rb +159 -0
- data/opal/opal/full.rb +1 -0
- data/spec/filters/bugs/array.rb +0 -1
- data/spec/filters/bugs/hash.rb +1 -6
- data/spec/filters/bugs/language.rb +21 -76
- data/spec/filters/bugs/string.rb +1 -0
- data/spec/filters/bugs/struct.rb +0 -10
- data/spec/filters/unsupported/refinements.rb +3 -0
- data/spec/opal/core/language/pattern_matching_spec.rb +124 -0
- data/stdlib/promise/v1.rb +1 -0
- data/stdlib/promise/v2.rb +386 -0
- data/tasks/releasing.rake +1 -1
- data/tasks/testing.rake +0 -1
- data/test/opal/promisev2/test_always.rb +63 -0
- data/test/opal/promisev2/test_error.rb +16 -0
- data/test/opal/promisev2/test_rescue.rb +59 -0
- data/test/opal/promisev2/test_then.rb +90 -0
- data/test/opal/promisev2/test_trace.rb +52 -0
- data/test/opal/promisev2/test_value.rb +16 -0
- data/test/opal/promisev2/test_when.rb +35 -0
- metadata +24 -8
data/opal/corelib/hash.rb
CHANGED
@@ -476,6 +476,15 @@ class Hash
|
|
476
476
|
|
477
477
|
alias eql? ==
|
478
478
|
|
479
|
+
def except(*keys)
|
480
|
+
dup.except!(*keys)
|
481
|
+
end
|
482
|
+
|
483
|
+
def except!(*keys)
|
484
|
+
keys.each { |key| delete(key) }
|
485
|
+
self
|
486
|
+
end
|
487
|
+
|
479
488
|
def fetch(key, defaults = undefined, &block)
|
480
489
|
%x{
|
481
490
|
var value = Opal.hash_get(self, key);
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# A "userland" implementation of pattern matching for Opal
|
2
|
+
|
3
|
+
class PatternMatching
|
4
|
+
def self.call(from, pattern)
|
5
|
+
pm = new(from, pattern)
|
6
|
+
pm.match || (return nil)
|
7
|
+
pm.returns
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(from, pattern)
|
11
|
+
@from, @pattern = from, pattern
|
12
|
+
@returns = []
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :returns
|
16
|
+
|
17
|
+
def match(from = @from, pattern = @pattern)
|
18
|
+
if pattern == :var
|
19
|
+
@returns << from
|
20
|
+
true
|
21
|
+
else # Pattern is otherwise an Array
|
22
|
+
type, *args = *pattern
|
23
|
+
|
24
|
+
case type
|
25
|
+
when :save # from =>
|
26
|
+
@returns << from
|
27
|
+
match(from, args[0])
|
28
|
+
when :lit # 3, 4, :a, (1..), ... (but also ^a)
|
29
|
+
args[0] === from
|
30
|
+
when :any # a | b
|
31
|
+
args.any? { |arg| match(from, arg) }
|
32
|
+
when :all # Array(1) which works as Array & [1] (& doesn't exist though...)
|
33
|
+
args.all? { |arg| match(from, arg) }
|
34
|
+
when :array # [...]
|
35
|
+
fixed_size, array_size, array_match = *args
|
36
|
+
return false unless from.respond_to? :deconstruct
|
37
|
+
a = from.deconstruct
|
38
|
+
return false if fixed_size && a.length != array_size
|
39
|
+
return false if a.length < array_size
|
40
|
+
|
41
|
+
skip_elems = 0
|
42
|
+
skip_rests = 0
|
43
|
+
|
44
|
+
array_match.each_with_index.all? do |elem, i|
|
45
|
+
type, *args = elem
|
46
|
+
case type
|
47
|
+
when :rest
|
48
|
+
skip_elems = a.size - array_size
|
49
|
+
skip_rests = 1
|
50
|
+
match(a[i...i + skip_elems], args[0]) if args[0] # :save?
|
51
|
+
true
|
52
|
+
else
|
53
|
+
match(a[i + skip_elems - skip_rests], elem)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
when :find # [*, a, b, *]
|
57
|
+
find_match, = *args
|
58
|
+
first, *find_match, last = *find_match
|
59
|
+
pattern_length = find_match.length
|
60
|
+
|
61
|
+
return false unless from.respond_to? :deconstruct
|
62
|
+
a = from.deconstruct
|
63
|
+
a_length = a.length
|
64
|
+
return false if a_length < pattern_length
|
65
|
+
|
66
|
+
# We will save the backup of returns, to be restored
|
67
|
+
# on each iteration to try again.
|
68
|
+
returns_backup = @returns.dup
|
69
|
+
|
70
|
+
# Extract the capture info from first and last.
|
71
|
+
# Both are of a form [:rest], or [:rest, :var].
|
72
|
+
# So our new variables will be either :var, or nil.
|
73
|
+
first, last = first[1], last[1]
|
74
|
+
|
75
|
+
# Let's try to match each possibility...
|
76
|
+
# [A, B, c, d], [a, B, C, d], [a, b, C, D]
|
77
|
+
iterations = a_length - pattern_length + 1
|
78
|
+
|
79
|
+
iterations.times.any? do |skip|
|
80
|
+
first_part = a[0, skip]
|
81
|
+
content = a[skip, pattern_length]
|
82
|
+
last_part = a[skip + pattern_length..-1]
|
83
|
+
|
84
|
+
match(first_part, first) if first
|
85
|
+
success = content.each_with_index.all? do |e, i|
|
86
|
+
match(e, find_match[i])
|
87
|
+
end
|
88
|
+
match(last_part, last) if last
|
89
|
+
|
90
|
+
# Match failed. Let's not return anything.
|
91
|
+
@returns = returns_backup.dup unless success
|
92
|
+
|
93
|
+
success
|
94
|
+
end
|
95
|
+
when :hash # {...}
|
96
|
+
any_size, hash_match = *args
|
97
|
+
|
98
|
+
hash_match = hash_match.to_h
|
99
|
+
|
100
|
+
return false unless from.respond_to? :deconstruct_keys
|
101
|
+
|
102
|
+
if any_size && any_size != true # a => {a:, **other}
|
103
|
+
a = from.deconstruct_keys(nil) # ^^^^^^^
|
104
|
+
else
|
105
|
+
a = from.deconstruct_keys(hash_match.keys)
|
106
|
+
end
|
107
|
+
|
108
|
+
hash_match.all? do |k, v|
|
109
|
+
return false unless a.key? k
|
110
|
+
match(a[k], v)
|
111
|
+
end || (return false)
|
112
|
+
|
113
|
+
if any_size && any_size != true
|
114
|
+
match(a.except(*hash_match.keys), args[0])
|
115
|
+
elsif !any_size
|
116
|
+
return false unless a.except(*hash_match.keys).empty?
|
117
|
+
end
|
118
|
+
|
119
|
+
true
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class Array
|
126
|
+
def deconstruct
|
127
|
+
self
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class Hash
|
132
|
+
def deconstruct_keys(_)
|
133
|
+
self
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class Struct
|
138
|
+
alias deconstruct to_a
|
139
|
+
# This function is specified in a very weird way...
|
140
|
+
def deconstruct_keys(keys)
|
141
|
+
return to_h if keys.nil?
|
142
|
+
raise TypeError, 'expected Array or nil' unless Array === keys
|
143
|
+
return {} if keys.length > values.length
|
144
|
+
out = {}
|
145
|
+
keys.each do |key|
|
146
|
+
should_break = case key
|
147
|
+
when Integer
|
148
|
+
values.length < key
|
149
|
+
when Symbol # Or String? Doesn't matter, we're in Opal.
|
150
|
+
!members.include?(key)
|
151
|
+
end
|
152
|
+
break if should_break
|
153
|
+
out[key] = self[key]
|
154
|
+
end
|
155
|
+
out
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
class NoMatchingPatternError < StandardError; end
|
data/opal/opal/full.rb
CHANGED
data/spec/filters/bugs/array.rb
CHANGED
@@ -19,7 +19,6 @@ opal_filter "Array" do
|
|
19
19
|
fails "Array#[]= with [m..] just sets the section defined by range to nil if m and n < 0 and the rhs is nil" # Opal::SyntaxError: undefined method `type' for nil
|
20
20
|
fails "Array#[]= with [m..] replaces the section defined by range" # Opal::SyntaxError: undefined method `type' for nil
|
21
21
|
fails "Array#[]= with [m..] replaces the section if m and n < 0" # Opal::SyntaxError: undefined method `type' for nil
|
22
|
-
fails "Array#deconstruct returns self" # NoMethodError: undefined method `deconstruct' for [1]
|
23
22
|
fails "Array#drop raises a TypeError when the passed argument can't be coerced to Integer" # Expected TypeError but no exception was raised ([1, 2] was returned)
|
24
23
|
fails "Array#drop raises a TypeError when the passed argument isn't an integer and #to_int returns non-Integer" # Expected TypeError but no exception was raised ([1, 2] was returned)
|
25
24
|
fails "Array#drop tries to convert the passed argument to an Integer using #to_int" # Expected [1, 2, 3] == [3] to be truthy but was false
|
data/spec/filters/bugs/hash.rb
CHANGED
@@ -9,9 +9,7 @@ opal_filter "Hash" do
|
|
9
9
|
fails "Hash#[]= does not dispatch to hash for Boolean, Integer, Float, String, or Symbol" # NoMethodError: undefined method `insert' for "rubyexe.rb"
|
10
10
|
fails "Hash#[]= keeps the existing String key in the hash if there is a matching one" # Expected "foo" not to be identical to "foo"
|
11
11
|
fails "Hash#compare_by_identity gives different identity for string literals" # Expected [2] to equal [1, 2]
|
12
|
-
fails "Hash#deconstruct_keys
|
13
|
-
fails "Hash#deconstruct_keys requires one argument" # Expected ArgumentError (/wrong number of arguments \(given 0, expected 1\)/) but got: NoMethodError (undefined method `deconstruct_keys' for {"a"=>1})
|
14
|
-
fails "Hash#deconstruct_keys returns self" # NoMethodError: undefined method `deconstruct_keys' for {"a"=>1, "b"=>2}
|
12
|
+
fails "Hash#deconstruct_keys requires one argument" # Expected ArgumentError (/wrong number of arguments \(given 0, expected 1\)/) but got: ArgumentError ([Hash#deconstruct_keys] wrong number of arguments(0 for 1))
|
15
13
|
fails "Hash#delete allows removing a key while iterating" # Exception: Cannot read property '$$is_string' of undefined
|
16
14
|
fails "Hash#each always yields an Array of 2 elements, even when given a callable of arity 2" # Expected ArgumentError but no exception was raised ({"a"=>1} was returned)
|
17
15
|
fails "Hash#each yields 2 values and not an Array of 2 elements when given a callable of arity 2" # ArgumentError: [Object#foo] wrong number of arguments(1 for 2)
|
@@ -21,9 +19,6 @@ opal_filter "Hash" do
|
|
21
19
|
fails "Hash#eql? computes equality for complex recursive hashes"
|
22
20
|
fails "Hash#eql? computes equality for recursive hashes & arrays"
|
23
21
|
fails "Hash#except always returns a Hash without a default" # NoMethodError: undefined method `except' for {"bar"=>12, "foo"=>42}
|
24
|
-
fails "Hash#except ignores keys not present in the original hash" # NoMethodError: undefined method `except' for {"a"=>1, "b"=>2, "c"=>3}
|
25
|
-
fails "Hash#except returns a hash without the requested subset" # NoMethodError: undefined method `except' for {"a"=>1, "b"=>2, "c"=>3}
|
26
|
-
fails "Hash#except returns a new duplicate hash without arguments" # NoMethodError: undefined method `except' for {"a"=>1, "b"=>2, "c"=>3}
|
27
22
|
fails "Hash#inspect calls #to_s on the object returned from #inspect if the Object isn't a String" # Expected "{\"a\"=>abc}" to equal "{:a=>abc}"
|
28
23
|
fails "Hash#inspect does not call #to_s on a String returned from #inspect" # Expected "{\"a\"=>\"abc\"}" to equal "{:a=>\"abc\"}"
|
29
24
|
fails "Hash#inspect does not call #to_str on the object returned from #inspect when it is not a String" # Expected "{\"a\"=>#<MockObject:0x30638>}" to match /^\{:a=>#<MockObject:0x[0-9a-f]+>\}$/
|
@@ -23,10 +23,8 @@ opal_filter "language" do
|
|
23
23
|
fails "A block yielded a single Array raises a TypeError if #to_hash does not return a Hash"
|
24
24
|
fails "A block yielded a single Array when non-symbol keys are in a keyword arguments Hash does not separate non-symbol keys and symbol keys and does not autosplat" # Expected [nil, {"a"=>10, "b"=>2}] == [[{"a"=>10, "b"=>2}], {}] to be truthy but was false
|
25
25
|
fails "A block yielded a single Array when non-symbol keys are in a keyword arguments Hash separates non-symbol keys and symbol keys" # Expected [nil, {"a"=>10, "b"=>2}] to equal [{"a"=>10}, {"b"=>2}]
|
26
|
-
fails "A class definition allows using self as the superclass if self is a class"
|
27
26
|
fails "A class definition extending an object (sclass) allows accessing the block of the original scope" # Opal::SyntaxError: undefined method `uses_block!' for nil
|
28
27
|
fails "A class definition extending an object (sclass) can use return to cause the enclosing method to return"
|
29
|
-
fails "A class definition extending an object (sclass) raises a TypeError when trying to extend non-Class" # Expected TypeError (/superclass must be a.* Class/) but no exception was raised (nil was returned)
|
30
28
|
fails "A class definition extending an object (sclass) raises a TypeError when trying to extend numbers"
|
31
29
|
fails "A class definition raises TypeError if any constant qualifying the class is not a Module"
|
32
30
|
fails "A class definition raises TypeError if the constant qualifying the class is nil"
|
@@ -116,6 +114,7 @@ opal_filter "language" do
|
|
116
114
|
fails "Global variable $\" is read-only"
|
117
115
|
fails "Hash literal checks duplicated keys on initialization" # Expected warning to match: /key 1000 is duplicated|duplicated key/ but got: ""
|
118
116
|
fails "Hash literal expands a BasicObject using ** into the containing Hash literal initialization" # NoMethodError: undefined method `respond_to?' for BasicObject
|
117
|
+
fails "Hash#deconstruct_keys requires one argument" # Expected ArgumentError (/wrong number of arguments \(given 0, expected 1\)/) but got: ArgumentError ([Hash#deconstruct_keys] wrong number of arguments(0 for 1))
|
119
118
|
fails "Heredoc string allow HEREDOC with <<\"identifier\", interpolated" # Expected #<Encoding:UTF-16LE> to equal #<Encoding:ASCII-8BIT (dummy)>
|
120
119
|
fails "Heredoc string allows HEREDOC with <<'identifier', no interpolation" # Expected #<Encoding:UTF-16LE> to equal #<Encoding:ASCII-8BIT (dummy)>
|
121
120
|
fails "Heredoc string allows HEREDOC with <<-'identifier', allowing to indent identifier, no interpolation" # Expected #<Encoding:UTF-16LE> to equal #<Encoding:ASCII-8BIT (dummy)>
|
@@ -208,84 +207,29 @@ opal_filter "language" do
|
|
208
207
|
fails "Optional variable assignments using compounded constants with &&= assignments" # Expected warning to match: /already initialized constant/ but got: ""
|
209
208
|
fails "Optional variable assignments using compounded constants with operator assignments" # Expected warning to match: /already initialized constant/ but got: ""
|
210
209
|
fails "Optional variable assignments using compunded constants with ||= assignments"
|
211
|
-
fails "Pattern matching
|
212
|
-
fails "Pattern matching
|
213
|
-
fails "Pattern matching Array pattern
|
214
|
-
fails "Pattern matching Array pattern
|
215
|
-
fails "Pattern matching Array pattern
|
216
|
-
fails "Pattern matching Array pattern
|
217
|
-
fails "Pattern matching Array pattern
|
218
|
-
fails "Pattern matching
|
219
|
-
fails "Pattern matching
|
220
|
-
fails "Pattern matching
|
221
|
-
fails "Pattern matching
|
222
|
-
fails "Pattern matching
|
223
|
-
fails "Pattern matching
|
224
|
-
fails "Pattern matching
|
225
|
-
fails "Pattern matching
|
226
|
-
fails "Pattern matching
|
227
|
-
fails "Pattern matching Array pattern supports form Constant[pat, pat, ...]" # Opal::SyntaxError: Unsupported sexp: case_match
|
228
|
-
fails "Pattern matching Array pattern supports form [pat, pat, ...]" # Opal::SyntaxError: Unsupported sexp: case_match
|
229
|
-
fails "Pattern matching Array pattern supports form pat, pat, ..." # Opal::SyntaxError: Unsupported sexp: case_match
|
230
|
-
fails "Pattern matching Array pattern supports splat operator *rest" # Opal::SyntaxError: `rest' is not allowed as a local variable name
|
231
|
-
fails "Pattern matching Hash pattern binds variables" # Opal::SyntaxError: `x' is not allowed as a local variable name
|
232
|
-
fails "Pattern matching Hash pattern calls #deconstruct_keys per pattern" # Opal::SyntaxError: Unsupported sexp: case_match
|
233
|
-
fails "Pattern matching Hash pattern can match partially" # Opal::SyntaxError: Unsupported sexp: case_match
|
234
|
-
fails "Pattern matching Hash pattern can mix key (a:) and key-value (a: b) declarations" # Opal::SyntaxError: `a' is not allowed as a local variable name
|
235
|
-
fails "Pattern matching Hash pattern does not match object if #deconstruct_keys method does not return Hash" # Expected TypeError (/deconstruct_keys must return Hash/) but got: Opal::SyntaxError (Unsupported sexp: case_match)
|
236
|
-
fails "Pattern matching Hash pattern does not match object if #deconstruct_keys method returns Hash with non-symbol keys" # Opal::SyntaxError: Unsupported sexp: case_match
|
237
|
-
fails "Pattern matching Hash pattern does not match object if Constant === object returns false" # Opal::SyntaxError: Unsupported sexp: case_match
|
238
|
-
fails "Pattern matching Hash pattern does not match object if elements of Hash returned by #deconstruct_keys method does not match values in pattern" # Opal::SyntaxError: Unsupported sexp: case_match
|
239
|
-
fails "Pattern matching Hash pattern does not match object without #deconstruct_keys method" # Mock '#<Object:0x78c14>' expected to receive respond_to?("deconstruct_keys") exactly 1 times but received it 0 times
|
240
|
-
fails "Pattern matching Hash pattern matches an object with #deconstruct_keys method which returns a Hash with equal keys and each value in Hash matches value in pattern" # Opal::SyntaxError: Unsupported sexp: case_match
|
241
|
-
fails "Pattern matching Hash pattern matches anything with **" # Opal::SyntaxError: Unsupported sexp: case_match
|
242
|
-
fails "Pattern matching Hash pattern matches {} with {}" # Opal::SyntaxError: Unsupported sexp: case_match
|
243
|
-
fails "Pattern matching Hash pattern passes keys specified in pattern as arguments to #deconstruct_keys method" # Opal::SyntaxError: Unsupported sexp: case_match
|
244
|
-
fails "Pattern matching Hash pattern passes keys specified in pattern to #deconstruct_keys method if pattern contains double splat operator **" # Opal::SyntaxError: Unsupported sexp: case_match
|
245
|
-
fails "Pattern matching Hash pattern passes nil to #deconstruct_keys method if pattern contains double splat operator **rest" # Opal::SyntaxError: `rest' is not allowed as a local variable name
|
210
|
+
fails "Pattern matching Array pattern accepts a subclass of Array from #deconstruct" # NoMethodError: undefined method `obj' for #<MSpecEnv:0x1af0a>
|
211
|
+
fails "Pattern matching Array pattern calls #deconstruct even on objects that are already an array" # NoMethodError: undefined method `obj' for #<MSpecEnv:0x1af0a>
|
212
|
+
fails "Pattern matching Array pattern calls #deconstruct once for multiple patterns, caching the result" # NoMethodError: undefined method `obj' for #<MSpecEnv:0x1af0a>
|
213
|
+
fails "Pattern matching Array pattern does not match object if elements of array returned by #deconstruct method does not match elements in pattern" # NoMethodError: undefined method `obj' for #<MSpecEnv:0x1af0a>
|
214
|
+
fails "Pattern matching Array pattern does not match object without #deconstruct method" # Mock '#<Object:0x239ca>' expected to receive respond_to?("deconstruct") exactly 1 times but received it 0 times
|
215
|
+
fails "Pattern matching Array pattern matches an object with #deconstruct method which returns an array and each element in array matches element in pattern" # NoMethodError: undefined method `obj' for #<MSpecEnv:0x1af0a>
|
216
|
+
fails "Pattern matching Array pattern raises TypeError if #deconstruct method does not return array" # Expected TypeError (/deconstruct must return Array/) but got: NoMethodError (undefined method `obj' for #<MSpecEnv:0x1af0a>)
|
217
|
+
fails "Pattern matching Hash pattern calls #deconstruct_keys per pattern" # NoMethodError: undefined method `obj' for #<MSpecEnv:0x1af0a>
|
218
|
+
fails "Pattern matching Hash pattern does not match object if #deconstruct_keys method does not return Hash" # Expected TypeError (/deconstruct_keys must return Hash/) but got: NoMethodError (undefined method `obj' for #<MSpecEnv:0x1af0a>)
|
219
|
+
fails "Pattern matching Hash pattern does not match object if #deconstruct_keys method returns Hash with non-symbol keys" # NoMethodError: undefined method `obj' for #<MSpecEnv:0x1af0a>
|
220
|
+
fails "Pattern matching Hash pattern does not match object if elements of Hash returned by #deconstruct_keys method does not match values in pattern" # NoMethodError: undefined method `obj' for #<MSpecEnv:0x1af0a>
|
221
|
+
fails "Pattern matching Hash pattern does not match object without #deconstruct_keys method" # Mock '#<Object:0x29382>' expected to receive respond_to?("deconstruct_keys") exactly 1 times but received it 0 times
|
222
|
+
fails "Pattern matching Hash pattern matches an object with #deconstruct_keys method which returns a Hash with equal keys and each value in Hash matches value in pattern" # NoMethodError: undefined method `obj' for #<MSpecEnv:0x1af0a>
|
223
|
+
fails "Pattern matching Hash pattern passes keys specified in pattern as arguments to #deconstruct_keys method" # NoMethodError: undefined method `obj' for #<MSpecEnv:0x1af0a>
|
224
|
+
fails "Pattern matching Hash pattern passes keys specified in pattern to #deconstruct_keys method if pattern contains double splat operator **" # NoMethodError: undefined method `obj' for #<MSpecEnv:0x1af0a>
|
225
|
+
fails "Pattern matching Hash pattern passes nil to #deconstruct_keys method if pattern contains double splat operator **rest" # NoMethodError: undefined method `obj' for #<MSpecEnv:0x1af0a>
|
246
226
|
fails "Pattern matching Hash pattern raise SyntaxError when keys duplicate in pattern" # Expected SyntaxError (/duplicated key name/) but got: Opal::SyntaxError (duplicate hash pattern key a)
|
247
|
-
fails "Pattern matching
|
248
|
-
fails "Pattern matching Hash pattern supports a: which means a: a" # Opal::SyntaxError: `a' is not allowed as a local variable name
|
249
|
-
fails "Pattern matching Hash pattern supports double splat operator **rest" # Opal::SyntaxError: `rest' is not allowed as a local variable name
|
250
|
-
fails "Pattern matching Hash pattern supports form Constant(id: pat, id: pat, ...)" # Opal::SyntaxError: Unsupported sexp: case_match
|
251
|
-
fails "Pattern matching Hash pattern supports form Constant[id: pat, id: pat, ...]" # Opal::SyntaxError: Unsupported sexp: case_match
|
252
|
-
fails "Pattern matching Hash pattern supports form id: pat, id: pat, ..." # Opal::SyntaxError: Unsupported sexp: case_match
|
253
|
-
fails "Pattern matching Hash pattern supports form {id: pat, id: pat, ...}" # Opal::SyntaxError: Unsupported sexp: case_match
|
254
|
-
fails "Pattern matching Hash pattern treats **nil like there should not be any other keys in a matched Hash" # Opal::SyntaxError: Unsupported sexp: case_match
|
255
|
-
fails "Pattern matching allows using then operator" # Opal::SyntaxError: Unsupported sexp: case_match
|
256
|
-
fails "Pattern matching alternative pattern does not support variable binding" # Expected SyntaxError (/illegal variable in alternative pattern/) but got: Opal::SyntaxError (`a' is not allowed as a local variable name)
|
257
|
-
fails "Pattern matching alternative pattern matches if any of patterns matches" # Opal::SyntaxError: Unsupported sexp: case_match
|
258
|
-
fails "Pattern matching alternative pattern support underscore prefixed variables in alternation" # Opal::SyntaxError: `_' is not allowed as a local variable name
|
259
|
-
fails "Pattern matching binds variables" # Opal::SyntaxError: `a' is not allowed as a local variable name
|
260
|
-
fails "Pattern matching can be standalone assoc operator that deconstructs value" # Opal::SyntaxError: `a' is not allowed as a local variable name
|
227
|
+
fails "Pattern matching alternative pattern does not support variable binding" # Expected SyntaxError (/illegal variable in alternative pattern/) but no exception was raised (nil was returned)
|
261
228
|
fails "Pattern matching cannot mix in and when operators" # Expected SyntaxError (/syntax error, unexpected `in'/) but got: Opal::SyntaxError (unexpected token kIN)
|
262
|
-
fails "Pattern matching
|
263
|
-
fails "Pattern matching
|
264
|
-
fails "Pattern matching executes else clause if no pattern matches" # Opal::SyntaxError: Unsupported sexp: case_match
|
265
|
-
fails "Pattern matching extends case expression with case/in construction" # Opal::SyntaxError: Unsupported sexp: case_match
|
266
|
-
fails "Pattern matching guards does not evaluate guard if pattern does not match" # Opal::SyntaxError: Unsupported sexp: case_match
|
267
|
-
fails "Pattern matching guards executes else clause if no guarded pattern matches" # Opal::SyntaxError: Unsupported sexp: case_match
|
268
|
-
fails "Pattern matching guards makes bound variables visible in guard" # Opal::SyntaxError: `a' is not allowed as a local variable name
|
269
|
-
fails "Pattern matching guards raises NoMatchingPatternError if no guarded pattern matches and no else clause" # NameError: uninitialized constant NoMatchingPatternError
|
270
|
-
fails "Pattern matching guards supports if guard" # Opal::SyntaxError: Unsupported sexp: case_match
|
271
|
-
fails "Pattern matching guards supports unless guard" # Opal::SyntaxError: Unsupported sexp: case_match
|
272
|
-
fails "Pattern matching guards takes guards into account when there are several matching patterns" # Opal::SyntaxError: Unsupported sexp: case_match
|
273
|
-
fails "Pattern matching raises NoMatchingPatternError if no pattern matches and no else clause" # NameError: uninitialized constant NoMatchingPatternError
|
274
|
-
fails "Pattern matching refinements are used for #=== in constant pattern" # NoMethodError: undefined method `refine' for #<Module:0x79d4c>
|
275
|
-
fails "Pattern matching refinements are used for #deconstruct" # NoMethodError: undefined method `refine' for #<Module:0x79d50>
|
276
|
-
fails "Pattern matching refinements are used for #deconstruct_keys" # NoMethodError: undefined method `refine' for #<Module:0x79d54>
|
277
|
-
fails "Pattern matching value pattern allows string literal with interpolation" # Opal::SyntaxError: Unsupported sexp: case_match
|
278
|
-
fails "Pattern matching value pattern matches an object such that pattern === object" # Opal::SyntaxError: Unsupported sexp: case_match
|
279
|
-
fails "Pattern matching variable pattern allow using _ name to drop values" # Opal::SyntaxError: `a' is not allowed as a local variable name
|
280
|
-
fails "Pattern matching variable pattern allows applying ^ operator to bound variables" # Opal::SyntaxError: `n' is not allowed as a local variable name
|
281
|
-
fails "Pattern matching variable pattern create local variables even if a pattern doesn't match" # Opal::SyntaxError: `a' is not allowed as a local variable name
|
282
|
-
fails "Pattern matching variable pattern does not support using variable name (except _) several times" # Expected SyntaxError (/duplicated variable name/) but got: Opal::SyntaxError (`a' is not allowed as a local variable name)
|
283
|
-
fails "Pattern matching variable pattern makes bounded variable visible outside a case statement scope" # Opal::SyntaxError: `a' is not allowed as a local variable name
|
284
|
-
fails "Pattern matching variable pattern matches a value and binds variable name to this value" # Opal::SyntaxError: `a' is not allowed as a local variable name
|
229
|
+
fails "Pattern matching variable pattern allows applying ^ operator to bound variables" # NoMatchingPatternError: [1, 1]
|
230
|
+
fails "Pattern matching variable pattern does not support using variable name (except _) several times" # Expected SyntaxError (/duplicated variable name/) but got: Opal::SyntaxError (duplicate variable name a)
|
285
231
|
fails "Pattern matching variable pattern requires bound variable to be specified in a pattern before ^ operator when it relies on a bound variable" # Expected SyntaxError (/n: no such local variable/) but got: Opal::SyntaxError (no such local variable: `n')
|
286
232
|
fails "Pattern matching variable pattern supports existing variables in a pattern specified with ^ operator" # Opal::SyntaxError: no such local variable: `a'
|
287
|
-
fails "Pattern matching variable pattern supports using _ in a pattern several times" # Opal::SyntaxError: `_' is not allowed as a local variable name
|
288
|
-
fails "Pattern matching variable pattern supports using any name with _ at the beginning in a pattern several times" # Opal::SyntaxError: `_x' is not allowed as a local variable name
|
289
233
|
fails "Pattern matching warning warns about pattern matching is experimental feature" # NameError: uninitialized constant Warning
|
290
234
|
fails "Post-args with optional args with a circular argument reference shadows an existing local with the same name as the argument"
|
291
235
|
fails "Post-args with optional args with a circular argument reference shadows an existing method with the same name as the argument"
|
@@ -319,6 +263,7 @@ opal_filter "language" do
|
|
319
263
|
fails "Safe navigator allows assignment methods"
|
320
264
|
fails "Safe navigator allows assignment operators"
|
321
265
|
fails "Safe navigator does not call the operator method lazily with an assignment operator"
|
266
|
+
fails "Struct#deconstruct_keys requires one argument" # Expected ArgumentError (/wrong number of arguments \(given 0, expected 1\)/) but got: ArgumentError ([#deconstruct_keys] wrong number of arguments(0 for 1))
|
322
267
|
fails "The =~ operator with named captures on syntax of 'string_literal' =~ /regexp/ does not set local variables" # Exception: named captures are not supported in javascript: "(?<matched>foo)(?<unmatched>bar)?"
|
323
268
|
fails "The =~ operator with named captures on syntax of /regexp/ =~ string_variable sets local variables by the captured pairs"
|
324
269
|
fails "The =~ operator with named captures on syntax of regexp_variable =~ string_variable does not set local variables"
|
data/spec/filters/bugs/string.rb
CHANGED
@@ -299,4 +299,5 @@ opal_filter "String" do
|
|
299
299
|
fails "String#valid_encoding? returns true for IBM720 encoding self is valid in" # ArgumentError: unknown encoding name - IBM720
|
300
300
|
fails "String.new accepts an encoding argument" # ArgumentError: [String.new] wrong number of arguments(2 for -1)
|
301
301
|
fails "String.new is called on subclasses" # Expected nil to equal "subclass"
|
302
|
+
fails "Struct#deconstruct_keys requires one argument" # Expected ArgumentError (/wrong number of arguments \(given 0, expected 1\)/) but got: ArgumentError ([#deconstruct_keys] wrong number of arguments(0 for 1))
|
302
303
|
end
|
data/spec/filters/bugs/struct.rb
CHANGED
@@ -1,15 +1,5 @@
|
|
1
1
|
# NOTE: run bin/format-filters after changing this file
|
2
2
|
opal_filter "Struct" do
|
3
|
-
fails "Struct#deconstruct returns an array of attribute values" # NoMethodError: undefined method `deconstruct' for #<struct x=1, y=2>
|
4
|
-
fails "Struct#deconstruct_keys accepts argument position number as well but returns them as keys" # NoMethodError: undefined method `deconstruct_keys' for #<struct x=10, y=20, z=30>
|
5
|
-
fails "Struct#deconstruct_keys accepts nil argument and return all the attributes" # NoMethodError: undefined method `deconstruct_keys' for #<struct x=1, y=2>
|
6
|
-
fails "Struct#deconstruct_keys accepts string attribute names" # NoMethodError: undefined method `deconstruct_keys' for #<struct x=1, y=2>
|
7
|
-
fails "Struct#deconstruct_keys raise TypeError if passed anything accept nil or array" # Expected TypeError (/expected Array or nil/) but got: NoMethodError (undefined method `deconstruct_keys' for #<struct x=1, y=2>)
|
8
|
-
fails "Struct#deconstruct_keys requires one argument" # Expected ArgumentError (/wrong number of arguments \(given 0, expected 1\)/) but got: NoMethodError (undefined method `deconstruct_keys' for #<struct x=1>)
|
9
|
-
fails "Struct#deconstruct_keys returns a hash of attributes" # NoMethodError: undefined method `deconstruct_keys' for #<struct x=1, y=2>
|
10
|
-
fails "Struct#deconstruct_keys returns an empty hash when there are more keys than attributes" # NoMethodError: undefined method `deconstruct_keys' for #<struct x=1, y=2>
|
11
|
-
fails "Struct#deconstruct_keys returns at first not existing attribute name" # NoMethodError: undefined method `deconstruct_keys' for #<struct x=1, y=2>
|
12
|
-
fails "Struct#deconstruct_keys returns only specified keys" # NoMethodError: undefined method `deconstruct_keys' for #<struct x=1, y=2, z=3>
|
13
3
|
fails "Struct#dig returns the value by the index" # Expected nil == "one" to be truthy but was false
|
14
4
|
fails "Struct#hash returns different hashes for different struct classes" # Expected "Hash" != "Hash" to be truthy but was false
|
15
5
|
fails "Struct#hash returns different hashes for structs with different values when using keyword_init: true" # NameError: wrong constant name 1 non symbol member
|
@@ -2,4 +2,7 @@
|
|
2
2
|
opal_unsupported_filter "Refinements" do
|
3
3
|
fails "Kernel#eval with refinements activates refinements from the binding" # NoMethodError: undefined method `refine' for #<Module:0x7ad6a>
|
4
4
|
fails "Kernel#eval with refinements activates refinements from the eval scope" # NoMethodError: undefined method `refine' for #<Module:0x7ad6e>
|
5
|
+
fails "Pattern matching refinements are used for #=== in constant pattern" # NoMethodError: undefined method `refine' for #<Module:0x46556>
|
6
|
+
fails "Pattern matching refinements are used for #deconstruct" # NoMethodError: undefined method `refine' for #<Module:0x4655e>
|
7
|
+
fails "Pattern matching refinements are used for #deconstruct_keys" # NoMethodError: undefined method `refine' for #<Module:0x4655a>
|
5
8
|
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "pattern matching" do
|
4
|
+
it "supports basic assignment" do
|
5
|
+
5 => a
|
6
|
+
a.should == 5
|
7
|
+
end
|
8
|
+
|
9
|
+
it "supports array pattern" do
|
10
|
+
[1,2,3,4] => [1,2,*rest]
|
11
|
+
rest.should == [3,4]
|
12
|
+
[1,2,3,4] => [*rest,3,x]
|
13
|
+
rest.should == [1,2]
|
14
|
+
x.should == 4
|
15
|
+
end
|
16
|
+
|
17
|
+
it "supports hash pattern" do
|
18
|
+
{a: 4} => {a:}
|
19
|
+
a.should == 4
|
20
|
+
{a: 4, b: 6} => {b:}
|
21
|
+
b.should == 6
|
22
|
+
{a: 1, b: 2, c: 3} => {a: 1, **rest}
|
23
|
+
rest.should == {b: 2, c: 3}
|
24
|
+
end
|
25
|
+
|
26
|
+
it "supports pinning" do
|
27
|
+
a = 6
|
28
|
+
6 => ^a
|
29
|
+
a.should == 6
|
30
|
+
end
|
31
|
+
|
32
|
+
it "supports a lambda literal" do
|
33
|
+
[6, 7] => [->(a) { a == 6 }, b]
|
34
|
+
b.should == 7
|
35
|
+
end
|
36
|
+
|
37
|
+
it "supports constants" do
|
38
|
+
[6, 7, 8] => [Integer, a, Integer]
|
39
|
+
a.should == 7
|
40
|
+
end
|
41
|
+
|
42
|
+
it "supports regexps" do
|
43
|
+
"test" => /e(s)t/
|
44
|
+
$1.should == 's'
|
45
|
+
end
|
46
|
+
|
47
|
+
it "supports save pattern" do
|
48
|
+
[6, 7, 8] => [Integer=>a, Integer=>b, Integer=>c]
|
49
|
+
[a,b,c].should == [6, 7, 8]
|
50
|
+
end
|
51
|
+
|
52
|
+
it "supports find pattern with save" do
|
53
|
+
[1, 2, 3, 4, 5] => [*before, 3 => three, 4 => four, *after]
|
54
|
+
before.should == [1,2]
|
55
|
+
three.should == 3
|
56
|
+
four.should == 4
|
57
|
+
after.should == [5]
|
58
|
+
end
|
59
|
+
|
60
|
+
it "supports custom classes" do
|
61
|
+
class TotallyArrayOrHashLike
|
62
|
+
def deconstruct
|
63
|
+
[1,2,3]
|
64
|
+
end
|
65
|
+
|
66
|
+
def deconstruct_keys(_)
|
67
|
+
{a: 1, b: 2}
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
TotallyArrayOrHashLike.new => TotallyArrayOrHashLike[*array]
|
72
|
+
array.should == [1,2,3]
|
73
|
+
|
74
|
+
TotallyArrayOrHashLike.new => TotallyArrayOrHashLike(**hash)
|
75
|
+
hash.should == {a: 1, b: 2}
|
76
|
+
end
|
77
|
+
|
78
|
+
it "supports case expressions" do
|
79
|
+
case 4
|
80
|
+
in 4
|
81
|
+
z = true
|
82
|
+
in 5
|
83
|
+
z = false
|
84
|
+
end
|
85
|
+
|
86
|
+
z.should == true
|
87
|
+
end
|
88
|
+
|
89
|
+
it "supports case expressions with guards" do
|
90
|
+
case 4
|
91
|
+
in 4 if false
|
92
|
+
z = true
|
93
|
+
in 4 if true
|
94
|
+
z = false
|
95
|
+
end
|
96
|
+
|
97
|
+
z.should == false
|
98
|
+
end
|
99
|
+
|
100
|
+
it "raises if case expression is unmatched" do
|
101
|
+
proc do
|
102
|
+
case 4
|
103
|
+
in 5
|
104
|
+
:test
|
105
|
+
end
|
106
|
+
end.should raise_error NoMatchingPatternError
|
107
|
+
end
|
108
|
+
|
109
|
+
it "doesn't raise when else in a case expression is present" do
|
110
|
+
case 4
|
111
|
+
in 5
|
112
|
+
z = true
|
113
|
+
else
|
114
|
+
z = false
|
115
|
+
end
|
116
|
+
|
117
|
+
z.should == false
|
118
|
+
end
|
119
|
+
|
120
|
+
it "doesn't raise or set variables if an in expression is unmatched" do
|
121
|
+
4 in String => a
|
122
|
+
a.should == nil
|
123
|
+
end
|
124
|
+
end
|