sbuilder-eth 0.0.4

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.
@@ -0,0 +1,122 @@
1
+ require 'sexp_processor'
2
+
3
+ module Sbuilder
4
+ module Eth
5
+ class SexpProcessorResolve < SexpProcessorEth
6
+ # ------------------------------------------------------------------
7
+ # @!group Constructor
8
+
9
+ def initialize( options={} )
10
+ super( options )
11
+ # Allow Sexp nodes to remain intact, see 'process_rest'
12
+ self.require_empty = false
13
+
14
+ # visitors just recurses - do not shift sexp (as sexp_processor default does)
15
+ self.default_method = :process_rest
16
+
17
+ end
18
+
19
+ # @!endgroup
20
+
21
+ # ------------------------------------------------------------------
22
+ # @!group Resolvers for reference node types
23
+
24
+ # Resolve 'reference' on 'NewExpression' to point to constructor
25
+ # in contract pointed by 'classConstructed'
26
+ def process_NewExpression( sexp )
27
+
28
+ # resolve child particulary 'UserDefinedTypeName'
29
+ process_rest( sexp )
30
+
31
+ # Navigate to resolved contract
32
+ contract = sexp.classConstructed.reference
33
+
34
+ # resolve for constructor method
35
+ sexp.reference = contract.resolve( sexp.classConstructed["name"])
36
+
37
+ s()
38
+ end
39
+
40
+ def process_UserDefinedTypeName( sexp )
41
+ logger.debug "#{__method__}: sexp:#{sexp}" if logger.debug?
42
+
43
+ raise "Unscoped identifier '#{sexp['name']}' for #{sexp}" if sexp.enclosingScope.nil?
44
+ # Resolve user defined type name in 'enclosingScope'
45
+ sexp.reference = sexp.enclosingScope.resolve(sexp['name'])
46
+
47
+ s()
48
+ end
49
+
50
+ def process_MemberAccess( sexp )
51
+ logger.debug "#{__method__}: sexp:#{sexp}" if logger.debug?
52
+
53
+ # resolve childs (ie. :Identifier, :MemberAccess) NB: can also
54
+ # be FunctionCall as is case for f.gas(1).value(2)(3,5,6)
55
+ process_rest( sexp )
56
+
57
+ # 'struct' should have been resolved by process_rest.
58
+ # No need to resolve function call modifiers
59
+
60
+ if sexp.struct.sexp_type == :FunctionCall
61
+
62
+ # sexp.reference = scope.resolve( sexp["member_name"] ) unless sexp["member_name"] == Constants::FIELD_NAME_VALUE ||
63
+ # # sexp["member_name"] == Constants::FIELD_NAME_GAS
64
+
65
+ else
66
+ # scope is pointed by reference
67
+ scope = sexp.struct.reference
68
+ # Indirection for :VariableDeclaration: scope is actually the
69
+ # type of the :VariableDeclaration
70
+ scope = scope.variableType.reference if scope.sexp_type == :VariableDeclaration
71
+
72
+ if scope.sexp_type == :FunctionDefinition
73
+ #
74
+ # Case f.value(1).gas(1)(3,4) - value and gas modifiers of
75
+ # the function CALL are NOT defined in 'FunctionDefinition'
76
+ #
77
+ # Gas, value - No need to resolve - raise exception for surprieses if
78
+ # 'member_name' is something else that 'value' or 'gas'.
79
+ #
80
+ # raise "Unexpected sexp #{sexp["member_name"]} of in #{sexp}" unless sexp["member_name"] == Constants::FIELD_NAME_VALUE ||
81
+ # sexp["member_name"] == Constants::FIELD_NAME_GAS
82
+ # sexp.reference = sexp.functionCalled.reference
83
+ if sexp["member_name"] == Constants::FIELD_NAME_VALUE || sexp["member_name"] == Constants::FIELD_NAME_GAS
84
+ # point to called function
85
+ sexp.reference = sexp.struct.reference
86
+ else
87
+ sexp.reference = scope.resolve( sexp["member_name"] )
88
+ end
89
+
90
+ else
91
+ # easy part
92
+ sexp.reference = scope.resolve( sexp["member_name"] )
93
+ end
94
+
95
+ end
96
+
97
+ s()
98
+
99
+ end
100
+
101
+ def process_Identifier( sexp )
102
+ logger.debug "#{__method__}: sexp:#{sexp}" if logger.debug?
103
+
104
+ raise "Unscoped identifier '#{sexp['value']}' for #{sexp}" if sexp.enclosingScope.nil?
105
+ # Resolve identifier 'value' in 'enclosingScope' of 'sexp'
106
+ sexp.reference = sexp.enclosingScope.resolve(sexp['value'])
107
+
108
+ # continue with tree traversal
109
+ # process_rest( sexp )
110
+ s()
111
+
112
+ end
113
+
114
+
115
+ # @!endgroup
116
+
117
+
118
+
119
+ end
120
+ end
121
+ end
122
+
@@ -0,0 +1,355 @@
1
+ # coding: utf-8
2
+ require 'sexp_processor'
3
+
4
+ module Sbuilder
5
+ module Eth
6
+
7
+ # Visit AstSexp, 1) create hierarchical symbol tables 2) define
8
+ # symbols, and 3) add pointer to encosing scope to expression
9
+ # needed to be resolved.
10
+ #
11
+ # Techinically:
12
+ # - 1) Scope#enclosingScope : build scope hierarchy
13
+ # - 2) Scope#define( name, sexp ): build symbol table defines
14
+ # - 3) Reference#enclosingScope : set scope for use for Scope#resolve
15
+ #
16
+ # Scope hierarchy built:
17
+ # - contract
18
+ # - function
19
+ # - input parameter = request message (key name "msg"), scope where expression
20
+ # references are resolved
21
+ # - return parameters = response message ( key name "return-")
22
+ #
23
+ # TODO:
24
+ # - global scope
25
+ # - structure types
26
+ # - enums
27
+ #
28
+ #
29
+ #
30
+
31
+ class SexpProcessorScope < SexpProcessorEth
32
+
33
+ # ------------------------------------------------------------------
34
+ # @!group Object state
35
+
36
+ # @!attribute [Scope] currentScope which currently opened
37
+ attr_accessor :currentScope
38
+
39
+ # @!attribute [Hash] options
40
+ attr_accessor :options
41
+
42
+ # @!endgroup
43
+
44
+
45
+ # ------------------------------------------------------------------
46
+ # @!group Constructor
47
+
48
+ def initialize( options={} )
49
+ super( options )
50
+
51
+ self.options = options
52
+
53
+ initGlobalScope
54
+ end
55
+
56
+ def initGlobalScope
57
+ self.currentScope = GlobalScope.start( options )
58
+ end
59
+
60
+ # @!endgroup
61
+
62
+ # ------------------------------------------------------------------
63
+ # @!group Recursion helpers
64
+
65
+ def scoped( ethScope )
66
+ raise "Scope support missing in #{ethScope}" unless ethScope.respond_to?( :isScope ) && ethScope.isScope
67
+
68
+ # remember 'prevScope', update enclosing scope, switch '@currentScope',
69
+ prevScope = currentScope
70
+ ethScope.enclosingScope = currentScope
71
+ self.currentScope = ethScope
72
+
73
+ begin
74
+ yield
75
+ ensure
76
+ # logger.debug "#{__method__}: pop scope for '#{ethScope.sexp_type}' - resume #{prevScope ? prevScope.sexp_type : 'global scope'}" if logger.debug?
77
+ self.currentScope = prevScope
78
+ end
79
+ end
80
+
81
+ def getScopeExpressionProcessor( scope )
82
+ proc = ScopeExpressionProcessor.new( options )
83
+ proc.currentScope = scope
84
+ proc
85
+
86
+ end
87
+
88
+ # @!endgroup
89
+
90
+
91
+ # ------------------------------------------------------------------
92
+ # @!group Node rules to recurse down
93
+
94
+ def process_ContractDefinition( sexp )
95
+
96
+ # add contract to global scope
97
+ currentScope.define( sexp['name'], sexp )
98
+
99
+ scoped( sexp ) do
100
+ getScopeExpressionProcessor( currentScope ).process( sexp )
101
+ process_rest( sexp )
102
+ end
103
+ s()
104
+ end
105
+
106
+ # Scope ModifierDefinitionReferences
107
+ # to 'msg' and :ParameterList parameters in
108
+ # :ModifierDefinition must be resolved dynamically during
109
+ # :ModifierInvocation. Global references can be resolved
110
+ # "normally" (=statically by sexp_processor_scope and
111
+ # sexp_processor_resolve), as well as :Idenfier references in
112
+ # sexp_processor_resolve.
113
+ #
114
+ #
115
+ # Solution: we define 'msg' scope as :ParameterListWrapper
116
+ # encapsulating :ParameterList of :ModifierDefinition. Method
117
+ # 'scopeDefining', which would otherwise return :ParameterList
118
+ # or :ModifierDefinition, is changed to return :ParameterList of
119
+ # :FunctionDefintion from a value set dynamically into
120
+ # :ParameterListWrapper, when scope of modifier is known (=when
121
+ # processing :ModifierInvocation in :FunctionDefinition
122
+ # statements).
123
+ #
124
+ def process_ModifierDefinition( sexp )
125
+ logger.debug "#{__method__}: sexp=#{sexp}" if logger.debug?
126
+
127
+ currentScope.define( sexp['name'], sexp )
128
+
129
+ # Wrap :ModifierDefinition 'requestParameters' ParameterListWrapper
130
+ # sexp.wrapRequestParameters
131
+
132
+ blockScope = sexp
133
+ scoped( sexp ) do
134
+
135
+
136
+ # Define msg
137
+ currentScope.define( Constants::SOL_MSG, sexp.requestParameters)
138
+
139
+ # Add common params
140
+ addCommonRequestParameters( sexp.requestParameters, sexp)
141
+
142
+ # Scope 'normal' request paramers in () ModifierDefinition scope
143
+ sexp.requestParameters.parameters.each { |paramDecl| process( paramDecl ) }
144
+ scoped( sexp.requestParameters ) do
145
+ # scope request params within contract:func:input:output
146
+ process( sexp.requestParameters )
147
+ end
148
+
149
+
150
+ # currentScope.define( Constants::SYMBOL_RESPONSE_MSG, sexp.responseParameters )
151
+ # process( sexp.responseParameters )
152
+ # local variables
153
+ process( sexp.block )
154
+ end
155
+
156
+ # process expression on block with requestParameteres in scope context
157
+ getScopeExpressionProcessor( blockScope ).process( sexp.block )
158
+
159
+ s()
160
+
161
+ end
162
+
163
+ # Create scope hierarchy for :FunctionDefintion, define symbols
164
+ # for declarations in a scope, and add pointers to scope for
165
+ # identfiers.
166
+ #
167
+ # Implementation:
168
+ #
169
+ # - assumes preprocessing ensures return parameters have name
170
+ #
171
+ # - scope hieararchy: :FunctionDefintion, requestParameters,
172
+ # responseParameters
173
+ #
174
+ # -symbol definitions:
175
+ # - FunctionDefintion
176
+ # - msg --> requestParameters
177
+ # - local variables
178
+ # - requestParameters
179
+ # - request parameter name -> :VarDecl
180
+ # - responseParameters
181
+ # - response parameter name -> :VarDecl
182
+ #
183
+ # - scope identifiers
184
+ # - requestParameters
185
+ # -- modifierInvocations
186
+ # - responseParameters
187
+ # -- function body block
188
+ #
189
+ #
190
+
191
+ def process_FunctionDefinition( sexp )
192
+ logger.debug "#{__method__}: sexp=#{sexp}" if logger.debug?
193
+
194
+
195
+ currentScope.define( sexp['name'], sexp )
196
+ currentScope.define( Constants::SOL_THIS, sexp )
197
+
198
+ scoped( sexp ) do
199
+
200
+ # validate existing names for return value
201
+ logger.debug "#{__method__}: sexp.responseParameters=#{sexp.responseParameters}" if logger.debug?
202
+ # assume preprocessing ensures return parameters have name
203
+ sexp.responseParameters.parameters.each { |parameter| raise "Unnamed return parameters are not supported yet #{parameter} in #{sexp}" if parameter["name"].empty? }
204
+
205
+ # Support msg.X, where X=sender, value, ...
206
+ currentScope.define( Constants::SOL_MSG, sexp.requestParameters )
207
+
208
+ # TODO: remove, currently used to create response data types
209
+ # for this function
210
+ currentScope.define( Constants::SYMBOL_RESPONSE_MSG, sexp.responseParameters )
211
+
212
+ # add msg.sender, msg.recipient, msg.value
213
+ addCommonRequestParameters( sexp.requestParameters, sexp)
214
+
215
+ scoped( sexp.requestParameters ) do
216
+
217
+ # scope request params within contract:func:input:output
218
+ process( sexp.requestParameters )
219
+
220
+ # scope for modfifiers: contract:func:input
221
+ sexp.modifierInvocations.each do |modifier|
222
+ logger.debug "#{__method__}: scope-modifierInvocation:#{modifier}" if logger.debug?
223
+ # getScopeExpressionProcessor( sexp.requestParameters ).process( modifier )
224
+ getScopeExpressionProcessor( currentScope ).process( modifier )
225
+
226
+ end
227
+
228
+ # declare return parametes within 'responseParameters' scope
229
+ scoped( sexp.responseParameters ) do
230
+ process( sexp.responseParameters )
231
+ # add scope for identifier references
232
+ getScopeExpressionProcessor( currentScope ).process( sexp.block )
233
+ end # scoped( sexp.responseParameters )
234
+
235
+ end # request parameters
236
+
237
+ # local variables
238
+ process( sexp.block )
239
+
240
+ end # scoped(sexp): function
241
+
242
+ s()
243
+ end
244
+
245
+ # def process_ParameterList( sexp )
246
+ # # logger.info "#{__method__}: #{sexp} entered"
247
+ # scoped( sexp ) do
248
+ # process_rest( sexp )
249
+ # end
250
+ # end
251
+
252
+ # @!endgroup
253
+
254
+ # ------------------------------------------------------------------
255
+ # @!group Node rules for scoped ie. nodes using scope
256
+
257
+ def process_VariableDeclaration( sexp )
258
+ logger.debug "#{__method__}: sexp=#{sexp}" if logger.debug?
259
+ logger.info "#{__method__}: define variable name=#{sexp['name']}, type=#{sexp.sexp_type} within #{currentScope}"
260
+
261
+ currentScope.define( sexp['name'], sexp )
262
+ process_rest( sexp )
263
+ end
264
+
265
+ def process_VariableDeclarationWithDomain( sexp )
266
+ logger.debug "#{__method__}: sexp=#{sexp}" if logger.debug?
267
+ logger.info "#{__method__}: define variable name=#{sexp['name']}, type=#{sexp.sexp_type} within #{currentScope}"
268
+
269
+ currentScope.define( sexp['name'], sexp )
270
+
271
+ # No need to go deeper
272
+ # process_rest( sexp )
273
+
274
+ s()
275
+ end
276
+
277
+
278
+
279
+ # @!endgroup
280
+
281
+ # ------------------------------------------------------------------
282
+ # @!group AST modifiers
283
+
284
+ # Add common request parameters (e.g. sender, value, recipient)
285
+ # to 'sexpRequestParameters' of a constructor or a normal
286
+ # contract function 'sexpFunctionDefinition'.
287
+ #
288
+ # @param sexpFuncOrModifier
289
+ # [:FunctionDefinition,:ModifierDefinition] to add common
290
+ # parametes rto
291
+ #
292
+ #
293
+ def addCommonRequestParameters( sexpRequestParameters, sexpFuncOrModifier )
294
+ (sexpFuncOrModifier.sexp_type != :ModifierDefinition && sexpFuncOrModifier["isConstructor"] ?
295
+ Constants::COMMON_REQ_CONSTRUCTOR :
296
+ Constants::COMMON_REQ_MSG).each do |name_domain|
297
+ sexpRequestParameters.addChild( VariableDeclarationWithDomain.create( name_domain))
298
+ end
299
+
300
+ end
301
+
302
+ # @!endgroup
303
+
304
+
305
+ end
306
+
307
+ class ScopeExpressionProcessor < SexpProcessorEth
308
+
309
+ # @!attribute [Scope] currentScope which currently opened
310
+ attr_accessor :currentScope
311
+
312
+
313
+ def initialize( options={} )
314
+ super( options )
315
+
316
+ # self.options = options
317
+
318
+ end
319
+
320
+ # ------------------------------------------------------------------
321
+ # @!group Individual expressions - set enclosingScope
322
+
323
+ def process_UserDefinedTypeName( sexp )
324
+
325
+ logger.debug "#{__method__}: sexp==#{sexp}" if logger.debug?
326
+ raise "No current scope for #{sexp}" if currentScope.nil?
327
+
328
+ # save 'currentScope' to use in resolve declaration reference
329
+ sexp.enclosingScope = currentScope
330
+
331
+ process_rest(sexp)
332
+
333
+ end
334
+
335
+
336
+ def process_Identifier( sexp )
337
+
338
+ logger.debug "#{__method__}: sexp==#{sexp}" if logger.debug?
339
+ logger.info "#{__method__}: sexp=#{sexp['value']}"
340
+
341
+ # save 'currentScope' to use in resolve declaration reference
342
+ sexp.enclosingScope = currentScope
343
+
344
+ process_rest(sexp)
345
+ end
346
+
347
+ # @!endgroup
348
+
349
+
350
+
351
+ end
352
+ end
353
+
354
+ end
355
+