kaiser-ruby 0.5.1 → 0.7
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/CHANGELOG.md +70 -29
- data/Gemfile.lock +3 -3
- data/README.md +60 -39
- data/TODO.md +0 -8
- data/exe/kaiser-ruby +1 -0
- data/kaiser-ruby.gemspec +1 -1
- data/lib/kaiser_ruby.rb +30 -19
- data/lib/kaiser_ruby/cli.rb +11 -3
- data/lib/kaiser_ruby/parser.rb +710 -0
- data/lib/kaiser_ruby/refinements.rb +200 -0
- data/lib/kaiser_ruby/transformer.rb +358 -0
- data/lib/kaiser_ruby/version.rb +1 -1
- metadata +13 -12
- data/lib/kaiser_ruby/rockstar_parser.rb +0 -310
- data/lib/kaiser_ruby/rockstar_transform.rb +0 -224
@@ -0,0 +1,200 @@
|
|
1
|
+
module KaiserRuby
|
2
|
+
module Refinements
|
3
|
+
refine NilClass do
|
4
|
+
def to_bool
|
5
|
+
false
|
6
|
+
end
|
7
|
+
|
8
|
+
def +(other)
|
9
|
+
'mysterious' + other if other.is_a?(String)
|
10
|
+
end
|
11
|
+
|
12
|
+
def -(other)
|
13
|
+
'mysterious' + other if other.is_a?(String)
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
'mysterious'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
refine Float do
|
22
|
+
alias_method :old_add, :+
|
23
|
+
alias_method :old_mul, :*
|
24
|
+
alias_method :old_div, :/
|
25
|
+
alias_method :old_gt, :<
|
26
|
+
alias_method :old_gte, :<=
|
27
|
+
alias_method :old_lt, :>
|
28
|
+
alias_method :old_lte, :>=
|
29
|
+
alias_method :old_eq, :==
|
30
|
+
|
31
|
+
def to_bool
|
32
|
+
self.zero? ? false : true
|
33
|
+
end
|
34
|
+
|
35
|
+
def +(other)
|
36
|
+
other.is_a?(String) ? self.to_s + other : self.old_add(other)
|
37
|
+
end
|
38
|
+
|
39
|
+
def *(other)
|
40
|
+
other.is_a?(String) ? other * self : self.old_mul(other)
|
41
|
+
end
|
42
|
+
|
43
|
+
def /(other)
|
44
|
+
raise ZeroDivisionError if other.zero?
|
45
|
+
self.old_div(other)
|
46
|
+
end
|
47
|
+
|
48
|
+
def <(other)
|
49
|
+
other.is_a?(String) ? self < Float(other) : self.old_gt(other)
|
50
|
+
end
|
51
|
+
|
52
|
+
def <=(other)
|
53
|
+
other.is_a?(String) ? self <= Float(other) : self.old_gte(other)
|
54
|
+
end
|
55
|
+
|
56
|
+
def >(other)
|
57
|
+
other.is_a?(String) ? self > Float(other) : self.old_lt(other)
|
58
|
+
end
|
59
|
+
|
60
|
+
def >=(other)
|
61
|
+
other.is_a?(String) ? self >= Float(other) : self.old_lte(other)
|
62
|
+
end
|
63
|
+
|
64
|
+
def ==(other)
|
65
|
+
if other.is_a?(TrueClass) || other.is_a?(FalseClass)
|
66
|
+
self.to_bool == other
|
67
|
+
elsif other.is_a?(String)
|
68
|
+
t = Float(other) rescue other
|
69
|
+
self.old_eq(t)
|
70
|
+
else
|
71
|
+
self.old_eq(other)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
refine String do
|
77
|
+
alias_method :old_add, :+
|
78
|
+
alias_method :old_gt, :<
|
79
|
+
alias_method :old_gte, :<=
|
80
|
+
alias_method :old_lt, :>
|
81
|
+
alias_method :old_lte, :>=
|
82
|
+
alias_method :old_eq, :==
|
83
|
+
|
84
|
+
def to_bool
|
85
|
+
self.size == 0 ? false : true
|
86
|
+
end
|
87
|
+
|
88
|
+
def __booleanize
|
89
|
+
if self =~ /\A\bfalse\b|\bno\b|\blies\b|\bwrong\b\Z/i
|
90
|
+
return false
|
91
|
+
elsif self =~ /\A\btrue\b|\byes\b|\bok\b|\bright\b\Z/i
|
92
|
+
return true
|
93
|
+
end
|
94
|
+
|
95
|
+
return self
|
96
|
+
end
|
97
|
+
|
98
|
+
def +(other)
|
99
|
+
other.is_a?(String) ? self.old_add(other) : self + other.to_s
|
100
|
+
end
|
101
|
+
|
102
|
+
def <(other)
|
103
|
+
other.is_a?(Float) ? Float(self) < other : self.old_gt(other)
|
104
|
+
end
|
105
|
+
|
106
|
+
def <=(other)
|
107
|
+
other.is_a?(Float) ? Float(self) <= other : self.old_gte(other)
|
108
|
+
end
|
109
|
+
|
110
|
+
def >(other)
|
111
|
+
other.is_a?(Float) ? Float(self) > other : self.old_lt(other)
|
112
|
+
end
|
113
|
+
|
114
|
+
def >=(other)
|
115
|
+
other.is_a?(Float) ? Float(self) >= other : self.old_lte(other)
|
116
|
+
end
|
117
|
+
|
118
|
+
def ==(other)
|
119
|
+
if other.is_a?(TrueClass) || other.is_a?(FalseClass)
|
120
|
+
self.__booleanize == other
|
121
|
+
elsif other.is_a?(Float)
|
122
|
+
t = Float(self) rescue self
|
123
|
+
t == other
|
124
|
+
else
|
125
|
+
self.old_eq(other)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
refine TrueClass do
|
131
|
+
alias_method :old_eq, :==
|
132
|
+
|
133
|
+
def to_bool
|
134
|
+
true
|
135
|
+
end
|
136
|
+
|
137
|
+
def +(other)
|
138
|
+
return 'true' + other if other.is_a?(String)
|
139
|
+
other.even? ? self : !self
|
140
|
+
end
|
141
|
+
|
142
|
+
def -(other)
|
143
|
+
other.even? ? self : !self
|
144
|
+
end
|
145
|
+
|
146
|
+
def ==(other)
|
147
|
+
if other.is_a?(Float)
|
148
|
+
self == other.to_bool
|
149
|
+
elsif other.is_a?(String)
|
150
|
+
self.old_eq(other.__booleanize)
|
151
|
+
else
|
152
|
+
self.old_eq(other)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
refine FalseClass do
|
158
|
+
alias_method :old_eq, :==
|
159
|
+
|
160
|
+
def to_bool
|
161
|
+
false
|
162
|
+
end
|
163
|
+
|
164
|
+
def +(other)
|
165
|
+
return 'false' + other if other.is_a?(String)
|
166
|
+
other.even? ? self : !self
|
167
|
+
end
|
168
|
+
|
169
|
+
def -(other)
|
170
|
+
other.even? ? self : !self
|
171
|
+
end
|
172
|
+
|
173
|
+
def ==(other)
|
174
|
+
if other.is_a?(Float)
|
175
|
+
self == other.to_bool
|
176
|
+
elsif other.is_a?(String)
|
177
|
+
self.old_eq(other.__booleanize)
|
178
|
+
else
|
179
|
+
self.old_eq(other)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
refine BasicObject do
|
185
|
+
alias_method :old_not, :!
|
186
|
+
|
187
|
+
def !
|
188
|
+
if self.is_a?(String)
|
189
|
+
return self.size == 0 ? true : false
|
190
|
+
elsif self.is_a?(Float)
|
191
|
+
return self.zero? ? true : false
|
192
|
+
elsif self.is_a?(NilClass)
|
193
|
+
return true
|
194
|
+
else
|
195
|
+
self.old_not
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
@@ -0,0 +1,358 @@
|
|
1
|
+
module KaiserRuby
|
2
|
+
class Transformer
|
3
|
+
attr_reader :parsed_tree, :output
|
4
|
+
|
5
|
+
def initialize(tree)
|
6
|
+
@parsed_tree = tree
|
7
|
+
@output = []
|
8
|
+
@method_names = []
|
9
|
+
@nesting = 0
|
10
|
+
@indentation = ''
|
11
|
+
@lnum = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def transform
|
15
|
+
@last_variable = nil # name of last used variable for pronouns
|
16
|
+
@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
|
18
|
+
|
19
|
+
@parsed_tree.each_with_index do |line_object, lnum|
|
20
|
+
@lnum = lnum
|
21
|
+
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
|
29
|
+
@output << @indentation + transformed_line
|
30
|
+
end
|
31
|
+
|
32
|
+
# at end of file, close all the blocks that are still started
|
33
|
+
while @nesting > 0
|
34
|
+
@nesting -= 1
|
35
|
+
@indentation = ' ' * @nesting
|
36
|
+
@output << @indentation + "end"
|
37
|
+
end
|
38
|
+
|
39
|
+
@output << '' if @output.size > 1
|
40
|
+
@output.join("\n")
|
41
|
+
end
|
42
|
+
|
43
|
+
def select_transformer(object)
|
44
|
+
key = object.keys.first
|
45
|
+
send("transform_#{key}", object)
|
46
|
+
end
|
47
|
+
|
48
|
+
def method_missing(m, *args, &block)
|
49
|
+
raise ArgumentError, "missing Transform rule: #{m}, #{args}"
|
50
|
+
end
|
51
|
+
|
52
|
+
# transform language tree into Ruby
|
53
|
+
|
54
|
+
def transform_print(object)
|
55
|
+
var = select_transformer(object[:print])
|
56
|
+
"puts #{var}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def transform_listen_to(object)
|
60
|
+
var = select_transformer(object[:listen_to])
|
61
|
+
"print '> '\n__input = $stdin.gets.chomp\n#{var} = Float(__input) rescue __input"
|
62
|
+
end
|
63
|
+
|
64
|
+
def transform_listen(_object)
|
65
|
+
"print '> '\n$stdin.gets.chomp"
|
66
|
+
end
|
67
|
+
|
68
|
+
def transform_return(object)
|
69
|
+
raise KaiserRuby::RockstarSyntaxError, "Return used outside of a function" if object[:nesting].to_i.zero?
|
70
|
+
var = select_transformer(object[:return])
|
71
|
+
"return #{var}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def transform_continue(object)
|
75
|
+
raise KaiserRuby::RockstarSyntaxError, "Continue used outside of a loop" if object[:nesting].to_i.zero?
|
76
|
+
"next"
|
77
|
+
end
|
78
|
+
|
79
|
+
def transform_break(object)
|
80
|
+
raise KaiserRuby::RockstarSyntaxError, "Break used outside of a loop" if object[:nesting].to_i.zero?
|
81
|
+
"break"
|
82
|
+
end
|
83
|
+
|
84
|
+
def transform_variable_name(object)
|
85
|
+
varname = object[:variable_name]
|
86
|
+
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
|
96
|
+
end
|
97
|
+
|
98
|
+
@last_variable = varname
|
99
|
+
varname
|
100
|
+
end
|
101
|
+
|
102
|
+
def transform_local_variable_name(object)
|
103
|
+
object[:local_variable_name]
|
104
|
+
end
|
105
|
+
|
106
|
+
def transform_function_name(object)
|
107
|
+
object[:function_name]
|
108
|
+
end
|
109
|
+
|
110
|
+
def transform_pronoun(_object)
|
111
|
+
@last_variable
|
112
|
+
end
|
113
|
+
|
114
|
+
def transform_string(object)
|
115
|
+
object[:string]
|
116
|
+
end
|
117
|
+
|
118
|
+
def transform_number(object)
|
119
|
+
object[:number]
|
120
|
+
end
|
121
|
+
|
122
|
+
def transform_argument_list(object)
|
123
|
+
list = []
|
124
|
+
object[:argument_list].each do |arg|
|
125
|
+
list << select_transformer(arg)
|
126
|
+
end
|
127
|
+
|
128
|
+
list.join(', ')
|
129
|
+
end
|
130
|
+
|
131
|
+
def transform_addition(object)
|
132
|
+
left = select_transformer(object[:addition][:left])
|
133
|
+
right = select_transformer(object[:addition][:right])
|
134
|
+
"#{left} + #{right}"
|
135
|
+
end
|
136
|
+
|
137
|
+
def transform_multiplication(object)
|
138
|
+
left = select_transformer(object[:multiplication][:left])
|
139
|
+
right = select_transformer(object[:multiplication][:right])
|
140
|
+
"#{left} * #{right}"
|
141
|
+
end
|
142
|
+
|
143
|
+
def transform_subtraction(object)
|
144
|
+
left = select_transformer(object[:subtraction][:left])
|
145
|
+
right = select_transformer(object[:subtraction][:right])
|
146
|
+
"#{left} - #{right}"
|
147
|
+
end
|
148
|
+
|
149
|
+
def transform_division(object)
|
150
|
+
left = select_transformer(object[:division][:left])
|
151
|
+
right = select_transformer(object[:division][:right])
|
152
|
+
"#{left} / #{right}"
|
153
|
+
end
|
154
|
+
|
155
|
+
def transform_assignment(object)
|
156
|
+
left = select_transformer(object[:assignment][:left])
|
157
|
+
right = select_transformer(object[:assignment][:right])
|
158
|
+
"#{left} = #{right}"
|
159
|
+
end
|
160
|
+
|
161
|
+
def transform_decrement(object)
|
162
|
+
argument = select_transformer(object[:decrement])
|
163
|
+
amount = object.dig(:decrement, :amount)
|
164
|
+
"#{argument} -= #{amount}"
|
165
|
+
end
|
166
|
+
|
167
|
+
def transform_increment(object)
|
168
|
+
argument = select_transformer(object[:increment])
|
169
|
+
amount = object.dig(:increment, :amount)
|
170
|
+
"#{argument} += #{amount}"
|
171
|
+
end
|
172
|
+
|
173
|
+
def transform_function_call(object)
|
174
|
+
func_name = select_transformer(object[:function_call][:left])
|
175
|
+
argument = select_transformer(object[:function_call][:right])
|
176
|
+
|
177
|
+
"#{func_name}(#{argument})"
|
178
|
+
end
|
179
|
+
|
180
|
+
def transform_passed_function_call(object)
|
181
|
+
return transform_function_call(object[:passed_function_call])
|
182
|
+
end
|
183
|
+
|
184
|
+
def transform_poetic_string(object)
|
185
|
+
var = select_transformer(object[:poetic_string][:left])
|
186
|
+
value = select_transformer(object[:poetic_string][:right])
|
187
|
+
|
188
|
+
"#{var} = #{value}"
|
189
|
+
end
|
190
|
+
|
191
|
+
def transform_poetic_type(object)
|
192
|
+
var = select_transformer(object[:poetic_type][:left])
|
193
|
+
value = select_transformer(object[:poetic_type][:right])
|
194
|
+
"#{var} = #{value}"
|
195
|
+
end
|
196
|
+
|
197
|
+
def transform_poetic_number(object)
|
198
|
+
var = select_transformer(object[:poetic_number][:left])
|
199
|
+
value = select_transformer(object[:poetic_number][:right])
|
200
|
+
"#{var} = #{value}"
|
201
|
+
end
|
202
|
+
|
203
|
+
def transform_number_literal(object)
|
204
|
+
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
|
212
|
+
end
|
213
|
+
|
214
|
+
def transform_type(object)
|
215
|
+
case object[:type]
|
216
|
+
when "nil"
|
217
|
+
'nil'
|
218
|
+
when "null"
|
219
|
+
'0.0'
|
220
|
+
when "true"
|
221
|
+
'true'
|
222
|
+
when "false"
|
223
|
+
'false'
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def transform_empty_line(_object)
|
228
|
+
if @nesting == 0
|
229
|
+
return ""
|
230
|
+
elsif @nesting == 1
|
231
|
+
@local_variables = []
|
232
|
+
return "end\n"
|
233
|
+
else
|
234
|
+
@else_already = nil
|
235
|
+
return "end\n"
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def additional_argument_transformation(argument)
|
240
|
+
# testing function existence
|
241
|
+
arg = @method_names.include?(argument) ? "defined?(#{argument})" : argument
|
242
|
+
|
243
|
+
# single variable without any operator needs to return a refined boolean
|
244
|
+
arg = "#{arg}.to_bool" if arg !~ /==|>|>=|<|<=|!=/
|
245
|
+
|
246
|
+
return arg
|
247
|
+
end
|
248
|
+
|
249
|
+
def transform_if(object)
|
250
|
+
argument = select_transformer(object[:if][:argument])
|
251
|
+
argument = additional_argument_transformation(argument)
|
252
|
+
"if #{argument}"
|
253
|
+
end
|
254
|
+
|
255
|
+
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
|
258
|
+
|
259
|
+
@else_already = object[:nesting_start_line]
|
260
|
+
"else"
|
261
|
+
end
|
262
|
+
|
263
|
+
def transform_while(object)
|
264
|
+
argument = select_transformer(object[:while][:argument])
|
265
|
+
argument = additional_argument_transformation(argument)
|
266
|
+
"while #{argument}"
|
267
|
+
end
|
268
|
+
|
269
|
+
def transform_until(object)
|
270
|
+
argument = select_transformer(object[:until][:argument])
|
271
|
+
argument = additional_argument_transformation(argument)
|
272
|
+
"until #{argument}"
|
273
|
+
end
|
274
|
+
|
275
|
+
def transform_equality(object)
|
276
|
+
left = select_transformer(object[:equality][:left])
|
277
|
+
right = select_transformer(object[:equality][:right])
|
278
|
+
"#{left} == #{right}"
|
279
|
+
end
|
280
|
+
|
281
|
+
def transform_inequality(object)
|
282
|
+
left = select_transformer(object[:inequality][:left])
|
283
|
+
right = select_transformer(object[:inequality][:right])
|
284
|
+
"#{left} != #{right}"
|
285
|
+
end
|
286
|
+
|
287
|
+
def transform_gt(object)
|
288
|
+
left = select_transformer(object[:gt][:left])
|
289
|
+
right = select_transformer(object[:gt][:right])
|
290
|
+
"#{left} > #{right}"
|
291
|
+
end
|
292
|
+
|
293
|
+
def transform_gte(object)
|
294
|
+
left = select_transformer(object[:gte][:left])
|
295
|
+
right = select_transformer(object[:gte][:right])
|
296
|
+
"#{left} >= #{right}"
|
297
|
+
end
|
298
|
+
|
299
|
+
def transform_lt(object)
|
300
|
+
left = select_transformer(object[:lt][:left])
|
301
|
+
right = select_transformer(object[:lt][:right])
|
302
|
+
"#{left} < #{right}"
|
303
|
+
end
|
304
|
+
|
305
|
+
def transform_lte(object)
|
306
|
+
left = select_transformer(object[:lte][:left])
|
307
|
+
right = select_transformer(object[:lte][:right])
|
308
|
+
"#{left} <= #{right}"
|
309
|
+
end
|
310
|
+
|
311
|
+
def transform_function(object)
|
312
|
+
funcname = transform_function_name(object[:function][:name])
|
313
|
+
argument = select_transformer(object[:function][:argument])
|
314
|
+
|
315
|
+
# save method name and make local variables out of the function arguments
|
316
|
+
@method_names << funcname
|
317
|
+
@local_variables = argument.split(', ')
|
318
|
+
|
319
|
+
"def #{funcname}(#{argument})"
|
320
|
+
end
|
321
|
+
|
322
|
+
def transform_and(object)
|
323
|
+
left = select_transformer(object[:and][:left])
|
324
|
+
right = select_transformer(object[:and][:right])
|
325
|
+
|
326
|
+
"#{left} && #{right}"
|
327
|
+
end
|
328
|
+
|
329
|
+
def transform_not(object)
|
330
|
+
arg = select_transformer(object[:not])
|
331
|
+
"!#{arg}"
|
332
|
+
end
|
333
|
+
|
334
|
+
def transform_or(object)
|
335
|
+
left = select_transformer(object[:or][:left])
|
336
|
+
right = select_transformer(object[:or][:right])
|
337
|
+
|
338
|
+
"#{left} || #{right}"
|
339
|
+
end
|
340
|
+
|
341
|
+
def transform_nor(object)
|
342
|
+
left = select_transformer(object[:nor][:left])
|
343
|
+
right = select_transformer(object[:nor][:right])
|
344
|
+
|
345
|
+
"!(#{left} || #{right})"
|
346
|
+
end
|
347
|
+
|
348
|
+
# private
|
349
|
+
|
350
|
+
def str_to_num(string)
|
351
|
+
filter_string(string).map { |e| e.length % 10 }.join
|
352
|
+
end
|
353
|
+
|
354
|
+
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? }
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|