csquare 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,53 @@
1
+ class CSquare::Generator::AssignOp < CSquare::Generator::Op
2
+
3
+ def decorated args_types, return_type, default_type_symbol
4
+ raise(ArgumentError, "expected 1-2 args, found #{args_types.size}") unless args_types.size == 1 || args_types.size == 2
5
+
6
+ super(args_types, return_type, default_type_symbol)
7
+ end
8
+
9
+ # Determine which pattern to apply
10
+ def select_pattern args_types, return_type
11
+
12
+ arg = args_types.keys[1] || args_types.keys[0] # assign || unary
13
+ type = args_types[arg]
14
+
15
+ pat = begin
16
+ if self.has_key?(arg)
17
+ self[arg]
18
+ elsif type.is_a?(Symbol)
19
+ self[type]
20
+ elsif type == return_type # homogeneous
21
+ self[blueprint.key]
22
+ else # heterogeneous
23
+
24
+ if type == blueprint.long_key
25
+ self[type]
26
+ else
27
+ self[:cast]
28
+ end
29
+
30
+ end
31
+ end
32
+
33
+ # If we have TYPE on the right, we need to change it to LONG_TYPE if the return type is also LONG_TYPE
34
+ if pat =~ /\(struct\s([A-Z]+)\)/
35
+ orig_struct_type = $1
36
+ if return_type == "LONG_#{orig_struct_type}"
37
+ return pat.gsub("(struct #{orig_struct_type})", "(struct LONG_#{orig_struct_type})")
38
+ end
39
+ end
40
+
41
+ pat
42
+ end
43
+
44
+ # Determine a type symbol to apply to function calls based on argument and return types.
45
+ def select_type_symbol types, return_type, default_symbol
46
+
47
+ if return_type == blueprint.long_key
48
+ blueprint.types[default_symbol].long_id
49
+ else
50
+ default_symbol
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,72 @@
1
+ class CSquare::Generator::BinaryOp < CSquare::Generator::Op
2
+ #def initialize blueprint, id, patterns
3
+ # @blueprint = blueprint
4
+ # @id = id
5
+ # @patterns = patterns
6
+ #end
7
+
8
+
9
+ def decorated args_types, return_type, default_type_symbol
10
+ unless args_types.size == 2
11
+ binding.pry
12
+ raise(ArgumentError, "expected two args, found #{args_types.size}")
13
+ end
14
+
15
+ super(args_types, return_type, default_type_symbol)
16
+ end
17
+
18
+
19
+ # Determine which pattern to apply
20
+ def select_pattern args_types, return_type
21
+ args = args_types.keys
22
+ types = args_types.values
23
+
24
+ # If the exact arg has a pattern, prefer to use that
25
+ if self.has_key?(args[1]) # look for exact arg
26
+ self[args[1]]
27
+ elsif types[1].is_a?(Symbol) # look for type symbol
28
+ self[types[1]]
29
+ else # look for TYPE or LONG_TYPE or :cast
30
+ types = args_types.values
31
+
32
+ reduced_types = (types + [return_type]).uniq
33
+
34
+
35
+ if reduced_types.size == 1 # homogeneous
36
+
37
+ if blueprint.long_key == reduced_types[0]
38
+ self[blueprint.long_key] || self[:cast] || self[blueprint.key]
39
+ else
40
+ self[reduced_types[0]]
41
+ end
42
+
43
+ elsif reduced_types.size == 2
44
+
45
+ self[:cast] || begin
46
+ if types[0] == types[1] # homogeneous
47
+ self[blueprint.key]
48
+ else # heterogeneous
49
+ self[blueprint.long_key]
50
+ end
51
+ end
52
+
53
+ else
54
+
55
+ # This should not happen
56
+ raise(ArgumentError, "Error: three types were supplied: #{args_types.inspect}, #{return_type.inspect}")
57
+
58
+ end
59
+ end
60
+ end
61
+
62
+ # Determine a type symbol to apply to function calls based on argument and return types.
63
+ def select_type_symbol types, return_type, default_symbol
64
+ reduced_types = (types + [return_type]).uniq
65
+
66
+ if reduced_types.include?(blueprint.long_key)
67
+ blueprint.types[default_symbol].long_id
68
+ else
69
+ default_symbol
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,357 @@
1
+ class CSquare::Generator::Blueprint
2
+ include CSquare::Indexable
3
+ acts_as_indexable
4
+
5
+ alias_method :keys_for, :template_keys
6
+
7
+ def initialize generator_obj, id, template_param, field_params
8
+ @id = id
9
+ @key = template_param
10
+ @fields = field_params
11
+ @types = {}
12
+ @externs = {
13
+ "#{@key}_MAX" => @id,
14
+ "#{@key}_MIN" => @id,
15
+ "LONG_#{@key}_MAX" => @id,
16
+ "LONG_#{@key}_MIN" => @id
17
+ }
18
+ @ops = {}
19
+ @sources = []
20
+ @generator= generator_obj
21
+ @inline_sources = {}
22
+ @extra_templates_by_source = Hash.new { |h,k| h[k] = {} }
23
+
24
+ yield self if block_given?
25
+
26
+ # STDERR.puts "#{@key.inspect} EXTERNS: " + @externs.inspect
27
+ end
28
+
29
+
30
+ attr_reader :generator, :id, :key, :fields, :types, :externs, :extra_templates_by_source
31
+
32
+ def indices
33
+ defined?(@indices) ? @indices : {}
34
+ end
35
+
36
+ def inspect
37
+ obj_id = "0x#{(self.object_id << 1).to_s(16)}"
38
+ "#<#{self.class.to_s}:#{obj_id} #{@id.inspect}=>#{@key.inspect}=>#{@fields.inspect} type_ids=#{@types.keys.inspect} ops=#{@ops.keys.inspect} sources=#{@sources.inspect}>"
39
+ end
40
+
41
+ def long_key
42
+ "LONG_#{@key}"
43
+ end
44
+
45
+ # Declare a type within this blueprint. It will be used to transform the blueprint and create a set of functions.
46
+ #
47
+ # Must provide at least an id (a shorthand symbol, such as :i64) and a ctype String (e.g., "int64_t").
48
+ # Optionally, may also provide a hash of options
49
+ #
50
+ # * :long :: Gives the id for another type in this blueprint which is the long version of this type
51
+ # * template params which correspond to the field parameters in the blueprint.
52
+ #
53
+ # Example:
54
+ #
55
+ # c.blueprint(:complex, 'TYPE', :r => 'FLOAT', :i => 'FLOAT') do |t|
56
+ # t.type :c64, 'complex64', :long => :c128, 'FLOAT' => :f32 # f32 and f64 are declared in a separate blueprint
57
+ # t.type :c128, 'complex128', 'FLOAT' => :f64
58
+ # end
59
+ #
60
+ def type id, ctype, options = {}
61
+ raise(ArgumentError, "id #{id} already declared") if @types.has_key?(id) || @generator.blueprints.has_key?(id)
62
+
63
+ @types[id] = CSquare::Generator::Type.new(self, id, ctype, options)
64
+
65
+ @externs[@types[id].max] = self.id unless @types[id].max.nil?
66
+ @externs[@types[id].min] = self.id unless @types[id].min.nil?
67
+
68
+ self
69
+ end
70
+
71
+ # Does this blueprint contain instructions for dealing with some key (e.g., 'TYPE')?
72
+ def responds_to_typename? k
73
+ return true if k == @key || k == "LONG_#{@key}"
74
+ false
75
+ end
76
+
77
+ def recognizes? id, k
78
+ if has_op?(id)
79
+ return true if responds_to_typename?(k)
80
+ return true if @ops[id].has_key?(k)
81
+ end
82
+ false
83
+ end
84
+
85
+
86
+ def has_op? id
87
+ @ops.has_key?(id)
88
+ end
89
+
90
+
91
+ # Return a hash giving mappings from template typenames (keys, e.g., 'TYPE') to ctypes.
92
+ def params type_symbol
93
+ k = @types[type_symbol].ctype_keys # hash of typenames to type ids
94
+
95
+ begin
96
+ k[@key] = @generator.types[type_symbol].ctype
97
+ rescue NoMethodError
98
+ STDERR.puts "NoMethodError: @key=#{@key}, type_symbol = #{type_symbol}"
99
+ end
100
+ k["LONG_#{@key}"] = (generator.types[type_symbol].long || generator.types[type_symbol]).ctype
101
+ k
102
+ end
103
+
104
+
105
+ # Return a hash giving mappings from template variables (e.g., UINT_MAX, LONG_UINT_MAX) to actual variables.
106
+ def vars type_symbol
107
+ @vars ||= {}
108
+ @vars[type_symbol] = begin
109
+ h = {}
110
+
111
+ type = @types[type_symbol]
112
+ h["#{@key}_MIN"] = type.min unless type.min.nil?
113
+ h["#{@key}_MAX"] = type.max unless type.max.nil?
114
+
115
+ long_type = type.long
116
+ if long_type
117
+ h["LONG_#{@key}_MIN"] = long_type.min unless long_type.min.nil?
118
+ h["LONG_#{@key}_MAX"] = long_type.max unless long_type.max.nil?
119
+ end
120
+
121
+ h
122
+ end
123
+ end
124
+
125
+
126
+ # Register symbols available to functions in this blueprint, and their template parameters. Note that this is only used
127
+ # to register symbols from other blueprints. Takes a hash as only argument.
128
+ #
129
+ # Example:
130
+ #
131
+ # c.blueprint(:int, 'TYPE') do |t|
132
+ # t.type :i16, 'int16_t', :long => :i32
133
+ # t.type :i32, 'int32_t'
134
+ # t.sources %w{gcf} # gcf is a templated function for :i16 and :i32
135
+ # end
136
+ #
137
+ # c.blueprint(:rational, 'TYPE', :n => 'INT', :d => 'INT') do |t|
138
+ # t.type :r32, 'rational32', :long => :r64, 'INT' => :i16
139
+ # t.type :r64, 'rational64', 'INT' => :i32
140
+ # t.externs 'gcf' => 'INT' # lets us know that gcf is a templated function returning :i16 or :i32 depending
141
+ # end
142
+ #
143
+ def externs h=nil
144
+ return @externs if h.nil?
145
+ @externs.merge!(h)
146
+ end
147
+
148
+ # Declare source files containing functions for types within this blueprint.
149
+ #
150
+ # Example:
151
+ #
152
+ # t.sources %w{gemm div2 div4 mul2 mul4}
153
+ #
154
+ # These will frequently be referenced by the op method.
155
+ def sources ary = [], extra_typenames_and_blueprint_ids = {}
156
+ ary.each do |str|
157
+ # Do this first so that the extra typenames are registered
158
+ @extra_templates_by_source[str] = extra_typenames_and_blueprint_ids
159
+
160
+ # go ahead and load the source to get the return type
161
+ f = read_and_parse_source(str)
162
+
163
+ # Add this to our list of externs
164
+ STDERR.puts("Warning: overriding extern '#{str}' (value='#{@externs[str]}') --> '#{f.return_type}'") if @externs.has_key?(str)
165
+ @externs[str] = f.return_type
166
+
167
+ @sources << str
168
+ end
169
+
170
+ @sources
171
+ end
172
+
173
+
174
+ def has_source? f_name
175
+ @sources.include?(f_name)
176
+ end
177
+
178
+
179
+
180
+ # Declare an operation that this blueprint should manage. First argument is a symbol, e.g., :'*' (for C::Multiply).
181
+ # Second argument is a hash: keys are template parameters (e.g., 'TYPE'), C basic types (e.g., 'int'), values (e.g.,
182
+ # 0, 1), or blueprint IDs (e.g., :integer). Values of this hash are patterns to substitute the operations for.
183
+ #
184
+ # Expect boolean operations in C to return a blueprint ID of :boolean. This is declared implicitly, as are :integer
185
+ # and :float. All can be overridden.
186
+ #
187
+ # Example:
188
+ #
189
+ # t,sources %w{mul2 mul4}
190
+ # t.op :'*', 'TYPE' => 'mul2($0, $1)', :integer => 'mul4($0.r, $0.i, $1, 0)', :float => 'mul4($0.r, $0.i, $1, 0)' # for complex multiplication
191
+ #
192
+ def op id, types_and_patterns = {}
193
+ raise(ArgumentError, "op id #{id} already declared") if @ops.has_key?(id)
194
+
195
+ op_class =
196
+ if CSquare::Generator::BOOL_CAST_TO_OP.has_value?(id)
197
+ CSquare::Generator::BooleanOp
198
+ elsif CSquare::Generator::BINARY_ASSIGN_CAST_TO_OP.has_value?(id) || CSquare::Generator::BINARY_CAST_TO_OP.has_value?(id)
199
+ CSquare::Generator::BinaryOp
200
+ elsif id == :'='
201
+ CSquare::Generator::AssignOp
202
+ else # UNARY #TODO: This may need its own class
203
+ CSquare::Generator::AssignOp
204
+ end
205
+
206
+ @ops[id] ||= op_class.send(:new, self, id)
207
+
208
+ types_and_patterns.each_pair do |type_id, pattern|
209
+ @ops[id][type_id] = pattern
210
+ end
211
+
212
+ self
213
+ end
214
+
215
+
216
+ # To be called by Generator only after everything is setup.
217
+ def mutate_functions type_symbol
218
+ functions = []
219
+
220
+ local_template_params = self.params(type_symbol)
221
+
222
+ # Handle templates added by the index method.
223
+ @inline_sources.each_pair do |op, f|
224
+ functions << f.clone.mutate!(self, type_symbol, local_template_params)
225
+ end
226
+
227
+ # Handle templates added by the sources method.
228
+ @sources.each do |source|
229
+
230
+ # First, mutate on the current blueprint
231
+ function = read_and_parse_source(source).mutate!(self, type_symbol, local_template_params)
232
+
233
+ if @extra_templates_by_source[source].size > 1
234
+ raise NotImplementedError, "only two templates supported at a time"
235
+
236
+ elsif @extra_templates_by_source[source].size == 1
237
+ @extra_templates_by_source[source].each_pair do |typename,blueprint_id|
238
+
239
+ other_blueprint = generator.blueprints[blueprint_id]
240
+ other_blueprint.types.each_pair do |type_id, type_obj|
241
+ functions << function.clone.mutate!(other_blueprint, type_id, {typename => @generator.types[type_id].ctype})
242
+ end
243
+ end
244
+ else
245
+ functions << function
246
+ end
247
+
248
+ end
249
+
250
+ functions
251
+ end
252
+
253
+
254
+
255
+ # Helper function called by Generator after all blueprints are declared. Goes back and replaces blueprint IDs with ctypes.
256
+ def expand_ops!
257
+ @ops.clone.each_pair do |id, types_and_patterns|
258
+ h = {}
259
+ types_and_patterns.patterns.each_pair do |arg1,pattern|
260
+ if arg1.is_a?(Symbol) && @generator.blueprints.has_key?(arg1)
261
+ @generator[arg1].types.each_pair do |type_id, type|
262
+ next if types_and_patterns.patterns.has_key?(type.ctype) # Don't override a user op
263
+ h[type.ctype] = pattern
264
+ end
265
+ end
266
+ end
267
+ @ops[id].patterns.merge!(h)
268
+ end
269
+ end
270
+
271
+
272
+ # Generate a C Abstract Syntax Tree based on a pattern given to generator/blueprint in
273
+ # configuration.
274
+ #
275
+ # Returns the return type of the final expression and the expression AST.
276
+ def decorated_expression type_symbol, op_id, args_and_types, should_return
277
+ return nil unless @ops.has_key?(op_id)
278
+ @ops[op_id].decorated(args_and_types, should_return, type_symbol)
279
+ end
280
+
281
+
282
+
283
+ #
284
+ # These functions are all for indices which have the :inline symbol.
285
+ #
286
+
287
+
288
+ def inline_op_return_type op
289
+ CSquare::Generator::OP_RETURN_TYPES[op] == :key ? @key : CSquare::Generator::OP_RETURN_TYPES[op]
290
+ end
291
+
292
+
293
+ def inline_op_return_typename op
294
+ type = inline_op_return_type(op)
295
+ return 'bool' if type == :boolean
296
+ type
297
+ end
298
+
299
+
300
+ def inline_op_function_def op
301
+ "inline #{inline_op_return_typename(op)} #{inline_op_function_name(op)}(const #{@key} x, const #{@key} y) { return (x #{op.to_s} y); }"
302
+ end
303
+
304
+
305
+ def parsed_inline_op_function op
306
+ code = inline_op_function_def(op)
307
+ CSquare::Function.new(code, self.keys)
308
+ end
309
+
310
+
311
+ # Get a list of op symbols used in this blueprint
312
+ def op_symbols
313
+ @ops.values.map { |o| o.id }
314
+ end
315
+
316
+ def read_source source_filename
317
+ begin
318
+ g = self.generator
319
+ Dir.chdir(g.path) do
320
+ # First check type's own directory, then check for global if type doesn't have it.
321
+ if File.exists?(File.join(self.id.to_s, source_filename))
322
+ Dir.chdir(self.id.to_s) do
323
+ preprocess_source File.new(source_filename, "r")
324
+ end
325
+ elsif File.exists?(source_filename)
326
+ preprocess_source File.new(source_filename, "r")
327
+ else
328
+ raise("source file not found: #{source_filename.inspect}")
329
+ end
330
+ end
331
+ rescue Errno::ENOENT => e
332
+ # This is really helpful for people who are using rake compile and don't know what
333
+ # the template directory should be, since they don't know their current location.
334
+ STDERR.puts "CSquare: Exception thrown: #{e.inspect}"
335
+ STDERR.puts "CSquare: Current directory: #{Dir.pwd.inspect}"
336
+ raise e
337
+ end
338
+ end
339
+
340
+
341
+ def extra_keys source
342
+ @extra_templates_by_source[source].keys
343
+ end
344
+
345
+
346
+ protected
347
+
348
+ def keys
349
+ k = []
350
+ @types.each_value do |type|
351
+ k = k.concat(type.keys.keys)
352
+ end
353
+ k << @key << "LONG_#{@key}"
354
+ k
355
+ end
356
+
357
+ end