kaiser-ruby 0.7.1 → 0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KaiserRuby
4
+ # taking the intermediate tree output of parsing, output Ruby code
2
5
  class Transformer
3
6
  attr_reader :parsed_tree, :output
4
7
 
@@ -6,34 +9,46 @@ module KaiserRuby
6
9
  @parsed_tree = tree
7
10
  @output = []
8
11
  @method_names = []
12
+ @global_variables = []
13
+ @nested_functions = {}
9
14
  @nesting = 0
10
15
  @indentation = ''
11
16
  @lnum = 0
17
+ @current_scope = [nil]
12
18
  end
13
19
 
14
20
  def transform
15
21
  @last_variable = nil # name of last used variable for pronouns
16
22
  @else_already = nil # line number of a current if block, so we can avoid double else
17
- @local_variables = [] # locally defined variable names in current function block
23
+
24
+ # first pass over the tree to find out which variables are global and which are not
25
+ # in case some are declared *after* the function definition that uses them
26
+ @parsed_tree.each do |line_object|
27
+ next unless line_object[:current_scope].nil?
28
+
29
+ line_object.extend(Hashie::Extensions::DeepLocate)
30
+
31
+ line_object.deep_locate(:variable_name).each do |vname_obj|
32
+ @global_variables.push vname_obj.dig(:variable_name)
33
+ end
34
+ end
35
+ @global_variables = @global_variables.compact.uniq
18
36
 
19
37
  @parsed_tree.each_with_index do |line_object, lnum|
38
+ @current_scope.push(line_object[:current_scope]) unless @current_scope.last == line_object[:current_scope]
20
39
  @lnum = lnum
21
40
  transformed_line = select_transformer(line_object)
22
- if line_object[:nesting]
23
- @nesting = line_object[:nesting]
24
- else
25
- @nesting = 0
26
- end
27
-
28
- @indentation = ' ' * @nesting
41
+ @nesting = line_object[:nesting] || 0
42
+ actual_nesting = line_object.key?(:else) ? @nesting - 1 : @nesting
43
+ @indentation = ' ' * actual_nesting
29
44
  @output << @indentation + transformed_line
30
45
  end
31
46
 
32
47
  # at end of file, close all the blocks that are still started
33
- while @nesting > 0
48
+ while @nesting.positive?
34
49
  @nesting -= 1
35
50
  @indentation = ' ' * @nesting
36
- @output << @indentation + "end"
51
+ @output << @indentation + 'end'
37
52
  end
38
53
 
39
54
  @output << '' if @output.size > 1
@@ -45,15 +60,14 @@ module KaiserRuby
45
60
  send("transform_#{key}", object)
46
61
  end
47
62
 
48
- def method_missing(m, *args, &block)
49
- raise ArgumentError, "missing Transform rule: #{m}, #{args}"
63
+ def method_missing(rule, *args, &_block)
64
+ raise ArgumentError, "missing Transform rule: #{rule}, #{args}"
50
65
  end
51
66
 
52
- # transform language tree into Ruby
53
-
54
67
  def transform_print(object)
55
68
  var = select_transformer(object[:print])
56
- "puts #{var}"
69
+
70
+ "puts (#{var}).to_s"
57
71
  end
58
72
 
59
73
  def transform_listen_to(object)
@@ -66,36 +80,37 @@ module KaiserRuby
66
80
  end
67
81
 
68
82
  def transform_return(object)
69
- raise KaiserRuby::RockstarSyntaxError, "Return used outside of a function" if object[:nesting].to_i.zero?
83
+ raise KaiserRuby::RockstarSyntaxError, 'Return used outside of a function' if object[:nesting].to_i.zero?
84
+
70
85
  var = select_transformer(object[:return])
71
86
  "return #{var}"
72
87
  end
73
88
 
74
89
  def transform_continue(object)
75
- raise KaiserRuby::RockstarSyntaxError, "Continue used outside of a loop" if object[:nesting].to_i.zero?
76
- "next"
90
+ raise KaiserRuby::RockstarSyntaxError, 'Continue used outside of a loop' if object[:nesting].to_i.zero?
91
+
92
+ 'next'
77
93
  end
78
94
 
79
95
  def transform_break(object)
80
- raise KaiserRuby::RockstarSyntaxError, "Break used outside of a loop" if object[:nesting].to_i.zero?
81
- "break"
96
+ raise KaiserRuby::RockstarSyntaxError, 'Break used outside of a loop' if object[:nesting].to_i.zero?
97
+
98
+ 'break'
82
99
  end
83
100
 
84
101
  def transform_variable_name(object)
85
102
  varname = object[:variable_name]
103
+
86
104
  if object[:type] == :assignment
87
- if @local_variables.empty?
88
- varname = "@#{varname}"
89
- else
90
- @local_variables << varname
91
- end
92
- else
93
- unless @local_variables.include?(varname)
94
- varname = @method_names.include?(varname) ? varname : "@#{varname}"
95
- end
105
+ varname = "@#{varname}" if @global_variables&.include?(varname)
106
+ elsif @global_variables.include?(varname)
107
+ varname = @method_names.include?(varname) ? varname : "@#{varname}"
96
108
  end
97
109
 
98
- @last_variable = varname
110
+ # have to break this to make 99 beers example work as it only updates the pronoun
111
+ # on assignment, which is technically a bug but seems like a good feature though
112
+ # so will most likely make it way to spec as is
113
+ # @last_variable = varname
99
114
  varname
100
115
  end
101
116
 
@@ -116,7 +131,7 @@ module KaiserRuby
116
131
  end
117
132
 
118
133
  def transform_number(object)
119
- object[:number]
134
+ normalize_num(object[:number])
120
135
  end
121
136
 
122
137
  def transform_argument_list(object)
@@ -131,42 +146,50 @@ module KaiserRuby
131
146
  def transform_addition(object)
132
147
  left = select_transformer(object[:addition][:left])
133
148
  right = select_transformer(object[:addition][:right])
149
+
134
150
  "#{left} + #{right}"
135
151
  end
136
152
 
137
153
  def transform_multiplication(object)
138
154
  left = select_transformer(object[:multiplication][:left])
139
155
  right = select_transformer(object[:multiplication][:right])
156
+
140
157
  "#{left} * #{right}"
141
158
  end
142
159
 
143
160
  def transform_subtraction(object)
144
161
  left = select_transformer(object[:subtraction][:left])
145
162
  right = select_transformer(object[:subtraction][:right])
163
+
146
164
  "#{left} - #{right}"
147
165
  end
148
166
 
149
167
  def transform_division(object)
150
168
  left = select_transformer(object[:division][:left])
151
169
  right = select_transformer(object[:division][:right])
170
+
152
171
  "#{left} / #{right}"
153
172
  end
154
173
 
155
174
  def transform_assignment(object)
156
175
  left = select_transformer(object[:assignment][:left])
157
176
  right = select_transformer(object[:assignment][:right])
177
+
178
+ @last_variable = left
158
179
  "#{left} = #{right}"
159
180
  end
160
181
 
161
182
  def transform_decrement(object)
162
183
  argument = select_transformer(object[:decrement])
163
184
  amount = object.dig(:decrement, :amount)
185
+
164
186
  "#{argument} -= #{amount}"
165
187
  end
166
188
 
167
189
  def transform_increment(object)
168
190
  argument = select_transformer(object[:increment])
169
191
  amount = object.dig(:increment, :amount)
192
+
170
193
  "#{argument} += #{amount}"
171
194
  end
172
195
 
@@ -174,65 +197,75 @@ module KaiserRuby
174
197
  func_name = select_transformer(object[:function_call][:left])
175
198
  argument = select_transformer(object[:function_call][:right])
176
199
 
177
- "#{func_name}(#{argument})"
200
+ if @nested_functions[@current_scope.last]&.include?(func_name)
201
+ "#{func_name}.call(#{argument})"
202
+ else
203
+ "#{func_name}(#{argument})"
204
+ end
178
205
  end
179
206
 
180
207
  def transform_passed_function_call(object)
181
- return transform_function_call(object[:passed_function_call])
208
+ transform_function_call(object[:passed_function_call])
182
209
  end
183
210
 
184
211
  def transform_poetic_string(object)
185
212
  var = select_transformer(object[:poetic_string][:left])
186
213
  value = select_transformer(object[:poetic_string][:right])
187
214
 
215
+ @last_variable = var
188
216
  "#{var} = #{value}"
189
217
  end
190
218
 
191
219
  def transform_poetic_type(object)
192
220
  var = select_transformer(object[:poetic_type][:left])
193
221
  value = select_transformer(object[:poetic_type][:right])
222
+
223
+ @last_variable = var
194
224
  "#{var} = #{value}"
195
225
  end
196
226
 
197
227
  def transform_poetic_number(object)
198
228
  var = select_transformer(object[:poetic_number][:left])
199
229
  value = select_transformer(object[:poetic_number][:right])
230
+
231
+ @last_variable = var
200
232
  "#{var} = #{value}"
201
233
  end
202
234
 
203
235
  def transform_number_literal(object)
204
236
  string = object[:number_literal]
205
- if string.include?('.')
206
- string.split('.', 2).map do |sub|
207
- str_to_num(sub.strip)
208
- end.join('.').to_f
209
- else
210
- str_to_num(string).to_f
211
- end
237
+ out = if string.include?('.')
238
+ string.split('.', 2).map do |sub|
239
+ str_to_num(sub.strip)
240
+ end.join('.').to_f
241
+ else
242
+ str_to_num(string).to_f
243
+ end
244
+
245
+ normalize_num(out)
212
246
  end
213
247
 
214
248
  def transform_type(object)
215
249
  case object[:type]
216
- when "nil"
250
+ when 'mysterious'
251
+ 'KaiserRuby::Mysterious.new'
252
+ when 'null'
217
253
  'nil'
218
- when "null"
219
- '0.0'
220
- when "true"
254
+ when 'true'
221
255
  'true'
222
- when "false"
256
+ when 'false'
223
257
  'false'
224
258
  end
225
259
  end
226
260
 
227
261
  def transform_empty_line(_object)
228
- if @nesting == 0
229
- return ""
262
+ if @nesting.zero?
263
+ ''
230
264
  elsif @nesting == 1
231
- @local_variables = []
232
- return "end\n"
265
+ "end\n"
233
266
  else
234
267
  @else_already = nil
235
- return "end\n"
268
+ "end\n"
236
269
  end
237
270
  end
238
271
 
@@ -243,68 +276,78 @@ module KaiserRuby
243
276
  # single variable without any operator needs to return a refined boolean
244
277
  arg = "#{arg}.to_bool" if arg !~ /==|>|>=|<|<=|!=/
245
278
 
246
- return arg
279
+ arg
247
280
  end
248
281
 
249
282
  def transform_if(object)
250
283
  argument = select_transformer(object[:if][:argument])
251
284
  argument = additional_argument_transformation(argument)
285
+
252
286
  "if #{argument}"
253
287
  end
254
288
 
255
289
  def transform_else(object)
256
- raise KaiserRuby::RockstarSyntaxError, "Else outside an if block" if object[:nesting].to_i.zero?
257
- raise KaiserRuby::RockstarSyntaxError, "Double else inside if block" if @else_already != nil && object[:nesting_start_line] == @else_already
290
+ raise KaiserRuby::RockstarSyntaxError, 'Else outside an if block' if object[:nesting].to_i.zero?
291
+ raise KaiserRuby::RockstarSyntaxError, 'Double else inside if block' if !@else_already.nil? && object[:nesting_start_line] == @else_already
258
292
 
259
293
  @else_already = object[:nesting_start_line]
260
- "else"
294
+
295
+ 'else'
261
296
  end
262
297
 
263
298
  def transform_while(object)
264
299
  argument = select_transformer(object[:while][:argument])
265
300
  argument = additional_argument_transformation(argument)
301
+
266
302
  "while #{argument}"
267
303
  end
268
304
 
269
305
  def transform_until(object)
270
306
  argument = select_transformer(object[:until][:argument])
271
307
  argument = additional_argument_transformation(argument)
308
+
272
309
  "until #{argument}"
273
310
  end
274
311
 
275
312
  def transform_equality(object)
276
313
  left = select_transformer(object[:equality][:left])
277
314
  right = select_transformer(object[:equality][:right])
315
+
278
316
  "#{left} == #{right}"
279
317
  end
280
318
 
281
319
  def transform_inequality(object)
282
320
  left = select_transformer(object[:inequality][:left])
283
321
  right = select_transformer(object[:inequality][:right])
322
+
284
323
  "#{left} != #{right}"
285
324
  end
286
325
 
287
326
  def transform_gt(object)
288
327
  left = select_transformer(object[:gt][:left])
289
328
  right = select_transformer(object[:gt][:right])
329
+
290
330
  "#{left} > #{right}"
291
331
  end
292
332
 
293
333
  def transform_gte(object)
294
334
  left = select_transformer(object[:gte][:left])
295
335
  right = select_transformer(object[:gte][:right])
336
+
296
337
  "#{left} >= #{right}"
297
338
  end
298
339
 
299
340
  def transform_lt(object)
300
341
  left = select_transformer(object[:lt][:left])
301
342
  right = select_transformer(object[:lt][:right])
343
+
302
344
  "#{left} < #{right}"
303
345
  end
304
346
 
305
347
  def transform_lte(object)
306
348
  left = select_transformer(object[:lte][:left])
307
349
  right = select_transformer(object[:lte][:right])
350
+
308
351
  "#{left} <= #{right}"
309
352
  end
310
353
 
@@ -312,11 +355,15 @@ module KaiserRuby
312
355
  funcname = transform_function_name(object[:function][:name])
313
356
  argument = select_transformer(object[:function][:argument])
314
357
 
315
- # save method name and make local variables out of the function arguments
316
- @method_names << funcname
317
- @local_variables = argument.split(', ')
358
+ if @current_scope.last.nil?
359
+ @method_names << funcname
360
+ "def #{funcname}(#{argument})"
361
+ else
362
+ @nested_functions[@current_scope.last] ||= []
363
+ @nested_functions[@current_scope.last].push funcname
318
364
 
319
- "def #{funcname}(#{argument})"
365
+ "#{funcname} = ->(#{argument}) do"
366
+ end
320
367
  end
321
368
 
322
369
  def transform_and(object)
@@ -328,6 +375,7 @@ module KaiserRuby
328
375
 
329
376
  def transform_not(object)
330
377
  arg = select_transformer(object[:not])
378
+
331
379
  "!#{arg}"
332
380
  end
333
381
 
@@ -352,7 +400,11 @@ module KaiserRuby
352
400
  end
353
401
 
354
402
  def filter_string(string, rxp: /[[:alpha:]]/)
355
- string.to_s.split(/\s+/).map { |e| e.chars.select { |c| c =~ rxp }.join }.reject { |a| a.empty? }
403
+ string.to_s.split(/\s+/).map { |e| e.chars.select { |c| c =~ rxp }.join }.reject(&:empty?)
404
+ end
405
+
406
+ def normalize_num(num)
407
+ num.modulo(1).zero? ? num.to_i : num
356
408
  end
357
409
  end
358
- end
410
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KaiserRuby
2
- VERSION = "0.7.1"
4
+ VERSION = '0.8'
3
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kaiser-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: '0.8'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcin Ruszkiewicz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-01-11 00:00:00.000000000 Z
11
+ date: 2019-03-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,72 +16,86 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.16'
19
+ version: 1.17.0
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.16'
26
+ version: 1.17.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rake
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
- - - "~>"
45
+ - - ">="
32
46
  - !ruby/object:Gem::Version
33
- version: '10.0'
47
+ version: '0'
34
48
  type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
- - - "~>"
52
+ - - ">="
39
53
  - !ruby/object:Gem::Version
40
- version: '10.0'
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rspec
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
- - - "~>"
59
+ - - ">="
46
60
  - !ruby/object:Gem::Version
47
- version: '3.0'
61
+ version: '0'
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
- - - "~>"
66
+ - - ">="
53
67
  - !ruby/object:Gem::Version
54
- version: '3.0'
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
- name: pry
70
+ name: rubocop
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
- - - "~>"
73
+ - - ">="
60
74
  - !ruby/object:Gem::Version
61
- version: '0.11'
75
+ version: '0'
62
76
  type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
- - - "~>"
80
+ - - ">="
67
81
  - !ruby/object:Gem::Version
68
- version: '0.11'
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
- name: thor
84
+ name: hashie
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
- - - "~>"
87
+ - - ">="
74
88
  - !ruby/object:Gem::Version
75
- version: '0.20'
89
+ version: '0'
76
90
  type: :runtime
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
- - - "~>"
94
+ - - ">="
81
95
  - !ruby/object:Gem::Version
82
- version: '0.20'
96
+ version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
- name: hashie
98
+ name: thor
85
99
  requirement: !ruby/object:Gem::Requirement
86
100
  requirements:
87
101
  - - ">="
@@ -104,6 +118,7 @@ extra_rdoc_files: []
104
118
  files:
105
119
  - ".gitignore"
106
120
  - ".rspec"
121
+ - ".rubocop.yml"
107
122
  - ".ruby-version"
108
123
  - ".travis.yml"
109
124
  - CHANGELOG.md
@@ -142,8 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
142
157
  - !ruby/object:Gem::Version
143
158
  version: '0'
144
159
  requirements: []
145
- rubyforge_project:
146
- rubygems_version: 2.7.6
160
+ rubygems_version: 3.0.3
147
161
  signing_key:
148
162
  specification_version: 4
149
163
  summary: Transpiler of Rockstar language to Ruby