tealrb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ccb46efa84c6dfdf2378ab562eaf2345d947f0a9af9850c05220acfa1b540bc3
4
+ data.tar.gz: d7bfaa9d68b2fe60adeeb229f54f087a56b73319f0d3f6f8bf8d673b8a103258
5
+ SHA512:
6
+ metadata.gz: '0805c1e920c22e3d0fc64975135850a08147b3a3c9f1fb9390868ed60220d504f3fd20c89ea4ecdc96f71068e75b0748f62e502155335007ae0d3530fcbd4af4'
7
+ data.tar.gz: ec0abbd56e219ea3082200d9fb5bab2cd6ead37957af0c7dccc359e6c94064967cdac907b776bad0d07d9f1ecfb22fa46c37a84322777dfeb1d7fde0d1df57a2
data/lib/tealrb/abi.rb ADDED
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module TEALrb
6
+ module ABI
7
+ def abi_return(data)
8
+ log(concat('151f7c75', data.teal).teal)
9
+ end
10
+
11
+ class ABIDescription
12
+ attr_accessor :name
13
+ attr_reader :methods
14
+
15
+ def initialize
16
+ @name = ''
17
+ @networks = {}
18
+ @methods = []
19
+ end
20
+
21
+ def add_method(name:, desc:, args:, returns:)
22
+ @methods << {
23
+ name: name,
24
+ desc: desc,
25
+ args: args,
26
+ returns: { type: returns }
27
+ }
28
+ end
29
+
30
+ def add_id(network, id)
31
+ @networks[network] = { appID: id }
32
+ end
33
+
34
+ def to_h
35
+ {
36
+ 'name' => @name,
37
+ 'networks' => @networks,
38
+ 'methods' => @methods
39
+ }
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stringio'
4
+ module TEALrb
5
+ module CmdLine
6
+ module TEAL2TEALrb
7
+ def self.convert(input)
8
+ output = StringIO.new
9
+
10
+ input.each_line do |line|
11
+ line.strip!
12
+ next if line[/^#/]
13
+
14
+ if line[%r{^//}]
15
+ line = %(comment("#{line.sub(%r{//}, '')}"))
16
+ output.puts line
17
+ next
18
+ elsif line[%r{//.*}]
19
+ comment = line[%r{(?<=//).*}]
20
+ line.sub!(%r{//.*}, '')
21
+ inline_comment = %(comment("#{comment}", inline: true))
22
+ end
23
+
24
+ opcode = line.split.first
25
+
26
+ opcode_mappings = TEALrb::Opcodes::BINARY_OPCODE_METHOD_MAPPING
27
+ opcode_mappings.merge TEALrb::Opcodes::UNARY_OPCODE_METHOD_MAPPING
28
+
29
+ opcode_mappings.each do |op, method|
30
+ line.sub!(opcode, method) if opcode == op.to_s
31
+ end
32
+
33
+ if line[/^(b|bz|bnz|callsub) /]
34
+ label = line.split.last
35
+ line.gsub!(label, ":#{label}")
36
+ elsif line[/\w+:/]
37
+ line = ":#{line[0..-2]}"
38
+ elsif opcode == 'return'
39
+ line = 'teal_return'
40
+ elsif opcode == 'method'
41
+ line.sub!('method', 'method_signature')
42
+ end
43
+
44
+ if line.count(' ').positive? && !%w[byte pushbytes method].include?(opcode)
45
+ args = line.split[1..]
46
+ args.map! do |arg|
47
+ arg_as_int = Integer(arg, exception: false)
48
+ if arg[/^:/]
49
+ arg
50
+ else
51
+ arg_as_int || "'#{arg}'"
52
+ end
53
+ end
54
+
55
+ line = "#{opcode} #{args.join(', ')}"
56
+ end
57
+
58
+ output.puts line
59
+ output.puts inline_comment if inline_comment
60
+ end
61
+
62
+ output.string
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TEALrb
4
+ MAINNET = 'wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8='
5
+ end
@@ -0,0 +1,253 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TEALrb
4
+ class Contract
5
+ include TEALrb
6
+ include Opcodes
7
+ include ABI
8
+ include Rewriters
9
+
10
+ attr_reader :teal
11
+
12
+ class << self
13
+ attr_accessor :subroutines, :version, :teal_methods, :abi_method_hash, :abi_description, :debug
14
+
15
+ private
16
+
17
+ def inherited(klass)
18
+ klass.version = 6
19
+ klass.subroutines = {}
20
+ klass.teal_methods = {}
21
+ klass.abi_description = ABI::ABIDescription.new
22
+ klass.abi_method_hash = {}
23
+ klass.debug = false
24
+ super
25
+ end
26
+ end
27
+
28
+ # abi description for the method
29
+ def self.abi(desc:, args:, returns:)
30
+ args = args.map do |name, h|
31
+ h[:name] = name.to_s
32
+ h[:type] = h[:type].to_s.split('::').last.downcase
33
+ h
34
+ end
35
+
36
+ self.abi_method_hash = { desc: desc, args: args, returns: returns.to_s.split('::').last.downcase }
37
+ end
38
+
39
+ # specifies a TEAL subroutine that will be defined upon intialization
40
+ # @return [nil]
41
+ # @overload subroutine(name)
42
+ # @param name [Symbol] name of the subroutine and the method to use as the subroutine definition
43
+ #
44
+ # @overload subroutine(name)
45
+ # @param name [Symbol] name of the subroutine
46
+ #
47
+ # @yield [*args] the definition of the subroutine
48
+ def self.subroutine(name, &blk)
49
+ @subroutines[name] = (blk || instance_method(name))
50
+ abi_description.add_method(**({ name: name.to_s }.merge abi_method_hash)) unless abi_method_hash.empty?
51
+ @abi_method_hash = {}
52
+ nil
53
+ end
54
+
55
+ # specifies a method to be defined upon intialization that will be transpiled to TEAL when called
56
+ # @return [nil]
57
+ # @overload subroutine(name)
58
+ # @param name [Symbol] name of the method to use as the TEAL definition
59
+ #
60
+ # @overload subroutine(name)
61
+ # @param name [Symbol] name of the method
62
+ #
63
+ # @yield [*args] the definition of the TEAL method
64
+ def self.teal(name, &blk)
65
+ @teal_methods[name] = (blk || instance_method(name))
66
+ nil
67
+ end
68
+
69
+ # sets the `#pragma version`, defines teal methods, and defines subroutines
70
+ def initialize
71
+ @teal = TEAL.new ["#pragma version #{self.class.version}"]
72
+
73
+ self.class.subroutines.each do |name, blk|
74
+ define_subroutine name, blk
75
+ end
76
+
77
+ self.class.teal_methods.each do |name, blk|
78
+ define_teal_method name, blk
79
+ end
80
+ end
81
+
82
+ # defines a method that is transpiled to TEAL
83
+ # @param name [Symbol] name of the method
84
+ # @param definition [Lambda, Proc, UnboundMethod] the method definition
85
+ # @return [nil]
86
+ def define_teal_method(name, definition)
87
+ @teal.set_as_current
88
+
89
+ new_source = rewrite(definition.source, method_rewriter: true)
90
+
91
+ pre_string = StringIO.new
92
+
93
+ definition.parameters.reverse.each_with_index do |param, i|
94
+ param_name = param.last
95
+ pre_string.puts "store #{200 + i}"
96
+ pre_string.puts "comment('#{param_name}', inline: true)"
97
+ pre_string.puts "#{param_name} = -> { load #{200 + i}; comment('#{param_name}', inline: true) }"
98
+ end
99
+
100
+ new_source = "#{pre_string.string}#{new_source}"
101
+ define_singleton_method(name) do |*_args|
102
+ eval_tealrb(new_source, debug_context: "teal method: #{name}")
103
+ end
104
+
105
+ nil
106
+ end
107
+
108
+ # defines a TEAL subroutine
109
+ # @param name [Symbol] name of the method
110
+ # @param definition [Lambda, Proc, UnboundMethod] the method definition
111
+ # @return [nil]
112
+ def define_subroutine(name, definition)
113
+ @teal.set_as_current
114
+
115
+ @teal << 'b main' unless @teal.include? 'b main'
116
+
117
+ label(name) # add teal label
118
+
119
+ comment_params = definition.parameters.map(&:last).join(', ')
120
+ comment_content = "#{name}(#{comment_params})"
121
+ comment(comment_content, inline: true)
122
+
123
+ new_source = rewrite(definition.source, method_rewriter: true)
124
+
125
+ pre_string = StringIO.new
126
+
127
+ definition.parameters.reverse.each_with_index do |param, i|
128
+ param_name = param.last
129
+ pre_string.puts "store #{200 + i}"
130
+ pre_string.puts "comment('#{param_name}', inline: true)"
131
+ pre_string.puts "#{param_name} = -> { load #{200 + i}; comment('#{param_name}', inline: true) }"
132
+ end
133
+
134
+ new_source = "#{pre_string.string}#{new_source}retsub"
135
+ eval_tealrb(new_source, debug_context: "subroutine: #{name}")
136
+
137
+ define_singleton_method(name) do |*_args|
138
+ callsub(name)
139
+ end
140
+
141
+ nil
142
+ end
143
+
144
+ # insert comment into TEAL source
145
+ # @param content [String] content of the comment
146
+ # @param inline [Boolean] whether the comment should be on the previous TEAL line
147
+ def comment(content, inline: false)
148
+ if inline
149
+ last_line = @teal.pop
150
+ @teal << "#{last_line} // #{content}"
151
+ else
152
+ @teal << "// #{content}"
153
+ end
154
+ end
155
+
156
+ # inserts a string into TEAL source
157
+ # @param string [String] the string to insert
158
+ def placeholder(string)
159
+ @teal << string
160
+ end
161
+
162
+ # the hash of the abi description
163
+ def abi_hash
164
+ self.class.abi_description.to_h
165
+ end
166
+
167
+ # transpiles the given string to TEAL
168
+ # @param string [String] string to transpile
169
+ # @return [nil]
170
+ def compile_string(string)
171
+ @teal.set_as_current
172
+ eval_tealrb(rewrite(string), debug_context: 'compile_string')
173
+ nil
174
+ end
175
+
176
+ # transpiles #main
177
+ def compile
178
+ @teal.set_as_current
179
+ @teal << 'main:' if @teal.include? 'b main'
180
+ eval_tealrb(rewrite(method(:main).source, method_rewriter: true), debug_context: 'main')
181
+ end
182
+
183
+ private
184
+
185
+ def rewrite_with_rewriter(string, rewriter)
186
+ process_source = RuboCop::ProcessedSource.new(string, RUBY_VERSION[/\d\.\d/].to_f)
187
+ rewriter.new.rewrite(process_source.buffer, process_source.ast)
188
+ end
189
+
190
+ def rewrite(string, method_rewriter: false)
191
+ if self.class.debug
192
+ puts 'DEBUG: Rewriting the following code:'
193
+ puts string
194
+ puts ''
195
+ end
196
+
197
+ [ComparisonRewriter, IfRewriter, OpRewriter, AssignRewriter].each do |rw|
198
+ string = rewrite_with_rewriter(string, rw)
199
+ end
200
+
201
+ string = rewrite_with_rewriter(string, MethodRewriter) if method_rewriter
202
+
203
+ if self.class.debug
204
+ puts 'DEBUG: Resulting TEALrb code:'
205
+ puts string
206
+ puts ''
207
+ end
208
+
209
+ string
210
+ end
211
+
212
+ def eval_tealrb(s, debug_context:)
213
+ pre_teal = Array.new @teal
214
+
215
+ if self.class.debug
216
+ puts "DEBUG: Evaluating the following code (#{debug_context}):"
217
+ puts s
218
+ puts ''
219
+ end
220
+
221
+ eval s # rubocop:disable Security/Eval
222
+
223
+ if self.class.debug
224
+ puts "DEBUG: Resulting TEAL (#{debug_context}):"
225
+ puts Array.new(@teal) - pre_teal
226
+ puts ''
227
+ end
228
+ rescue SyntaxError, StandardError => e
229
+ @eval_tealrb_rescue_count ||= 0
230
+
231
+ eval_locations = e.backtrace.select {_1[/\(eval\)/]}
232
+ error_line = eval_locations[@eval_tealrb_rescue_count].split(':')[1].to_i
233
+
234
+ warn "'#{e}' when evaluating transpiled TEALrb source" if @eval_tealrb_rescue_count.zero?
235
+
236
+ warn "Backtrace location (#{@eval_tealrb_rescue_count + 1} / #{eval_locations.size}):"
237
+
238
+ @eval_tealrb_rescue_count += 1
239
+
240
+ s.lines.each_with_index do |line, i|
241
+ line_num = i + 1
242
+ if error_line == line_num
243
+ warn "=> #{line_num}: #{line}"
244
+ else
245
+ warn " #{line_num}: #{line}"
246
+ end
247
+ end
248
+
249
+ warn ''
250
+ raise e
251
+ end
252
+ end
253
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TEALrb
4
+ class IfBlock
5
+ class << self
6
+ attr_accessor :id
7
+ end
8
+
9
+ @id = 0
10
+
11
+ def initialize(teal, _cond, &blk)
12
+ @teal = teal
13
+ @else_count = 0
14
+ @id = self.class.id
15
+ @end_label = "if#{@id}_end:"
16
+
17
+ @id = self.class.id
18
+ self.class.id += 1
19
+
20
+ @teal << "bz if#{@id}_else0"
21
+ blk.call
22
+ @teal << "b if#{@id}_end"
23
+ @teal << "if#{@id}_else0:"
24
+ @teal << @end_label
25
+ end
26
+
27
+ def elsif(_cond, &blk)
28
+ @else_count += 1
29
+ @teal.delete @end_label
30
+ @teal << "bz if#{@id}_else#{@else_count}"
31
+ blk.call
32
+ @teal << "b if#{@id}_end"
33
+ @teal << "if#{@id}_else#{@else_count}:"
34
+ @teal << @end_label
35
+ self
36
+ end
37
+
38
+ def else(&blk)
39
+ @teal.delete @end_label
40
+ blk.call
41
+ @teal << @end_label
42
+ end
43
+ end
44
+ end