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