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