csquare 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
File without changes
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source :rubygems
2
+ gemspec
3
+
4
+ gem 'cast'
5
+
6
+ #group :test do
7
+ gem 'rspec'
8
+ #end
@@ -0,0 +1,12 @@
1
+ === 0.1.0 / 2012-06-25
2
+
3
+ * Complete refactoring of initial release (which didn't actually work).
4
+ * Function pointer array and enumerator support (1D and 2D).
5
+ * Uses the original author's cast gem instead of our fork.
6
+ * Handles #RUBY "precompiler" directives.
7
+ * Note: These work on the Blueprint level, not the Type level.
8
+ * These allow processing of Ruby code within C templates.
9
+
10
+ === 0.0.1 / not released
11
+
12
+ * Hello, world!
@@ -0,0 +1,21 @@
1
+ Rakefile
2
+ Gemfile
3
+ README.rdoc
4
+ Manifest.txt
5
+ History.txt
6
+ lib/csquare.rb
7
+ lib/csquare/base.rb
8
+ lib/csquare/cerb.rb
9
+ lib/csquare/function.rb
10
+ lib/csquare/generator.rb
11
+ lib/csquare/generator/type.rb
12
+ lib/csquare/generator/blueprint.rb
13
+ lib/csquare/generator/op.rb
14
+ lib/csquare/generator/assign_op.rb
15
+ lib/csquare/generator/binary_op.rb
16
+ lib/csquare/generator/boolean_op.rb
17
+ lib/csquare/generator/enum.rb
18
+ lib/csquare/generator/enum/namer.rb
19
+ lib/csquare/generator/enum/op_namer.rb
20
+ lib/csquare/generator/enum/sparse_op_namer.rb
21
+ lib/csquare/generator/index.rb
@@ -0,0 +1,63 @@
1
+ = CSquare
2
+
3
+ http://github.com/mohawkjohn/csquare
4
+
5
+ == Description
6
+
7
+ C, then D, C++, C# -- now C^2, simple C templates using Ruby.
8
+
9
+ Consider this to be a sort of carpenter's square. We call it C^2, or csquare. It's a simple tool for simple jobs.
10
+
11
+ This gem was developed for use in NMatrix (part of the SciRuby Project). We wanted to be able to write a single function
12
+ and have it be modified to produce C sources for each datatype (rational, complex, integer, float, Ruby object, etc).
13
+
14
+ It also produces some rudimentary function pointer arrays if you so desire, so that these functions can be accessed using
15
+ array notation.
16
+
17
+ Experimental! Use at your own risk. Actually, don't use this at all! It's extremely buggy and probably won't be useful
18
+ for your purposes. It's really custom-designed to handle a specific use case: NMatrix dtype templates.
19
+
20
+ == Example
21
+
22
+ A full example is available in NMatrix starting at line 696: https://github.com/mohawkjohn/nmatrix/blob/csquare/ext/nmatrix/generator.rb
23
+
24
+ Full examples of templates are available here: https://github.com/mohawkjohn/nmatrix/tree/csquare/ext/nmatrix/templates
25
+
26
+ (Note that those templates and example are licensed as part of SciRuby.)
27
+
28
+ Here's a simple CSquare source for complex numbers:
29
+
30
+ # looks in templates/ directory, outputs "test.c" and "test.h"
31
+ g = CSquare::Generator.new("templates", "test") do |c|
32
+ # Do this to register abbreviations for basic types
33
+ c.template_type(:float, 'TYPE') do |t|
34
+ t.type :f32, 'float'
35
+ t.type :f64, 'double'
36
+ end
37
+
38
+ c.template_type(:complex, 'TYPE', :r => 'FLOAT', :i => 'FLOAT') do |t|
39
+
40
+ t.type :c64, 'complex64', :long => :c128, 'FLOAT' => :f32
41
+ t.type :c128, 'complex128', 'FLOAT' => :f64
42
+
43
+ t.sources %w{div2 div4 mul2 mul4}
44
+
45
+ t.op :'==', 'TYPE' => '$0.r == $1.r && $0.i == $1.i', [:integer, :float] => '$0.r == $1 && $0.i == 0'
46
+
47
+ assign_str =
48
+ t.op :'=', [:integer, :boolean, :float] => '$0 = (struct $t) { $1, 0 }'
49
+
50
+ t.op :'*', 'TYPE' => 'mul2($0, $1)', :integer => 'mul4($0.r, $0.i, $1, 0)', :float => 'mul4($0.r, $0.i, $1, 0)'
51
+ t.op :'/', 'TYPE' => 'div2($0, $1)', :integer => 'div4($0.r, $0.i, $1, 0)', :float => 'div4($0.r, $0.i, $1, 0)'
52
+ t.op :'+', 'TYPE' => 'add2($0, $1)'
53
+ t.op :'-', 'TYPE' => 'sub2($0, $1)'
54
+ end
55
+ end
56
+
57
+ == Installation
58
+
59
+ gem install cast csquare
60
+
61
+ == License
62
+
63
+ BSD 2-clause. Copyright John Woods (mohawkjohn on github), 2012.
@@ -0,0 +1,39 @@
1
+ # -*- mode: ruby -*-
2
+
3
+ module Gem
4
+ Deprecate = Module.new do
5
+ include Deprecate
6
+ end
7
+ end
8
+
9
+ require 'hoe'
10
+
11
+ Hoe.plugin :bundler
12
+ Hoe.plugin :git
13
+ Hoe.plugin :gemspec
14
+
15
+ h = Hoe.spec 'csquare' do
16
+ self.developer('John Woods', 'john.woods@marcottelab.org')
17
+ self.readme_file = 'README.rdoc'
18
+ self.extra_dev_deps += [
19
+ ["rspec", ">= 2.6"]
20
+ ]
21
+ self.extra_deps += [
22
+ ["hoe", ">= 3.0.3"],
23
+ ["cast", ">= 0.2.0"]
24
+ ]
25
+ end
26
+
27
+ def run *cmd
28
+ sh(cmd.join(" "))
29
+ end
30
+
31
+ task :console do |task|
32
+ cmd = [ 'irb', "-Ilib", "-r 'csquare.rb'" ]
33
+ run *cmd
34
+ end
35
+
36
+ task :pry do |task|
37
+ cmd = [ 'pry', "-Ilib", "-r 'csquare.rb'"]
38
+ run *cmd
39
+ end
@@ -0,0 +1,1089 @@
1
+ require "rubygems"
2
+ require "cast"
3
+
4
+ module CSquare
5
+ VERSION = "0.1.0"
6
+
7
+ module Indexable
8
+ def self.included(base)
9
+ base.send :extend, SingletonMethods
10
+ end
11
+
12
+ module InstanceMethods
13
+
14
+ # TODO: Unify caches between Generator and Blueprint
15
+ def read_and_parse_source source
16
+ code = read_source "#{source}.c"
17
+
18
+ begin
19
+ CSquare::Function.new(code, self.template_keys(source) + keys_for(source))
20
+ rescue C::ParseError => e
21
+ STDERR.puts "Error parsing source #{source.inspect}."
22
+ STDERR.puts "custom type names were #{self.template_keys(source).inspect}"
23
+ STDERR.puts e.inspect
24
+ STDERR.puts code.size
25
+
26
+ raise e
27
+ end
28
+ end
29
+
30
+
31
+ def preprocess_source file
32
+ r, w = IO.pipe
33
+ source = nil
34
+ pid = fork do
35
+ $stdout.reopen(w)
36
+ r.close
37
+ cerb = CERB.new(file, self)
38
+ w.close
39
+ end
40
+
41
+ w.close
42
+ source = r.read
43
+ Process.wait2(pid)
44
+
45
+ source
46
+ end
47
+
48
+
49
+ def get_binding
50
+ binding
51
+ end
52
+
53
+
54
+ # TODO: Change the 2 to be the number of arguments it takes or something more indicative
55
+ def inline_op_function_name op
56
+ "#{CSquare::Generator::OP_FUNCTION_NAMES[op]}2"
57
+ end
58
+
59
+
60
+ def template_keys(source)
61
+ k = self.keys
62
+ k = k.concat(extra_keys(source)) if respond_to?(:extra_keys)
63
+ k = k.concat(extra_template_keys_for(source)) if respond_to?(:extra_template_keys_for)
64
+ k
65
+ end
66
+
67
+
68
+ # Given some function call, see if it needs to be decorated. If so, return the decorated version; if not, return
69
+ # the original.
70
+ #
71
+ # You should make sure your function is listed in sources if calls to it aren't being decorated when it should be.
72
+ #
73
+ # Returns a String.
74
+ def decorated_call name, type_symbol
75
+
76
+ if @externs.has_key?(name)
77
+ return_typename = @externs[name]
78
+
79
+ return force_decorated_call(name, type_symbol) if return_typename == C::CustomType.new(@key)
80
+ end
81
+
82
+ # If all else fails, return as-is
83
+ name
84
+ end
85
+
86
+
87
+ def force_decorated_call name, type_symbol
88
+ [name.to_s, type_symbol.to_s].compact.join('_')
89
+ end
90
+
91
+
92
+ def expand_options options
93
+ expanded_opts = {}
94
+ options.each_pair do |opt, val|
95
+ if opt.is_a?(Array)
96
+ opt.each do |o|
97
+ expanded_opts[o] = val
98
+ end
99
+ else
100
+ expanded_opts[opt] = val
101
+ end
102
+ end
103
+ expanded_opts
104
+ end
105
+
106
+
107
+ def declare_inline_source enumeree
108
+ @inline_sources[enumeree] = begin
109
+ if self.respond_to?(:parsed_inline_op_function)
110
+ parsed_inline_op_function(enumeree)
111
+ else
112
+ blueprint_for(enumeree).parsed_inline_op_function(enumeree)
113
+ end
114
+ end
115
+ end
116
+
117
+
118
+ def index basename, options_and_mappings = {}
119
+ @indices ||= Hash.new { |h,k| h[k] = [] }
120
+ on = options_and_mappings[:on]
121
+ new_index = CSquare::Generator::Index.new(self, basename, expand_options(options_and_mappings))
122
+
123
+ @externs[basename] = new_index.return_typename
124
+
125
+ # Save the index by its enumerator id
126
+ @indices[on || basename] << new_index
127
+ end
128
+
129
+
130
+
131
+
132
+ # Like Blueprint#sources, but builds a function pointer array for names given as an argument.
133
+ #
134
+ # May also supply a hash of options:
135
+ #
136
+ # * :on :: How to hash the second-dimension of the function pointer array -- give an array of
137
+ # operator symbols or an Enum object.
138
+ =begin
139
+ def index basename, signatures = {}
140
+ @indices = Hash.new{ |h,k| h[k] = [] }
141
+
142
+ init_opts = {}
143
+ sigs_for_index = {}
144
+
145
+ g = self.is_a?(Generator) ? self : self.generator
146
+
147
+ # :on may be 1) an array of operators or types, 2) the name of an enumerator, or 3) undefined.
148
+ # If it's undefined, defaults to #2, and uses basename as the name of the enumerator.
149
+ init_opts[:on] = signatures.delete(:on)
150
+ init_opts[:with] = signatures.delete(:with)
151
+
152
+ enumerees =
153
+ if !init_opts[:on].nil?
154
+ init_opts[:on].is_a?(String) ? g.enumerators[init_opts[:on]].enumerees : init_opts[:on]
155
+ else
156
+ g.enumerators[basename].enumerees
157
+ end
158
+
159
+ STDERR.puts("enumerees = " + enumerees.inspect)
160
+
161
+ STDERR.puts "A) signatures = " + signatures.inspect
162
+
163
+ # Expand out those keys that are actually arrays of keys, and generate signatures
164
+ # for enumerations on types.
165
+ signatures = begin
166
+ singles = {}
167
+
168
+ if !init_opts[:with].nil?
169
+ enumerees.each do |enumeree|
170
+ next if enumeree.nil?
171
+ singles[enumeree] = decorated_call(init_opts[:with], enumeree)
172
+ end
173
+ else
174
+ signatures.each_pair do |enumerees, name|
175
+ enumerees.each do |enumeree|
176
+ singles[enumeree] = name
177
+ end if enumerees.is_a?(Array)
178
+ end
179
+ end
180
+
181
+ # Remove arrays from signatures and add in the singles
182
+ signatures.delete_if{ |k,v| k.is_a?(Array) }.merge(singles)
183
+ end
184
+
185
+ STDERR.puts "B) signatures = " + signatures.inspect
186
+
187
+ signatures = begin # Handle inlines, load functions
188
+ new_signatures = {}
189
+
190
+ signatures.each_pair do |enumeree, name|
191
+
192
+ if name == :inline # concoct a function, a name, and act as if we loaded it
193
+ real_name = new_signatures[enumeree] = inline_op_function_name(enumeree)
194
+ f = @inline_sources[enumeree] = parsed_inline_op_function(enumeree)
195
+ g.externs[real_name] = f.return_type
196
+ sigs_for_index[real_name] = f.entity.type
197
+
198
+ else # load a function from source and figure out return type and such
199
+ new_signatures[enumeree] = name
200
+ @sources << name if self.is_a?(::CSquare::Generator::Blueprint)
201
+ f = read_and_parse_source(name) # go ahead and load the source to get the return type
202
+ @externs[name] = f.return_type
203
+ sigs_for_index[name] = f.entity.type
204
+
205
+ end
206
+ end
207
+
208
+ new_signatures
209
+ end
210
+
211
+ STDERR.puts "C) signatures = " + signatures.inspect
212
+
213
+ new_index = CSquare::Generator::Index.new(self, basename, sigs_for_index, signatures, init_opts)
214
+ @externs[basename] = new_index.return_typename
215
+
216
+ # Save the index by its enumerator id
217
+ @indices[init_opts[:on] || basename] << new_index
218
+ end
219
+ =end
220
+ end
221
+
222
+ module SingletonMethods
223
+ def acts_as_indexable
224
+ unless self.is_a?(ClassMethods)
225
+ include InstanceMethods
226
+ end
227
+ end
228
+ end
229
+
230
+ module ClassMethods
231
+ end
232
+ end
233
+
234
+ module Hashable
235
+ def self.included(base)
236
+ base.send :extend, SingletonMethods
237
+ end
238
+
239
+ module SingletonMethods
240
+ def acts_as_hashable(array_name)
241
+ unless self.is_a?(ClassMethods)
242
+ #include InstanceMethods
243
+ # extend ClassMethods
244
+
245
+ # Add a to_h function which returns a hash of variables and their types.
246
+ module_eval <<-"end_eval", __FILE__, __LINE__
247
+ def to_h(as=nil)
248
+ h = {}
249
+ self.#{array_name}.each do |obj|
250
+ if as == :string
251
+ h[obj.name] = obj.type.CustomType? || obj.type.Struct? ? obj.type.name : obj.type.to_s
252
+ else
253
+ h[obj.name] = obj.type
254
+ end
255
+ end
256
+ h
257
+ end
258
+ end_eval
259
+
260
+ end
261
+ end
262
+ end
263
+
264
+ module ClassMethods
265
+ end
266
+ end
267
+ end
268
+
269
+
270
+ # Add helper functions to CAST's C module.
271
+
272
+ class C::FunctionDef
273
+
274
+ # Get function parameters.
275
+ def params(as=nil)
276
+ self.type.to_h(as)
277
+ end
278
+
279
+ # Does some local exist at the base scope?
280
+ def has_local? var, blueprint=nil
281
+ h = params.has_key?(var) || self.def.locals.has_key?(var)
282
+ h = blueprint.externs.has_key?(var) || blueprint.generator.externs.has_key?(var) if !h && !blueprint.nil?
283
+ h
284
+ end
285
+
286
+ # Determine the type of some variable declared in this scope.
287
+ def type_of var, blueprint=nil
288
+ t = params[var] || self.def.locals[var]
289
+ t = blueprint.externs[var] || blueprint.generator.externs[var] if t.nil? && !blueprint.nil?
290
+ return nil if t.nil?
291
+
292
+ if t.is_a?(C::Type)
293
+ t = t.clone # return a copy, which should be marked as non-const
294
+ t.const = false
295
+ end
296
+
297
+ t
298
+ end
299
+ end
300
+
301
+
302
+ class C::Node
303
+ include CSquare::Hashable # enables acts_as_hashable class method
304
+
305
+ def op
306
+ CSquare::Generator::CAST_TO_OP[self.class]
307
+ end
308
+
309
+ # Recursively replace typenames with actual types.
310
+ def recursively_replace_casts! typenames_and_types
311
+ each do |n|
312
+ n.recursively_replace_casts! typenames_and_types
313
+ end
314
+ end
315
+
316
+ def replace_types! typenames_and_types
317
+ each do |n|
318
+ n.replace_types! typenames_and_types
319
+ end
320
+ end
321
+
322
+
323
+ def decorate_calls! type_symbol, blueprint_obj
324
+ each do |n|
325
+ n.decorate_calls! type_symbol, blueprint_obj
326
+ end
327
+ end
328
+
329
+
330
+ def new_temp_local type
331
+ parent_block.new_temp_local(type)
332
+ end
333
+
334
+ def new_temp_statement statement, before_statement
335
+ parent_block.new_temp_statement(statement, before_statement)
336
+ end
337
+
338
+
339
+ # Find the parent block
340
+ def parent_block
341
+ p = self.parent
342
+ while !p.Block?
343
+ p = p.parent
344
+ end
345
+ p
346
+ end
347
+
348
+
349
+ def parent_stmt
350
+ p = self.parent
351
+ while !p.Statement?
352
+ p = p.parent
353
+ end
354
+ p
355
+ end
356
+
357
+
358
+ # Find the parent FunctionDef
359
+ def parent_function_def
360
+ p = self.parent
361
+ while !p.FunctionDef?
362
+ p = p.parent
363
+ end
364
+ p
365
+ end
366
+
367
+
368
+ # Replace some expression with the pattern from Blueprint.
369
+ def recombine! function, blueprint, type_symbol, return_type=nil
370
+
371
+ args_types = {}
372
+
373
+ if respond_to?(:recombine_recursive!)
374
+ self.recombine_recursive! function, blueprint, type_symbol, return_type
375
+ else
376
+
377
+ self.fields.each_with_index do |f,i|
378
+ n = self.send(f.reader)
379
+
380
+ if n.respond_to?(:recombine!)
381
+ n = expand_node(n) if blueprint.has_op?(n.op)
382
+ result = n.recombine! function, blueprint, type_symbol, return_type
383
+ replace_node(n, result[0]) unless result.nil? || result[0].nil? || result[0] == n
384
+ end
385
+ end
386
+ end
387
+
388
+ if respond_to?(:return_typename)
389
+ [self, self.return_typename(function, blueprint)]
390
+ else
391
+ [self, nil]
392
+ end
393
+ end
394
+
395
+
396
+ # Takes some assignment expression node and turns it into a regular assign.
397
+ #
398
+ # e.g., x += y ---> x = x + y
399
+ def expand_node node
400
+ return node unless node.AssignmentExpression? && !node.Assign?
401
+
402
+ # Figure out the new class
403
+ begin
404
+ klass = C.const_get(node.class.to_s[3...-6])
405
+ new_node = C::Assign.new(:lval => node.lval.clone, :rval => klass.send(:new, :expr1 => node.lval, :expr2 => node.rval))
406
+
407
+ node.replace_with(new_node)
408
+ new_node
409
+ rescue NameError => e
410
+ STDERR.puts "Invalid CAST class #{node.class.to_s[3...-6].inspect}"
411
+ raise e
412
+ end
413
+ end
414
+
415
+
416
+ # Is some local 'var' declared anywhere in this scope or below? (Is it unsafe to declare it in this scope?)
417
+ def has_local_below? var
418
+ return true if self.respond_to?(:locals) && locals.has_key?(var)
419
+
420
+ self.each do |n|
421
+ return true if has_local_below?(var)
422
+ end
423
+
424
+ false
425
+ end
426
+
427
+ def csquare_key
428
+ self.to_s
429
+ end
430
+
431
+ end
432
+
433
+
434
+ class C::Block
435
+ attr_reader :temp_statements, :temp_locals
436
+
437
+ def recombine_recursive! function, blueprint, type_symbol, outside_return_type
438
+ result = self.stmts.map do |n|
439
+ replacement, return_type = n.recombine! function, blueprint, type_symbol, outside_return_type
440
+ n.replace_with(replacement) unless replacement.nil? || replacement == n
441
+ return_type # return an array of return types
442
+ end
443
+
444
+ before_offset = Hash.new { |h,k| h[k] = 0 }
445
+
446
+ # Add temp statements to the beginning of the block
447
+ if defined?(@temp_statements)
448
+ @temp_statements.reverse.each do |statement, before|
449
+
450
+ # Figure out the index of the statement that we want to insert before
451
+ before_index = self.stmts.index(before)
452
+
453
+ # Account for statements already inserted
454
+ self.stmts.insert(before_index - before_offset[before], statement)
455
+ before_offset[before] += 1 # increment every time a statement is added before some 'before' statement.
456
+ end
457
+ end
458
+
459
+ # Add the declarations to the beginning of the block, before the statements
460
+ temp_local_declarations.each do |dec|
461
+ self.stmts.unshift dec
462
+ end
463
+
464
+ result
465
+ end
466
+
467
+ # Create declarations for locals
468
+ def temp_local_declarations
469
+ by_type = {}
470
+
471
+ return [] unless defined?(@temp_locals)
472
+
473
+ @temp_locals.each_pair do |name, type|
474
+ by_type[type] ||= C::NodeArray.new
475
+ by_type[type] << C::Declarator.new(:name => name)
476
+ end
477
+
478
+ ary = []
479
+ by_type.each_pair do |type, declarators|
480
+ ary << C::Declaration.new(:type => type, :declarators => declarators)
481
+ end
482
+ ary
483
+ end
484
+
485
+ # Create a new temporary variable
486
+ def new_temp_local type
487
+ raise(ArgumentError, "type needs to be a string: #{type.inspect}") unless type.is_a?(String)
488
+
489
+ @temp_count ||= 0
490
+ @temp_locals ||= {}
491
+
492
+ name = "temp#{@temp_count}"
493
+ @temp_count += 1
494
+
495
+ parent_fd = parent_function_def
496
+
497
+ while parent_fd.has_local?(name) #|| has_local_below?(name) # check for conflicts just in case the programmer put in temp0 or something
498
+ name = "temp#{@temp_count}"
499
+ @temp_count += 1
500
+ end
501
+
502
+ @temp_locals[name] = C::CustomType.new(type).clone
503
+
504
+ name
505
+ end
506
+
507
+ # Queue a statement for addition before some existing statement.
508
+ def new_temp_statement statement, before_statement
509
+ @temp_statements ||= []
510
+ begin
511
+ @temp_statements << [C::Statement.parse("#{statement};"), before_statement]
512
+ rescue C::ParseError => e
513
+ STDERR.puts "Exception in new_temp_statement: #{e.inspect}"
514
+ STDERR.puts "statement = #{statement.inspect}"
515
+ raise e
516
+ end
517
+ end
518
+
519
+ # Return a hash of function local variables => types. You may pass :string as the sole argument if you want
520
+ #
521
+ # Note: only returns local variables at root scope in the function.
522
+ def locals(as=nil)
523
+ h = {}
524
+ self.stmts.each do |statement|
525
+ h.merge!(statement.to_h(as)) if statement.Declaration?
526
+ end
527
+ h
528
+ end
529
+
530
+ # Does some local exist at the base scope?
531
+ def has_local? var, blueprint=nil
532
+ return true if defined?(@temp_locals) && temp_locals.has_key?(var)
533
+ return true if locals.has_key?(var)
534
+ return true if self.parent_function_def.params.has_key?(var)
535
+ return true if self.parent_function_def.has_local?(var)
536
+ return true if !blueprint.nil? && (blueprint.externs.has_key?(var) || blueprint.generator.externs.has_key?(var))
537
+ false
538
+ end
539
+
540
+
541
+ def replace_types! typenames_and_types
542
+ super typenames_and_types
543
+
544
+ self.stmts.each_with_index do |stmt, i|
545
+ next unless stmt.Declaration?
546
+ next unless typenames_and_types.has_key?(stmt.type.underlying_typename)
547
+ stmt.type.underlying_typename = typenames_and_types[stmt.type.underlying_typename]
548
+ end
549
+ end
550
+
551
+ end
552
+
553
+
554
+
555
+ class C::Sizeof
556
+ def replace_types! typenames_and_types
557
+ self.expr.name = typenames_and_types[self.expr.name] if self.expr.CustomType? && typenames_and_types.has_key?(self.expr.name)
558
+ end
559
+ end
560
+
561
+
562
+ class C::PrefixExpression
563
+ def prefixed_literal?
564
+ self.expr.Literal? || self.expr.prefixed_literal?
565
+ end
566
+
567
+ def prefixed_variable?
568
+ self.expr.Variable? || self.expr.prefixed_variable?
569
+ end
570
+
571
+ def return_typename function, blueprint
572
+ return :boolean if self.Not?
573
+ self.expr.return_typename function, blueprint
574
+ end
575
+ end
576
+
577
+
578
+ class C::Negative
579
+ def underlying_value
580
+ self.prefixed_literal? ? -self.expr.underlying_value : self.expr.underlying_value
581
+ end
582
+ end
583
+
584
+
585
+ class C::Literal
586
+ def involves? literal_or_variables
587
+ literal_or_variables.is_a?(Array) ? literal_or_variables.include?(self.val) : literal_or_variables == self.val
588
+ end
589
+
590
+ def underlying_value
591
+ self.val
592
+ end
593
+
594
+ def return_typename function, blueprint
595
+ return :integer if val.is_a?(Fixnum)
596
+ return :float if val.is_a?(Float)
597
+ return :string if val.is_a?(String)
598
+ raise(NotImplementedError, "unhandled literal type: #{self.inspect}")
599
+ end
600
+
601
+ def depth
602
+ 0
603
+ end
604
+
605
+ def csquare_key
606
+ self.val
607
+ end
608
+ end
609
+
610
+
611
+ class C::CompoundLiteral # e.g., (struct TYPE) { -x.n, x.d }
612
+ def return_typename function, blueprint
613
+ return self.type.name
614
+ end
615
+
616
+ def underlying_typename
617
+ self.type.name
618
+ end
619
+
620
+ # Recursively replace typenames with actual types.
621
+ def recursively_replace_casts! typenames_and_types
622
+ each do |n|
623
+ n.recursively_replace_casts! typenames_and_types
624
+ end
625
+
626
+ self.type.name = typenames_and_types[self.type.name] if (self.type.CustomType? || self.type.Struct?) && typenames_and_types.has_key?(self.type.name)
627
+ end
628
+ end
629
+
630
+
631
+ class C::Variable
632
+ def involves? literal_or_variables
633
+ literal_or_variables.is_a?(Array) ? literal_or_variables.include?(self.name) : literal_or_variables == self.name
634
+ end
635
+
636
+ def underlying_value
637
+ self.name
638
+ end
639
+
640
+ def return_typename function, blueprint=nil
641
+ return :boolean if %w{true false}.include?(self.name)
642
+
643
+ if parent_block.has_local?(self.name, blueprint)
644
+ t = function.type_of(self.name, blueprint)
645
+ return t.to_s if t.is_a?(C::PrimitiveType) # int, float, double, etc
646
+ return t.is_a?(C::DirectType) ? t.name : t
647
+ end
648
+
649
+ # This is kind of a hack. To do it the real way, we really need to know the type symbol
650
+ if self.name =~ /_(MIN|MAX)$/
651
+ return blueprint.externs[self.name] if blueprint.externs.has_key?(self.name)
652
+ name_without_minmax = self.name[0...-4]
653
+ if blueprint.extra_templates_by_source.has_key?(function.name) && blueprint.extra_templates_by_source[function.name].has_key?(name_without_minmax)
654
+ return blueprint.extra_templates_by_source[function.name][name_without_minmax]
655
+ end
656
+ end
657
+
658
+ require 'pry'
659
+ binding.pry
660
+
661
+ raise(NotImplementedError, "undeclared variable #{self.name.inspect}")
662
+ end
663
+
664
+ def depth
665
+ 0
666
+ end
667
+
668
+ # Replace some expression with the pattern from Blueprint.
669
+ def recombine! function, blueprint, type_symbol, return_type=nil
670
+
671
+ args_types = {}
672
+
673
+ replace_vars = blueprint.vars(type_symbol)
674
+ return_node =
675
+ if replace_vars.has_key?(self.name)
676
+ n = self.clone
677
+ n.name = replace_vars[self.name]
678
+ n
679
+ else
680
+ self
681
+ end
682
+
683
+ [return_node, self.return_typename(function, blueprint)]
684
+ end
685
+
686
+ end
687
+
688
+
689
+ class C::Return
690
+ # This is not what you think it is. This function gives the typename that the statement would return if
691
+ # you set some variable equal to it. e.g., here, x = return false;
692
+ #
693
+ # This is an invalid statement, so the return_typename for a return statement is nil.
694
+ def return_typename function, blueprint=nil
695
+ nil
696
+ end
697
+ end
698
+
699
+
700
+ # These add to_h functions for figuring out what the parameters and their types are, and the local variables and their types, respectively.
701
+ # See csquare/function.rb to see how these to_h functions are used.
702
+
703
+ class C::Declaration
704
+ acts_as_hashable :declarators
705
+
706
+ # Replace some expression with the pattern from Blueprint.
707
+ def recombine! function, blueprint, type_symbol, return_type=nil
708
+ args_types = {}
709
+
710
+ declarators.each do |n|
711
+ result = n.recombine! function, blueprint, type_symbol
712
+ end
713
+
714
+ [self, nil]
715
+ end
716
+ end
717
+
718
+ class C::Declarator
719
+ # Replace some expression with the pattern from Blueprint.
720
+ def recombine! function, blueprint, type_symbol, return_type=nil
721
+ args_types = {}
722
+
723
+ result = init.recombine!(function, blueprint, type_symbol, return_type) unless init.nil?
724
+
725
+ [self, nil]
726
+ end
727
+ end
728
+
729
+ class C::Function
730
+ acts_as_hashable :params
731
+
732
+ def return_typename function, blueprint
733
+ self.type.to_s
734
+ end
735
+
736
+ def replace_types! h
737
+ # Replace return typename
738
+ type.name = h[type.name] if (type.CustomType? || type.Struct?) && h.has_key?(type.name)
739
+
740
+ # Replace params' typenames
741
+ params.each do |param|
742
+ if h.has_key?(param.type.underlying_typename)
743
+ param.replace_with C::Parameter.parse(param.to_s.gsub(param.type.underlying_typename, h[param.type.underlying_typename]))
744
+ end
745
+ end
746
+ end
747
+ end
748
+
749
+
750
+ class C::DirectType
751
+ def underlying_typename
752
+ self.name
753
+ end
754
+
755
+ def underlying_typename= new_typename
756
+ self.name = new_typename
757
+ end
758
+
759
+ def return_typename function, blueprint
760
+ self.underlying_typename
761
+ end
762
+ end
763
+
764
+
765
+ class C::PrimitiveType
766
+ def underlying_typename
767
+ self.class.to_s.split('::')[1].downcase
768
+ end
769
+ end
770
+
771
+
772
+ class C::IndirectType
773
+ def underlying_typename
774
+ self.type.underlying_typename
775
+ end
776
+ end
777
+
778
+
779
+ class C::Int
780
+ def return_typename function, blueprint
781
+ :integer
782
+ end
783
+ end
784
+
785
+
786
+ class C::Expression
787
+ def prefixed_literal?
788
+ false
789
+ end
790
+
791
+ def prefixed_variable?
792
+ false
793
+ end
794
+
795
+ def involves? literal_or_variables
796
+ involves_on_left?(literal_or_variables) || involves_on_right?(literal_or_variables)
797
+ end
798
+
799
+ end
800
+
801
+
802
+ class C::UnaryExpression
803
+ def involves? literal_or_variables
804
+ return expr.involves?(literal_or_variables) if expr.Expression?
805
+ false
806
+ end
807
+ end
808
+
809
+
810
+
811
+ class C::AssignmentExpression
812
+ def involves_on_left? literal_or_variables
813
+ return lval.involves?(literal_or_variables) if lval.Expression?
814
+ false
815
+ end
816
+
817
+ def involves_on_right? literal_or_variables
818
+ return rval.involves?(literal_or_variables) if rval.Expression?
819
+ false
820
+ end
821
+
822
+ def return_typename function, blueprint
823
+ self.lval.return_typename function, blueprint
824
+ end
825
+
826
+ # Replace some expression with the pattern from Blueprint.
827
+ def recombine! function, blueprint, type_symbol, return_type=nil
828
+ args_types = {}
829
+
830
+ # Determine the left-hand return type
831
+ return_type = self.lval.return_typename(function, blueprint)
832
+
833
+ # Recombine on the right-hand side
834
+ # r_result: ast, return type (or just nil)
835
+ r_result = self.rval.recombine! function, blueprint, type_symbol, return_type
836
+
837
+ args_types[self.lval.csquare_key] = self.lval.return_typename(function, blueprint)
838
+ args_types[r_result[0].csquare_key] = r_result[1]
839
+
840
+ rval.replace_with(r_result[0]) unless r_result.nil? || rval == r_result[0]
841
+
842
+ if blueprint.has_op?(op) && blueprint.responds_to_typename?(return_type)
843
+ blueprint.decorated_expression(type_symbol, op, args_types, return_type)
844
+ end
845
+ end
846
+ end
847
+
848
+
849
+ class C::Cast
850
+ # Recursively replace typenames with actual types.
851
+ def recursively_replace_casts! typenames_and_types
852
+ each do |n|
853
+ n.recursively_replace_casts! typenames_and_types
854
+ end
855
+ self.type.name = typenames_and_types[self.type.name] if (self.type.CustomType? || self.type.Struct?) && typenames_and_types.has_key?(self.type.name)
856
+ end
857
+ end
858
+
859
+
860
+ class C::Call
861
+ def return_typename function, blueprint
862
+ function.type_of(self.expr.name, blueprint)
863
+ end
864
+
865
+ def decorate_calls! type_symbol, blueprint_obj
866
+ super(type_symbol, blueprint_obj)
867
+ self.expr.name = blueprint_obj.decorated_call(self.expr.name, type_symbol)
868
+ end
869
+
870
+ # Recursively decorate function calls as appropriate.
871
+ def recursively_decorate_calls! blueprint, type_symbol
872
+ each do |n|
873
+ n.recursively_decorate_calls! blueprint, type_symbol
874
+ end
875
+
876
+ if blueprint.externs.has_key?(self.expr.name)
877
+ # Let's figure out what to replace this call with. Ask the template type for the
878
+ # decorated function names corresponding to *this* function.
879
+ decorated_externs = blueprint.decorated_externs(type_symbol, self.expr.name)
880
+
881
+ self.expr.name = decorated_externs[self.expr.name].keys.first
882
+ else
883
+ blueprint.generator.externs[self.expr.name]
884
+ end
885
+
886
+ end
887
+
888
+ # Determine the depth of this tree of binary expressions. Only include BinaryExpression nodes.
889
+ def depth
890
+ left_depth = expr1.respond_to?(:depth) ? expr1.depth : 0
891
+ right_depth = expr2.respond_to?(:depth) ? expr2.depth : 0
892
+ (left_depth > right_depth ? left_depth : right_depth) + 1
893
+ end
894
+
895
+ # Replace some expression with the pattern from Blueprint.
896
+ def recombine! function, blueprint, type_symbol, return_type=nil
897
+
898
+ args_types = {}
899
+
900
+ call_returns = self.return_typename(function, blueprint)
901
+
902
+ expr_name = self.expr.Index? ? self.expr.expr.name : self.expr.name
903
+
904
+ #if expr_name == "ew_bool"
905
+ # require 'pry'
906
+ # binding.pry
907
+ #end
908
+
909
+ # Handle externs
910
+ if blueprint.externs.has_key?(expr_name)
911
+ field_key = blueprint.externs[expr_name]
912
+ current_type = blueprint.types[type_symbol]
913
+ if current_type.keys.has_key?(field_key)
914
+ field_type_symbol = current_type.keys[field_key]
915
+ new_expr_name = blueprint.generator.types[field_type_symbol].blueprint.decorated_call(expr_name, field_type_symbol)
916
+
917
+ # Correctly update the expression name
918
+ if self.expr.Index?
919
+ self.expr.expr.name = new_expr_name
920
+ else
921
+ self.expr.name = new_expr_name
922
+ end
923
+
924
+ elsif self.expr.Index?
925
+ self.expr.expr.name = blueprint.decorated_call(expr_name, type_symbol)
926
+ elsif self.expr.Variable?
927
+ self.expr.name = blueprint.force_decorated_call(expr_name, type_symbol)
928
+ end
929
+ end
930
+
931
+ # Don't do fields -- do args instead. We don't want to recombine the expression name, as it won't have a return type.
932
+
933
+ self.args.each_with_index do |n,i|
934
+ if n.respond_to?(:recombine!)
935
+ n = expand_node(n) if blueprint.has_op?(n.op)
936
+ result = n.recombine! function, blueprint, type_symbol, return_type
937
+ replace_node(n, result[0]) unless result.nil? || result[0].nil? || result[0] == n
938
+ end
939
+ end
940
+
941
+ [self, call_returns]
942
+ end
943
+ end
944
+
945
+
946
+ class C::Index
947
+ def return_typename function, blueprint
948
+ t = function.type_of(self.expr.name, blueprint)
949
+ t.is_a?(C::Pointer) || t.is_a?(C::Array) ? t.type.name : t.name
950
+ end
951
+
952
+ # Replace some expression with the pattern from Blueprint.
953
+ def recombine! function, blueprint, type_symbol, return_type=nil
954
+
955
+ index_result = self.index.recombine! function, blueprint, type_symbol, :integer
956
+ self.index.replace_with(index_result[0]) unless index_result.nil?
957
+
958
+ # Recombine on the right-hand side
959
+ # r_result: ast, return type (or just nil)
960
+ expr_result = self.expr.recombine! function, blueprint, type_symbol, return_type
961
+ self.expr.replace_with(expr_result[0]) unless expr_result.nil?
962
+
963
+ # Mostly only need to figure out the return typename
964
+ [self, self.return_typename(function, blueprint)]
965
+ end
966
+
967
+ def depth
968
+ self.expr.depth
969
+ end
970
+ end
971
+
972
+
973
+ class C::Dereference
974
+ def return_typename function, blueprint
975
+ function.type_of(self.expr.name, blueprint).type.name # de-Pointerize
976
+ end
977
+
978
+ def depth
979
+ self.expr.depth
980
+ end
981
+ end
982
+
983
+ class C::Dot
984
+ def return_typename function, blueprint
985
+ raise("no field keys found for type #{function.type_of(self.expr.name, blueprint).inspect}") if blueprint.fields.nil?
986
+ blueprint.fields[self.member.name.to_sym]
987
+ end
988
+
989
+ def depth
990
+ 0
991
+ end
992
+ end
993
+
994
+
995
+ class C::BinaryExpression
996
+
997
+ # Replace some expression with the pattern from Blueprint.
998
+ def recombine! function, blueprint, type_symbol, return_type
999
+
1000
+ # Get depth before we recombine children
1001
+ d = depth
1002
+
1003
+ local_return_type = self.return_typename(function, blueprint)
1004
+ return_type ||= local_return_type
1005
+ return_type = local_return_type if local_return_type == :boolean
1006
+
1007
+
1008
+ # Upgrade the return type for expressions that aren't simple
1009
+ final_return_type = (d > 1 && return_type == blueprint.key) ? blueprint.long_key : return_type
1010
+
1011
+ args_types = {}
1012
+
1013
+ first_field_type = nil # type which the templater recognizes for the right-hand argument
1014
+
1015
+ self.fields.each_with_index do |f,i|
1016
+
1017
+ # Get the node
1018
+ n = self.send(f.reader)
1019
+
1020
+ # n_result: ast, return type (or just nil)
1021
+ n_result = n.recombine! function, blueprint, type_symbol, final_return_type
1022
+
1023
+ first_field_type = n_result[1] if i == 0
1024
+
1025
+ # Basically this says: don't do it for boolean binary expressions
1026
+ if blueprint.responds_to_typename?(first_field_type)
1027
+
1028
+ if blueprint.has_op?(self.op) && final_return_type.is_a?(String) && (d > 1 || final_return_type != n_result[1]) && n_result[0].depth > 0
1029
+
1030
+ # TODO: Reusable temp locals (e.g., temp0, temp1, temp2, ...) for better -O0 performance
1031
+ #if blueprint.id == :rational && function.name =~ /^numbmm/
1032
+ # require 'pry'
1033
+ # binding.pry
1034
+ #end
1035
+
1036
+ temp = self.new_temp_local(final_return_type)
1037
+ self.new_temp_statement("#{temp} = #{n_result[0].csquare_key}", self.parent_stmt)
1038
+
1039
+ n_result[0] = C::Expression.parse(temp)
1040
+ end
1041
+
1042
+ args_types[n_result[0].csquare_key] = n_result[1]
1043
+
1044
+ end
1045
+
1046
+ replace_node(n, n_result[0]) unless n == n_result[0]
1047
+ end
1048
+
1049
+
1050
+ result = if blueprint.has_op?(op) && blueprint.responds_to_typename?(first_field_type)
1051
+ blueprint.decorated_expression(type_symbol, self.op, args_types, final_return_type)
1052
+ end
1053
+
1054
+ result.nil? ? [self, local_return_type] : result
1055
+ end
1056
+
1057
+ # Determine the depth of this tree of binary expressions. Only include BinaryExpression nodes.
1058
+ def depth
1059
+ left_depth = expr1.respond_to?(:depth) ? expr1.depth : 0
1060
+ right_depth = expr2.respond_to?(:depth) ? expr2.depth : 0
1061
+ (left_depth > right_depth ? left_depth : right_depth) + 1
1062
+ end
1063
+
1064
+
1065
+ def involves_on_left? literal_or_variables
1066
+ return expr1.involves?(literal_or_variables) if expr1.Expression?
1067
+ false
1068
+ end
1069
+
1070
+ def involves_on_right? literal_or_variables
1071
+ return expr2.involves?(literal_or_variables) if expr2.Expression?
1072
+ false
1073
+ end
1074
+
1075
+ # Which type should we expect this binary expression to return?
1076
+ #
1077
+ # Needed for determining template-based mutations.
1078
+ def return_typename function, blueprint
1079
+ return :boolean if CSquare::Generator::BOOL_CAST_TO_OP.has_key?(self.class)
1080
+ self.expr1.return_typename(function, blueprint)
1081
+ end
1082
+
1083
+ end
1084
+
1085
+
1086
+ require 'csquare/cerb'
1087
+ require 'csquare/base'
1088
+ require 'csquare/function'
1089
+ require 'csquare/generator'