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.
- checksums.yaml +4 -4
- data/.rubocop.yml +11 -0
- data/.ruby-version +1 -1
- data/.travis.yml +5 -4
- data/CHANGELOG.md +34 -10
- data/Gemfile +4 -2
- data/Gemfile.lock +39 -24
- data/Rakefile +5 -3
- data/bin/console +7 -3
- data/exe/kaiser-ruby +8 -6
- data/kaiser-ruby.gemspec +19 -16
- data/lib/kaiser_ruby.rb +4 -6
- data/lib/kaiser_ruby/cli.rb +19 -7
- data/lib/kaiser_ruby/parser.rb +189 -144
- data/lib/kaiser_ruby/refinements.rb +409 -28
- data/lib/kaiser_ruby/transformer.rb +112 -60
- data/lib/kaiser_ruby/version.rb +3 -1
- metadata +39 -25
@@ -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
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
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
|
48
|
+
while @nesting.positive?
|
34
49
|
@nesting -= 1
|
35
50
|
@indentation = ' ' * @nesting
|
36
|
-
@output << @indentation +
|
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(
|
49
|
-
raise ArgumentError, "missing Transform rule: #{
|
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
|
-
|
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,
|
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,
|
76
|
-
|
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,
|
81
|
-
|
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 @
|
88
|
-
|
89
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
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
|
250
|
+
when 'mysterious'
|
251
|
+
'KaiserRuby::Mysterious.new'
|
252
|
+
when 'null'
|
217
253
|
'nil'
|
218
|
-
when
|
219
|
-
'0.0'
|
220
|
-
when "true"
|
254
|
+
when 'true'
|
221
255
|
'true'
|
222
|
-
when
|
256
|
+
when 'false'
|
223
257
|
'false'
|
224
258
|
end
|
225
259
|
end
|
226
260
|
|
227
261
|
def transform_empty_line(_object)
|
228
|
-
if @nesting
|
229
|
-
|
262
|
+
if @nesting.zero?
|
263
|
+
''
|
230
264
|
elsif @nesting == 1
|
231
|
-
|
232
|
-
return "end\n"
|
265
|
+
"end\n"
|
233
266
|
else
|
234
267
|
@else_already = nil
|
235
|
-
|
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
|
-
|
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,
|
257
|
-
raise KaiserRuby::RockstarSyntaxError,
|
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
|
-
|
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
|
-
|
316
|
-
|
317
|
-
|
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
|
-
|
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
|
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
|
data/lib/kaiser_ruby/version.rb
CHANGED
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.
|
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-
|
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:
|
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:
|
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: '
|
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: '
|
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: '
|
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: '
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
70
|
+
name: rubocop
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
|
-
- - "
|
73
|
+
- - ">="
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version: '0
|
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
|
82
|
+
version: '0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
84
|
+
name: hashie
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
|
-
- - "
|
87
|
+
- - ">="
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version: '0
|
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
|
96
|
+
version: '0'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
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
|
-
|
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
|