drgdsl 1.1.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a9715079e711fd5f608888b4b6433221db58145b85d58562e5419170496201c1
4
- data.tar.gz: 7c83b0126a22e86431e5d8ededa65b75cb982d8fdd8b1dd2db726308c94e5b25
3
+ metadata.gz: 346dd382500a954f17d93e1d1385e6cb8af10b02059298776973f0af8389686d
4
+ data.tar.gz: 3add8eaaa026e2bd054c230933be1e546592da9c919b67155e35165025a89e5a
5
5
  SHA512:
6
- metadata.gz: 8051b841182ad48e4e13f57bd06bfe999f694321c1abd3c88d16065a6149d0c80d357d6a6e75fd0d56f96356bc16fabaf85f321da6a7deb66d7510e8cb8695a9
7
- data.tar.gz: af4454d07118b2966da5074332d7549fda5b9ffc3fafae56dd151f86d40342ae23be3d089967d68e0ddcf6d8c6a7c1d388fba19b6aa61718d72ad9d1dc9f4b29
6
+ metadata.gz: f7fda718baaaa0077ed81705b6178a11b8ba8d170b19d53aa7b4fc61d1ed86f18514ec153422b9406d4b429b5d53f467fb91e92dec3e4289d2ded36f1b6eae5f
7
+ data.tar.gz: '00138b8f522e3c277163a86724a011e92e83845aae9a9fd6fdf67921b7f0bc7dde2030de8f78ed80bf4756dd2765a109057e1a41e724e8333fe907dd2394ec31'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- drgdsl (1.1.0)
4
+ drgdsl (1.2.0)
5
5
  oj (~> 3.5.0)
6
6
  parslet (~> 1.8.2)
7
7
 
data/bin/cache ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require "bundler/setup"
6
+ require "drgdsl"
7
+ require 'benchmark'
8
+
9
+ string_printer = DrgDSL::PrettyPrinter.new(output_format: :string)
10
+ html_printer = DrgDSL::PrettyPrinter.new(output_format: :html)
11
+ bash_printer = DrgDSL::PrettyPrinter.new(output_format: :bash)
12
+
13
+ asts = File.read('./test/expressions.txt', encoding: 'utf-8')
14
+ .split("\n\n")
15
+ .map { |exp| DrgDSL.parse(exp) }
16
+
17
+ N = 2
18
+
19
+ puts "Pretty printing #{N} * #{asts.count} ASTs:"
20
+
21
+ t1 = Benchmark.realtime do
22
+ N.times do
23
+ asts.each do |ast|
24
+ ast.accept(string_printer)
25
+ ast.accept(html_printer)
26
+ ast.accept(bash_printer)
27
+ end
28
+ end
29
+ end
30
+ puts "PrettyPrinter while building up cache: \t#{t1}"
31
+
32
+ t2 = Benchmark.realtime do
33
+ N.times do
34
+ asts.each do |ast|
35
+ ast.accept(string_printer)
36
+ ast.accept(html_printer)
37
+ ast.accept(bash_printer)
38
+ end
39
+ end
40
+ end
41
+ puts "PrettyPrinter with cache hits only: \t#{t2}"
42
+
43
+ [string_printer, html_printer, bash_printer].each do |printer|
44
+ class << printer
45
+ def cache?
46
+ false
47
+ end
48
+ end
49
+ end
50
+
51
+ t3 = Benchmark.realtime do
52
+ N.times do
53
+ asts.each do |ast|
54
+ ast.accept(string_printer)
55
+ ast.accept(html_printer)
56
+ ast.accept(bash_printer)
57
+ end
58
+ end
59
+ end
60
+ puts "PrettyPrinter with caching disabled: \t#{t3}"
61
+
62
+ puts "Speed increase when enabling caching: \t#{(100 / t3 * (t3 - t1)).round(2)}%"
63
+ puts "Speed increase when cache fully built up: \t#{(100 / t3 * (t3 - t2)).round(2)}%"
data/bin/html CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ # frozen_string_literal: true
4
+
3
5
  # Generates highlighted html snippets for a bunch of expressions for
4
6
  # visualization purposes.
5
7
 
data/lib/drgdsl/ast.rb CHANGED
@@ -1,9 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DrgDSL
2
4
 
3
5
  # Nodes in DRG logic AST.
4
6
  #
5
7
  # @see AstBuilder
6
8
  module Ast
9
+ def self.node_classes
10
+ Node.node_classes
11
+ end
12
+
7
13
  module Node
8
14
  class << self
9
15
  def node_classes
@@ -46,6 +52,19 @@ module DrgDSL
46
52
  raise NotImplementedError
47
53
  end
48
54
 
55
+ # @return [Integer] object hash for this AST node. Node instances for the
56
+ # same expression should always return the same hash code. This is
57
+ # useful for optimizations.
58
+ def hash
59
+ raise NotImplementedError
60
+ end
61
+
62
+ def ==(other)
63
+ type == other.type && hash == other.hash
64
+ end
65
+
66
+ alias eql? ==
67
+
49
68
  def pretty_print(**pretty_printer_options)
50
69
  accept PrettyPrinter.new(**pretty_printer_options)
51
70
  end
@@ -79,6 +98,10 @@ module DrgDSL
79
98
  def to_hash
80
99
  { or: [expressions.map(&:to_hash)] }
81
100
  end
101
+
102
+ def hash
103
+ @hash ||= [type, *expressions].hash
104
+ end
82
105
  end
83
106
 
84
107
  # Represents a list of expressions joined with an "AND",
@@ -93,7 +116,11 @@ module DrgDSL
93
116
  end
94
117
 
95
118
  def to_hash
96
- { and: [ expressions.map(&:to_hash) ] }
119
+ { and: [expressions.map(&:to_hash)] }
120
+ end
121
+
122
+ def hash
123
+ @hash ||= [type, *expressions].hash
97
124
  end
98
125
  end
99
126
 
@@ -110,6 +137,10 @@ module DrgDSL
110
137
  def to_hash
111
138
  { paren: expression.to_hash }
112
139
  end
140
+
141
+ def hash
142
+ @hash ||= [type, expression].hash
143
+ end
113
144
  end
114
145
 
115
146
  # A NotExpression is a logic text of the form
@@ -126,6 +157,10 @@ module DrgDSL
126
157
  def to_hash
127
158
  { not: expression.to_hash }
128
159
  end
160
+
161
+ def hash
162
+ @hash ||= [type, expression].hash
163
+ end
129
164
  end
130
165
 
131
166
  # "Komplizierende_Prozeduren", "Dialyse", ...
@@ -141,6 +176,10 @@ module DrgDSL
141
176
  def to_hash
142
177
  { function_call: name }
143
178
  end
179
+
180
+ def hash
181
+ @hash ||= [type, name].hash
182
+ end
144
183
  end
145
184
 
146
185
  # A drg link originates from the grammar rule basic_or_drg_link
@@ -160,6 +199,10 @@ module DrgDSL
160
199
  def to_hash
161
200
  { drg_link: { var: variable.to_hash, name: name } }
162
201
  end
202
+
203
+ def hash
204
+ @hash ||= [type, name, variable].hash
205
+ end
163
206
  end
164
207
 
165
208
  # A BasicExpression consists of a variable and a condition
@@ -187,9 +230,14 @@ module DrgDSL
187
230
  def to_hash
188
231
  { basic: { var: variable.to_hash, condition: condition.to_hash } }
189
232
  end
233
+
234
+ def hash
235
+ @hash ||= [type, variable, condition].hash
236
+ end
190
237
  end
191
238
 
192
- # A comparison is either from a table_condition, a condition or a date_expression
239
+ # A comparison is either from a table_condition, a condition or a
240
+ # date_expression
193
241
  class Comparison
194
242
  include Node
195
243
 
@@ -210,6 +258,10 @@ module DrgDSL
210
258
  }
211
259
  }
212
260
  end
261
+
262
+ def hash
263
+ @hash ||= [type, op, value, table_condition].hash
264
+ end
213
265
  end
214
266
 
215
267
  # Has a certain operator (i.e. not / different) and a condition
@@ -227,13 +279,17 @@ module DrgDSL
227
279
  end
228
280
 
229
281
  def to_hash
230
- {
282
+ {
231
283
  unary_condition: {
232
284
  op: op,
233
285
  condition: condition.to_hash
234
286
  }
235
287
  }
236
288
  end
289
+
290
+ def hash
291
+ @hash ||= [type, op, condition].hash
292
+ end
237
293
  end
238
294
 
239
295
  class Empty
@@ -242,15 +298,21 @@ module DrgDSL
242
298
  def to_hash
243
299
  { empty: nil }
244
300
  end
301
+
302
+ def hash
303
+ @hash ||= type.hash
304
+ end
245
305
  end
246
306
 
247
- # There are 3 variants of table conditions (in_table, in_tables, all_in_table)
248
- # A table condition can have an optional comparison.
249
- # Tables refers to the table names (the internal names)
307
+ # There are 3 variants of table conditions (in_table, in_tables,
308
+ # all_in_table)
309
+ #
310
+ # A table condition can have an optional comparison. Tables refers to the
311
+ # table names (the internal names)
250
312
  class TableCondition
251
- IN_TABLE = 'in table'.freeze
252
- IN_TABLES = 'in tables'.freeze
253
- ALL_IN_TABLE = 'all in table'.freeze
313
+ IN_TABLE = 'in table'
314
+ IN_TABLES = 'in tables'
315
+ ALL_IN_TABLE = 'all in table'
254
316
 
255
317
  include Node
256
318
 
@@ -273,6 +335,10 @@ module DrgDSL
273
335
  }
274
336
  }
275
337
  end
338
+
339
+ def hash
340
+ @hash ||= [type, op, *tables, comparison].hash
341
+ end
276
342
  end
277
343
 
278
344
  # INFO: srg (prozedur) l (links) r (rechts) b(beidseitig)
@@ -294,17 +360,21 @@ module DrgDSL
294
360
  }
295
361
  }
296
362
  end
363
+
364
+ def hash
365
+ @hash ||= [type, variable, condition].hash
366
+ end
297
367
  end
298
368
 
299
369
  class DateExpression
300
370
  include Node
301
371
 
302
372
  attr_reader :left_variable,
303
- :right_variable,
304
- :left_condition,
305
- :right_condition,
306
- :comparison,
307
- :opd
373
+ :right_variable,
374
+ :left_condition,
375
+ :right_condition,
376
+ :comparison,
377
+ :opd
308
378
 
309
379
  # @param opd [String] E.g. "opd4" => "mind. 4 erfüllen" query
310
380
  def initialize(left_variable:,
@@ -312,8 +382,7 @@ module DrgDSL
312
382
  left_condition:,
313
383
  right_condition: nil,
314
384
  comparison:,
315
- opd:
316
- )
385
+ opd:)
317
386
 
318
387
  @left_variable = left_variable
319
388
  @right_variable = right_variable
@@ -335,6 +404,18 @@ module DrgDSL
335
404
  }
336
405
  }
337
406
  end
407
+
408
+ def hash
409
+ @hash ||= [
410
+ type,
411
+ left_variable,
412
+ right_variable,
413
+ left_condition,
414
+ right_condition,
415
+ comparison,
416
+ opd
417
+ ].hash
418
+ end
338
419
  end
339
420
 
340
421
  # "PDX", "AGEYEARS", ...
@@ -350,6 +431,10 @@ module DrgDSL
350
431
  def to_hash
351
432
  { name: name }
352
433
  end
434
+
435
+ def hash
436
+ @hash ||= [type, name].hash
437
+ end
353
438
  end
354
439
 
355
440
  # "3", "'MDC'", ...
@@ -369,6 +454,10 @@ module DrgDSL
369
454
  def to_hash
370
455
  { constant: { value: value } }
371
456
  end
457
+
458
+ def hash
459
+ @hash ||= [type, value].hash
460
+ end
372
461
  end
373
462
  end
374
463
  end
data/lib/drgdsl/parser.rb CHANGED
@@ -171,7 +171,7 @@ module DrgDSL
171
171
 
172
172
  # condition
173
173
  # ::= comparison
174
- # | unary_operator
174
+ # | unary_condition
175
175
  # | empty
176
176
  # | table_condition
177
177
  rule(:condition) do
@@ -52,6 +52,20 @@ module DrgDSL
52
52
  ''
53
53
  end
54
54
 
55
+ def cache?
56
+ true
57
+ end
58
+
59
+ def cache_key(n)
60
+ [
61
+ n.hash,
62
+ @indentation_level,
63
+ @indentation_string,
64
+ @output_format,
65
+ @multiline
66
+ ].hash
67
+ end
68
+
55
69
  private
56
70
 
57
71
  def visit_Expression(n)
@@ -1,3 +1,3 @@
1
1
  module DrgDSL
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -1,9 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DrgDSL
4
+
4
5
  # Mixin for Ast::Node visitors. Visits all child nodes and returns the
5
6
  # visited node by default.
6
7
  module Visitor
8
+ def self.included(visitor)
9
+ visitor.prepend Cache
10
+ visitor.extend ClassMethods
11
+ end
12
+
13
+ module Cache
14
+ # @param n [Ast::Node]
15
+ def visit(n)
16
+ return super(n) unless cache?
17
+ self.class.cache[cache_key(n)] ||= super(n)
18
+ end
19
+ end
20
+
21
+ module ClassMethods
22
+ # @return [Hash] in-memory Ruby Hash
23
+ def cache
24
+ @cache ||= {}
25
+ end
26
+
27
+ def clear_cache!
28
+ @cache = {}
29
+ end
30
+ end
31
+
32
+ # Whether this visitor should cache visit results. Note that one might need
33
+ # to override #cache_key(n) if a visit is not deterministic, e.g. when it
34
+ # depends on some instance variables.
35
+ #
36
+ # @see PrettyPrinter#cache_key
37
+ #
38
+ # @return [Boolean]
39
+ def cache?
40
+ false
41
+ end
42
+
43
+ # @param n [Ast::Node]
44
+ # @return [Integer] ideally an object hash
45
+ def cache_key(n)
46
+ n.hash
47
+ end
48
+
49
+ # @param n [Ast::Node]
7
50
  def visit(n)
8
51
  return visit_nil if n.nil?
9
52
  send("visit_#{n.type}", n)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: drgdsl
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SwissDRG AG
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-07-11 00:00:00.000000000 Z
11
+ date: 2018-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parslet
@@ -138,6 +138,7 @@ files:
138
138
  - LICENSE.txt
139
139
  - README.md
140
140
  - Rakefile
141
+ - bin/cache
141
142
  - bin/console
142
143
  - bin/html
143
144
  - bin/setup