drgdsl 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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