cgialib 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,446 @@
1
+ # File: CLanguageScanner.rb
2
+ # Author: Jack Herrington
3
+ # Purpose: The CLanguageScanner object specialized to look for C prototypes
4
+ # Date: 12/21/02
5
+
6
+ #require "Tokenizer"
7
+ #require "Language"
8
+
9
+ module LanguageParser
10
+ # class : CLanguageScanner
11
+ #
12
+ # This is the CLanguageScanner specialized to read prototypes for C
13
+ # functions.
14
+
15
+ class CLanguageScanner < LanguageScanner
16
+
17
+ # initialize()
18
+ #
19
+ # Constructs the C language scanner class
20
+
21
+ def initialize()
22
+
23
+ # Create the prototype array
24
+
25
+ @prototypes = []
26
+
27
+ # Create the defines array
28
+
29
+ @defines = {}
30
+
31
+ # Set the prototype class to build to the default
32
+ # prototype class
33
+
34
+ @prototypeClass = Prototype
35
+
36
+ end
37
+
38
+ attr_reader :prototypes # The array of prototypes found
39
+ attr_accessor :prototypeClass # The prototype class to build
40
+ attr_reader :defines # The array of '#defines' found
41
+
42
+ def to_s()
43
+
44
+ text = "Prototypes:\n"
45
+
46
+ @prototypes.each { |proto|
47
+ text += " #{proto}\n"
48
+ }
49
+
50
+ text += "\nDefines:\n"
51
+
52
+ @defines.each { |key,value|
53
+ text += " #{key} = '#{value}'\n"
54
+ }
55
+
56
+ text
57
+
58
+ end
59
+
60
+ # parse( tokens )
61
+ #
62
+ # tokens - An array of tokens built by a Tokenizer
63
+ #
64
+ # This method reads the stream of tokens built by a Tokenizer
65
+ # and fills the @prototypes array with the prototypes
66
+ # that are found.
67
+
68
+ def parse( tokens )
69
+
70
+ # This is the code fragment leading up to the interior
71
+ # of the function
72
+
73
+ codefrag = TokenStream.new()
74
+
75
+ # 'level' is the level of bracket nesting.
76
+
77
+ level = 0
78
+
79
+ # This will be true if we are looking at precompiler tokens
80
+
81
+ hold_until_return = false
82
+
83
+ # This will be true if we are looking for a '#define'
84
+
85
+ parsing_define = false
86
+ define_name = nil
87
+ define_value = nil
88
+
89
+ # Look through each token
90
+
91
+ tokens.each { |tok|
92
+
93
+ if ( parsing_define )
94
+
95
+ unless ( define_name )
96
+
97
+ define_name = tok.to_s if ( tok.is_a?( CodeToken ) )
98
+
99
+ else
100
+
101
+ if ( tok.to_s =~ /^\n/ )
102
+
103
+ define_value.strip!
104
+ @defines[ define_name ] = define_value.to_s
105
+
106
+ parsing_define = false
107
+ define_name = nil
108
+ define_value = nil
109
+
110
+ else
111
+
112
+ define_value.push( tok )
113
+
114
+ end
115
+
116
+ end
117
+
118
+ next
119
+
120
+ elsif ( hold_until_return )
121
+
122
+ hold_until_return = false if ( tok.to_s =~ /^\n/ )
123
+ next
124
+
125
+ end
126
+
127
+ if tok.to_s =~ /^#define$/
128
+
129
+ # This is a #define macro, so we need to start parsing
130
+ # it
131
+
132
+ parsing_define = true
133
+ define_name = nil
134
+ define_value = TokenStream.new()
135
+
136
+ elsif tok.to_s =~ /^#/
137
+
138
+ # This is a precompiler directive, so
139
+ # we should ignore all of the tokens
140
+ # until a return
141
+
142
+ hold_until_return = true
143
+
144
+ elsif tok.to_s == "{"
145
+
146
+ # If we are at level zero then we are
147
+ # opening a function for code, so we
148
+ # should interpret what we have
149
+ # up until now.
150
+
151
+ if level == 0
152
+
153
+ parse_prototype( codefrag )
154
+
155
+ codefrag = TokenStream.new()
156
+
157
+ end
158
+
159
+ # Now increment the level
160
+
161
+ level += 1
162
+
163
+ elsif tok.to_s == "}"
164
+
165
+ # Decrement the level
166
+
167
+ level -= 1
168
+
169
+ elsif tok.to_s == ";"
170
+
171
+ # If we see a ";" and we are at level
172
+ # zero then we have a constant
173
+ # declaration
174
+
175
+ codefrag = TokenStream.new() if ( level == 0 )
176
+
177
+ else
178
+
179
+ # Otherwise push the fragment
180
+
181
+ codefrag.push( tok ) if level == 0
182
+
183
+ end
184
+
185
+ }
186
+
187
+ end
188
+
189
+ protected
190
+
191
+ # clean_comment( comment )
192
+ #
193
+ # comment - The comment text
194
+ #
195
+ # This removes the actual comment prefixes from the comment
196
+ # text (e.g. /*, */, //)
197
+
198
+ def clean_comment( comment )
199
+
200
+ if ( comment =~ /^\/\*/ )
201
+
202
+ comment.sub!( /^\/\*/, "" )
203
+ comment.sub!( /\*\/$/, "" )
204
+
205
+ else
206
+
207
+ comment.gsub!( /^\/\//, "" )
208
+
209
+ end
210
+
211
+ comment.strip!()
212
+ comment
213
+
214
+ end
215
+
216
+ # parse_declaration( codefrag )
217
+ #
218
+ # codefrag - The code token array
219
+ #
220
+ # This turns the set of tokens that represent a declaration
221
+ # ("int *a[]") into a name ("a"), a type "int *", and an
222
+ # array boolean (true).
223
+
224
+ def parse_declaration( codefrag )
225
+
226
+ code = codefrag.code_only
227
+
228
+ # Check for an equals with a value
229
+
230
+ value = nil
231
+
232
+ if ( code.find( "=" ) )
233
+
234
+ value = code.pop
235
+ code.pop
236
+
237
+ end
238
+
239
+ # By default this will not be an array
240
+
241
+ array = false
242
+
243
+ # Here we are backtracking from the end to find the name
244
+ # within the declaration
245
+
246
+ while( code.length > 0 )
247
+
248
+ frag = code.last
249
+
250
+ array = true if ( frag == "[" )
251
+
252
+ break unless ( frag == "[" || frag == "]" )
253
+
254
+ code.delete_at( code.length - 1 )
255
+
256
+ end
257
+
258
+ # We assume that the name is the last non-whitespace,
259
+ # non-array token
260
+
261
+ name = code.last
262
+
263
+ code.delete_at( code.length - 1 )
264
+
265
+ # Then we build the type from the remainder
266
+
267
+ type = ""
268
+
269
+ type = code.map { |tok| tok.to_s }.join( " " )
270
+
271
+ type.strip!
272
+
273
+ # Look for special cases
274
+
275
+ if name.to_s == "void"
276
+
277
+ name = CodeToken.new( "" )
278
+ type = "void"
279
+
280
+ end
281
+
282
+ if name.to_s == "*" || name.to_s == "&"
283
+
284
+ type += " #{name.to_s}"
285
+ name = CodeToken.new( "" )
286
+
287
+ end
288
+
289
+ # Then we return a structure that contains the declaration
290
+ # data
291
+
292
+ return { 'name' => name, 'type' => type, 'array' => array, 'value' => value }
293
+
294
+ end
295
+
296
+ # parse_prototype( codefrag, comment )
297
+ #
298
+ # codefrag - The tokens leading up to a function definition
299
+ # comment - A comment if one was found
300
+ #
301
+ # This turns the series of tokens leading up to the function
302
+ # definition and turns it into a prototype object which it
303
+ # adds to the prototype list.
304
+
305
+ def parse_prototype( codefrag, comment = nil )
306
+
307
+ # Contains any comments found within the tokens
308
+
309
+ comments = TokenStream.new()
310
+
311
+ # Add the comment if there is one
312
+
313
+ comments.push( clean_comment( comment.to_s() ) ) if ( comment )
314
+
315
+ # This will be true when we have found the first
316
+ # code token
317
+
318
+ found_code = false
319
+
320
+ # True when our iterator is within the arguments tokens
321
+
322
+ in_arguments = false
323
+
324
+ # Start contains the tokens before the arguments
325
+
326
+ start = TokenStream.new()
327
+
328
+ # args contains the sets of arguments tokens
329
+
330
+ args = TokenStream.new()
331
+
332
+ # cur_arg contains the tokens of the argument
333
+ # currently being parsed
334
+
335
+ cur_arg = TokenStream.new()
336
+
337
+ # Iterate through the codefrag tokens
338
+
339
+ codefrag.each { |tok|
340
+
341
+ # Set found_code to true when we find a CodeToken
342
+
343
+ found_code = true if ( tok.is_a?( CodeToken ) )
344
+
345
+ # Add the comment if the token is a comment
346
+
347
+ if tok.is_a?( CommentToken )
348
+
349
+ comments.push( clean_comment( tok.to_s() ) )
350
+ next
351
+
352
+ end
353
+
354
+ # Go to the next token if we have not found code
355
+
356
+ next unless ( found_code )
357
+
358
+ if tok.to_s == "("
359
+
360
+ # Look for the start of the arguments
361
+
362
+ in_arguments = true
363
+ cur_arg = TokenStream.new()
364
+
365
+ elsif tok.to_s == ")"
366
+
367
+ # Look for the end of the arguments, when
368
+ # we find it we dump out of the iterator
369
+
370
+ args.push( cur_arg ) if cur_arg.length > 0
371
+ break
372
+
373
+ elsif in_arguments == false
374
+
375
+ # If we are not in the arguments then
376
+ # push these code tokens into the start
377
+ # fragment list
378
+
379
+ start.push( tok )
380
+
381
+ else
382
+
383
+ # We are in the arguments, so look for
384
+ # the comments that seperate the arguments
385
+
386
+ if tok.to_s == ","
387
+
388
+ args.push( cur_arg ) if cur_arg.length > 0
389
+ cur_arg = TokenStream.new()
390
+
391
+ else
392
+
393
+ cur_arg.push( tok )
394
+ end
395
+
396
+ end
397
+
398
+ }
399
+
400
+ # Have the base class build the new prototype
401
+
402
+ proto = build_prototype()
403
+
404
+ # Parse the starting declaration and set the prototype
405
+
406
+ start_decl = parse_declaration( start )
407
+
408
+ proto.method_name = start_decl['name']
409
+ proto.method_type = start_decl['type']
410
+
411
+ # Parse the arguments and add them to the prototype
412
+
413
+ args.each { |arg|
414
+
415
+ arg_decl = parse_declaration( arg )
416
+
417
+ proto.add_argument( arg_decl[ 'name' ], arg_decl['type'] )
418
+
419
+ }
420
+
421
+ # Add the comments
422
+
423
+ comments.each { |comment| proto.add_comment( comment.to_s ) }
424
+
425
+ # Add this prototype to the array of found prototypes
426
+
427
+ if ( proto.method_type != "class" )
428
+
429
+ @prototypes.push( proto )
430
+
431
+ end
432
+
433
+ proto
434
+
435
+ end
436
+
437
+ # build_prototype()
438
+ #
439
+ # Builds and returns a prototype object
440
+
441
+ def build_prototype()
442
+ @prototypeClass.new()
443
+ end
444
+
445
+ end
446
+ end