drgdsl 1.0.0 → 1.1.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: c17114e6f5723fac59d6acf342e6038be2a74a6b1d31cc300cc83e8c77a1ae2b
4
- data.tar.gz: 820f8aea9f44b5e911f6112bef704cb3b8a198ac551038819364840e1cefbe4b
3
+ metadata.gz: a9715079e711fd5f608888b4b6433221db58145b85d58562e5419170496201c1
4
+ data.tar.gz: 7c83b0126a22e86431e5d8ededa65b75cb982d8fdd8b1dd2db726308c94e5b25
5
5
  SHA512:
6
- metadata.gz: 26aba3e5db76bf2e10899f1c007fb3463736ab3d3bc05f7883b5250992ad6fb8b755319aab3446c61f269a4688e317b46b219f46f190ea5360f4a59ce36e3bd8
7
- data.tar.gz: 473c59b7476fc637493742ae50814d76a9cb63f58f45921b60fa3a51880c11b4e564312556eadf2c8f0134d49ba966d0b71c69f90cda4d6c074c5a66eded7853
6
+ metadata.gz: 8051b841182ad48e4e13f57bd06bfe999f694321c1abd3c88d16065a6149d0c80d357d6a6e75fd0d56f96356bc16fabaf85f321da6a7deb66d7510e8cb8695a9
7
+ data.tar.gz: af4454d07118b2966da5074332d7549fda5b9ffc3fafae56dd151f86d40342ae23be3d089967d68e0ddcf6d8c6a7c1d388fba19b6aa61718d72ad9d1dc9f4b29
data/.rubocop.yml ADDED
@@ -0,0 +1,133 @@
1
+ # https://github.com/bbatsov/rubocop/blob/master/config/default.yml
2
+
3
+ AllCops:
4
+ DisplayCopNames: true
5
+ DisplayStyleGuide: true
6
+ TargetRubyVersion: 2.5
7
+ Include:
8
+ - Rakefile
9
+
10
+ LineLength:
11
+ Max: 80
12
+ Exclude:
13
+ - test/**/**
14
+ - lib/drgdsl/parser.rb
15
+ - lib/drgdsl/ast_builer.rb
16
+
17
+ Layout/SpaceInsideStringInterpolation:
18
+ Enabled: false
19
+
20
+ Layout/EmptyLinesAroundBlockBody:
21
+ Enabled: false
22
+
23
+ Layout/EmptyLinesAroundClassBody:
24
+ Enabled: false
25
+
26
+ Layout/EmptyLinesAroundModuleBody:
27
+ Enabled: false
28
+
29
+ Layout/EmptyLineBetweenDefs:
30
+ NumberOfEmptyLines: [1, 2]
31
+
32
+ Layout/MultilineOperationIndentation:
33
+ EnforcedStyle: indented
34
+
35
+ Layout/AlignParameters:
36
+ EnforcedStyle: with_first_parameter
37
+
38
+ Layout/DotPosition:
39
+ Enabled: false
40
+
41
+ Style/PercentLiteralDelimiters:
42
+ Enabled: false
43
+
44
+ Style/StringLiterals:
45
+ Enabled: false
46
+
47
+ Style/StringLiteralsInInterpolation:
48
+ Enabled: false
49
+
50
+ Style/SingleLineBlockParams:
51
+ Enabled: false
52
+
53
+ Style/TrivialAccessors:
54
+ AllowPredicates: true
55
+
56
+ Style/Semicolon:
57
+ AllowAsExpressionSeparator: true
58
+
59
+ Style/PerlBackrefs:
60
+ Enabled: false
61
+
62
+ Style/SingleLineMethods:
63
+ Enabled: false
64
+
65
+ Style/Documentation:
66
+ Enabled: false
67
+
68
+ Style/RegexpLiteral:
69
+ Enabled: false
70
+
71
+ Style/CommandLiteral:
72
+ Enabled: false
73
+
74
+ Style/FormatString:
75
+ Enabled: false
76
+
77
+ Style/AsciiComments:
78
+ Enabled: false
79
+
80
+ Style/SymbolProc:
81
+ AutoCorrect: false
82
+
83
+ Style/EmptyMethod:
84
+ Enabled: false
85
+
86
+ Style/MultilineTernaryOperator:
87
+ Enabled: false
88
+
89
+ Style/DoubleNegation:
90
+ Enabled: false
91
+
92
+ Metrics/ClassLength:
93
+ Enabled: false
94
+
95
+ Metrics/ModuleLength:
96
+ Enabled: false
97
+
98
+ Metrics/MethodLength:
99
+ Enabled: false
100
+
101
+ Metrics/CyclomaticComplexity:
102
+ Enabled: false
103
+
104
+ Metrics/PerceivedComplexity:
105
+ Enabled: false
106
+
107
+ Metrics/AbcSize:
108
+ Enabled: false
109
+
110
+ Metrics/ParameterLists:
111
+ CountKeywordArgs: false
112
+
113
+ Metrics/BlockLength:
114
+ Enabled: false
115
+
116
+ Naming/HeredocDelimiterNaming:
117
+ Enabled: false
118
+
119
+ Naming/PredicateName:
120
+ Enabled: false
121
+
122
+ Naming/MethodName:
123
+ Enabled: false
124
+
125
+ Naming/UncommunicativeMethodParamName:
126
+ Enabled: false
127
+
128
+ Performance/RedundantBlockCall:
129
+ Enabled: false
130
+
131
+ Lint/UnusedMethodArgument:
132
+ Enabled: true
133
+ AutoCorrect: false
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- drgdsl (1.0.0)
4
+ drgdsl (1.1.0)
5
5
  oj (~> 3.5.0)
6
6
  parslet (~> 1.8.2)
7
7
 
data/bin/html ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Generates highlighted html snippets for a bunch of expressions for
4
+ # visualization purposes.
5
+
6
+ require "bundler/setup"
7
+ require "drgdsl"
8
+
9
+ logics = []
10
+
11
+ File.read('./test/expressions.txt', encoding: 'utf-8').split("\n\n").each_with_index do |exp, i|
12
+ html = DrgDSL.pretty_print(exp, output_format: :html)
13
+ logics << html
14
+ end
15
+
16
+ puts <<~HTML
17
+ <html>
18
+
19
+ <head>
20
+ <style>
21
+ .drgdsl-keyword {
22
+ color: blue;
23
+ }
24
+
25
+ .drgdsl-function {
26
+ color: green;
27
+ }
28
+
29
+ .drgdsl-op {
30
+ color: grey;
31
+ }
32
+
33
+ .drgdsl-table {
34
+ color: pink;
35
+ }
36
+
37
+ .drgdsl-variable {
38
+ color: black;
39
+ }
40
+
41
+ .drgdsl-constant {
42
+ color: purple;
43
+ }
44
+
45
+ .drgdsl-drg, .drgdsl-adrg {
46
+ color: black;
47
+ }
48
+ </style>
49
+ </head>
50
+
51
+ <body>
52
+ #{logics.join("\n")}
53
+ </body>
54
+
55
+ </html>
56
+ HTML
data/exe/drgdsl CHANGED
@@ -9,6 +9,14 @@ option_parser = OptionParser.new do |opts|
9
9
  $options[:compact] = true
10
10
  end
11
11
 
12
+ opts.on('-o', '--output [OUTPUT]', %i[json html]) do |output|
13
+ $options[:output] = output
14
+ end
15
+
16
+ opts.on('--clean-parens', 'Remove redundant parentheses') do
17
+ $options[:clean_parens] = true
18
+ end
19
+
12
20
  opts.on_tail('-h', '--help', 'Show this message') do
13
21
  puts opts
14
22
  exit
@@ -25,15 +33,30 @@ input = if (arg = ARGV.first).is_a? String
25
33
  end
26
34
 
27
35
  begin
28
- json = DrgDSL.json(input)
36
+ case $options[:output]
37
+ when :json
38
+ json = DrgDSL.json(input)
39
+
40
+ if $options[:compact]
41
+ puts json
42
+ else
43
+ # Forgive me Matz for I have sinned
44
+ system %{echo '#{json}' | python -m json.tool}
45
+ end
46
+ when :html
47
+ puts DrgDSL.pretty_print(
48
+ input,
49
+ output_format: :html,
50
+ remove_redundant_parens: $options[:clean_parens]
51
+ )
52
+ else
53
+ puts DrgDSL.pretty_print(
54
+ input,
55
+ output_format: :bash,
56
+ remove_redundant_parens: $options[:clean_parens]
57
+ )
58
+ end
29
59
  rescue => e
30
60
  puts e.message
31
61
  exit(1)
32
62
  end
33
-
34
- if $options[:compact]
35
- puts json
36
- else
37
- # Forgive me Matz for I have sinned
38
- system %{echo '#{json}' | python -m json.tool}
39
- end
data/lib/drgdsl/ast.rb CHANGED
@@ -45,6 +45,12 @@ module DrgDSL
45
45
  def to_hash
46
46
  raise NotImplementedError
47
47
  end
48
+
49
+ def pretty_print(**pretty_printer_options)
50
+ accept PrettyPrinter.new(**pretty_printer_options)
51
+ end
52
+
53
+ alias to_s pretty_print
48
54
  end
49
55
 
50
56
  # Represents a list of expressions joined with an "OR",
@@ -314,7 +320,7 @@ module DrgDSL
314
320
  @left_condition = left_condition
315
321
  @right_condition = right_condition
316
322
  @comparison = comparison
317
- @opd = opd
323
+ @opd = opd.to_s.strip.upcase
318
324
  end
319
325
 
320
326
  def to_hash
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'pp'
2
4
 
3
5
  module DrgDSL
@@ -13,13 +15,13 @@ module DrgDSL
13
15
 
14
16
  def message
15
17
  <<~EOM
16
- Don't know how to build AST from this CST:
18
+ Don't know how to build AST from this CST:
17
19
 
18
- #{pretty cst}
20
+ #{pretty cst}
19
21
 
20
- Intermediate result:
22
+ Intermediate result:
21
23
 
22
- #{pretty result}
24
+ #{pretty result}
23
25
  EOM
24
26
  end
25
27
 
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DrgDSL
4
+ # Add core extensions here as refinements. Only load them when necessary, so
5
+ # that we don't monkeypatch applications that load DrgDSL.
6
+ #
7
+ # @example Loading the core extensions to the current lexical scope
8
+ #
9
+ # module MyScope
10
+ # using DrgDSL::CoreExtensions
11
+ #
12
+ # # Extensions exist here
13
+ #
14
+ # end
15
+ #
16
+ # # Extensions no longer exist here
17
+ #
18
+ module CoreExtensions
19
+ refine String do
20
+ def colorize(color_code)
21
+ "\e[#{color_code}m#{self}\e[0m"
22
+ end
23
+
24
+ def magenta
25
+ colorize 35
26
+ end
27
+
28
+ def cyan
29
+ colorize 36
30
+ end
31
+
32
+ def red
33
+ colorize 31
34
+ end
35
+
36
+ def yellow
37
+ colorize 33
38
+ end
39
+
40
+ def green
41
+ colorize 32
42
+ end
43
+
44
+ def blue
45
+ colorize 34
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DrgDSL
4
+ class ParenCleaner
5
+ include Visitor
6
+
7
+ # @param expression [String]
8
+ # @return [String] pretty printed expression without any redundant
9
+ # parentheses.
10
+ def self.remove_redundant_parens(expression)
11
+ ast = Parser.parse(expression)
12
+ reduced_ast = clean(ast)
13
+ reduced_ast.to_s
14
+ end
15
+
16
+ # @param ast [Node]
17
+ # @return [Node] ast without redundant ParenExpressions
18
+ def self.clean(ast, remove_outer_parens: true)
19
+ # If the whole expression is wrapped in parentheses, we can start
20
+ # visiting the first child.
21
+ if ast.type == 'ParenExpression' && remove_outer_parens
22
+ ast = ast.expression
23
+ end
24
+
25
+ ast.accept(new)
26
+ end
27
+
28
+ private
29
+
30
+ def visit_Expression(n)
31
+ Ast::Expression.new(n.expressions.map { |e| visit(e) })
32
+ end
33
+
34
+ def visit_AndExpression(n)
35
+ Ast::AndExpression.new(n.expressions.map { |e| visit(e) })
36
+ end
37
+
38
+ def visit_ParenExpression(n)
39
+ types = %w(
40
+ ParenExpression
41
+ DrgLink
42
+ BasicExpression
43
+ AndExpression
44
+ NotExpression
45
+ FunctionCall
46
+ Empty
47
+ TableCondition
48
+ )
49
+ return visit(n.expression) if types.include?(n.expression.type)
50
+
51
+ if n.expression.type == 'Expression'
52
+ return Ast::ParenExpression.new(visit(n.expression))
53
+ end
54
+
55
+ n
56
+ end
57
+ end
58
+ end
data/lib/drgdsl/parser.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DrgDSL
2
4
  class ParserError < StandardError
3
5
  attr_reader :parslet_error, :input
@@ -39,7 +41,9 @@ module DrgDSL
39
41
  # https://kschiess.github.io/parslet/tricks.html
40
42
  def stri(str)
41
43
  key_chars = str.split(//)
42
- key_chars.map { |c| match["#{c.upcase}#{c.downcase}"] }.inject(:>>)
44
+ key_chars
45
+ .map { |c| match["#{c.upcase}#{c.downcase}".squeeze] }
46
+ .inject(:>>)
43
47
  end
44
48
 
45
49
  # Tokens
@@ -51,9 +55,9 @@ module DrgDSL
51
55
  rule(:k_empty) { stri('empty') }
52
56
  rule(:k_in) { stri('in') }
53
57
  rule(:mdc) { stri('mdc') }
54
- rule(:in_table) { stri('in table') }
55
- rule(:in_tables) { stri('in tables') }
56
- rule(:all_in_table) { stri('all in table') }
58
+ rule(:in_table) { stri('in') >> sp? >> stri('table') }
59
+ rule(:in_tables) { stri('in') >> sp? >> stri('tables') }
60
+ rule(:all_in_table) { stri('all') >> sp? >> stri('in') >> sp? >> stri('table') }
57
61
  rule(:lpar) { str('(') >> sp? }
58
62
  rule(:rpar) { sp? >> str(')') }
59
63
  rule(:underscore) { str('_') }
@@ -65,11 +69,11 @@ module DrgDSL
65
69
  rule(:number) { digit.repeat(1) }
66
70
 
67
71
  rule(:comparison_operator) do
68
- stri(">=") |
69
- stri(">") |
70
- stri("<=") |
71
- stri("<") |
72
- stri("=")
72
+ str(">=") |
73
+ str(">") |
74
+ str("<=") |
75
+ str("<") |
76
+ str("=")
73
77
  end
74
78
 
75
79
  # name
@@ -106,11 +110,11 @@ module DrgDSL
106
110
 
107
111
  # srglrb
108
112
  # ::= 'SRGLRB' number{0,1}
109
- rule(:srglrb) { stri('SRGLRB') >> number.maybe }
113
+ rule(:srglrb) { stri('SRGLRB') >> number.maybe }
110
114
 
111
115
  # opd
112
116
  # ::= 'OPD' number{0,1}
113
- rule(:opd) { stri('OPD') >> number.maybe }
117
+ rule(:opd) { stri('OPD') >> number.maybe }
114
118
 
115
119
  # By "double nesting" the variable, the AST builder (a parslet transformer)
116
120
  # can use subtree(:variable) to retrieve a variable node, given there's a
@@ -243,12 +247,12 @@ module DrgDSL
243
247
  end
244
248
 
245
249
  rule(:sp) do
246
- (match("[ \t\r\n]") | comment).repeat(1)
250
+ (match("[ \t\r\n\f]") | comment).repeat(1)
247
251
  end
248
252
 
249
253
  # Optional whitespace rule. Unfortunately, skipping whitespace can't be
250
254
  # automated:
251
- # https://github.com/kschiess/parslet/issues/4://github.com/kschiess/parslet/issues/49
255
+ # https://github.com/kschiess/parslet/issues/49
252
256
  rule(:sp?) { sp.repeat }
253
257
 
254
258
  rule(:root) { expression }
@@ -0,0 +1,208 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cgi'
4
+
5
+ module DrgDSL
6
+ class PrettyPrinter
7
+ using CoreExtensions
8
+ include Visitor
9
+
10
+ OUTPUT_FORMATS = %i[string html bash].freeze
11
+ OUTPUT_FORMATS.each do |format|
12
+ define_method "output_#{format}?" do
13
+ @output_format == format
14
+ end
15
+ end
16
+
17
+ attr_reader :indentation_level, :indentation_string, :output_format
18
+
19
+ # @param expression [String]
20
+ # @return [String]
21
+ def self.pretty_print(expression, **options)
22
+ ast = Parser.parse(expression)
23
+ ast = ParenCleaner.clean(ast) if options.delete(:remove_redundant_parens)
24
+
25
+ output = ast.accept(new(**options))
26
+ output_format = options[:output_format]
27
+ return "<pre>#{output}</pre>" if output_format == :html
28
+ output
29
+ end
30
+
31
+ # @param output_format [Symbol]
32
+ # @param multiline [Boolean]
33
+ def initialize(output_format: :string,
34
+ multiline: true)
35
+
36
+ @indentation_level = 0
37
+ @indentation_string = ' '
38
+
39
+ unless OUTPUT_FORMATS.include?(output_format)
40
+ raise "Unknown output format: #{output_format.inspect}"
41
+ end
42
+
43
+ @output_format = output_format
44
+ @multiline = multiline
45
+ end
46
+
47
+ def multiline?
48
+ @multiline
49
+ end
50
+
51
+ def visit_nil
52
+ ''
53
+ end
54
+
55
+ private
56
+
57
+ def visit_Expression(n)
58
+ keyword = wrap('OR', type: :keyword)
59
+
60
+ separator = " #{keyword} "
61
+ separator = "\n#{f(keyword)} " if multiline?
62
+
63
+ n.expressions.map { |e| visit(e) }.join(separator)
64
+ end
65
+
66
+ def visit_AndExpression(n)
67
+ keyword = wrap('AND', type: :keyword)
68
+
69
+ separator = " #{keyword} "
70
+ separator = "\n#{f(keyword)} " if multiline?
71
+
72
+ n.expressions.map { |e| visit(e) }.join(separator)
73
+ end
74
+
75
+ def visit_ParenExpression(n)
76
+ return "(#{visit(n.expression)})" unless multiline?
77
+
78
+ str = +"(\n"
79
+ indented do
80
+ str << f(visit(n.expression))
81
+ end
82
+ str << "\n"
83
+ str << f(')')
84
+ end
85
+
86
+ def visit_NotExpression(n)
87
+ "#{wrap('NOT', type: :keyword)} (#{visit(n.expression)})"
88
+ end
89
+
90
+ def visit_FunctionCall(n)
91
+ wrap(n.name, type: :function)
92
+ end
93
+
94
+ def visit_DrgLink(n)
95
+ "#{visit(n.variable)} (#{wrap(n.name, type: n.variable.name.downcase)})"
96
+ end
97
+
98
+ def visit_BasicExpression(n)
99
+ "#{visit(n.variable)} #{visit(n.condition)}"
100
+ end
101
+
102
+ def visit_Comparison(n)
103
+ "#{wrap(n.op, type: :op)} #{visit(n.value)} #{visit(n.table_condition)}".strip
104
+ end
105
+
106
+ def visit_UnaryCondition(n)
107
+ "#{wrap(n.op.upcase, type: :keyword)} #{visit(n.condition)}"
108
+ end
109
+
110
+ def visit_Empty(_)
111
+ wrap('EMPTY', type: :keyword)
112
+ end
113
+
114
+ def visit_TableCondition(n)
115
+ str = +"#{wrap(n.op.upcase, type: :keyword)} "
116
+
117
+ if multiline? && n.tables.count > 4
118
+ str << "(\n"
119
+ indented do
120
+ str << n.tables.map { |t| f(wrap(t.upcase, type: :table)) }.join(",\n")
121
+ end
122
+ str << "\n"
123
+ str << f(')')
124
+ else
125
+ str << "(#{n.tables.map { |t| wrap(t.upcase, type: :table) }.join(', ')})"
126
+ end
127
+
128
+ str
129
+ end
130
+
131
+ def visit_SrglrbTableCondition(n)
132
+ "#{visit(n.variable)} #{visit(n.condition)}"
133
+ end
134
+
135
+ def visit_DateExpression(n)
136
+ "#{wrap(n.opd, type: :variable)} IN (#{visit(n.left_variable)} #{visit(n.left_condition)}#{n.right_variable && " AND #{visit(n.right_variable)} #{visit(n.right_condition)}"}) #{visit(n.comparison)}".strip
137
+ end
138
+
139
+ def visit_Variable(n)
140
+ wrap(n.name.upcase, type: :variable)
141
+ end
142
+
143
+ def visit_Constant(n)
144
+ wrap(n.value, type: :constant)
145
+ end
146
+
147
+ def indented
148
+ indent!
149
+ yield
150
+ deindent!
151
+ end
152
+
153
+ def indent!
154
+ @indentation_level += 1
155
+ end
156
+
157
+ def deindent!
158
+ @indentation_level -= 1
159
+ end
160
+
161
+ def indentation
162
+ return '' unless multiline?
163
+ @indentation_string * @indentation_level
164
+ end
165
+
166
+ # @param str [String]
167
+ # @return [String] indented string
168
+ def f(str)
169
+ "#{indentation}#{str}"
170
+ end
171
+
172
+ def wrap(str, **options)
173
+ return str if output_string?
174
+ return span(str, type: options[:type]) if output_html?
175
+ return ansi_colored(str, type: options[:type]) if output_bash?
176
+ end
177
+
178
+ def span(str, type:)
179
+ class_name = "drgdsl-#{type}"
180
+ %{<span class="#{class_name}">#{escape_html(str)}</span>}
181
+ end
182
+
183
+ def escape_html(str)
184
+ CGI.escapeHTML(str)
185
+ end
186
+
187
+ def ansi_colored(str, type:)
188
+ case type.to_sym
189
+ when :keyword
190
+ str.blue
191
+ when :function
192
+ str.green
193
+ when :op
194
+ str.blue
195
+ when :table
196
+ str.magenta
197
+ when :variable
198
+ str.cyan
199
+ when :constant
200
+ str.red
201
+ when :drg, :adrg
202
+ str.yellow
203
+ else
204
+ str
205
+ end
206
+ end
207
+ end
208
+ end
@@ -1,3 +1,3 @@
1
1
  module DrgDSL
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -1,17 +1,97 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DrgDSL
4
+ # Mixin for Ast::Node visitors. Visits all child nodes and returns the
5
+ # visited node by default.
2
6
  module Visitor
3
7
  def visit(n)
8
+ return visit_nil if n.nil?
4
9
  send("visit_#{n.type}", n)
5
10
  end
6
11
 
7
- Ast::Node.node_classes.each do |node_class|
8
- define_method("visit_#{Ast::Node.type(node_class)}") do |n|
9
- default_value
10
- end
12
+ # Override to define what should happen when one calls #visit with nil (can
13
+ # occur on optional AST branches).
14
+ #
15
+ # Does nothing by default. A string visitor might want to return an empty
16
+ # string here.
17
+ def visit_nil
18
+ end
19
+
20
+ def visit_Expression(n)
21
+ n.expressions.map { |e| visit(e) }
22
+ n
23
+ end
24
+
25
+ def visit_AndExpression(n)
26
+ n.expressions.map { |e| visit(e) }
27
+ n
28
+ end
29
+
30
+ def visit_ParenExpression(n)
31
+ visit(n.expression)
32
+ n
33
+ end
34
+
35
+ def visit_NotExpression(n)
36
+ visit(n.expression)
37
+ n
38
+ end
39
+
40
+ def visit_FunctionCall(n)
41
+ n
42
+ end
43
+
44
+ def visit_DrgLink(n)
45
+ visit(n.variable)
46
+ n
47
+ end
48
+
49
+ def visit_BasicExpression(n)
50
+ visit(n.variable)
51
+ visit(n.condition)
52
+ n
53
+ end
54
+
55
+ def visit_Comparison(n)
56
+ visit(n.value)
57
+ visit(n.table_condition)
58
+ n
59
+ end
60
+
61
+ def visit_UnaryCondition(n)
62
+ visit(n.condition)
63
+ n
64
+ end
65
+
66
+ def visit_Empty(n)
67
+ n
68
+ end
69
+
70
+ def visit_TableCondition(n)
71
+ visit(n.comparison)
72
+ n
73
+ end
74
+
75
+ def visit_SrglrbTableCondition(n)
76
+ visit(n.condition)
77
+ n
78
+ end
79
+
80
+ def visit_DateExpression(n)
81
+ visit(n.left_variable)
82
+ visit(n.left_condition)
83
+ visit(n.right_variable)
84
+ visit(n.right_condition)
85
+ visit(n.comparison)
86
+ n
87
+ end
88
+
89
+ def visit_Variable(n)
90
+ n
11
91
  end
12
92
 
13
- # @return [Object] what the visit_<node_class> methods return by default.
14
- def default_value
93
+ def visit_Constant(n)
94
+ n
15
95
  end
16
96
  end
17
97
  end
data/lib/drgdsl.rb CHANGED
@@ -1,14 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'oj'
2
4
  require 'parslet'
3
5
 
4
6
  require_relative "./drgdsl/version"
7
+ require_relative "./drgdsl/core_extensions"
5
8
  require_relative "./drgdsl/ast"
6
9
  require_relative "./drgdsl/ast_builder"
7
10
  require_relative "./drgdsl/parser"
8
11
  require_relative "./drgdsl/visitor"
12
+ require_relative "./drgdsl/pretty_printer"
13
+ require_relative "./drgdsl/paren_cleaner"
9
14
 
10
15
  module DrgDSL
11
-
12
16
  # @param input [String]
13
17
  # @return [Ast::Node]
14
18
  def self.parse(input)
@@ -16,8 +20,58 @@ module DrgDSL
16
20
  end
17
21
 
18
22
  # @param input [String]
19
- # @return [String]
23
+ # @return [String] JSON representation of the AST
20
24
  def self.json(input)
21
25
  Oj.dump parse(input).to_hash, mode: :compat
22
26
  end
27
+
28
+ # @param input [String]
29
+ # @param remove_redundant_parens [Boolean] (false)
30
+ # @param multiline [Boolean] (true) pass false to print the expression
31
+ # without any newlines.
32
+ # @param output_format [Symbol] (:string) also supports :html and :bash.
33
+ #
34
+ # @return [String] pretty printed DrgDSL expression
35
+ #
36
+ # @example Pretty printing with various options
37
+ # input = "(pdx in table (a )) and (drg (f46) or (drg (g22) and los < 3))"
38
+ # DrgDSL.pretty_print(input)
39
+ # # =>
40
+ # # (
41
+ # # PDX IN TABLE (A)
42
+ # # )
43
+ # # AND (
44
+ # # DRG (F46)
45
+ # # OR (
46
+ # # DRG (G22)
47
+ # # AND LOS < 3
48
+ # # )
49
+ # # )
50
+ #
51
+ # DrgDSL.pretty_print(input, remove_redundant_parens: true)
52
+ # # =>
53
+ # # PDX IN TABLE (A)
54
+ # # AND (
55
+ # # DRG (F46)
56
+ # # OR DRG (G22)
57
+ # # AND LOS < 3
58
+ # # )
59
+ #
60
+ # DrgDSL.pretty_print(input, remove_redundant_parens: true, multiline: false)
61
+ # # => PDX IN TABLE (A) AND (DRG (F46) OR DRG (G22) AND LOS < 3)
62
+ #
63
+ # DrgDSL.pretty_print(input, remove_redundant_parens: true, output_format: html)
64
+ # # =>
65
+ # # <pre>
66
+ # # <span class="drgdsl-variable">PDX</span> <span class="drgdsl-keyword">IN TABLE</span> (<span class="drgdsl-table">A</span>)
67
+ # # <span class="drgdsl-keyword">AND</span> (
68
+ # # <span class="drgdsl-variable">DRG</span> (<span class="drgdsl-drg">F46</span>)
69
+ # # <span class="drgdsl-keyword">OR</span> <span class="drgdsl-variable">DRG</span> (<span class="drgdsl-drg">G22</span>)
70
+ # # <span class="drgdsl-keyword">AND</span> <span class="drgdsl-variable">LOS</span> <span class="drgdsl-op">&lt;</span> <span class="drgdsl-constant">3</span>
71
+ # # )
72
+ # # </pre>
73
+ #
74
+ def self.pretty_print(input, **options)
75
+ PrettyPrinter.pretty_print(input, **options)
76
+ end
23
77
  end
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.0.0
4
+ version: 1.1.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-04-18 00:00:00.000000000 Z
11
+ date: 2018-07-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parslet
@@ -131,6 +131,7 @@ extensions: []
131
131
  extra_rdoc_files: []
132
132
  files:
133
133
  - ".gitignore"
134
+ - ".rubocop.yml"
134
135
  - ".travis.yml"
135
136
  - Gemfile
136
137
  - Gemfile.lock
@@ -138,13 +139,17 @@ files:
138
139
  - README.md
139
140
  - Rakefile
140
141
  - bin/console
142
+ - bin/html
141
143
  - bin/setup
142
144
  - drgdsl.gemspec
143
145
  - exe/drgdsl
144
146
  - lib/drgdsl.rb
145
147
  - lib/drgdsl/ast.rb
146
148
  - lib/drgdsl/ast_builder.rb
149
+ - lib/drgdsl/core_extensions.rb
150
+ - lib/drgdsl/paren_cleaner.rb
147
151
  - lib/drgdsl/parser.rb
152
+ - lib/drgdsl/pretty_printer.rb
148
153
  - lib/drgdsl/version.rb
149
154
  - lib/drgdsl/visitor.rb
150
155
  homepage: https://www.swissdrg.org