cgialib 0.0.1

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