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,153 @@
|
|
1
|
+
# If there are additional platform specific options that
|
2
|
+
# belong in this file, please contribute them.
|
3
|
+
|
4
|
+
# Removes dots and zero pads
|
5
|
+
def expand_version(pre, version)
|
6
|
+
version.delete!('.')
|
7
|
+
(3 - version.length).times { version << '0' }
|
8
|
+
"#{pre}#{version}"
|
9
|
+
end
|
10
|
+
|
11
|
+
class Preprocessor::Parser
|
12
|
+
|
13
|
+
def configure(options, includes, compiler=nil, version=nil)
|
14
|
+
|
15
|
+
# __FILE__ and __LINE__ macros are define in preprocessor.y
|
16
|
+
|
17
|
+
# so '##' is used to paste tokens, not '/**/'
|
18
|
+
self.define('__STDC__', nil, '1')
|
19
|
+
self.define('__STDC_VERSION__', nil, '199901L')
|
20
|
+
comp_time = Time.now
|
21
|
+
self.define('__DATE__', nil, comp_time.strftime('%m %d %Y'))
|
22
|
+
self.define('__TIME__', nil, comp_time.strftime('%H:%M:%S'))
|
23
|
+
|
24
|
+
case RUBY_PLATFORM
|
25
|
+
when /i[0-9]86/
|
26
|
+
self.define('__i386__', nil, '1')
|
27
|
+
when /powerpc/
|
28
|
+
self.define('__ppc__', nil, '1')
|
29
|
+
# when /ia64/
|
30
|
+
# self.define('__ia64__', nil, '1')
|
31
|
+
else
|
32
|
+
warn "unrecognized platform: #{RUBY_PLATFORM}"
|
33
|
+
end
|
34
|
+
|
35
|
+
case RUBY_PLATFORM
|
36
|
+
#when /darwin/
|
37
|
+
#when /netbsd/
|
38
|
+
when /mswin32/
|
39
|
+
self.define('__WIN32', nil, '1')
|
40
|
+
when /cygwin/
|
41
|
+
self.define('__WIN32', nil, '1')
|
42
|
+
self.define('__CYGWIN__', nil, '1')
|
43
|
+
when /mingw/
|
44
|
+
self.define('__WIN32', nil, '1')
|
45
|
+
self.define('__MINGW32__', nil, '1')
|
46
|
+
#when /bccwin/
|
47
|
+
#when /os2/
|
48
|
+
#when /sparc/
|
49
|
+
#when /AIX/
|
50
|
+
end
|
51
|
+
|
52
|
+
case compiler # gcc is the default
|
53
|
+
when nil, 'gcc'
|
54
|
+
|
55
|
+
warn "invalid gcc version (got #{version} expecting major.minor.patch_level)" \
|
56
|
+
if version and not version =~ /\A(\d+)\.(\d+)(?:\.(\d+))?\Z/
|
57
|
+
|
58
|
+
# defaults to gcc 3.3.0
|
59
|
+
self.define('__GNUC__', nil, $1 || '3')
|
60
|
+
self.define('__GNUC_MINOR__', nil, $2 || '3')
|
61
|
+
self.define('__GNUC_PATCHLEVEL__', nil, $3 || '0') # default must be '0'
|
62
|
+
|
63
|
+
self.define('__complex__', nil, '_Complex')
|
64
|
+
|
65
|
+
# define all these as blank
|
66
|
+
empty_str = ''.freeze
|
67
|
+
self.define('__asm__', ['...'], empty_str)
|
68
|
+
self.define('__attribute__', ['...'], empty_str)
|
69
|
+
|
70
|
+
# this is now handled by the parser
|
71
|
+
#self.define('throw', ['...'], empty_str)
|
72
|
+
|
73
|
+
# note that __cdecl is included here
|
74
|
+
[ '__const__', '__extension__', '__inline__',
|
75
|
+
'__signed__', '__volatile__', '__cdecl' ].each do |kw|
|
76
|
+
self.define(kw, nil, empty_str)
|
77
|
+
end
|
78
|
+
|
79
|
+
self.define('__restrict_arr', nil, 'restrict')
|
80
|
+
|
81
|
+
# define psuedo-ANSI C keywords. Note these
|
82
|
+
# may be redefined later in the code
|
83
|
+
[ '__const', '__inline', '__restrict', '__signed', '__volatile' ].each do |kw|
|
84
|
+
self.define(kw, nil, kw[2, kw.length-2])
|
85
|
+
end
|
86
|
+
|
87
|
+
# for gcc 3.4+ compatibility
|
88
|
+
self.define('__GLIBC_HAVE_LONG_LONG', nil, '1')
|
89
|
+
self.define('__builtin_va_list', nil, 'int *')
|
90
|
+
self.define('SHLIB_COMPAT', ['...'], '(0)')
|
91
|
+
|
92
|
+
when 'vc++'
|
93
|
+
# win32 should already be defined
|
94
|
+
|
95
|
+
self.define('__cdecl', nil, ' ')
|
96
|
+
# make up a time stamp
|
97
|
+
self.define('__TIMESTAMP__', nil, comp_time.strftime('%a %b %d %H:%M:%S %y'))
|
98
|
+
|
99
|
+
# default version is 6.0 => 1200
|
100
|
+
version = version ? ((version.to_f + 6) * 100).to_i.to_s : '1200'
|
101
|
+
self.define('_MSC_VER', nil, version)
|
102
|
+
|
103
|
+
# Not sure if these should be added
|
104
|
+
#if RUBY_PLATFORM =~ /i[0-9]86/
|
105
|
+
# self.define('_M_IX86', nil, '500') # default
|
106
|
+
#else
|
107
|
+
# self.define('_M_MPPC', nil, '601') # default
|
108
|
+
# self.define('_M_PPC', nil, '604') # default
|
109
|
+
#end
|
110
|
+
when 'tinyc'
|
111
|
+
# tinyc compilers do not specify version numbers
|
112
|
+
self.define('__TINYC__', nil, '1')
|
113
|
+
|
114
|
+
when 'turboc'
|
115
|
+
warn "ignoring version number, defaulting to 3.0" if version
|
116
|
+
self.define('__TURBOC__', nil, '661') # 3.0
|
117
|
+
|
118
|
+
when 'borland'
|
119
|
+
version = version ? expand_version('0x', version) : '0x550'
|
120
|
+
self.define('__BORLANDC__', nil, version)
|
121
|
+
|
122
|
+
when 'intel'
|
123
|
+
version = version ? expand_version(nil, version) : '600'
|
124
|
+
self.define('__INTEL_COMPILER', nil, version)
|
125
|
+
self.define('__ICC', nil, version)
|
126
|
+
|
127
|
+
when 'mars'
|
128
|
+
version = version ? expand_version('0x', version) : '0x800'
|
129
|
+
self.define('__DMC__', nil, version)
|
130
|
+
|
131
|
+
when 'watcom'
|
132
|
+
version = version ? expand_version(version) << '0' : '1100'
|
133
|
+
self.define('__WATCOMC__', nil, version)
|
134
|
+
|
135
|
+
else
|
136
|
+
warn "unrecognized compiler: #{compiler}"
|
137
|
+
exit(2)
|
138
|
+
end # case
|
139
|
+
|
140
|
+
# options should be applied after the preprocessor is configured
|
141
|
+
options.each do |opt|
|
142
|
+
if opt[0] == :DEFINE
|
143
|
+
self.define(opt[1], opt[2], opt[3])
|
144
|
+
elsif opt[0] == :UNDEF
|
145
|
+
self.undef(opt[1])
|
146
|
+
else
|
147
|
+
raise "impossible"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
includes.each { |f| self.include_arg(f) }
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
File without changes
|
data/lib/dbc/dbc.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
# THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
|
4
4
|
# See included LICENCE file.
|
5
5
|
|
6
|
-
require '
|
7
|
-
require '
|
6
|
+
require 'caphir/ctokenizer'
|
7
|
+
require 'caphir/ctype'
|
8
8
|
require 'dbc/ocl'
|
9
9
|
|
10
10
|
module DBC
|
@@ -25,50 +25,72 @@ module DBC
|
|
25
25
|
str =~ /\A\/\*\*[ \t]*[\r\n]+(\s*\*?\s*(?:inv|pre|post|context).+)[ \t]*\*\/\Z/m
|
26
26
|
$1
|
27
27
|
end
|
28
|
-
|
29
|
-
class
|
28
|
+
|
29
|
+
class Parser < CTokenizer::LexerBase
|
30
|
+
include CTokenizer::Scoped
|
30
31
|
def initialize(str, file=nil, line=1)
|
31
32
|
super(str, file, line)
|
32
|
-
|
33
|
+
|
34
|
+
@scope = 0
|
35
|
+
@macro = false
|
36
|
+
@start_line = true # begining of file
|
37
|
+
|
38
|
+
@contract = nil
|
39
|
+
@conditions = {} # hash of contracts
|
40
|
+
# @ctype_parser.parse(source) -> parses source until a compelete
|
41
|
+
# top level C statement has been parsed. If the statement is a
|
42
|
+
# function definition it stops when the opening '{' is reached.
|
43
|
+
@ctype_parser = CType::Parser.new()
|
33
44
|
end
|
34
45
|
|
35
|
-
|
36
|
-
str = super
|
37
|
-
@token_cache << [:UNKNOWN, str.freeze].freeze
|
38
|
-
str
|
39
|
-
end
|
46
|
+
attr_reader :conditions
|
40
47
|
|
41
48
|
def shift
|
42
|
-
t = super
|
43
|
-
|
49
|
+
t = process_scope(super)
|
50
|
+
if t.at(0) == :COMMENT and start_of_line? and ocl = DBC.get_ocl(t.at(1))
|
51
|
+
self.error("no context for previous contract") if @contract
|
52
|
+
@contract = OCL::Parser.new(ocl, self.file, self.line - CTokenizer.line_count(ocl))
|
53
|
+
self.merge_current_contract() if @contract.context
|
54
|
+
end
|
44
55
|
t
|
45
56
|
end
|
46
57
|
|
47
|
-
def
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
until tokens.scope == 0 or tokens.empty?
|
64
|
-
# get back to group zero
|
65
|
-
tokens.shift
|
58
|
+
def each_statement
|
59
|
+
until @source.empty?
|
60
|
+
context = @ctype_parser.parse(self)
|
61
|
+
# if scope != 0 then we are in a function body
|
62
|
+
if f_body = (@scope != 0)
|
63
|
+
# first yield function header
|
64
|
+
yield(nil)
|
65
|
+
until @scope == 0 or @source.empty?
|
66
|
+
# get back to group zero
|
67
|
+
# don't check for contract comments
|
68
|
+
process_scope(@source.shift)
|
69
|
+
end
|
70
|
+
end #if
|
71
|
+
# add type definitions
|
72
|
+
context.each do |ctxt|
|
73
|
+
CType[ctxt.identifier] = ctxt if ctxt.typedef?
|
66
74
|
end
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
75
|
+
if @contract
|
76
|
+
# the contract should have no context
|
77
|
+
#raise "impossible" if @contract.context
|
78
|
+
self.error("expecting one context for contract") unless context.length == 1
|
79
|
+
@contract.context = context.first
|
80
|
+
self.merge_current_contract()
|
81
|
+
end # if
|
82
|
+
yield(f_body ? context.first : nil)
|
83
|
+
end # until
|
84
|
+
end # each_statement
|
85
|
+
|
86
|
+
def merge_current_contract()
|
87
|
+
context = @contract.context
|
88
|
+
context = context.short_name unless context.class == String
|
89
|
+
@conditions[context] = @contract
|
90
|
+
@contract = nil # done with current contract
|
91
|
+
end
|
92
|
+
|
93
|
+
end # class Parser
|
72
94
|
|
73
95
|
def DBC.format_conditions(conditions)
|
74
96
|
outstr = ''
|
@@ -95,7 +117,9 @@ module DBC
|
|
95
117
|
until tokens.empty?
|
96
118
|
t = tokens.shift
|
97
119
|
# note: comments don't affect start_of_line?
|
98
|
-
if t
|
120
|
+
if t.at(0) == :COMMENT and \
|
121
|
+
tokens.start_of_line? and \
|
122
|
+
str = DBC.get_ocl(t.at(1))
|
99
123
|
context = nil
|
100
124
|
# take out context information
|
101
125
|
str.gsub!(/^[ \t]*\*?[ \t]*context[ \t]+(.+)$/) do
|
@@ -148,105 +172,17 @@ module DBC
|
|
148
172
|
# try to insert the tag after then next opening braket
|
149
173
|
if conditions.first[0] =~ /pre|post/ and not context
|
150
174
|
until tokens.empty?
|
151
|
-
t = tokens.shift
|
152
|
-
outstr << t
|
153
|
-
break if t
|
175
|
+
t = tokens.shift.at(1)
|
176
|
+
outstr << t
|
177
|
+
break if t == '{'
|
154
178
|
end
|
155
179
|
end
|
156
180
|
outstr << doc_str
|
157
181
|
else
|
158
|
-
outstr << t
|
182
|
+
outstr << t.at(1)
|
159
183
|
end
|
160
184
|
end
|
161
185
|
outstr
|
162
186
|
end
|
163
|
-
|
164
|
-
class Parser < CTokenizer::LexerBase
|
165
|
-
include CTokenizer::Scoped
|
166
|
-
def initialize(str, file=nil, line=1)
|
167
|
-
super(str, file, line)
|
168
|
-
@token_cache = CTokenizer::Cache.new(super.file, super.line)
|
169
|
-
@scope = 0
|
170
|
-
@macro = false
|
171
|
-
@start_line = true # begining of file
|
172
|
-
end
|
173
|
-
|
174
|
-
def shift
|
175
|
-
process_scope(@token_cache.empty? ? super : @token_cache.shift)
|
176
|
-
end
|
177
|
-
|
178
|
-
def empty?
|
179
|
-
@token_cache.empty? and super
|
180
|
-
end
|
181
|
-
|
182
|
-
def file
|
183
|
-
if @token_cache.empty?
|
184
|
-
super
|
185
|
-
else
|
186
|
-
@token_cache.file
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
def line
|
191
|
-
if @token_cache.empty?
|
192
|
-
super
|
193
|
-
else
|
194
|
-
@token_cache.line
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
end # Parser
|
199
|
-
|
200
|
-
class OCLParser < Parser
|
201
|
-
|
202
|
-
def initialize(str, file=nil, line=1)
|
203
|
-
super(str, file, line)
|
204
|
-
@conditions = {}
|
205
|
-
@ctype_parser = CType::Parser.new()
|
206
|
-
end
|
207
|
-
|
208
|
-
attr_reader :conditions
|
209
|
-
|
210
|
-
def shift
|
211
|
-
t = super
|
212
|
-
# note: comments don't affect start_of_line?
|
213
|
-
if t.at(0) == :COMMENT and start_of_line? and ocl = DBC.get_ocl(t[1])
|
214
|
-
parse_ocl(ocl)
|
215
|
-
end
|
216
|
-
t
|
217
|
-
end
|
218
|
-
|
219
|
-
private
|
220
|
-
def parse_ocl(str)
|
221
|
-
self.error("no context for previous contract") \
|
222
|
-
unless @token_cache.empty?
|
223
|
-
parser = OCL::Parser.new(str, self.file, self.line - CTokenizer.line_count(str))
|
224
|
-
self.error("no conditions given") if parser.conditions.empty?
|
225
|
-
|
226
|
-
unless parser.context
|
227
|
-
@token_cache.reset(self.file, self.line)
|
228
|
-
# Use the Splitter to cache the tokens used by the type parser
|
229
|
-
splitter = CTokenizer::Splitter.new(@token_cache, @source)
|
230
|
-
splitter = CTokenizer::SkipMacros.new(splitter)
|
231
|
-
context = @ctype_parser.parse(splitter)
|
232
|
-
self.error("multiple contexts given") unless context.length == 1
|
233
|
-
parser.context = context.first
|
234
|
-
end
|
235
|
-
|
236
|
-
if parser.conditions.last.type =~ /\A(?:pre|post)/
|
237
|
-
# should be followed by a function
|
238
|
-
self.error("pre and post conditions must be in the context of a function") \
|
239
|
-
unless parser.context.function?
|
240
|
-
end
|
241
|
-
|
242
|
-
context_str = if parser.context.class == String
|
243
|
-
parser.context
|
244
|
-
else
|
245
|
-
parser.context.short_name
|
246
|
-
end
|
247
|
-
@conditions[context_str] = parser
|
248
|
-
end
|
249
|
-
end # OCLParser
|
250
|
-
|
251
187
|
end # DBC
|
252
188
|
|
data/lib/dbc/expand_function.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
|
4
4
|
# See included LICENCE file.
|
5
5
|
|
6
|
-
require '
|
6
|
+
require 'caphir/ctokenizer'
|
7
7
|
|
8
8
|
def expand_function(conditions, context, body, start_line, line_info)
|
9
9
|
raise 'should be a function' unless context.function?
|
@@ -11,11 +11,13 @@ def expand_function(conditions, context, body, start_line, line_info)
|
|
11
11
|
context_str = context.short_name
|
12
12
|
f_cond = conditions[context_str]
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
outstr = ''
|
15
|
+
outstr << "\n"
|
16
|
+
|
17
|
+
pre_text = '' # preconditions
|
18
|
+
post_text = '' # postconditions
|
19
|
+
inv_text = '' # invariants
|
20
|
+
final_text = '' # stuff at the end
|
19
21
|
|
20
22
|
if f_cond
|
21
23
|
# get the conditions
|
@@ -37,8 +39,9 @@ def expand_function(conditions, context, body, start_line, line_info)
|
|
37
39
|
rescue CType::EvaluationError
|
38
40
|
raise CType::EvaluationError, "#{line_info}: in #{context_str}, #{$!} for #{identifier}"
|
39
41
|
end
|
40
|
-
|
41
|
-
|
42
|
+
# initializations
|
43
|
+
outstr << "\t" << p_type.to_init_s(var_used)
|
44
|
+
outstr << ' = ' << identifier << ";\n"
|
42
45
|
end
|
43
46
|
end
|
44
47
|
|
@@ -54,26 +57,18 @@ def expand_function(conditions, context, body, start_line, line_info)
|
|
54
57
|
inv_text << exp_str
|
55
58
|
end; end
|
56
59
|
end
|
57
|
-
|
58
|
-
void_return_type = (context.base_type.to_s == 'void')
|
59
|
-
|
60
|
-
unless void_return_type or \
|
61
|
-
(post_text.empty? and inv_text.empty?)
|
62
|
-
init_text << "\t" << context.base_type.to_init_s('ocl__ret_val') << ";\n"
|
63
|
-
end
|
64
60
|
|
65
|
-
outstr = ''
|
66
|
-
|
67
61
|
if pre_text.empty? and post_text.empty? and inv_text.empty?
|
68
62
|
# we're done early
|
69
|
-
body.
|
70
|
-
outstr << t.at(1)
|
71
|
-
end
|
72
|
-
return outstr
|
63
|
+
return body.collect! { |t| t.at(1) }.join
|
73
64
|
end
|
74
65
|
|
75
|
-
|
66
|
+
void_return_type = (context.base_type.to_s == 'void')
|
76
67
|
|
68
|
+
unless void_return_type or (post_text.empty? and inv_text.empty?)
|
69
|
+
outstr << "\t" << context.base_type.to_init_s('ocl__ret_val') << ";\n"
|
70
|
+
end
|
71
|
+
|
77
72
|
# output invariants and preconditions
|
78
73
|
outstr << "\t/* Invariants: */\n" << inv_text unless inv_text.empty?
|
79
74
|
outstr << "\t/* Preconditions: */\n" << pre_text unless pre_text.empty?
|
@@ -83,37 +78,39 @@ def expand_function(conditions, context, body, start_line, line_info)
|
|
83
78
|
|
84
79
|
# output function body
|
85
80
|
if post_text.empty? and inv_text.empty?
|
86
|
-
body.
|
87
|
-
|
88
|
-
|
89
|
-
end
|
81
|
+
outstr << body.collect! do |t|
|
82
|
+
start_line += CTokenizer.line_count(t = t.at(1))
|
83
|
+
t
|
84
|
+
end.join
|
90
85
|
else
|
91
86
|
output_label = false
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
if t.at(1) == 'return'
|
99
|
-
output_label = true
|
100
|
-
if void_return_type
|
101
|
-
outstr << 'goto ocl__return_here'
|
87
|
+
if void_return_type
|
88
|
+
outstr << body.collect do |t|
|
89
|
+
start_line += CTokenizer.line_count( t = t.at(1) )
|
90
|
+
if t == 'return'
|
91
|
+
output_label = true
|
92
|
+
'goto ocl__return_here'
|
102
93
|
else
|
94
|
+
t
|
95
|
+
end
|
96
|
+
end.join # do
|
97
|
+
else
|
98
|
+
until body.empty?
|
99
|
+
start_line += CTokenizer.line_count( t = body.shift.at(1) )
|
100
|
+
if t == 'return'
|
101
|
+
output_label = true
|
103
102
|
outstr << '{ ocl__ret_val = ( '
|
104
|
-
|
105
|
-
|
106
|
-
t
|
107
|
-
|
108
|
-
break if t.at(1) == ';'
|
109
|
-
outstr << t.at(1)
|
103
|
+
until body.empty?
|
104
|
+
start_line += CTokenizer.line_count( t = body.shift.at(1) )
|
105
|
+
break if t == ';'
|
106
|
+
outstr << t
|
110
107
|
end
|
111
108
|
outstr << ' ); goto ocl__return_here; }'
|
109
|
+
else
|
110
|
+
outstr << t
|
112
111
|
end
|
113
|
-
|
114
|
-
|
115
|
-
end
|
116
|
-
end
|
112
|
+
end # until
|
113
|
+
end # if
|
117
114
|
outstr << "\nocl__return_here:\n" if output_label
|
118
115
|
# must explicitly return the return value unless return value is void
|
119
116
|
final_text << "\treturn ocl__ret_val;\n" unless void_return_type
|