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