csquare 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -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
|