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.
- data/.gemtest +0 -0
- data/Gemfile +8 -0
- data/History.txt +12 -0
- data/Manifest.txt +21 -0
- data/README.rdoc +63 -0
- data/Rakefile +39 -0
- data/lib/csquare.rb +1089 -0
- data/lib/csquare/base.rb +36 -0
- data/lib/csquare/cerb.rb +82 -0
- data/lib/csquare/function.rb +175 -0
- data/lib/csquare/generator.rb +374 -0
- data/lib/csquare/generator/assign_op.rb +53 -0
- data/lib/csquare/generator/binary_op.rb +72 -0
- data/lib/csquare/generator/blueprint.rb +357 -0
- data/lib/csquare/generator/boolean_op.rb +32 -0
- data/lib/csquare/generator/enum.rb +99 -0
- data/lib/csquare/generator/enum/namer.rb +13 -0
- data/lib/csquare/generator/enum/op_namer.rb +34 -0
- data/lib/csquare/generator/enum/sparse_op_namer.rb +7 -0
- data/lib/csquare/generator/index.rb +340 -0
- data/lib/csquare/generator/op.rb +102 -0
- data/lib/csquare/generator/type.rb +62 -0
- metadata +140 -0
data/.gemtest
ADDED
File without changes
|
data/Gemfile
ADDED
data/History.txt
ADDED
@@ -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!
|
data/Manifest.txt
ADDED
@@ -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
|
data/README.rdoc
ADDED
@@ -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.
|
data/Rakefile
ADDED
@@ -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
|
data/lib/csquare.rb
ADDED
@@ -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'
|