kaiser-ruby 0.7.1 → 0.8

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.
@@ -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