gloss 0.0.3 → 0.1.1

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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +3 -0
  3. data/.github/workflows/{crystal.yml → crystal_specs.yml} +1 -1
  4. data/.github/workflows/{ruby.yml → ruby_specs.yml} +2 -2
  5. data/.gloss.yml +1 -0
  6. data/.rspec +1 -0
  7. data/Gemfile.lock +10 -12
  8. data/README.md +36 -5
  9. data/Rakefile +1 -1
  10. data/exe/gloss +13 -2
  11. data/ext/gloss/Makefile +8 -19
  12. data/ext/gloss/{src/lib → lib}/cr_ruby.cr +0 -0
  13. data/ext/gloss/lib/rbs_types.cr +3 -0
  14. data/ext/gloss/spec/parser_spec.cr +83 -83
  15. data/ext/gloss/src/cr_ast.cr +96 -77
  16. data/ext/gloss/src/gloss.cr +2 -2
  17. data/ext/gloss/src/lexer.cr +59 -1
  18. data/ext/gloss/src/rb_ast.cr +114 -63
  19. data/lib/gloss.rb +15 -7
  20. data/lib/gloss/cli.rb +85 -28
  21. data/lib/gloss/config.rb +13 -7
  22. data/lib/gloss/errors.rb +3 -4
  23. data/lib/gloss/initializer.rb +9 -9
  24. data/lib/gloss/logger.rb +29 -0
  25. data/lib/gloss/parser.rb +19 -5
  26. data/lib/gloss/prog_loader.rb +141 -0
  27. data/lib/gloss/scope.rb +7 -2
  28. data/lib/gloss/source.rb +17 -14
  29. data/lib/gloss/type_checker.rb +86 -33
  30. data/lib/gloss/utils.rb +44 -0
  31. data/lib/gloss/version.rb +1 -2
  32. data/lib/gloss/visitor.rb +667 -0
  33. data/lib/gloss/watcher.rb +51 -16
  34. data/lib/gloss/writer.rb +24 -15
  35. data/sig/core.rbs +2 -0
  36. data/sig/fast_blank.rbs +4 -0
  37. data/sig/{gloss.rbs → gls.rbs} +0 -0
  38. data/sig/listen.rbs +1 -0
  39. data/sig/optparse.rbs +6 -0
  40. data/sig/rubygems.rbs +9 -0
  41. data/sig/yaml.rbs +3 -0
  42. data/src/exe/gloss +19 -0
  43. data/src/lib/gloss.gl +26 -0
  44. data/src/lib/gloss/cli.gl +70 -0
  45. data/src/lib/gloss/config.gl +9 -3
  46. data/src/lib/gloss/initializer.gl +4 -6
  47. data/src/lib/gloss/logger.gl +21 -0
  48. data/src/lib/gloss/parser.gl +17 -5
  49. data/src/lib/gloss/prog_loader.gl +133 -0
  50. data/src/lib/gloss/scope.gl +7 -0
  51. data/src/lib/gloss/source.gl +32 -0
  52. data/src/lib/gloss/type_checker.gl +85 -36
  53. data/src/lib/gloss/utils.gl +38 -0
  54. data/src/lib/gloss/version.gl +1 -1
  55. data/src/lib/gloss/visitor.gl +575 -0
  56. data/src/lib/gloss/watcher.gl +44 -10
  57. data/src/lib/gloss/writer.gl +16 -14
  58. metadata +28 -8
  59. data/lib/gloss/builder.rb +0 -447
@@ -1,30 +1,64 @@
1
- # frozen_string_literal: true
2
-
3
1
  require "listen"
4
2
 
5
3
  module Gloss
6
4
  class Watcher
7
- def initialize
8
- @paths = %w[src/]
5
+ def initialize(@paths : Array[String])
6
+ if @paths.empty?
7
+ @paths = [File.join(Dir.pwd, Config.src_dir)]
8
+ # either any filepath with .gl extension, or executable with extension
9
+ @only = /(?:(\.gl|(?:(?<=\/)[^\.\/]+))\z|\A[^\.\/]+\z)/
10
+ else
11
+ file_names = Array.new
12
+ paths = Array.new
13
+ @paths.each do |pa|
14
+ pn = Pathname.new(pa)
15
+ paths << pn.parent.to_s
16
+ file_names << (pn.file? ? pn.basename.to_s : pa)
17
+ end
18
+ @paths = paths.uniq
19
+ @only = /#{Regexp.union(file_names)}/
20
+ end
9
21
  end
10
22
 
11
23
  def watch
12
- puts "=====> Now listening for changes in #{@paths.join(', ')}"
13
- listener = Listen.to(*@paths, latency: 2) do |modified, added, removed|
24
+ Gloss.logger.info "Now listening for changes in #{@paths.join(', ')}"
25
+ listener = Listen.to(
26
+ *@paths,
27
+ latency: 2,
28
+ only: @only
29
+ ) do |modified, added, removed|
14
30
  (modified + added).each do |f|
31
+ Gloss.logger.info "Rewriting #{f}"
15
32
  content = File.read(f)
16
- Writer.new(Builder.new(content).run, f).run
33
+ err = catch :error do
34
+ Writer.new(
35
+ Visitor.new(
36
+ Parser.new(
37
+ content
38
+ ).run
39
+ ).run, f
40
+ ).run
41
+ nil
42
+ end
43
+ if err
44
+ Gloss.logger.error err
45
+ else
46
+ Gloss.logger.info "Done"
47
+ end
17
48
  end
18
49
  removed.each do |f|
19
50
  out_path = Utils.src_path_to_output_path(f)
51
+ Gloss.logger.info "Removing #{out_path}"
20
52
  File.delete out_path if File.exist? out_path
53
+
54
+ Gloss.logger.info "Done"
21
55
  end
22
56
  end
23
- listener.start
24
57
  begin
25
- loop { sleep 10 }
58
+ listener.start
59
+ sleep
26
60
  rescue Interrupt
27
- puts "=====> Interrupt signal received, shutting down"
61
+ Gloss.logger.info "Interrupt signal received, shutting down"
28
62
  exit 0
29
63
  end
30
64
  end
@@ -4,29 +4,31 @@ require "pathname"
4
4
  require "fileutils"
5
5
 
6
6
  module Gloss
7
- module Utils
8
- module_function
9
-
10
- def src_path_to_output_path(src_path)
11
- src_path.sub(%r{\A(?:\./)?#{Config.src_dir}/?}, "")
12
- .sub(/\.gl$/, ".rb")
13
- end
14
- end
15
-
16
7
  class Writer
17
- include Utils
18
-
19
8
  def initialize(
20
9
  @content,
21
- src_path : String?,
22
- @output_path : Pathname = Pathname.new(src_path_to_output_path(src_path))
10
+ @src_path : String,
11
+ @output_path : Pathname? = Pathname.new(
12
+ Utils.src_path_to_output_path(src_path)
13
+ )
23
14
  )
24
15
  end
25
16
 
26
17
  def run
27
18
  FileUtils.mkdir_p(@output_path.parent) unless @output_path.parent.exist?
28
19
  File.open(@output_path, "wb") do |file|
29
- file << @content
20
+ sb = shebang
21
+ file.puts sb if sb
22
+ file.puts @content
23
+ end
24
+ end
25
+
26
+ private def shebang
27
+ if @output_path.executable?
28
+ first_line = File.open(@src_path) { |f| f.readline }
29
+ first_line.start_with?("#!") ? first_line : nil
30
+ else
31
+ nil
30
32
  end
31
33
  end
32
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gloss
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - johansenja
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-20 00:00:00.000000000 Z
11
+ date: 2021-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fast_blank
@@ -131,10 +131,12 @@ extensions:
131
131
  - ext/gloss/extconf.rb
132
132
  extra_rdoc_files: []
133
133
  files:
134
- - ".github/workflows/crystal.yml"
135
- - ".github/workflows/ruby.yml"
134
+ - ".gitattributes"
135
+ - ".github/workflows/crystal_specs.yml"
136
+ - ".github/workflows/ruby_specs.yml"
136
137
  - ".gitignore"
137
138
  - ".gloss.yml"
139
+ - ".rspec"
138
140
  - ".rubocop.yml"
139
141
  - Gemfile
140
142
  - Gemfile.lock
@@ -145,37 +147,55 @@ files:
145
147
  - exe/gloss
146
148
  - ext/gloss/Makefile
147
149
  - ext/gloss/extconf.rb
150
+ - ext/gloss/lib/cr_ruby.cr
151
+ - ext/gloss/lib/rbs_types.cr
148
152
  - ext/gloss/shard.yml
149
153
  - ext/gloss/spec/parser_spec.cr
150
154
  - ext/gloss/spec/spec_helper.cr
151
155
  - ext/gloss/src/cr_ast.cr
152
156
  - ext/gloss/src/gloss.cr
153
157
  - ext/gloss/src/lexer.cr
154
- - ext/gloss/src/lib/cr_ruby.cr
155
158
  - ext/gloss/src/parser.cr
156
159
  - ext/gloss/src/rb_ast.cr
157
160
  - gloss.gemspec
158
161
  - lib/gloss.rb
159
- - lib/gloss/builder.rb
160
162
  - lib/gloss/cli.rb
161
163
  - lib/gloss/config.rb
162
164
  - lib/gloss/errors.rb
163
165
  - lib/gloss/initializer.rb
166
+ - lib/gloss/logger.rb
164
167
  - lib/gloss/parser.rb
168
+ - lib/gloss/prog_loader.rb
165
169
  - lib/gloss/scope.rb
166
170
  - lib/gloss/source.rb
167
171
  - lib/gloss/type_checker.rb
172
+ - lib/gloss/utils.rb
168
173
  - lib/gloss/version.rb
174
+ - lib/gloss/visitor.rb
169
175
  - lib/gloss/watcher.rb
170
176
  - lib/gloss/writer.rb
171
- - sig/gloss.rbs
177
+ - sig/core.rbs
178
+ - sig/fast_blank.rbs
179
+ - sig/gls.rbs
172
180
  - sig/listen.rbs
181
+ - sig/optparse.rbs
182
+ - sig/rubygems.rbs
183
+ - sig/yaml.rbs
184
+ - src/exe/gloss
185
+ - src/lib/gloss.gl
186
+ - src/lib/gloss/cli.gl
173
187
  - src/lib/gloss/config.gl
174
188
  - src/lib/gloss/errors.gl
175
189
  - src/lib/gloss/initializer.gl
190
+ - src/lib/gloss/logger.gl
176
191
  - src/lib/gloss/parser.gl
192
+ - src/lib/gloss/prog_loader.gl
193
+ - src/lib/gloss/scope.gl
194
+ - src/lib/gloss/source.gl
177
195
  - src/lib/gloss/type_checker.gl
196
+ - src/lib/gloss/utils.gl
178
197
  - src/lib/gloss/version.gl
198
+ - src/lib/gloss/visitor.gl
179
199
  - src/lib/gloss/watcher.gl
180
200
  - src/lib/gloss/writer.gl
181
201
  homepage:
@@ -197,7 +217,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
197
217
  - !ruby/object:Gem::Version
198
218
  version: '0'
199
219
  requirements: []
200
- rubygems_version: 3.2.3
220
+ rubygems_version: 3.1.2
201
221
  signing_key:
202
222
  specification_version: 4
203
223
  summary: A superset of ruby
data/lib/gloss/builder.rb DELETED
@@ -1,447 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gloss
4
- class Builder
5
- attr_reader :tree
6
-
7
- def initialize(tree_hash, type_checker = nil)
8
- @indent_level = 0
9
- @inside_macro = false
10
- @eval_vars = false
11
- @current_scope = nil
12
- @tree = tree_hash
13
- @type_checker = type_checker
14
- end
15
-
16
- def run
17
- rb_output = visit_node(@tree)
18
- <<~RUBY
19
- #{"# frozen_string_literal: true\n" if Config.frozen_string_literals}
20
- ##### This file was generated by Gloss; any changes made here will be overwritten.
21
- ##### See #{Config.src_dir}/ to make changes
22
-
23
- #{rb_output}
24
- RUBY
25
- end
26
-
27
- def visit_node(node, scope = Scope.new)
28
- src = Source.new(@indent_level)
29
- case node[:type]
30
- when "ClassNode"
31
- class_name = visit_node(node[:name])
32
- current_namespace = @current_scope ? @current_scope.name.to_namespace : RBS::Namespace.root
33
- superclass_type = nil
34
- superclass_output = nil
35
- if node[:superclass]
36
- @eval_vars = true
37
- superclass_output = visit_node(node[:superclass])
38
- @eval_vars = false
39
- ns = if superclass_output.start_with? '::'
40
- RBS::Namespace.root
41
- elsif superclass_output.include? '::'
42
- current_namespace
43
- else
44
- RBS::Namespace.empty
45
- end
46
- superclass_type = RBS::AST::Declarations::Class::Super.new(
47
- name: RBS::TypeName.new(
48
- name: superclass_output.to_sym,
49
- namespace: ns
50
- ),
51
- args: [],
52
- location: nil
53
- )
54
- end
55
-
56
- src.write_ln "class #{class_name}#{" < #{superclass_output}" if superclass_output}"
57
-
58
- class_type = RBS::AST::Declarations::Class.new(
59
- name: RBS::TypeName.new(
60
- namespace: current_namespace,
61
- name: class_name.to_sym
62
- ),
63
- type_params: RBS::AST::Declarations::ModuleTypeParams.new, # responds to #add to add params
64
- super_class: superclass_type,
65
- members: [],
66
- annotations: [],
67
- location: node[:location],
68
- comment: node[:comment]
69
- )
70
- old_parent_scope = @current_scope
71
- @current_scope = class_type
72
-
73
- indented(src) { src.write_ln visit_node(node[:body]) if node[:body] }
74
-
75
- src.write_ln "end"
76
-
77
- @current_scope = old_parent_scope
78
-
79
- @current_scope.members << class_type if @current_scope
80
-
81
- if @type_checker
82
- @type_checker.top_level_decls[class_type.name.name] = class_type unless @current_scope
83
- end
84
- when "ModuleNode"
85
- module_name = visit_node node[:name]
86
- src.write_ln "module #{module_name}"
87
-
88
- current_namespace = @current_scope ? @current_scope.name.to_namespace : RBS::Namespace.root
89
-
90
- module_type = RBS::AST::Declarations::Module.new(
91
- name: RBS::TypeName.new(
92
- namespace: current_namespace,
93
- name: module_name.to_sym
94
- ),
95
- type_params: RBS::AST::Declarations::ModuleTypeParams.new, # responds to #add to add params
96
- self_types: [],
97
- members: [],
98
- annotations: [],
99
- location: node[:location],
100
- comment: node[:comment]
101
- )
102
- old_parent_scope = @current_scope
103
- @current_scope = module_type
104
-
105
- indented(src) { src.write_ln visit_node(node[:body]) if node[:body] }
106
-
107
- @current_scope = old_parent_scope
108
-
109
- @current_scope.members << module_type if @current_scope
110
-
111
- if @type_checker
112
- @type_checker.top_level_decls[module_type.name.name] = module_type unless @current_scope
113
- end
114
- src.write_ln "end"
115
- when "DefNode"
116
- args = render_args(node)
117
- src.write_ln "def #{node[:name]}#{args}"
118
-
119
- return_type = if node[:return_type]
120
- RBS::Types::ClassInstance.new(
121
- name: RBS::TypeName.new(
122
- name: eval(visit_node(node[:return_type])).to_s.to_sym,
123
- namespace: RBS::Namespace.root
124
- ),
125
- args: [],
126
- location: nil
127
- )
128
- else
129
- RBS::Types::Bases::Any.new(location: nil)
130
- end
131
-
132
- method_types = [
133
- RBS::MethodType.new(
134
- type_params: [],
135
- type: RBS::Types::Function.new(
136
- required_positionals: node[:rp_args]&.map do |a|
137
- RBS::Types::Function::Param.new(
138
- name: visit_node(a).to_sym,
139
- type: RBS::Types::Bases::Any.new(location: nil)
140
- )
141
- end || EMPTY_ARRAY,
142
- optional_positionals: node[:op_args] || EMPTY_ARRAY,
143
- rest_positionals: node[:rest_p_args],
144
- trailing_positionals: [],
145
- required_keywords: node[:req_kw_args] || EMPTY_HASH,
146
- optional_keywords: node[:opt_kw_args] || EMPTY_HASH,
147
- rest_keywords: node[:rest_kw_args] ?
148
- RBS::Types::Function::Param.new(
149
- name: visit_node(node[:rest_kw_args]).to_sym,
150
- type: RBS::Types::Bases::Any.new(location: nil)
151
- ) : nil,
152
- return_type: return_type
153
- ),
154
- block: nil,
155
- location: nil
156
- )
157
- ]
158
- method_definition = RBS::AST::Members::MethodDefinition.new(
159
- name: node[:name].to_sym,
160
- kind: :instance,
161
- types: method_types,
162
- annotations: [],
163
- location: node[:location],
164
- comment: node[:comment],
165
- overload: false
166
- )
167
-
168
- if @current_scope
169
- @current_scope.members << method_definition
170
- else
171
- @type_checker.type_env << method_definition if @type_checker # should be new class declaration for Object with method_definition as private method
172
- end
173
-
174
- indented(src) { src.write_ln visit_node(node[:body]) if node[:body] }
175
-
176
- src.write_ln "end"
177
- when "CollectionNode"
178
- src.write(*node[:children].map { |a| visit_node(a, scope) })
179
- when "Call"
180
- obj = node[:object] ? "#{visit_node(node[:object], scope)}." : ""
181
- args = node[:args] || EMPTY_ARRAY
182
- args += node[:named_args] if node[:named_args]
183
- args = if !args.empty? || node[:block_arg]
184
- "(#{args.map { |a| visit_node(a, scope).strip }.reject(&:blank?).join(", ")}#{"&#{visit_node(node[:block_arg]).strip}" if node[:block_arg]})"
185
- else
186
- nil
187
- end
188
- block = node[:block] ? " #{visit_node(node[:block])}" : nil
189
- src.write_ln "#{obj}#{node[:name]}#{args}#{block}"
190
-
191
- when "Block"
192
-
193
- src.write "{ |#{node[:args].map { |a| visit_node a }.join(", ")}|\n"
194
-
195
- indented(src) { src.write visit_node(node[:body]) }
196
-
197
- src.write_ln "}"
198
-
199
- when "RangeLiteral"
200
- dots = node[:exclusive] ? "..." : ".."
201
-
202
- # parentheses help the compatibility with precendence of operators in some situations
203
- # eg. (1..3).cover? 2 vs. 1..3.cover? 2
204
- src.write "(", visit_node(node[:from]), dots, visit_node(node[:to]), ")"
205
-
206
- when "LiteralNode"
207
-
208
- src.write node[:value]
209
-
210
- when "ArrayLiteral"
211
-
212
- src.write("[", *node[:elements].map { |e| visit_node e }.join(", "), "]")
213
- src.write ".freeze" if node[:frozen]
214
-
215
- when "StringInterpolation"
216
-
217
- contents = node[:contents].inject(String.new) do |str, c|
218
- str << case c[:type]
219
- when "LiteralNode"
220
- c[:value][1...-1]
221
- else
222
- "\#{#{visit_node(c).strip}}"
223
- end
224
- end
225
- src.write '"', contents, '"'
226
-
227
- when "Path"
228
-
229
- src.write node[:value]
230
-
231
- when "Require"
232
-
233
- src.write_ln %(require "#{node[:value]}")
234
-
235
- when "Assign", "OpAssign"
236
-
237
- src.write_ln "#{visit_node(node[:target])} #{node[:op]}= #{visit_node(node[:value]).strip}"
238
-
239
- when "Var"
240
-
241
- if @eval_vars
242
- src.write scope[node[:name]]
243
- else
244
- src.write node[:name]
245
- end
246
-
247
- when "InstanceVar"
248
-
249
- src.write node[:name]
250
-
251
- when "GlobalVar"
252
-
253
- src.write node[:name]
254
-
255
- when "Arg"
256
- val = node[:external_name]
257
- if node[:keyword_arg]
258
- val += ":"
259
- val += " #{visit_node(node[:value])}" if node[:value]
260
- elsif node[:value]
261
- val += " = #{visit_node(node[:value])}"
262
- end
263
-
264
- src.write val
265
-
266
- when "UnaryExpr"
267
-
268
- src.write "#{node[:op]}#{visit_node(node[:value]).strip}"
269
-
270
- when "BinaryOp"
271
-
272
- src.write visit_node(node[:left]).strip, " #{node[:op]} ", visit_node(node[:right]).strip
273
-
274
- when "HashLiteral"
275
-
276
- contents = node[:elements].map do |k, v|
277
- key = case k
278
- when String
279
- k.to_sym
280
- else
281
- visit_node k
282
- end
283
- value = visit_node v
284
- "#{key.inspect} => #{value}"
285
- end
286
-
287
- src.write "{#{contents.join(",\n")}}"
288
- src.write ".freeze" if node[:frozen]
289
-
290
- when "Enum"
291
- src.write_ln "module #{node[:name]}"
292
- node[:members].each_with_index do |m, i|
293
- indented(src) { src.write_ln(visit_node(m) + (!m[:value] ? " = #{i}" : "")) }
294
- end
295
- src.write_ln "end"
296
- when "If"
297
- src.write_ln "(if #{visit_node(node[:condition]).strip}"
298
-
299
- indented(src) { src.write_ln visit_node(node[:then]) }
300
-
301
- if node[:else]
302
- src.write_ln "else"
303
- indented(src) { src.write_ln visit_node(node[:else]) }
304
- end
305
-
306
- src.write_ln "end)"
307
- when "Unless"
308
- src.write_ln "unless #{visit_node node[:condition]}"
309
- indented(src) { src.write_ln visit_node(node[:then]) }
310
-
311
- if node[:else]
312
- src.write_ln "else"
313
- indented(src) { src.write_ln visit_node(node[:else]) }
314
- end
315
-
316
- src.write_ln "end"
317
- when "Case"
318
- src.write "case"
319
- src.write " #{visit_node(node[:condition]).strip}\n" if node[:condition]
320
- indented(src) do
321
- node[:whens].each do |w|
322
- src.write_ln visit_node(w)
323
- end
324
- end
325
- src.write_ln "end"
326
- when "When"
327
- src.write_ln "when #{node[:conditions].map { |n| visit_node(n) }.join(", ")}"
328
-
329
- indented(src) { src.write_ln visit_node(node[:body]) }
330
- when "MacroFor"
331
- vars, expr, body = node[:vars], node[:expr], node[:body]
332
- var_names = vars.map { |v| visit_node v }
333
- @inside_macro = true
334
- indent_level = @indent_level
335
- @indent_level -= 1 unless indent_level.zero?
336
- expanded = eval(visit_node(expr)).map do |*a|
337
- locals = Hash[[var_names.join(%(", "))].zip(a)]
338
- locals.merge!(scope) if @inside_macro
339
- visit_node(body, locals)
340
- end.flatten
341
- @indent_level += 1 unless indent_level.zero?
342
- src.write(*expanded)
343
- @inside_macro = false
344
- when "MacroLiteral"
345
- src.write node[:value]
346
- when "MacroExpression"
347
- if node[:output]
348
- expr = visit_node node[:expr], scope
349
- val = scope[expr]
350
- src.write val
351
- end
352
- when "MacroIf"
353
- if evaluate_macro_condition(node[:condition], scope)
354
- src.write_ln visit_node(node[:then], scope) if node[:then]
355
- else
356
- src.write_ln visit_node(node[:else], scope) if node[:else]
357
- end
358
- when "Return"
359
- val = node[:value] ? " #{visit_node(node[:value]).strip}" : nil
360
- src.write "return#{val}"
361
- when "TypeDeclaration"
362
- src.write_ln "# @type var #{visit_node(node[:var])}: #{visit_node(node[:declared_type])}"
363
- src.write_ln "#{visit_node(node[:var])} = #{visit_node(node[:value])}"
364
- when "ExceptionHandler"
365
- src.write_ln "begin"
366
- indented src do
367
- src.write_ln visit_node(node[:body])
368
- end
369
- node[:rescues]&.each do |r|
370
- src.write_ln "rescue #{r[:types].map { |n| visit_node n }.join(", ") if r[:types]}#{" => #{r[:name]}" if r[:name]}"
371
- indented(src) { src.write_ln visit_node(r[:body]) } if r[:body]
372
- end
373
- if node[:else]
374
- src.write_ln "else"
375
- indented(src) { src.write_ln visit_node(node[:else]) }
376
- end
377
- if node[:ensure]
378
- src.write_ln "ensure"
379
- intended(src) { src.write_ln visit_node(node[:ensure]) }
380
- end
381
- src.write_ln "end"
382
- when "Generic"
383
- src.write "#{node[:name]}[#{node[:args].map { |a| visit_node a }.join(", ")}]"
384
- when "Proc"
385
- fn = node[:function]
386
- src.write "->#{render_args(fn)} { #{visit_node fn[:body]} }"
387
- when "Include"
388
- src.write_ln "include #{visit_node node[:name]}"
389
- when "Extend"
390
- src.write_ln "extend #{visit_node node[:name]}"
391
- when "RegexLiteral"
392
- contents = visit_node node[:value]
393
- src.write Regexp.new(contents.undump).inspect
394
- when "EmptyNode"
395
- # pass
396
- else
397
- raise "Not implemented: #{node[:type]}"
398
- end
399
-
400
- src
401
- end
402
-
403
- private
404
-
405
- def evaluate_macro_condition(condition_node, scope)
406
- @eval_vars = true
407
- eval(visit_node(condition_node, scope))
408
- @eval_vars = false
409
- end
410
-
411
- def indented(src)
412
- increment_indent(src)
413
- yield
414
- decrement_indent(src)
415
- end
416
-
417
- def increment_indent(src)
418
- @indent_level += 1
419
- src.increment_indent
420
- end
421
-
422
- def decrement_indent(src)
423
- @indent_level -= 1
424
- src.decrement_indent
425
- end
426
-
427
- def render_args(node)
428
- rp = node[:rp_args] || EMPTY_ARRAY
429
- op = node[:op_args] || EMPTY_ARRAY
430
- rkw = node[:req_kw_args] || EMPTY_HASH
431
- okw = node[:opt_kw_args] || EMPTY_HASH
432
- rest_p = node[:rest_p_args]
433
- rest_kw = node[:rest_kw_args]
434
- return nil unless [rp, op, rkw, okw, rest_p, rest_kw].any? { |a| !a.nil? || !a.empty? }
435
-
436
- contents = [
437
- rp.map { |a| visit_node(a) },
438
- op.map { |pos| "#{pos.name} = #{value}" },
439
- rkw.map { |name, _| "#{name}:" },
440
- okw.map { |name, _| "#{name}: #{value}" },
441
- rest_p ? "*#{rest_p}" : "",
442
- rest_kw ? "**#{visit_node(rest_kw)}" : ""
443
- ].reject(&:empty?).flatten.join(", ")
444
- "(#{contents})"
445
- end
446
- end
447
- end