csquare 0.1.0

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.
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'