gloss 0.0.3 → 0.1.1

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