tealrb 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,81 +3,104 @@
3
3
  module TEALrb
4
4
  class Contract
5
5
  include TEALrb
6
- include Opcodes
7
- include Opcodes::AllOpcodes
6
+ include Opcodes::TEALOpcodes
7
+ include MaybeOps
8
+ include ByteOpcodes
8
9
  include ABI
9
10
  include Rewriters
11
+ include Enums
10
12
 
11
- attr_reader :teal
13
+ alias global_opcode global
12
14
 
13
- class << self
14
- attr_accessor :subroutines, :version, :teal_methods, :abi_interface, :debug,
15
- :disable_abi_routing, :method_hashes
15
+ attr_reader :eval_location
16
+ attr_accessor :teal, :if_count
16
17
 
17
- private
18
+ class << self
19
+ attr_accessor :subroutines, :version, :abi_interface, :debug,
20
+ :disable_abi_routing, :method_hashes, :src_map
18
21
 
19
22
  def inherited(klass)
20
23
  klass.version = 6
21
24
  klass.subroutines = {}
22
- klass.teal_methods = {}
23
25
  klass.abi_interface = ABI::ABIDescription.new
24
26
  klass.abi_interface.name = klass.to_s
25
27
  klass.method_hashes = []
26
28
  klass.debug = false
27
29
  klass.disable_abi_routing = false
28
- parse(klass)
30
+ klass.src_map = true
29
31
  super
30
32
  end
31
33
 
32
34
  def parse(klass)
33
35
  YARD::Tags::Library.define_tag('ABI Method', :abi)
34
36
  YARD::Tags::Library.define_tag('Subroutine', :subroutine)
35
- YARD::Tags::Library.define_tag('TEAL Method', :teal)
37
+ YARD::Tags::Library.define_tag('OnCompletes', :on_completion)
38
+ YARD::Tags::Library.define_tag('Create', :create)
36
39
 
37
40
  YARD.parse Object.const_source_location(klass.to_s).first
38
41
 
42
+ parsed_methods = {}
39
43
  YARD::Registry.all.each do |y|
40
44
  next unless y.type == :method
41
- next unless y.parent.to_s == klass.to_s
45
+ next unless klass.instance_methods.include? y.name
46
+ next if y.parent.type == :class && y.parent.to_s != klass.to_s
42
47
 
43
- tags = y.tags.map(&:tag_name)
48
+ if parsed_methods.keys.include? y.name
49
+ raise "#{y.name} defined in two locations: \n #{parsed_methods[y.name]}\n #{y.file}:#{y.line}"
50
+ end
44
51
 
45
- if tags.include?('abi') || tags.include?('subroutine')
46
- method_hash = { name: y.name.to_s, desc: y.base_docstring, args: [], returns: { type: 'void' } }
52
+ parsed_methods[y.name] = "#{y.file}:#{y.line}"
47
53
 
48
- y.tags.each do |t|
49
- method_hash[:returns] = { type: t.types&.first } if t.tag_name == 'return'
54
+ tags = y.tags.map(&:tag_name)
55
+
56
+ next unless tags.include?('abi') || tags.include?('subroutine')
50
57
 
51
- next unless t.tag_name == 'param'
58
+ method_hash = { name: y.name.to_s, desc: y.base_docstring, args: [], returns: { type: 'void' },
59
+ on_completion: ['NoOp'], create: y.has_tag?('create') }
52
60
 
53
- method_hash[:args] << { name: t.name, type: t.types&.first, desc: t.text }
54
- end
61
+ y.tags.each do |t|
62
+ method_hash[:returns] = { type: t.types&.first&.downcase } if t.tag_name == 'return'
55
63
 
56
- klass.method_hashes << method_hash
64
+ method_hash[:on_completion] = t.text[1..-2].split(',').map(&:strip) if t.tag_name == 'on_completion'
65
+ next unless t.tag_name == 'param'
57
66
 
58
- klass.abi_interface.add_method(**method_hash) if tags.include? 'abi'
59
- elsif tags.include? 'teal'
60
- klass.teal_methods[y.name.to_s] = y
67
+ method_hash[:args] << { name: t.name, type: t.types&.first&.downcase, desc: t.text }
61
68
  end
69
+
70
+ klass.method_hashes << method_hash
71
+
72
+ klass.abi_interface.add_method(**method_hash) if tags.include? 'abi'
62
73
  end
63
74
  end
64
75
  end
65
76
 
77
+ TEALrb::Opcodes::BINARY_OPCODE_METHOD_MAPPING.each do |meth, opcode|
78
+ define_method(meth) do |other|
79
+ @contract.send(opcode, self, other)
80
+ end
81
+ end
82
+
83
+ TEALrb::Opcodes::UNARY_OPCODE_METHOD_MAPPING.each do |meth, opcode|
84
+ define_method(meth) do
85
+ @contract.send(opcode, self)
86
+ end
87
+ end
88
+
66
89
  # sets the `#pragma version`, defines teal methods, and defines subroutines
67
90
  def initialize
68
- @teal = TEAL.new ["#pragma version #{self.class.version}"]
69
- IfBlock.id = 0
70
- @scratch = Scratch.new
91
+ self.class.parse(self.class)
92
+
93
+ @teal = TEAL.new ["#pragma version #{self.class.version}"], self
94
+ @scratch = Scratch.new self
71
95
 
72
- @@active_contract = self # rubocop:disable Style/ClassVars
96
+ @contract = self
97
+ @if_count = 0
73
98
 
74
99
  self.class.method_hashes.each do |mh|
75
100
  define_subroutine(mh[:name], method(mh[:name]))
76
101
  end
77
102
 
78
- self.class.teal_methods.each do |name, definition|
79
- define_teal_method(name, definition)
80
- end
103
+ compile
81
104
  end
82
105
 
83
106
  VOID_OPS = %w[assert err return app_global_put b bnz bz store
@@ -85,47 +108,209 @@ module TEALrb
85
108
  log itxn_submit itxn_next].freeze
86
109
 
87
110
  def teal_source
88
- teal_lines = []
111
+ @teal.compact.join("\n")
112
+ end
89
113
 
90
- @teal.each_with_index do |line, i|
91
- ln = line.strip
114
+ def account(_account = nil)
115
+ @account ||= Account.new self
116
+ end
92
117
 
93
- teal_lines << '' if i != 0 && VOID_OPS.include?(@teal[i - 1][/\S+/])
118
+ alias accounts account
94
119
 
95
- if (ln[%r{\S+:($| //)}] && !ln[/^if\d+/]) || ln == 'b main' || ln[/^#/]
96
- teal_lines << ln
97
- elsif ln == 'retsub'
98
- teal_lines << " #{ln}"
99
- teal_lines << ''
100
- else
101
- teal_lines << " #{ln}"
120
+ def app(_app = nil)
121
+ @app ||= App.new self
122
+ end
123
+
124
+ alias apps app
125
+
126
+ def asset(_asset = nil)
127
+ @asset ||= Asset.new self
128
+ end
129
+
130
+ alias assets asset
131
+
132
+ def group_txn(_group_txn = nil)
133
+ @group_txn ||= GroupTxn.new self
134
+ end
135
+
136
+ alias group_txns group_txn
137
+
138
+ def global(field = nil, *_args)
139
+ if field
140
+ global_opcode(field)
141
+ else
142
+ @global ||= Global.new self
143
+ end
144
+ end
145
+
146
+ def this_txn
147
+ ThisTxn.new self
148
+ end
149
+
150
+ def box
151
+ Box.new self
152
+ end
153
+
154
+ def inner_txn
155
+ InnerTxn.new self
156
+ end
157
+
158
+ def logs
159
+ Logs.new self
160
+ end
161
+
162
+ def app_args
163
+ AppArgs.new self
164
+ end
165
+
166
+ def local
167
+ Local.new self
168
+ end
169
+
170
+ def txn_type
171
+ TxnType.new self
172
+ end
173
+
174
+ def generate_source_map(src)
175
+ last_location = nil
176
+ src_map_hash = {}
177
+
178
+ src.each_line.with_index do |ln, i|
179
+ if ln[/src_map:/]
180
+ last_location = ln[/(?<=src_map:)\S+/]
181
+ next
102
182
  end
183
+
184
+ src_map_hash[i + 1] ||= { location: last_location } if last_location
103
185
  end
104
186
 
105
- teal_lines.delete_if.with_index do |t, i|
106
- t.empty? && teal_lines[i - 1].empty?
187
+ compile_response = Algod.new.compile(src)
188
+
189
+ case compile_response.status
190
+ when 400
191
+ msg = JSON.parse(compile_response.body)['message']
192
+ e_msg = StringIO.new
193
+ e_msg.puts 'Error(s) while attempting to compile TEAL'
194
+ msg.each_line do |ln|
195
+ teal_line = ln.split(':').first.to_i
196
+ ln_msg = ln.split(':')[1..].join(':').strip
197
+ next if ln_msg == '"0"'
198
+
199
+ if src_map_hash[teal_line]
200
+ e_msg.puts " #{teal_line} - #{src_map_hash[teal_line][:location]}: #{ln_msg}"
201
+ else
202
+ e_msg.puts " #{ln}"
203
+ end
204
+ end
205
+ raise e_msg.string
206
+ when 200
207
+ json_body = JSON.parse(compile_response.body)
208
+ pc_mapping = json_body['sourcemap']['mapping']
209
+
210
+ pc_array = pc_mapping.split(';').map do |v|
211
+ SourceMap::VLQ.decode_array(v)[2]
212
+ end
213
+
214
+ last_line = 1
215
+
216
+ line_to_pc = {}
217
+ pc_to_line = {}
218
+ pc_array.each_with_index do |line_delta, pc|
219
+ last_line += line_delta.to_i
220
+
221
+ next if last_line == 1
222
+
223
+ line_to_pc[last_line] ||= []
224
+ line_to_pc[last_line] << pc
225
+ pc_to_line[pc] = last_line
226
+ end
227
+
228
+ line_to_pc.each do |line, pcs|
229
+ src_map_hash[line][:pcs] = pcs if src_map_hash[line]
230
+ end
107
231
  end
108
232
 
109
- teal_lines.join("\n")
233
+ src_map_hash
110
234
  end
111
235
 
112
- # return the input without transpiling to TEAL
113
- def rb(input)
114
- input
236
+ def dump(directory = Dir.pwd, name: self.class.to_s.downcase, abi: true, src_map: true)
237
+ src = formatted_teal
238
+
239
+ File.write(File.join(directory, "#{name}.teal"), src)
240
+ File.write(File.join(directory, "#{name}.abi.json"), JSON.pretty_generate(abi_hash)) if abi
241
+
242
+ return unless src_map
243
+
244
+ src_map_json = JSON.pretty_generate(generate_source_map(src))
245
+ File.write(File.join(directory, "#{name}.src_map.json"), src_map_json)
115
246
  end
116
247
 
117
- # defines a method that is transpiled to TEAL
118
- # @param name [Symbol] name of the method
119
- # @param definition [Lambda, Proc, UnboundMethod] the method definition
120
- # @return [nil]
121
- def define_teal_method(name, definition)
122
- new_source = generate_method_source(name, definition)
248
+ def formatted_teal
249
+ new_lines = []
250
+ comments = []
123
251
 
124
- define_singleton_method(name) do |*_args|
125
- eval_tealrb(new_source, debug_context: "teal method: #{name}")
252
+ @teal.compact.each do |ln|
253
+ ln = ln.strip
254
+
255
+ next if ln.empty?
256
+
257
+ if ln[/^#pragma/]
258
+ new_lines << { text: ln, void: true, comments: comments }
259
+ comments = []
260
+ elsif ln[%r{^//}]
261
+ comments << ln
262
+ else
263
+ op = ln.split.first
264
+ label_regex = %r{\S+:($| //)}
265
+
266
+ label_type = label_number = label_end = nil
267
+
268
+ if ln[label_regex]
269
+ label_type = ln[/^if/] || ln[/^while/]
270
+ label_number = ln[/(?<=^if)\d+/] || ln[/(?<=^while)\d+/]
271
+ label_end = ln[/_end:/]
272
+ end
273
+
274
+ new_lines << { text: ln, void: VOID_OPS.include?(op), comments: comments, label: ln[label_regex],
275
+ label_type: label_type, label_number: label_number, label_end: label_end }
276
+ comments = []
277
+ end
126
278
  end
127
279
 
128
- nil
280
+ output = []
281
+ indent_level = 0
282
+ current_labels = { 'while' => [], 'if' => [] }
283
+
284
+ new_lines.each do |ln|
285
+ indent_level = 1 if ln[:label] && indent_level.zero? && !ln[:label_type]
286
+
287
+ if ln[:label_type]
288
+ indent_level += 1 unless current_labels[ln[:label_type]].include?(ln[:label_number])
289
+ current_labels[ln[:label_type]] << ln[:label_number]
290
+ end
291
+
292
+ ln_indent_level = indent_level
293
+ ln_indent_level -= 1 if ln[:label]
294
+
295
+ output << '' if !output.last&.empty? && (ln[:label] || ln[:comments].any?)
296
+
297
+ ln[:comments].each { output << (("\t" * ln_indent_level) + _1) }
298
+ output << (("\t" * ln_indent_level) + ln[:text])
299
+
300
+ output << '' if ln[:void] && !output.last.empty?
301
+
302
+ next unless ln[:label_end]
303
+
304
+ indent_level -= 1
305
+ current_labels[ln[:label_type]].delete ln[:label_number]
306
+ end
307
+
308
+ output.join("\n")
309
+ end
310
+
311
+ # return the input without transpiling to TEAL
312
+ def rb(input)
313
+ input
129
314
  end
130
315
 
131
316
  # defines a TEAL subroutine
@@ -133,11 +318,15 @@ module TEALrb
133
318
  # @param definition [Lambda, Proc, UnboundMethod] the method definition
134
319
  # @return [nil]
135
320
  def define_subroutine(name, definition)
321
+ return if method(name).source_location.first == __FILE__
322
+
323
+ @eval_location = method(name).source_location
324
+
136
325
  define_singleton_method(name) do |*_args|
137
326
  callsub(name)
138
327
  end
139
328
 
140
- TEAL.instance << 'b main' unless TEAL.instance.include? 'b main'
329
+ @teal << 'b main' unless @teal.include? 'b main'
141
330
 
142
331
  label(name) # add teal label
143
332
 
@@ -161,18 +350,17 @@ module TEALrb
161
350
  def comment(content, inline: false)
162
351
  content = " #{content}" unless content[0] == ' '
163
352
  if inline
164
- last_line = TEAL.instance.pop
165
- TEAL.instance << "#{last_line} //#{content}"
353
+ last_line = @teal.pop
354
+ @teal << "#{last_line} //#{content}"
166
355
  else
167
- TEAL.instance << '' unless TEAL.instance.last[%r{^//}]
168
- TEAL.instance << "//#{content}"
356
+ @teal << "//#{content}"
169
357
  end
170
358
  end
171
359
 
172
360
  # inserts a string into TEAL source
173
361
  # @param string [String] the string to insert
174
362
  def placeholder(string)
175
- TEAL.instance << string
363
+ @teal << string
176
364
  end
177
365
 
178
366
  # the hash of the abi description
@@ -191,29 +379,52 @@ module TEALrb
191
379
  # transpiles #main and routes abi methods. To disable abi routing, set `@disable_abi_routing` to true in your
192
380
  # Contract subclass
193
381
  def compile
194
- TEAL.instance << 'main:' if TEAL.instance.include? 'b main'
382
+ @teal << 'main:' if @teal.include? 'b main'
195
383
  route_abi_methods unless self.class.disable_abi_routing
196
- eval_tealrb(rewrite(method(:main).source, method_rewriter: true), debug_context: 'main') if respond_to? :main
384
+ return unless respond_to? :main
385
+
386
+ @eval_location = method(:main).source_location
387
+
388
+ eval_tealrb(
389
+ rewrite(
390
+ method(:main).source,
391
+ method_rewriter: true
392
+ ),
393
+ debug_context: 'main'
394
+ )
395
+ end
396
+
397
+ def main
398
+ nil
399
+ end
400
+
401
+ def compiled_program
402
+ compile_response = Algod.new.compile(formatted_teal)
403
+
404
+ generate_source_map(formatted_teal) if compile_response.status != 200
405
+
406
+ JSON.parse(compile_response.body)['result']
197
407
  end
198
408
 
199
409
  private
200
410
 
201
411
  def route_abi_methods
202
- self.class.abi_interface.methods.each do |meth|
412
+ self.class.abi_interface.methods.each_with_index do |meth, i|
203
413
  signature = "#{meth[:name]}(#{meth[:args].map { _1[:type] }.join(',')})#{meth[:returns][:type]}"
204
414
  selector = OpenSSL::Digest.new('SHA512-256').hexdigest(signature)[..7]
205
415
 
206
- IfBlock.new(AppArgs[0] == byte(selector)) do
207
- callsub(meth[:name])
208
- approve
209
- end
416
+ app_args[int(0)] == byte(selector) # rubocop:disable Lint/Void
417
+ bz("abi_routing#{i}")
418
+ callsub(meth[:name])
419
+ approve
420
+ label("abi_routing#{i}")
210
421
  end
211
422
  end
212
423
 
213
424
  def generate_method_source(name, definition)
214
425
  new_source = rewrite(definition.source, method_rewriter: true)
215
426
 
216
- pre_string = StringIO.new
427
+ pre_string = []
217
428
 
218
429
  scratch_names = []
219
430
  definition.parameters.reverse.each_with_index do |param, _i|
@@ -221,17 +432,17 @@ module TEALrb
221
432
  scratch_name = [name, param_name].map(&:to_s).join(': ')
222
433
  scratch_names << scratch_name
223
434
 
224
- pre_string.puts "@scratch.store('#{scratch_name}')"
225
- pre_string.puts "#{param_name} = -> { @scratch['#{scratch_name}'] }"
435
+ pre_string << "@scratch.store('#{scratch_name}')"
436
+ pre_string << "#{param_name} = -> { @scratch['#{scratch_name}'] }"
226
437
  end
227
438
 
228
- "#{pre_string.string}#{new_source}"
439
+ "#{pre_string.join(';')};#{new_source}"
229
440
  end
230
441
 
231
442
  def generate_subroutine_source(definition, method_hash)
232
443
  new_source = rewrite(definition.source, method_rewriter: true)
233
444
 
234
- pre_string = StringIO.new
445
+ pre_string = []
235
446
 
236
447
  scratch_names = []
237
448
 
@@ -245,29 +456,42 @@ module TEALrb
245
456
  account_param_index = 0
246
457
  args_index = 0
247
458
 
459
+ method_hash[:on_completion].each_with_index do |oc, i|
460
+ this_txn.on_completion
461
+ int(oc)
462
+ equal
463
+ boolean_or unless i.zero?
464
+ end
465
+ assert
466
+
467
+ assert(this_txn.application_id == (int(0))) if method_hash[:create]
468
+
248
469
  if abi_hash['methods'].find { _1[:name] == method_hash[:name].to_s }
249
470
  definition.parameters.each_with_index do |param, i|
250
471
  param_name = param.last
251
472
 
252
- scratch_name = "#{definition.original_name}: #{param_name} [#{arg_types[i] || 'any'}] #{if args[i]
253
- args[i][:desc]
254
- end}"
473
+ scratch_name = "#{definition.original_name}: #{param_name} [#{arg_types[i] || 'any'}] #{
474
+ args[i][:desc] if args[i]}"
255
475
  scratch_names << scratch_name
256
476
 
257
- if txn_types.include? arg_types[i]
258
- pre_string.puts "@scratch['#{scratch_name}'] = Gtxns[Txn.group_index - int(#{txn_params})]"
477
+ type = arg_types[i]&.downcase
478
+
479
+ if txn_types.include? type
480
+ @scratch[scratch_name] = group_txns[this_txn.group_index.subtract int(txn_params)]
259
481
  txn_params -= 1
260
- elsif arg_types[i] == 'application'
261
- pre_string.puts "@scratch['#{scratch_name}'] = Apps[#{app_param_index += 1}]"
262
- elsif arg_types[i] == 'asset'
263
- pre_string.puts "@scratch['#{scratch_name}'] = Assets[#{asset_param_index += 1}]"
264
- elsif arg_types[i] == 'account'
265
- pre_string.puts "@scratch['#{scratch_name}'] = Accounts[#{account_param_index += 1}]"
482
+ elsif type == 'application'
483
+ @scratch[scratch_name] = apps[app_param_index += 1]
484
+ elsif type == 'asset'
485
+ @scratch[scratch_name] = assets[asset_param_index += 1]
486
+ elsif type == 'account'
487
+ @scratch[scratch_name] = accounts[account_param_index += 1]
488
+ elsif type == 'uint64'
489
+ @scratch[scratch_name] = btoi(app_args[args_index += 1])
266
490
  else
267
- pre_string.puts "@scratch['#{scratch_name}'] = AppArgs[#{args_index += 1}]"
491
+ @scratch[scratch_name] = app_args[args_index += 1]
268
492
  end
269
493
 
270
- pre_string.puts "#{param_name} = -> { @scratch['#{scratch_name}'] }"
494
+ pre_string << "#{param_name} = -> {@scratch['#{scratch_name}'] }"
271
495
  end
272
496
 
273
497
  else
@@ -277,34 +501,35 @@ module TEALrb
277
501
  definition.parameters.reverse.each_with_index do |param, i|
278
502
  param_name = param.last
279
503
 
280
- scratch_name = "#{definition.original_name}: #{param_name} [#{arg_types[i] || 'any'}] #{if args[i]
281
- args[i][:desc]
282
- end}"
504
+ scratch_name = "#{definition.original_name}: #{param_name} [#{arg_types[i] || 'any'}] #{
505
+ args[i][:desc] if args[i]}"
283
506
  scratch_names << scratch_name
284
507
 
285
- if txn_types.include? arg_types[i]
286
- pre_string.puts "@scratch['#{scratch_name}'] = Gtxns"
287
- elsif arg_types[i] == 'application'
288
- pre_string.puts "@scratch['#{scratch_name}'] = Applications.new"
289
- elsif arg_types[i] == 'asset'
290
- pre_string.puts "@scratch['#{scratch_name}'] = Assets.new"
291
- elsif arg_types[i] == 'account'
292
- pre_string.puts "@scratch['#{scratch_name}'] = Accounts.new"
508
+ type = arg_types[i]&.downcase
509
+
510
+ if txn_types.include? type
511
+ @scratch[scratch_name] = group_txn
512
+ elsif type == 'application'
513
+ @scratch[scratch_name] = application
514
+ elsif type == 'asset'
515
+ @scratch[scratch_name] = asset
516
+ elsif type == 'account'
517
+ @scratch[scratch_name] = account
293
518
  else
294
- pre_string.puts "@scratch.store('#{scratch_name}')"
519
+ @scratch.store(scratch_name)
295
520
  end
296
521
 
297
- pre_string.puts "#{param_name} = -> { @scratch['#{scratch_name}'] }"
522
+ pre_string << "#{param_name} = -> { @scratch['#{scratch_name}'] }"
298
523
  end
299
524
 
300
525
  end
301
526
 
302
- "#{pre_string.string}#{new_source}"
527
+ "#{pre_string.join(';')};#{new_source}"
303
528
  end
304
529
 
305
530
  def rewrite_with_rewriter(string, rewriter)
306
531
  process_source = RuboCop::ProcessedSource.new(string, RUBY_VERSION[/\d\.\d/].to_f)
307
- rewriter.new.rewrite(process_source)
532
+ rewriter.new.rewrite(process_source, self)
308
533
  end
309
534
 
310
535
  def rewrite(string, method_rewriter: false)
@@ -315,7 +540,7 @@ module TEALrb
315
540
  end
316
541
 
317
542
  [CommentRewriter, ComparisonRewriter, WhileRewriter, InlineIfRewriter, IfRewriter, OpRewriter,
318
- AssignRewriter, InternalMethodRewriter].each do |rw|
543
+ AssignRewriter].each do |rw|
319
544
  string = rewrite_with_rewriter(string, rw)
320
545
  end
321
546
 
@@ -331,7 +556,7 @@ module TEALrb
331
556
  end
332
557
 
333
558
  def eval_tealrb(s, debug_context:)
334
- pre_teal = Array.new TEAL.instance
559
+ pre_teal = Array.new @teal
335
560
 
336
561
  if self.class.debug
337
562
  puts "DEBUG: Evaluating the following code (#{debug_context}):"
@@ -343,7 +568,7 @@ module TEALrb
343
568
 
344
569
  if self.class.debug
345
570
  puts "DEBUG: Resulting TEAL (#{debug_context}):"
346
- puts Array.new(TEAL.instance) - pre_teal
571
+ puts Array.new(@teal) - pre_teal
347
572
  puts ''
348
573
  end
349
574
  rescue SyntaxError, StandardError => e
@@ -354,23 +579,9 @@ module TEALrb
354
579
  error_line = eval_locations[@eval_tealrb_rescue_count].split(':')[1].to_i
355
580
  end
356
581
 
357
- warn "'#{e}' when evaluating transpiled TEALrb source" if @eval_tealrb_rescue_count.zero?
358
-
359
- warn "Backtrace location (#{@eval_tealrb_rescue_count + 1} / #{eval_locations.size}):"
360
-
361
- @eval_tealrb_rescue_count += 1
362
-
363
- s.lines.each_with_index do |line, i|
364
- line_num = i + 1
365
- if error_line == line_num
366
- warn "=> #{line_num}: #{line}"
367
- else
368
- warn " #{line_num}: #{line}"
369
- end
370
- end
582
+ msg = "#{@eval_location.first}:#{error_line + @eval_location.last}"
371
583
 
372
- warn ''
373
- raise e
584
+ raise e, "#{msg}: #{e}"
374
585
  end
375
586
  end
376
587
  end