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,36 @@
1
+ class CSquare::Base
2
+ EXTRA_TYPE_NAMES = %w{bool}
3
+
4
+ def initialize code, type_names
5
+ @parser = C::Parser.new
6
+ type_names = (type_names + EXTRA_TYPE_NAMES).uniq
7
+ type_names.each { |t| @parser.type_names << t }
8
+
9
+ tree = begin
10
+ @parser.parse(code)
11
+ rescue C::ParseError => e
12
+ if e.message.include?("parse error on ID (")
13
+ STDERR.puts "CSquare::Base: A parse error was encountered: #{e.message}"
14
+ STDERR.puts "This typically means you're using a special typename which CSquare does not know how to recognize."
15
+ STDERR.puts "It is recommended that you avoid doing so; however, if you must, try modifying CSquare::Base::EXTRA_TYPE_NAMES."
16
+ else
17
+ STDERR.puts "CSquare::Base: An unrecognized parse error was encountered."
18
+ STDERR.puts code.to_s
19
+ end
20
+
21
+ STDERR.puts "The currently defined types are: #{type_names.inspect}"
22
+
23
+ raise(e)
24
+ end
25
+
26
+ raise(ArgumentError, "multiple entities not supported") if tree.entities.size > 1
27
+
28
+ @entity = tree.entities[0]
29
+ end
30
+
31
+ def code
32
+ entity.to_s
33
+ end
34
+
35
+ attr_reader :entity, :parser
36
+ end
@@ -0,0 +1,82 @@
1
+ # Loosely based on ERB by Masatoshi Seki.
2
+ #
3
+ # For handling Ruby code embedded in CSquare (C) sources.
4
+ #
5
+ #
6
+ # int main() {
7
+ # #RUBY if blueprint.id == :rational
8
+ # return 0;
9
+ # #RUBY else
10
+ # return 1;
11
+ # #RUBY end
12
+ # }
13
+ #
14
+ # TODO: Rewrite this so it uses a grammar that can recognize comments.
15
+ #
16
+ # NOTE: DO NOT USE PRY IN HERE. Annoying to kill.
17
+
18
+
19
+ class CERB
20
+ SPLIT_REGEXP = /^\s*#\s*RUBY\s+/
21
+
22
+ def initialize file, blueprint
23
+ @blueprint = blueprint
24
+
25
+ out = Buffer.new
26
+ eval_str = nil
27
+ @src = []
28
+
29
+ while line = file.gets
30
+ line.chomp!
31
+
32
+ c_code, eval_str = line.split(SPLIT_REGEXP, 2)
33
+
34
+ #binding.pry if line == "#RUBY if blueprint.id != :complex"
35
+
36
+ if eval_str.nil?
37
+ out.push c_code
38
+ else
39
+ unless out.empty?
40
+ @src << "puts #{out.result.dump}"
41
+ out.reset!
42
+ end
43
+
44
+ @src << "puts ''" # empty line in place of #RUBY statements (ensures line numbers match source files in debugging)
45
+ @src << eval_str
46
+ end
47
+ end
48
+
49
+ @src << "puts #{out.result.dump}" if eval_str.nil? && !out.empty?
50
+ @src << "puts ''" # prevent EOF errors
51
+
52
+ #binding.pry
53
+
54
+ eval @src.join("\n")
55
+ end
56
+ attr_reader :blueprint, :src
57
+
58
+
59
+
60
+ class Buffer
61
+ def initialize
62
+ @code = []
63
+ end
64
+
65
+ def push code
66
+ @code << code
67
+ end
68
+
69
+ def result
70
+ @code.join("\n")
71
+ end
72
+
73
+ def reset!
74
+ @code = []
75
+ end
76
+
77
+ def empty?
78
+ @code.size == 0
79
+ end
80
+ end
81
+
82
+ end
@@ -0,0 +1,175 @@
1
+ # Represents a C function template.
2
+ class CSquare::Function < CSquare::Base
3
+ def initialize code, type_names
4
+ super code, type_names
5
+ @decorated_name = false
6
+ raise(SyntaxError, "expected function definition") unless entity.FunctionDef?
7
+ end
8
+
9
+ # Make a deep copy of this function.
10
+ def clone
11
+ CSquare::Function.new(self.code, @parser.type_names.to_a)
12
+ end
13
+
14
+ # Return the function declaration for the header file
15
+ def declaration
16
+ t = @entity.type
17
+ "#{t.type.to_s} #{name}(#{t.params.to_s});"
18
+ end
19
+
20
+ def rename! new_name
21
+ @entity.name = new_name
22
+ end
23
+
24
+ def decorate_name! type_symbol
25
+ @entity.name = decorated_name(type_symbol)
26
+ @decorated_name = true
27
+ self
28
+ end
29
+
30
+ def decorated_name type_symbol
31
+ @decorated_name ? @entity.name : "#{@entity.name}_#{type_symbol}"
32
+ end
33
+
34
+ def name
35
+ @entity.name
36
+ end
37
+
38
+ # Return a hash of function parameters => types. Passing the argument :string will return type strings instead of CAST types.
39
+ def params(as=nil)
40
+ @entity.params(as)
41
+ end
42
+
43
+ # Return a hash of function local variables => types. You may pass :string as the sole argument if you want
44
+ def locals(as=nil)
45
+ @entity.def.locals(as)
46
+ end
47
+
48
+ def definition
49
+ @entity.def
50
+ end
51
+
52
+ def return_type
53
+ @entity.type.type
54
+ end
55
+
56
+
57
+ # Get those parameters which we need to watch, based on the template typenames given (as an argument).
58
+ def template_params typenames
59
+ watch_params = {}
60
+
61
+ params.each_pair do |varname, fparam|
62
+ if typenames.include?(fparam.underlying_typename)
63
+ watch_params[varname] = fparam.underlying_typename
64
+ end
65
+ end
66
+ watch_params
67
+ end
68
+
69
+
70
+ # Get those local variables which we need to watch when applying templates, based on the template typenames given (only argument).
71
+ def template_locals typenames
72
+ watch_locals = {}
73
+
74
+ locals.each_pair do |varname, fdec|
75
+ if typenames.include?(fdec.underlying_typename)
76
+ watch_locals[varname] = fdec.underlying_typename
77
+ end
78
+ end
79
+ watch_locals
80
+ end
81
+
82
+
83
+ # Given some hash of typenames to replacement types, change all parameters and declarations.
84
+ #
85
+ # Hash should consist of string values only.
86
+ def replace_types! h
87
+ # STDERR.puts "parser typenames: #{@parser.type_names.inspect}"
88
+
89
+ h.each_value do |new_typename|
90
+ @parser.type_names << new_typename
91
+ C.default_parser.type_names << new_typename
92
+ end
93
+
94
+ # Replace the types in the function prototype
95
+ @entity.entries[0].replace_types! h
96
+
97
+ # Recurse down into function blocks
98
+ @entity.entries[1].replace_types! h
99
+
100
+ # Replace typecasts
101
+ @entity.recursively_replace_casts! h
102
+ end
103
+
104
+
105
+ # Given all function calls, figure out what they should instead be and change the tree as we go.
106
+ def decorate_calls! blueprint, type_symbol
107
+ @entity.recursively_decorate_calls! blueprint, type_symbol
108
+ end
109
+
110
+
111
+ # Return a list of local variables, function names, parameters, etc; hashed to type (string).
112
+ def locals_and_types blueprint
113
+ vars_and_types = blueprint.generator.externs.merge(blueprint.externs).
114
+ merge(params).
115
+ merge(locals)
116
+
117
+ # Go through and remove const indicators, as these will only get in the way
118
+ vars_and_types.each_pair do |var, type|
119
+ if type.is_a?(C::DirectType)
120
+ vars_and_types[var].const = false
121
+ vars_and_types[var] = vars_and_types[var].to_s
122
+ end
123
+ end
124
+
125
+ vars_and_types
126
+ end
127
+
128
+
129
+ # Is some local identifier in use?
130
+ def has_local? var, blueprint=nil
131
+ @entity.has_local? var, blueprint
132
+ end
133
+
134
+
135
+ # Return the type associated with some variable. Optionally can look for globals if blueprint is given.
136
+ def type_of var, blueprint=nil
137
+ @entity.type_of var, blueprint
138
+ end
139
+
140
+
141
+ def mutate! blueprint, new_type_symbol, params = nil
142
+
143
+ # Create a frozen duplicate of this function, that hasn't had its typenames changed.
144
+ f = self.clone
145
+
146
+ self.decorate_name! new_type_symbol
147
+
148
+ self.entity.recombine! f, blueprint, new_type_symbol
149
+
150
+ self.replace_types!(params || blueprint.params(new_type_symbol))
151
+
152
+ self
153
+ end
154
+
155
+
156
+ class << self
157
+ def find_broken_ast tree
158
+ begin
159
+ tree.to_s
160
+ return nil
161
+ rescue TypeError
162
+ tree.each do |e|
163
+ x = CSquare::Function.find_broken_ast(e)
164
+ unless x.nil?
165
+ require "pry"
166
+ binding.pry
167
+ end
168
+ end
169
+ return tree
170
+ end
171
+ end
172
+ end
173
+
174
+
175
+ end
@@ -0,0 +1,374 @@
1
+ class CSquare::Generator
2
+ include CSquare::Indexable
3
+
4
+ DEFAULT_EXTERNS = {'fprintf' => 'int'}
5
+
6
+ acts_as_indexable
7
+
8
+ # To use for :inline index functions -- need to know return type
9
+ OP_RETURN_TYPES = {
10
+ :'<' => :boolean,
11
+ :'>' => :boolean,
12
+ :'<=' => :boolean,
13
+ :'>=' => :boolean,
14
+ :'==' => :boolean,
15
+ :'!=' => :boolean,
16
+ :'+' => :key,
17
+ :'-' => :key,
18
+ :'*' => :key,
19
+ :'/' => :key,
20
+ :'%' => :key
21
+ }
22
+
23
+ # To use for :inline index functions that should be automatically generated
24
+ OP_FUNCTION_NAMES = {
25
+ :'<' => 'lt',
26
+ :'>' => 'gt',
27
+ :'<=' => 'lte',
28
+ :'>=' => 'gte',
29
+ :'==' => 'eqeq',
30
+ :'!=' => 'neq',
31
+ :'+' => 'add',
32
+ :'-' => 'sub',
33
+ :'*' => 'mul',
34
+ :'/' => 'div',
35
+ :'%' => 'mod'
36
+ }
37
+
38
+ BOOL_CAST_TO_OP = {
39
+ C::Less => :'<',
40
+ C::More => :'>',
41
+ C::LessOrEqual => :'<=',
42
+ C::MoreOrEqual => :'>=',
43
+ C::Equal => :'==',
44
+ C::NotEqual => :'!=',
45
+ C::And => :'&&',
46
+ C::Or => :'||'
47
+ }
48
+
49
+ UNARY_CAST_TO_OP = {
50
+ C::Negative => :'-@',
51
+ C::Not => :'!@',
52
+ C::Arrow => :'->',
53
+ C::Dereference => :'*@'
54
+ }
55
+
56
+ ASSIGN_CAST_TO_OP = {
57
+ C::Assign => :'=',
58
+ }
59
+
60
+ BINARY_ASSIGN_CAST_TO_OP = {
61
+ C::AddAssign => :'+=',
62
+ C::SubtractAssign => :'-=',
63
+ C::MultiplyAssign => :'*=',
64
+ C::DivideAssign => :'/=',
65
+ C::ModAssign => :'%='
66
+ }
67
+
68
+ BINARY_CAST_TO_OP = {
69
+ C::Add => :'+',
70
+ C::Subtract => :'-',
71
+ C::Multiply => :'*',
72
+ C::Divide => :'/',
73
+ C::Mod => :'%',}
74
+
75
+ BIT_CAST_TO_OP = {
76
+ C::BitAnd => :'&',
77
+ C::BitOr => :'|',
78
+ C::BitXor => :'^',
79
+ C::ShiftLeft => :'<<',
80
+ C::ShiftRight => :'>>'
81
+ }
82
+
83
+ CAST_TO_OP = BOOL_CAST_TO_OP.
84
+ merge(UNARY_CAST_TO_OP).
85
+ merge(ASSIGN_CAST_TO_OP).
86
+ merge(BINARY_CAST_TO_OP).
87
+ merge(BIT_CAST_TO_OP).
88
+ merge(BINARY_ASSIGN_CAST_TO_OP)
89
+
90
+ def initialize path, c_output_basename, options = {}
91
+ options = {
92
+ :include_guards => true,
93
+ :header => true,
94
+ :include_header => true
95
+ }.merge(options)
96
+
97
+ @default_key = 'TYPE'
98
+ @path = path
99
+ @basename = c_output_basename
100
+ @externs = DEFAULT_EXTERNS
101
+ @blueprints = {}
102
+ @enumerators = {}
103
+
104
+ yield self
105
+
106
+ # STDERR.puts "GENERATOR EXTERNS: " + @externs.inspect
107
+
108
+ @blueprints.each_key do |id|
109
+ @blueprints[id].expand_ops!
110
+ end
111
+
112
+ mutant_functions = []
113
+ blueprints.each_value do |blueprint|
114
+ blueprint.types.each_pair do |type_symbol,type|
115
+ mutant_functions = mutant_functions.concat(blueprint.mutate_functions(type_symbol))
116
+ end
117
+ end
118
+
119
+ declarations = write_c_file(options[:include_guards], options[:include_header], mutant_functions)
120
+ write_h_file(options[:include_guards], declarations) if options[:header]
121
+ end
122
+
123
+ attr_reader :blueprints, :path, :basename, :enumerators
124
+
125
+ def inspect
126
+ obj_id = "0x#{(self.object_id << 1).to_s(16)}"
127
+ "#<#{self.class.to_s}:#{obj_id} basename=#{@basename.inspect} blueprint_ids=#{@blueprints.keys.inspect} path=#{@path.inspect}>"
128
+ end
129
+
130
+ def default_key k=nil
131
+ return @default_key if k.nil?
132
+ @default_key = k
133
+ end
134
+
135
+
136
+ def c_include_guard
137
+ "#{@basename.upcase}_C"
138
+ end
139
+
140
+ def h_include_guard
141
+ "#{@basename.upcase}_H"
142
+ end
143
+
144
+
145
+ def c_filename
146
+ "#{@basename}.c"
147
+ end
148
+
149
+ def h_filename
150
+ "#{@basename}.h"
151
+ end
152
+
153
+ # Shorthand for blueprints[id]
154
+ def [] blueprint_id
155
+ blueprints[blueprint_id]
156
+ end
157
+
158
+ # Register symbols available to all functions, and their types. Takes a hash as only argument.
159
+ #
160
+ # Example:
161
+ #
162
+ # c.externs 'NM_MAX' => :integer, 'CblasNoTrans' => 'char', 'fprintf' => 'int'
163
+ #
164
+ #
165
+ def externs h=nil
166
+ return @externs if h.nil?
167
+ @externs.merge!(h)
168
+ end
169
+
170
+ # Create a blueprint for a set of types that all behave a certain way.
171
+ #
172
+ # At minimum, you should provide the name of the blueprint (e.g., <tt>blueprint(:integer)</tt>). You may also provide
173
+ # a template parameter name, which defaults to TYPE, and field parameters.
174
+ #
175
+ # Example (complex):
176
+ #
177
+ # c.blueprint(:complex, 'TYPE', :r => 'FLOAT', :i => 'FLOAT') do |t|
178
+ # # ...
179
+ # end
180
+ #
181
+ def blueprint id, template_param = 'TYPE', field_params = {}, &block
182
+ raise(ArgumentError, "id #{id} already declared") if @blueprints.has_key?(id)
183
+
184
+ # Clear cache of blueprints by type_id
185
+ @blueprint_for = nil
186
+
187
+ @blueprints[id] = Blueprint.new(self, id, template_param, field_params, &block)
188
+
189
+ self
190
+ end
191
+
192
+
193
+ # Get all types stored in the various template types
194
+ def types
195
+ h = {}
196
+ @blueprints.each_value do |blueprint|
197
+ blueprint.types.each_pair do |id, type|
198
+ if h.has_key?(id)
199
+ raise(StandardError, "two types with same id: #{id}")
200
+ else
201
+ h[id] = type
202
+ end
203
+ end
204
+ end
205
+ h
206
+ end
207
+
208
+ # Returns the blueprint corresponding to some type_id. Caches types for faster lookup.
209
+ def blueprint_for type_id
210
+ @blueprint_for ||= types
211
+ @blueprint_for[type_id].blueprint
212
+ end
213
+
214
+
215
+ def enumerate name, opts = {}
216
+ @enumerators[name] = CSquare::Generator::Enum.new(name, opts)
217
+ end
218
+
219
+ # Get a list of ops used (just the symbols)
220
+ def op_symbols
221
+ ary = []
222
+ @blueprints.each_value do |blueprint|
223
+ ary = ary.concat blueprint.op_symbols
224
+ end
225
+ ary.sort.uniq
226
+ end
227
+
228
+ def indices_c_code
229
+ ary = []
230
+
231
+ # Blueprint-scoped indices
232
+ @blueprints.each_value do |blueprint|
233
+ blueprint.indices.values.flatten.each do |index|
234
+ blueprint.types.each_key do |type_id|
235
+ ary << index.to_c(type_id)
236
+ end
237
+ end
238
+ end
239
+
240
+ # Add generator-scoped indices
241
+ @indices.values.flatten.each do |index|
242
+ ary << index.to_c
243
+ end
244
+
245
+ ary
246
+ end
247
+
248
+
249
+ def read_source source_filename
250
+ begin
251
+ g = self.is_a?(CSquare::Generator) ? self : self.generator
252
+ Dir.chdir(g.path) do
253
+ if File.exists?(source_filename)
254
+ preprocess_source File.new(source_filename, "r")
255
+ else
256
+ raise IOError, "file '#{source_filename}' not found in '#{g.path}' or '#{File.join(g.path, self.id.to_s)}'"
257
+ end
258
+ end
259
+ rescue Errno::ENOENT => e
260
+ # This is really helpful for people who are using rake compile and don't know what
261
+ # the template directory should be, since they don't know their current location.
262
+ STDERR.puts "CSquare: Exception thrown: #{e.inspect}"
263
+ STDERR.puts "CSquare: Current directory: #{Dir.pwd.inspect}"
264
+ raise e
265
+ end
266
+ end
267
+
268
+
269
+ # Return a hash giving mappings from template typenames (keys, e.g., 'TYPE') to ctypes.
270
+ def params type_symbol
271
+ types_ = types[type_symbol]
272
+ k = types_.ctype_keys # hash of typenames to type ids
273
+
274
+ begin
275
+ k[@default_key] = types_.ctype
276
+ rescue NoMethodError
277
+ STDERR.puts "NoMethodError: @default_key=#{@default_key}, type_symbol = #{type_symbol}"
278
+ end
279
+ k["LONG_#{@default_key}"] = (types_.long || types_).ctype
280
+ k
281
+ end
282
+
283
+
284
+ # Return all possible typenames for a given source
285
+ def keys_for f_name
286
+ ary = self.template_keys(f_name)
287
+ @blueprints.each_value do |blueprint|
288
+ next unless blueprint.has_source?(f_name)
289
+ ary << blueprint.template_keys(f_name)
290
+ end
291
+ ary.flatten.compact.uniq
292
+ end
293
+
294
+
295
+ protected
296
+ def write_c_file with_guards = true, include_header = true, functions = []
297
+ declarations = []
298
+
299
+ out = File.new(c_filename, "w")
300
+
301
+ out.puts("#ifndef #{c_include_guard}\n# define #{c_include_guard}\n\n") if with_guards
302
+
303
+ h = include_header == true ? h_filename : include_header
304
+
305
+ out.puts("#include \"#{h}\"\n\n") if include_header
306
+
307
+ out.puts indices_c_code.join("\n\n")
308
+
309
+ functions.each do |f|
310
+ begin
311
+ out.puts f.entity.to_s + "\n"
312
+ rescue TypeError => e
313
+ STDERR.puts e.inspect
314
+ CSquare::Function.find_broken_ast(f.entity)
315
+ end
316
+ declarations << f.declaration
317
+ end
318
+
319
+ out.puts("#endif // #{c_include_guard}") if with_guards
320
+
321
+ out.close
322
+
323
+ declarations
324
+ end
325
+
326
+
327
+ def write_h_file with_guards = true, declarations = []
328
+ out = File.new(h_filename, "w")
329
+
330
+ out.puts("#ifndef #{h_include_guard}\n# define #{h_include_guard}\n\n") if with_guards
331
+
332
+ enumerators.each_value do |enum|
333
+ out.puts enum.to_c
334
+ end
335
+
336
+ declarations.each do |d|
337
+ out.puts d
338
+ end
339
+
340
+ out.puts("#endif // #{h_include_guard}") if with_guards
341
+
342
+ out.close
343
+ end
344
+
345
+
346
+ def keys
347
+ k = [@default_key, "LONG_#{@default_key}"]
348
+ types.each_pair do |type_symbol, type|
349
+ k = k.concat(type.keys.keys)
350
+ end
351
+ k
352
+ end
353
+
354
+ def extra_template_keys_for source
355
+ keys = []
356
+ blueprints.each_value do |blueprint|
357
+ new_keys = blueprint.extra_keys(source) if blueprint.extra_templates_by_source.has_key?(source)
358
+ keys = keys.concat(new_keys) unless new_keys.nil?
359
+ end
360
+ keys.compact.uniq
361
+ end
362
+ end
363
+
364
+ require_relative 'generator/blueprint'
365
+ require_relative 'generator/index'
366
+ require_relative 'generator/type'
367
+ require_relative 'generator/op'
368
+ require_relative 'generator/binary_op'
369
+ require_relative 'generator/boolean_op'
370
+ require_relative 'generator/assign_op'
371
+ require_relative 'generator/enum'
372
+ require_relative 'generator/enum/namer'
373
+ require_relative 'generator/enum/op_namer'
374
+ require_relative 'generator/enum/sparse_op_namer'