csquare 0.1.0

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