dbc 1.3.0 → 2.0.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/bin/caphir.rb +152 -0
- data/bin/dbcparse.rb +139 -285
- data/lib/{dbc → caphir}/ctokenizer.rb +48 -41
- data/lib/caphir/ctype.rb +2640 -0
- data/lib/caphir/define.rb +488 -0
- data/lib/{dbc → caphir}/parseerrorhandler.rb +0 -0
- data/lib/caphir/parser.rb +125 -0
- data/lib/caphir/parsersettings.rb +175 -0
- data/lib/caphir/preprocessor.rb +2173 -0
- data/lib/caphir/preprocessor_conf.rb +153 -0
- data/lib/{dbc → caphir}/searchpath.rb +0 -0
- data/lib/dbc/dbc.rb +65 -129
- data/lib/dbc/expand_function.rb +43 -46
- data/lib/dbc/ocl.rb +600 -577
- metadata +13 -9
- data/lib/dbc/ctype.rb +0 -2569
- data/lib/dbc/define.rb +0 -282
- data/lib/dbc/parameters.rb +0 -434
- data/lib/dbc/preprocessor.rb +0 -2078
@@ -0,0 +1,488 @@
|
|
1
|
+
# Copyright (c) 2004 Charles M Mills
|
2
|
+
# This document is licenced under The MIT Licence.
|
3
|
+
# THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
|
4
|
+
# See included LICENCE file.
|
5
|
+
|
6
|
+
require 'caphir/ctokenizer'
|
7
|
+
|
8
|
+
module Preprocessor
|
9
|
+
|
10
|
+
class AllTokens
|
11
|
+
|
12
|
+
include CTokenizer # brings in #error() functions
|
13
|
+
|
14
|
+
def initialize(source, macro_tokens)
|
15
|
+
@source = source
|
16
|
+
@macro_tokens = macro_tokens
|
17
|
+
end
|
18
|
+
|
19
|
+
def shift
|
20
|
+
if @macro_tokens.empty?
|
21
|
+
#raise "still resolving? - impossible" unless @macro_tokens.resolving.empty?
|
22
|
+
@source.shift
|
23
|
+
else
|
24
|
+
@macro_tokens.shift
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def resolving
|
29
|
+
@macro_tokens.resolving
|
30
|
+
end
|
31
|
+
|
32
|
+
def resolving?(t_str)
|
33
|
+
@macro_tokens.resolving?(t_str)
|
34
|
+
end
|
35
|
+
|
36
|
+
def shift_and_filter
|
37
|
+
if @macro_tokens.empty?
|
38
|
+
#raise "still resolving? - impossible" unless @macro_tokens.resolving.empty?
|
39
|
+
@source.shift
|
40
|
+
else
|
41
|
+
t = @macro_tokens.shift
|
42
|
+
t = self.shift_and_filter if t[0] == :RESOLVING
|
43
|
+
t
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def empty?
|
48
|
+
@source.empty? and @macro_tokens.empty?
|
49
|
+
end
|
50
|
+
|
51
|
+
def args_given?
|
52
|
+
if t = @macro_tokens.peek_nonspace
|
53
|
+
t[1] == '('
|
54
|
+
else
|
55
|
+
@source.match?(/\s*\(/)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def file; @source.file end
|
60
|
+
def line; @source.line end
|
61
|
+
|
62
|
+
# does not respond to match and scan
|
63
|
+
end
|
64
|
+
|
65
|
+
class SourceTokens
|
66
|
+
|
67
|
+
include CTokenizer
|
68
|
+
|
69
|
+
def initialize(source)
|
70
|
+
@tokens = []
|
71
|
+
self.add_source(source)
|
72
|
+
end
|
73
|
+
|
74
|
+
attr_reader :start_line
|
75
|
+
|
76
|
+
def add_source(source)
|
77
|
+
@tokens.push(source)
|
78
|
+
@start_line = true # begining of file
|
79
|
+
end
|
80
|
+
|
81
|
+
def scan(regexp)
|
82
|
+
@tokens.last.scan(regexp)
|
83
|
+
end
|
84
|
+
|
85
|
+
def match?(regexp)
|
86
|
+
@tokens.last.match?(regexp)
|
87
|
+
end
|
88
|
+
|
89
|
+
def base?
|
90
|
+
@tokens.length == 1
|
91
|
+
end
|
92
|
+
|
93
|
+
def empty?
|
94
|
+
@tokens.length == 1 and @tokens.last.empty?
|
95
|
+
end
|
96
|
+
|
97
|
+
def file
|
98
|
+
@tokens.last.file
|
99
|
+
end
|
100
|
+
def file=(val)
|
101
|
+
@tokens.last.file = val
|
102
|
+
end
|
103
|
+
|
104
|
+
def line
|
105
|
+
@tokens.last.line
|
106
|
+
end
|
107
|
+
def line=(val)
|
108
|
+
@tokens.last.line = val
|
109
|
+
end
|
110
|
+
|
111
|
+
def shift
|
112
|
+
t = @tokens.last.shift
|
113
|
+
while !t[0] and @tokens.length > 1
|
114
|
+
# we are done with the current file
|
115
|
+
@tokens.pop
|
116
|
+
t = @tokens.last.shift
|
117
|
+
end # while
|
118
|
+
t_sym = t[0]
|
119
|
+
unless t_sym == :SPACE or t_sym == :COMMENT
|
120
|
+
# spaces and comments don't effect @start_line
|
121
|
+
@start_line = (t_sym == :NEWLINE)
|
122
|
+
end
|
123
|
+
t
|
124
|
+
end
|
125
|
+
|
126
|
+
end # Tokens
|
127
|
+
|
128
|
+
|
129
|
+
class MacroTokens
|
130
|
+
|
131
|
+
def initialize(resolving=[], tokens=[])
|
132
|
+
# resolving is immutable
|
133
|
+
@resolving = resolving.freeze
|
134
|
+
@tokens = tokens
|
135
|
+
end
|
136
|
+
|
137
|
+
attr_reader :resolving
|
138
|
+
|
139
|
+
def resolving?(t_str)
|
140
|
+
@resolving.index(t_str)
|
141
|
+
end
|
142
|
+
|
143
|
+
# returns :RESOLVING tokens
|
144
|
+
def shift
|
145
|
+
t = @tokens.shift || CTokenizer::EOF_TOKEN
|
146
|
+
@resolving = t[1] if t[0] == :RESOLVING
|
147
|
+
t
|
148
|
+
end
|
149
|
+
|
150
|
+
def empty?
|
151
|
+
@tokens.empty?
|
152
|
+
end
|
153
|
+
|
154
|
+
def peek_filter
|
155
|
+
@tokens.each do |t|
|
156
|
+
return t unless t[0] == :RESOLVING
|
157
|
+
end
|
158
|
+
nil # is no non-resolving token
|
159
|
+
end
|
160
|
+
|
161
|
+
def peek_nonspace
|
162
|
+
@tokens.each do |t|
|
163
|
+
return t unless t[0] == :SPACE or t[0] == :RESOLVING
|
164
|
+
end
|
165
|
+
nil # is no non-space token
|
166
|
+
end
|
167
|
+
|
168
|
+
def args_given?
|
169
|
+
t = self.peek_nonspace
|
170
|
+
t and t[1] == '('
|
171
|
+
end
|
172
|
+
|
173
|
+
# IMPORTANT - source is modified by the add_*_macro() methods
|
174
|
+
|
175
|
+
def add_function_macro(t_str, source)
|
176
|
+
# macro should be unresolved, except for parameters
|
177
|
+
# we must add t_str to all resolving lists in the macro
|
178
|
+
unless source.empty?
|
179
|
+
# IMPORTANT - add t_str to each resolving token
|
180
|
+
add_tokens( source.collect! do |t|
|
181
|
+
t[0] == :RESOLVING ? [:RESOLVING, t[1].dup << t_str] : t
|
182
|
+
end )
|
183
|
+
# add t_str to current resolving array
|
184
|
+
@resolving = (@resolving.dup << t_str).freeze
|
185
|
+
end
|
186
|
+
self
|
187
|
+
end
|
188
|
+
|
189
|
+
def add_object_macro(t_str, source)
|
190
|
+
unless source.empty?
|
191
|
+
add_tokens(source)
|
192
|
+
@resolving = (@resolving.dup << t_str).freeze
|
193
|
+
end
|
194
|
+
self
|
195
|
+
end
|
196
|
+
|
197
|
+
private
|
198
|
+
def add_tokens(source)
|
199
|
+
# will restore old resolving value
|
200
|
+
source << [:RESOLVING, @resolving].freeze
|
201
|
+
source[source.length, 0] = @tokens # insert at end
|
202
|
+
@tokens = source
|
203
|
+
self
|
204
|
+
end
|
205
|
+
|
206
|
+
end # class
|
207
|
+
|
208
|
+
class Define
|
209
|
+
|
210
|
+
def Define.resolve_argument(resolving, tokens, defines)
|
211
|
+
return tokens if tokens.empty?
|
212
|
+
# this is important, as it sets the resolving value
|
213
|
+
# for the rest of the token
|
214
|
+
out = [ [:RESOLVING, resolving].freeze ]
|
215
|
+
stack = MacroTokens.new(resolving, tokens)
|
216
|
+
until stack.empty?
|
217
|
+
t = stack.shift
|
218
|
+
if t[0] == :IDENTIFIER and macro = defines[t_str = t[1]] \
|
219
|
+
and not stack.resolving?(t_str)
|
220
|
+
if macro.takes_args?
|
221
|
+
unless stack.args_given?
|
222
|
+
# no arguments given, don't resolve token
|
223
|
+
out << t
|
224
|
+
next
|
225
|
+
end
|
226
|
+
stack.add_function_macro(t_str, macro.value(stack) do |a|
|
227
|
+
Define.resolve_argument(resolving, a, defines)
|
228
|
+
end)
|
229
|
+
else
|
230
|
+
stack.add_object_macro(t_str, macro.value(stack))
|
231
|
+
end
|
232
|
+
# since we are saving out, must output this resolving token
|
233
|
+
out << [:RESOLVING, stack.resolving].freeze
|
234
|
+
else
|
235
|
+
out << t
|
236
|
+
end
|
237
|
+
end # until
|
238
|
+
out
|
239
|
+
end
|
240
|
+
|
241
|
+
def initialize(params, tokens)
|
242
|
+
tokens = tokens.to_s
|
243
|
+
tokens.strip!
|
244
|
+
@value = CTokenizer::Lexer.new(tokens).to_a
|
245
|
+
if params
|
246
|
+
unless params.class == Parameters
|
247
|
+
@parameters = Parameters.new
|
248
|
+
params.each { |p| @parameters << p }
|
249
|
+
else
|
250
|
+
@parameters = params
|
251
|
+
end
|
252
|
+
else
|
253
|
+
# will not contain any parameters
|
254
|
+
@value = parse(@value, nil)
|
255
|
+
@parameters = nil
|
256
|
+
end
|
257
|
+
@value.freeze
|
258
|
+
end
|
259
|
+
|
260
|
+
def takes_args?
|
261
|
+
# if @parameters is empty still want to scan for '(' and ')'
|
262
|
+
@parameters
|
263
|
+
end
|
264
|
+
|
265
|
+
# returns a copy of the macro's value with parameters substituted
|
266
|
+
def value(stack)
|
267
|
+
if takes_args?
|
268
|
+
args = @parameters.arguments(stack).collect! do |a|
|
269
|
+
# yield parameter tokens to be resolved
|
270
|
+
v = a.collect do |t|
|
271
|
+
case t[0]
|
272
|
+
when :RESOLVING
|
273
|
+
# these tokens are removed
|
274
|
+
when :COMMENT, :NEWLINE then ' '
|
275
|
+
else t[1]
|
276
|
+
end
|
277
|
+
end.join
|
278
|
+
v.strip! # whitespace should be removed
|
279
|
+
[:PARAMETER, v, yield(a)]
|
280
|
+
# [ :PARAMETER, token text, resolved token ]
|
281
|
+
# ie [ :PARAMETER, '__LINE__', [[:CONSTANT, 10]] ]
|
282
|
+
end
|
283
|
+
|
284
|
+
# do argument substitution
|
285
|
+
parse( \
|
286
|
+
@value.collect do |t|
|
287
|
+
if t[0] == :IDENTIFIER and idx = @parameters.index(t[1])
|
288
|
+
args[idx]
|
289
|
+
else
|
290
|
+
t
|
291
|
+
end
|
292
|
+
end , stack.resolving )
|
293
|
+
else
|
294
|
+
@value.dup
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
private
|
299
|
+
|
300
|
+
def parse(tokens, resolving)
|
301
|
+
tokens.each_with_index do |v, i|
|
302
|
+
case v[1]
|
303
|
+
when '##'
|
304
|
+
# j == previous non space token index
|
305
|
+
j = i
|
306
|
+
begin
|
307
|
+
j -= 1
|
308
|
+
prev_t = tokens[j]
|
309
|
+
end while prev_t and prev_t[0] == :SPACE
|
310
|
+
# k == next non space token index
|
311
|
+
k = i
|
312
|
+
begin
|
313
|
+
k += 1
|
314
|
+
next_t = tokens[k]
|
315
|
+
end while next_t and next_t[0] == :SPACE
|
316
|
+
|
317
|
+
if prev_t and next_t
|
318
|
+
# combine the two tokens (regardless of what type they are)
|
319
|
+
tokens.fill(nil, j...k)
|
320
|
+
tokens[k] = [:PASTED_TOKEN, prev_t[1] + next_t[1]].freeze
|
321
|
+
end
|
322
|
+
when '#'
|
323
|
+
k = i
|
324
|
+
begin
|
325
|
+
k += 1
|
326
|
+
next_t = tokens[k]
|
327
|
+
end while next_t and next_t[0] == :SPACE
|
328
|
+
|
329
|
+
# only stringify parameters
|
330
|
+
if next_t and next_t[0] == :PARAMETER
|
331
|
+
tokens.fill(nil, i...k)
|
332
|
+
tokens[k] = [:STRING, next_t[1].inspect].freeze
|
333
|
+
end
|
334
|
+
end if v # v may be nil
|
335
|
+
end
|
336
|
+
out = []
|
337
|
+
tokens.each do |t|
|
338
|
+
case t[0]
|
339
|
+
when :RESOVLING
|
340
|
+
resolving = t[1]
|
341
|
+
out << t
|
342
|
+
when :PARAMETER
|
343
|
+
out[out.length, 0] = t[2]
|
344
|
+
out << [:RESOLVING, resolving].freeze
|
345
|
+
when :PASTED_TOKEN
|
346
|
+
out[out.length, 0] = CTokenizer::Lexer.new(t[1]).to_a
|
347
|
+
else
|
348
|
+
out << t
|
349
|
+
end if t # unused tokens were replaced with nil
|
350
|
+
end
|
351
|
+
out
|
352
|
+
end
|
353
|
+
|
354
|
+
end # class
|
355
|
+
|
356
|
+
class Parameters < Array
|
357
|
+
VA_ARGS = '__VA_ARGS__'.freeze
|
358
|
+
ELLIPSIS = '...'.freeze
|
359
|
+
|
360
|
+
def initialize
|
361
|
+
super
|
362
|
+
@ellipses = nil
|
363
|
+
end
|
364
|
+
|
365
|
+
def with_ellipses(arg=nil)
|
366
|
+
raise "already has ellipses: #{self}" if ellipses?
|
367
|
+
if arg and arg != ELLIPSIS
|
368
|
+
self << arg
|
369
|
+
@ellipses = arg + ELLIPSIS # if arg is 'i' then 'i...'
|
370
|
+
else
|
371
|
+
self << VA_ARGS
|
372
|
+
@ellipses = ELLIPSIS
|
373
|
+
end
|
374
|
+
self
|
375
|
+
end
|
376
|
+
|
377
|
+
def ellipses?
|
378
|
+
@ellipses
|
379
|
+
end
|
380
|
+
|
381
|
+
def to_s
|
382
|
+
if ellipses?
|
383
|
+
self[0, self.length - 1] << @ellipses
|
384
|
+
else
|
385
|
+
self
|
386
|
+
end.join(',')
|
387
|
+
end
|
388
|
+
|
389
|
+
def inspect
|
390
|
+
"\#<#{self.class}:#{self.to_s}>"
|
391
|
+
end
|
392
|
+
|
393
|
+
# it is important that we keep track of the last resolving status when
|
394
|
+
# splitting up arguments. That way the argument does not inherit the
|
395
|
+
# last resolving status of the previous tokens when it is substituted.
|
396
|
+
def arguments(tokens)
|
397
|
+
|
398
|
+
# skip whitespace
|
399
|
+
while true
|
400
|
+
t = tokens.shift
|
401
|
+
case t[0]
|
402
|
+
when :RESOLVING, :SPACE, :COMMENT, :NEWLINE
|
403
|
+
# do nothing
|
404
|
+
else
|
405
|
+
break
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
tokens.error("expecting '('") unless t[1] == '('
|
410
|
+
|
411
|
+
arguments = [arg = [ [:RESOLVING, tokens.resolving].freeze ] ]
|
412
|
+
|
413
|
+
while true
|
414
|
+
t = tokens.shift
|
415
|
+
tokens.unmatched_error(')') unless t and t[0]
|
416
|
+
|
417
|
+
# comma and the closing ')' are not outputed
|
418
|
+
# bunch all the remaining parameters into the last parameter
|
419
|
+
if t[1] == ',' and arguments.length < self.length
|
420
|
+
# start of a new argument
|
421
|
+
arguments.push([ [:RESOLVING, tokens.resolving].freeze ])
|
422
|
+
arg = arguments.last
|
423
|
+
elsif t[1] == ')'
|
424
|
+
break # end of parameter list
|
425
|
+
else
|
426
|
+
arg << t
|
427
|
+
case t[1]
|
428
|
+
when '{'
|
429
|
+
Parameters.get_closing_braket(arg, tokens)
|
430
|
+
when '}'
|
431
|
+
tokens.unmatched_error('}')
|
432
|
+
when '('
|
433
|
+
Parameters.get_closing_paren(arg, tokens)
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
got = arguments.length
|
439
|
+
expected = self.length
|
440
|
+
if got < expected
|
441
|
+
tokens.error("wrong number of arguments (#{got} for #{expected})") unless ellipses?
|
442
|
+
expected -= 1 # the variable argument list can be blank
|
443
|
+
tokens.error("wrong number of arguments (#{got} for #{expected}+)") \
|
444
|
+
unless got == expected
|
445
|
+
arguments[expected] = [] # last arg is blank
|
446
|
+
end
|
447
|
+
|
448
|
+
arguments
|
449
|
+
end
|
450
|
+
|
451
|
+
def Parameters.get_closing_paren(arg, tokens)
|
452
|
+
begin
|
453
|
+
t = tokens.shift
|
454
|
+
tokens.unmatched_error(')') unless t and t[0]
|
455
|
+
|
456
|
+
arg << t
|
457
|
+
case t[1]
|
458
|
+
when '{'
|
459
|
+
get_closing_braket(arg, tokens)
|
460
|
+
when '}'
|
461
|
+
tokens.unmatched_error('}')
|
462
|
+
when '('
|
463
|
+
get_closing_paren(arg, tokens)
|
464
|
+
end
|
465
|
+
end until t[1] == ')'
|
466
|
+
end
|
467
|
+
|
468
|
+
def Parameters.get_closing_braket(arg, tokens)
|
469
|
+
begin
|
470
|
+
t = tokens.shift
|
471
|
+
tokens.unmatched_error('}') unless t and t[0]
|
472
|
+
|
473
|
+
arg << t
|
474
|
+
case t[1]
|
475
|
+
when '('
|
476
|
+
get_closing_paren(arg, tokens)
|
477
|
+
when ')'
|
478
|
+
tokens.unmatched_error(')')
|
479
|
+
when '{'
|
480
|
+
get_closing_braket(arg, tokens)
|
481
|
+
end
|
482
|
+
end until t[1] == '}'
|
483
|
+
end
|
484
|
+
|
485
|
+
end # Parameters
|
486
|
+
|
487
|
+
end # module
|
488
|
+
|