delorean_lang 2.3.0 → 2.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aefcc15664badb32e23cd629f7888a3984cf3fd0c01a351b652a71094c5ff3fc
4
- data.tar.gz: b3391aa0c8a00c49487ba6039838e9fd1e98f172539e06bbbb810b8c9e8cc737
3
+ metadata.gz: 84e137368e1c85b7005b7662f33e54e7cf6c7f1b0eb7fd2e3ebc8d9ebfbd8a04
4
+ data.tar.gz: 1b2039558b54352be0a030a2992aa50fc99c498fe3c1a96c84e36f9b4febb194
5
5
  SHA512:
6
- metadata.gz: beea3a643c91eaa8374ce126d0e07836545b7e141174ec38a05030b0b45b087fc48ba2ad880ce65a101125de9889c9ff67cfe786e752b0ebfff6127dbbc56633
7
- data.tar.gz: f3562421c5d037bf42da11f215b5f2c6d78c5c9987db1c0e1efdecaeb8c33c7f9ad38ad2b63db9b2d137b7179552b47ef99f0eb88d031d1ea98fb1fb9409f0c3
6
+ metadata.gz: 641d20f5de1251faaae77f7e571e6c2d43de03cc314d450b1c6246e00074c4fcb6a59506615d67968cc73b627901eedb7b3ee6375353b4a59e488ff43e4d06b7
7
+ data.tar.gz: 85afcad55380bb346f84fae8aa7e8b29601e28fe6a55e1f04d1bd9b968c064d40ab7235e2c19cf9791b3c7dc6b33d9339428e1809e9f51d5e81f1f7fea5cde9a
@@ -83,6 +83,12 @@ module Delorean
83
83
  end
84
84
 
85
85
  class BaseClass
86
+ def self._safe_navigation_get_attr(obj, attr, _e)
87
+ return nil if obj.nil?
88
+
89
+ _get_attr(obj, attr, _e)
90
+ end
91
+
86
92
  def self._get_attr(obj, attr, _e)
87
93
  # REALLY FIXME: this really needs to be another "when" in the
88
94
  # case statement below. However, Gemini appears to create Hash
@@ -195,6 +201,11 @@ module Delorean
195
201
  end
196
202
 
197
203
  ######################################################################
204
+ def self._safe_navigation_instance_call(obj, method, args, _e, &block)
205
+ return nil if obj.nil?
206
+
207
+ _instance_call(obj, method, args, _e, &block)
208
+ end
198
209
 
199
210
  def self._instance_call(obj, method, args, _e, &block)
200
211
  begin
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'delorean/cache/adapters/ruby_cache'
4
+ require 'delorean/cache/adapters/no_cache'
4
5
 
5
6
  module Delorean
6
7
  module Cache
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './base'
4
+
5
+ module Delorean
6
+ module Cache
7
+ module Adapters
8
+ class NoCache < ::Delorean::Cache::Adapters::Base
9
+ attr_reader :lookup_cache, :size_per_class
10
+
11
+ def initialize(size_per_class: 1000); end
12
+
13
+ def cache_item?(klass:, method_name:, args:)
14
+ false
15
+ end
16
+
17
+ def cache_item(klass:, cache_key:, item:); end
18
+
19
+ def fetch_item(klass:, cache_key:, default: nil)
20
+ default
21
+ end
22
+
23
+ def cache_key(klass:, method_name:, args:)
24
+ :no_cache_key
25
+ end
26
+
27
+ def clear!(klass:); end
28
+
29
+ def clear_all!; end
30
+
31
+ private
32
+
33
+ def clear_outdated_items(klass:); end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1764,6 +1764,17 @@ module Delorean
1764
1764
  elements[2]
1765
1765
  end
1766
1766
 
1767
+ def al
1768
+ elements[5]
1769
+ end
1770
+
1771
+ end
1772
+
1773
+ module DotExp5
1774
+ def i
1775
+ elements[2]
1776
+ end
1777
+
1767
1778
  def b_args
1768
1779
  elements[3]
1769
1780
  end
@@ -1773,7 +1784,13 @@ module Delorean
1773
1784
  end
1774
1785
  end
1775
1786
 
1776
- module DotExp5
1787
+ module DotExp6
1788
+ def i
1789
+ elements[2]
1790
+ end
1791
+ end
1792
+
1793
+ module DotExp7
1777
1794
  def i
1778
1795
  elements[2]
1779
1796
  end
@@ -2092,11 +2109,11 @@ module Delorean
2092
2109
  r0 = r35
2093
2110
  else
2094
2111
  i48, s48 = index, []
2095
- if (match_len = has_terminal?('.', false, index))
2096
- r49 = true
2112
+ if (match_len = has_terminal?('&.', false, index))
2113
+ r49 = instantiate_node(SyntaxNode,input, index...(index + match_len))
2097
2114
  @index += match_len
2098
2115
  else
2099
- terminal_parse_failure('\'.\'')
2116
+ terminal_parse_failure('\'&.\'')
2100
2117
  r49 = nil
2101
2118
  end
2102
2119
  s48 << r49
@@ -2112,40 +2129,56 @@ module Delorean
2112
2129
  r52 = _nt_identifier
2113
2130
  s48 << r52
2114
2131
  if r52
2115
- s53, i53 = [], index
2116
- loop do
2117
- r54 = _nt_block_args
2118
- if r54
2119
- s53 << r54
2120
- else
2121
- break
2122
- end
2132
+ if (match_len = has_terminal?('(', false, index))
2133
+ r53 = true
2134
+ @index += match_len
2135
+ else
2136
+ terminal_parse_failure('\'(\'')
2137
+ r53 = nil
2123
2138
  end
2124
- r53 = instantiate_node(SyntaxNode,input, i53...index, s53)
2125
2139
  s48 << r53
2126
2140
  if r53
2127
- s55, i55 = [], index
2128
- loop do
2129
- r56 = _nt_block_formulas
2130
- if r56
2131
- s55 << r56
2141
+ r55 = _nt_sp
2142
+ if r55
2143
+ r54 = r55
2144
+ else
2145
+ r54 = instantiate_node(SyntaxNode,input, index...index)
2146
+ end
2147
+ s48 << r54
2148
+ if r54
2149
+ r57 = _nt_fn_args
2150
+ if r57
2151
+ r56 = r57
2132
2152
  else
2133
- break
2153
+ r56 = instantiate_node(SyntaxNode,input, index...index)
2154
+ end
2155
+ s48 << r56
2156
+ if r56
2157
+ r59 = _nt_sp
2158
+ if r59
2159
+ r58 = r59
2160
+ else
2161
+ r58 = instantiate_node(SyntaxNode,input, index...index)
2162
+ end
2163
+ s48 << r58
2164
+ if r58
2165
+ if (match_len = has_terminal?(')', false, index))
2166
+ r60 = true
2167
+ @index += match_len
2168
+ else
2169
+ terminal_parse_failure('\')\'')
2170
+ r60 = nil
2171
+ end
2172
+ s48 << r60
2173
+ end
2134
2174
  end
2135
2175
  end
2136
- if s55.empty?
2137
- @index = i55
2138
- r55 = nil
2139
- else
2140
- r55 = instantiate_node(SyntaxNode,input, i55...index, s55)
2141
- end
2142
- s48 << r55
2143
2176
  end
2144
2177
  end
2145
2178
  end
2146
2179
  end
2147
2180
  if s48.last
2148
- r48 = instantiate_node(BlockExpression,input, i48...index, s48)
2181
+ r48 = instantiate_node(SafeNavigationCall,input, i48...index, s48)
2149
2182
  r48.extend(DotExp4)
2150
2183
  else
2151
2184
  @index = i48
@@ -2155,55 +2188,168 @@ module Delorean
2155
2188
  r48 = SyntaxNode.new(input, (index-1)...index) if r48 == true
2156
2189
  r0 = r48
2157
2190
  else
2158
- i57, s57 = index, []
2191
+ i61, s61 = index, []
2159
2192
  if (match_len = has_terminal?('.', false, index))
2160
- r58 = true
2193
+ r62 = true
2161
2194
  @index += match_len
2162
2195
  else
2163
2196
  terminal_parse_failure('\'.\'')
2164
- r58 = nil
2197
+ r62 = nil
2165
2198
  end
2166
- s57 << r58
2167
- if r58
2168
- r60 = _nt_sp
2169
- if r60
2170
- r59 = r60
2199
+ s61 << r62
2200
+ if r62
2201
+ r64 = _nt_sp
2202
+ if r64
2203
+ r63 = r64
2171
2204
  else
2172
- r59 = instantiate_node(SyntaxNode,input, index...index)
2205
+ r63 = instantiate_node(SyntaxNode,input, index...index)
2173
2206
  end
2174
- s57 << r59
2175
- if r59
2176
- i61 = index
2177
- r62 = _nt_identifier
2178
- if r62
2179
- r62 = SyntaxNode.new(input, (index-1)...index) if r62 == true
2180
- r61 = r62
2181
- else
2182
- r63 = _nt_integer
2183
- if r63
2184
- r63 = SyntaxNode.new(input, (index-1)...index) if r63 == true
2185
- r61 = r63
2186
- else
2187
- @index = i61
2188
- r61 = nil
2207
+ s61 << r63
2208
+ if r63
2209
+ r65 = _nt_identifier
2210
+ s61 << r65
2211
+ if r65
2212
+ s66, i66 = [], index
2213
+ loop do
2214
+ r67 = _nt_block_args
2215
+ if r67
2216
+ s66 << r67
2217
+ else
2218
+ break
2219
+ end
2220
+ end
2221
+ r66 = instantiate_node(SyntaxNode,input, i66...index, s66)
2222
+ s61 << r66
2223
+ if r66
2224
+ s68, i68 = [], index
2225
+ loop do
2226
+ r69 = _nt_block_formulas
2227
+ if r69
2228
+ s68 << r69
2229
+ else
2230
+ break
2231
+ end
2232
+ end
2233
+ if s68.empty?
2234
+ @index = i68
2235
+ r68 = nil
2236
+ else
2237
+ r68 = instantiate_node(SyntaxNode,input, i68...index, s68)
2238
+ end
2239
+ s61 << r68
2189
2240
  end
2190
2241
  end
2191
- s57 << r61
2192
2242
  end
2193
2243
  end
2194
- if s57.last
2195
- r57 = instantiate_node(GetAttr,input, i57...index, s57)
2196
- r57.extend(DotExp5)
2244
+ if s61.last
2245
+ r61 = instantiate_node(BlockExpression,input, i61...index, s61)
2246
+ r61.extend(DotExp5)
2197
2247
  else
2198
- @index = i57
2199
- r57 = nil
2248
+ @index = i61
2249
+ r61 = nil
2200
2250
  end
2201
- if r57
2202
- r57 = SyntaxNode.new(input, (index-1)...index) if r57 == true
2203
- r0 = r57
2251
+ if r61
2252
+ r61 = SyntaxNode.new(input, (index-1)...index) if r61 == true
2253
+ r0 = r61
2204
2254
  else
2205
- @index = i0
2206
- r0 = nil
2255
+ i70, s70 = index, []
2256
+ if (match_len = has_terminal?('.', false, index))
2257
+ r71 = true
2258
+ @index += match_len
2259
+ else
2260
+ terminal_parse_failure('\'.\'')
2261
+ r71 = nil
2262
+ end
2263
+ s70 << r71
2264
+ if r71
2265
+ r73 = _nt_sp
2266
+ if r73
2267
+ r72 = r73
2268
+ else
2269
+ r72 = instantiate_node(SyntaxNode,input, index...index)
2270
+ end
2271
+ s70 << r72
2272
+ if r72
2273
+ i74 = index
2274
+ r75 = _nt_identifier
2275
+ if r75
2276
+ r75 = SyntaxNode.new(input, (index-1)...index) if r75 == true
2277
+ r74 = r75
2278
+ else
2279
+ r76 = _nt_integer
2280
+ if r76
2281
+ r76 = SyntaxNode.new(input, (index-1)...index) if r76 == true
2282
+ r74 = r76
2283
+ else
2284
+ @index = i74
2285
+ r74 = nil
2286
+ end
2287
+ end
2288
+ s70 << r74
2289
+ end
2290
+ end
2291
+ if s70.last
2292
+ r70 = instantiate_node(GetAttr,input, i70...index, s70)
2293
+ r70.extend(DotExp6)
2294
+ else
2295
+ @index = i70
2296
+ r70 = nil
2297
+ end
2298
+ if r70
2299
+ r70 = SyntaxNode.new(input, (index-1)...index) if r70 == true
2300
+ r0 = r70
2301
+ else
2302
+ i77, s77 = index, []
2303
+ if (match_len = has_terminal?('&.', false, index))
2304
+ r78 = instantiate_node(SyntaxNode,input, index...(index + match_len))
2305
+ @index += match_len
2306
+ else
2307
+ terminal_parse_failure('\'&.\'')
2308
+ r78 = nil
2309
+ end
2310
+ s77 << r78
2311
+ if r78
2312
+ r80 = _nt_sp
2313
+ if r80
2314
+ r79 = r80
2315
+ else
2316
+ r79 = instantiate_node(SyntaxNode,input, index...index)
2317
+ end
2318
+ s77 << r79
2319
+ if r79
2320
+ i81 = index
2321
+ r82 = _nt_identifier
2322
+ if r82
2323
+ r82 = SyntaxNode.new(input, (index-1)...index) if r82 == true
2324
+ r81 = r82
2325
+ else
2326
+ r83 = _nt_integer
2327
+ if r83
2328
+ r83 = SyntaxNode.new(input, (index-1)...index) if r83 == true
2329
+ r81 = r83
2330
+ else
2331
+ @index = i81
2332
+ r81 = nil
2333
+ end
2334
+ end
2335
+ s77 << r81
2336
+ end
2337
+ end
2338
+ if s77.last
2339
+ r77 = instantiate_node(SafeNavigationGetAttr,input, i77...index, s77)
2340
+ r77.extend(DotExp7)
2341
+ else
2342
+ @index = i77
2343
+ r77 = nil
2344
+ end
2345
+ if r77
2346
+ r77 = SyntaxNode.new(input, (index-1)...index) if r77 == true
2347
+ r0 = r77
2348
+ else
2349
+ @index = i0
2350
+ r0 = nil
2351
+ end
2352
+ end
2207
2353
  end
2208
2354
  end
2209
2355
  end
@@ -83,10 +83,14 @@ grammar Delorean
83
83
  '.' sp? i:identifier '(' sp? al:fn_args? sp? ')' b_args:block_args* expressions:block_formulas+ <BlockExpression>
84
84
  /
85
85
  '.' sp? i:identifier '(' sp? al:fn_args? sp? ')' <Call>
86
+ /
87
+ '&.' sp? i:identifier '(' sp? al:fn_args? sp? ')' <SafeNavigationCall>
86
88
  /
87
89
  '.' sp? i:identifier b_args:block_args* expressions:block_formulas+ <BlockExpression>
88
90
  /
89
91
  '.' sp? i:(identifier / integer) <GetAttr>
92
+ /
93
+ '&.' sp? i:(identifier / integer) <SafeNavigationGetAttr>
90
94
  end
91
95
 
92
96
  rule unpack_args
@@ -45,6 +45,7 @@ module Delorean
45
45
  cache_key = delorean_cache_adapter.cache_key(
46
46
  klass: self, method_name: name, args: args
47
47
  )
48
+
48
49
  cached_item = delorean_cache_adapter.fetch_item(
49
50
  klass: self, cache_key: cache_key, default: :NF
50
51
  )
@@ -395,6 +395,14 @@ eos
395
395
  end
396
396
  end
397
397
 
398
+ class SafeNavigationGetAttr < GetAttr
399
+ def rewrite(_context, vcode)
400
+ attr = i.text_value
401
+ attr = "'#{attr}'" unless /\A[0-9]+\z/.match?(attr)
402
+ "_safe_navigation_get_attr(#{vcode}, #{attr}, _e)"
403
+ end
404
+ end
405
+
398
406
  class Call < SNode
399
407
  def check(context, *)
400
408
  al.text_value.empty? ? [] : al.check(context)
@@ -420,6 +428,29 @@ eos
420
428
  end
421
429
  end
422
430
 
431
+ class SafeNavigationCall < Call
432
+ def rewrite(context, vcode)
433
+ if al.text_value.empty?
434
+ args_str = ''
435
+ arg_count = 0
436
+ else
437
+ args_str = al.rewrite(context)
438
+ arg_count = al.arg_count
439
+ end
440
+
441
+ if vcode.is_a?(ClassText)
442
+ # FIXME: Do we really need this check here?
443
+ # ruby class call
444
+ class_name = vcode.text
445
+ context.parse_check_call_fn(i.text_value, arg_count, class_name)
446
+ end
447
+
448
+ "_safe_navigation_instance_call(
449
+ #{vcode}, '#{i.text_value}', [#{args_str}], _e
450
+ )"
451
+ end
452
+ end
453
+
423
454
  class BlockParameter < Parameter
424
455
  def check(context)
425
456
  context.parse_define_var(i.text_value)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Delorean
4
- VERSION = '2.3.0'
4
+ VERSION = '2.4.0'
5
5
  end
@@ -3,6 +3,10 @@
3
3
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
4
4
 
5
5
  describe 'Delorean cache' do
6
+ after do
7
+ ::Delorean::Cache.adapter = ::Delorean::Cache::Adapters::RubyCache.new
8
+ end
9
+
6
10
  before do
7
11
  Dummy.clear_lookup_cache!
8
12
  end
@@ -64,4 +68,19 @@ describe 'Delorean cache' do
64
68
  expect(item_10).to be_a(OpenStruct)
65
69
  expect(item_10['10']).to eq(10)
66
70
  end
71
+
72
+ describe 'No cache adapter' do
73
+ before do
74
+ ::Delorean::Cache.adapter = ::Delorean::Cache::Adapters::NoCache.new
75
+ end
76
+
77
+ it "doesn't use cache" do
78
+ expect(OpenStruct).to receive(:new).twice.and_call_original
79
+
80
+ res1 = Dummy.returns_cached_openstruct(1, 2)
81
+ res2 = Dummy.returns_cached_openstruct(1, 2)
82
+
83
+ expect(res1).to eq res2
84
+ end
85
+ end
67
86
  end
@@ -1588,6 +1588,29 @@ eof
1588
1588
  expect(r).to eq([[], 1])
1589
1589
  end
1590
1590
 
1591
+ it 'works with safe navigation' do
1592
+ engine.parse defn(*default_node,
1593
+ ' b = [1]',
1594
+ ' c = b[1] ',
1595
+ ' d = c&.round(1) ',
1596
+ ' e = c&.round ',
1597
+ ' f = c&.round(1)&.round(2)&.round(3) ',
1598
+ ' g = c&.round&.round&.round ',
1599
+ )
1600
+
1601
+ r = engine.evaluate('A', 'd')
1602
+ expect(r).to eq(nil)
1603
+
1604
+ r = engine.evaluate('A', 'e')
1605
+ expect(r).to eq(nil)
1606
+
1607
+ r = engine.evaluate('A', 'f')
1608
+ expect(r).to eq(nil)
1609
+
1610
+ r = engine.evaluate('A', 'g')
1611
+ expect(r).to eq(nil)
1612
+ end
1613
+
1591
1614
  describe 'methods' do
1592
1615
  it 'all?' do
1593
1616
  engine.parse defn(*default_node,
@@ -190,7 +190,7 @@ describe 'Delorean' do
190
190
  cache_factor = h['delorean_node_cache'] / h['delorean']
191
191
  # p cache_factor
192
192
 
193
- expected_cache_factor = ENV['COVERAGE'] ? 64 : 80
193
+ expected_cache_factor = ENV['COVERAGE'] ? 60 : 80
194
194
  expect(cache_factor).to be > expected_cache_factor
195
195
  end
196
196
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: delorean_lang
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arman Bostani
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-17 00:00:00.000000000 Z
11
+ date: 2020-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -145,6 +145,7 @@ files:
145
145
  - lib/delorean/cache.rb
146
146
  - lib/delorean/cache/adapters.rb
147
147
  - lib/delorean/cache/adapters/base.rb
148
+ - lib/delorean/cache/adapters/no_cache.rb
148
149
  - lib/delorean/cache/adapters/ruby_cache.rb
149
150
  - lib/delorean/const.rb
150
151
  - lib/delorean/debug.rb