dbc 1.3.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Simple test script to run the parser on the given input file
4
+ #
5
+
6
+
7
+ if RUBY_VERSION =~ /\A1\.6/
8
+ warn("attempt to run with Ruby 1.6.* (should be using Ruby 1.8+)")
9
+ exit(1)
10
+ end
11
+
12
+ require 'optparse'
13
+
14
+ # allows us to run the regression test with local source base
15
+ # without installing caphir
16
+ if $0 != File.basename($0)
17
+ $:.unshift(File.join(File.dirname($0),'..','lib'))
18
+ end
19
+
20
+ require 'caphir/parsersettings'
21
+ require 'caphir/parser'
22
+
23
+
24
+ if $0 == __FILE__
25
+
26
+ settings = ParserSettings.new
27
+
28
+ # initialize with defaults
29
+ always_output = false
30
+
31
+ #
32
+ # define OptionParser
33
+ #
34
+
35
+ opts = OptionParser.new do |opts|
36
+ opts.banner = "Usage: parser.rb [options] [input file]"
37
+ opts.separator ""
38
+ opts.separator "Specific options:"
39
+
40
+ # Compiler like options
41
+ opts.on("-o", "--output file", String,
42
+ "Output to file (default STDOUT)") do |file|
43
+ opts.error("multiple output files give") if settings.dest_file
44
+ settings.dest_file = file
45
+ end
46
+ opts.on("-D macro(=value)", String, "Define macro") do |macro|
47
+ settings.define_macro(macro)
48
+ end
49
+ # allow user to undefine compiler specific macros
50
+ opts.on("-U macro", String, "Cancel the definintion of macro") do |macro|
51
+ settings.undefine_macro(macro)
52
+ end
53
+ opts.on("-I directory", String,
54
+ "Use directory to search for header files") do |inc_dir|
55
+ settings.add_include_path(inc_dir)
56
+ end
57
+ opts.on("--include file", String, "Include file before processing") do |file|
58
+ settings.includes << file
59
+ end
60
+ opts.on("--cc=compiler(-version)", String, "Specify target compiler") do |cc|
61
+ opts.error("invalid compiler #{cc}") unless cc =~ /\A[^-]+(?:-(\d\.)+)?\Z/
62
+ settings.compiler = $1
63
+ settings.version = $2
64
+ end
65
+ opts.on("-E", "Preprocess input file only") do
66
+ settings.preprocess_only = true
67
+ end
68
+ opts.on("-a", "--always-output", "Always output to destination file") do
69
+ always_output = true
70
+ end
71
+ opts.on("--no-overwrite", "Don't overwrite output file") do
72
+ settings.overwrite = false
73
+ end
74
+ opts.on("-h", "--help", "Print this message") do
75
+ puts opts
76
+ exit(0)
77
+ end
78
+
79
+ opts.separator ""
80
+ opts.separator "Reads from STDIN if input file is '-' or none is given."
81
+ opts.separator "-D and -U options are processed in the order given."
82
+
83
+ end
84
+
85
+ #
86
+ # Handle command line options
87
+ #
88
+
89
+ begin
90
+ opts.parse!(ARGV)
91
+ rescue
92
+ warn "#{$!} (-h will show vaild options)"
93
+ exit(2)
94
+ end
95
+
96
+ # make sure opts gets GC'ed
97
+ opts = nil
98
+
99
+
100
+ if ARGV.length > 1
101
+ warn "expecting one input file, got #{ARGV.length}"
102
+ exit(2)
103
+ end
104
+
105
+ unless ARGV.empty? || ARGV[0] == '-'
106
+ settings.src_file = ARGV[0]
107
+ unless File.file?(settings.src_file)
108
+ warn "file does not exist: #{settings.src_file}"
109
+ exit(2)
110
+ end
111
+ end
112
+
113
+ begin
114
+ settings.check
115
+ settings.execute
116
+ rescue
117
+ $stderr.puts $!
118
+ exit(2)
119
+ end
120
+
121
+
122
+ begin
123
+ out_str = ''
124
+
125
+ parser = Caphir::EventParser.new(settings)
126
+
127
+ if settings.preprocess_only
128
+ parser.each do |t|
129
+ out_str << t
130
+ end
131
+ else
132
+ parser.each do |function|
133
+ stmt = parser.in_stream.reset([])
134
+
135
+ out_str << stmt.collect! { |t| t.at(1) }.join
136
+ end
137
+ end
138
+
139
+ rescue CTokenizer::Error, CType::EvaluationError
140
+ warn $!
141
+ exit(-1)
142
+ ensure
143
+ if !$! or always_output
144
+ if settings.dest_file
145
+ File.open(settings.dest_file, 'w') { |f| f.write(out_str) }
146
+ else
147
+ $stdout.write(out_str)
148
+ end
149
+ end
150
+ end
151
+
152
+ end
@@ -6,327 +6,181 @@
6
6
  # See included LICENCE file.
7
7
 
8
8
  if RUBY_VERSION =~ /\A1\.6/
9
- warn("attempting to run with Ruby 1.6.* (should be using Ruby 1.8+)")
10
- $stdin = STDIN
11
- $stdout = STDOUT
12
- $stderr = STDERR
9
+ warn("attempt to run with Ruby 1.6.* (should be using Ruby 1.8+)")
10
+ exit(1)
13
11
  end
14
12
 
15
- # create output directories if they don't exist
16
- def create_dest_dirs(dest_file)
17
- dest_dir = File.dirname(dest_file)
18
- dirs = []
19
- until File.directory?(dest_dir)
20
- # [ "/home/boson/fake", "/home/boson" ]
21
- # "/home/boson" exists so loop ends
22
- dirs << dest_dir
23
- dest_dir = File.dirname(dest_dir)
24
- end
25
- dirs.reverse_each do |d|
26
- Dir.mkdir(d)
27
- end
28
- nil
29
- end
30
-
31
- require 'getoptlong'
13
+ require 'optparse'
32
14
 
33
- command_dir = File.dirname($0)
34
- command = File.basename($0)
35
- if $0 != command
36
- $:.unshift(File.join(command_dir,'..','lib'))
15
+ # allows us to run the regression test with local source base
16
+ # without installing DBC
17
+ if $0 != File.basename($0)
18
+ $:.unshift(File.join(File.dirname($0),'..','lib'))
37
19
  end
38
- require 'dbc/searchpath'
39
- require 'dbc/preprocessor'
20
+
21
+ require 'caphir/parsersettings'
22
+ require 'caphir/parser'
40
23
  require 'dbc/expand_function'
41
- require 'dbc/ctype'
42
24
  require 'dbc/dbc'
43
25
 
44
- class GetoptLong
45
- def error(msg)
46
- raise GetoptLong::Error, msg
47
- end
48
- end
49
-
50
- opts = GetoptLong.new(
51
- [ "--always-output", "-a", GetoptLong::NO_ARGUMENT ],
52
- [ "--docs", "-d", GetoptLong::NO_ARGUMENT ],
53
- [ "--help", "-h", GetoptLong::NO_ARGUMENT ],
54
- [ "--no_line", "-n", GetoptLong::NO_ARGUMENT ],
55
- [ "--preprocess-only","-p", GetoptLong::NO_ARGUMENT ],
56
- # [ "--trace", "-t", GetoptLong::NO_ARGUMENT ],
57
- [ "--check_level", "-c", GetoptLong::REQUIRED_ARGUMENT ],
58
- [ "--output", "-o", GetoptLong::REQUIRED_ARGUMENT ],
59
- # cc compatibility options
60
- [ "--define", "-D", GetoptLong::REQUIRED_ARGUMENT ],
61
- [ "--include", "-I", GetoptLong::OPTIONAL_ARGUMENT ]
62
- )
63
-
64
26
  # initialize with defaults
65
- dest_file = nil
66
- src_file = nil
67
- search_path = SearchPath.new
68
- defines = []
69
- search_usr_include = true
27
+ settings = ParserSettings.new
70
28
 
71
29
  always_output = false
72
30
  check_level = nil
73
31
  docs = nil
74
32
  line_info = true
75
- preprocess_only = nil
76
33
 
77
- overwrite = true # unused
34
+ #
35
+ # define OptionParser
36
+ #
37
+ opts = OptionParser.new do |opts|
38
+ opts.banner = "Usage: dbcparse.rb [options] [input file]"
39
+ opts.separator ""
40
+ opts.separator "Specific options:"
41
+
42
+ # Compiler like options
43
+ opts.on("-o", "--output file", String, \
44
+ "Output to file (default STDOUT)") do |file|
45
+ opts.error("multiple output files give") if settings.dest_file
46
+ settings.dest_file = file
47
+ end
48
+ opts.on("-D macro(=value)", String, "Define macro") do |macro|
49
+ settings.define_macro(macro)
50
+ end
51
+ # allow user to undefine compiler specific macros
52
+ opts.on("-U macro", String, "Cancel the definintion of macro") do |macro|
53
+ settings.undefine_macro(macro)
54
+ end
55
+ opts.on("-I directory", String,
56
+ "Use directory to search for header files") do |inc_dir|
57
+ settings.add_include_path(inc_dir)
58
+ end
59
+ opts.on("--include file", String, "Include file before processing") do |file|
60
+ settings.includes << file
61
+ end
62
+ opts.on("--cc=compiler(-version)", String, "Specify target compiler") do |cc|
63
+ opts.error("invalid compiler #{cc}") unless cc =~ /\A([^-]+)(?:-(\d\.)+)?\Z/
64
+ settings.compiler = $1
65
+ settings.version = $2
66
+ end
67
+ opts.on("-E", "Preprocess input file only") do
68
+ settings.preprocess_only = true
69
+ end
70
+ opts.on("-c", "--check-level [0,1,2]", Integer,
71
+ "Set check level (default 2)") do |level|
72
+ opts.error("invalid check level '#{level}'") unless DBC.valid_check_level?(level)
73
+ check_level = level
74
+ end
75
+ opts.on("-a", "--always-output", "Always output to destination file") do
76
+ always_output = true
77
+ end
78
+ opts.on("-d", "--docs", "Generate Doxygen documentation") do
79
+ docs = true
80
+ end
81
+ opts.on("--no-overwrite", "Don't overwrite output file") do
82
+ settings.overwrite = false
83
+ end
84
+ opts.on("--no-line", "Don't output '\#line' directives") do
85
+ line_info = false
86
+ end
87
+ opts.on("-h", "--help", "Print this message") do
88
+ puts opts
89
+ exit(0)
90
+ end
91
+
92
+ opts.separator ""
93
+ opts.separator "Check levels: 0 = none, 1 = preconditions only, 2 = all conditions."
94
+ opts.separator "Reads from STDIN if input file is '-' or none is given."
95
+ opts.separator "-D and -U options are processed in the order given."
96
+ end
97
+
98
+ #
99
+ # Handle command line options
100
+ #
78
101
 
79
102
  begin
80
- i = 1
81
- opts.each do |opt, arg|
82
- case opt
83
- when "--check_level"
84
- check_level = arg.to_i
85
- unless DBC.valid_check_level?(check_level)
86
- opts.error("invalid check level: #{arg}")
87
- end
88
- when "--include"
89
- # should support including file in the future
90
- if arg
91
- begin
92
- search_path.unshift(arg)
93
- rescue
94
- opts.error($!)
95
- end
96
- else
97
- # -I with no argument means don't search /usr/include
98
- search_usr_include = false
99
- end
100
- when "--define"
101
- unless arg =~ /\A([A-Za-z_]\w*)(?:\(([\w,\s]*)\))?(?:=(.*))?\Z/
102
- opts.error("macro names must be identifiers: #{arg}")
103
- end
104
- macro = $1
105
- opts.error("cannot use 'defined' as a macro name") if macro == 'defined'
106
- value = $3 || '1'
107
- params = $2.split(/\s*,\s*/) if $2
108
- defines << [macro,params,value]
109
- when "--output"
110
- if dest_file
111
- opts.error("multiple output files give")
112
- end
113
- dest_file = arg
114
- when "--always-output" then always_output = true
115
- when "--docs" then docs = true
116
- when "--no_line" then line_info = false
117
- when "--preprocess-only" then preprocess_only = true
118
- when "--help"
119
- puts "Converts OCL design by contract tags to C code."
120
- puts "Usage:"
121
- puts "\t#{command} [options] [commands] input_file"
122
- puts "Options:"
123
- puts "\t-a, --always-output : always output to destination file"
124
- puts "\t-d, --docs : generate Doxygen documentation"
125
- puts "\t-h, --help : print this message"
126
- puts "\t-n, --no_line : do not output '\#line' directives"
127
- puts "\t-p, --preprocess-only : preprocess input files only"
128
- puts "Commands:"
129
- puts "\t-o, --output <file>"
130
- puts "\t-c, --check_level <0,1,2>"
131
- puts "\t-D, --define <macro(=value)>"
132
- puts "\t-I, --include <directory>"
133
- puts ""
134
- puts "Check levels: 0 = none, 1 = preconditions only, 2 = all conditions."
135
- puts "Reads from STDIN if input file is '-' or none is given."
136
- puts "Outputs to STDOUT if no output file is given."
137
- exit(0)
138
- else raise GetoptLong::InvalidOption, "unrecognized option '#{opt}'"
139
- end # case
140
- i += 1
141
- end # each
142
- rescue GetoptLong::InvalidOption, GetoptLong::Error
143
- msg = $!.message
144
- warn "<command line>:#{i}: #{msg} (-h will show vaild options)"
145
- exit(2)
103
+ opts.parse!(ARGV)
104
+ rescue
105
+ warn "#{$!} (-h will show vaild options)"
106
+ exit(2)
146
107
  end
147
108
 
148
- if [check_level, preprocess_only, docs].nitems > 1
149
- warn %q{conflicting options: only one of the following may be selected:
150
- --check_level
109
+ # make sure opts gets gc'ed
110
+ opts = nil
111
+
112
+ if [check_level, settings.preprocess_only, docs].nitems > 1
113
+ warn %q{conflicting options: only one of the following may be selected:
114
+ --check-level
151
115
  --preprocess-only
152
116
  --docs}
153
- exit(2)
117
+ exit(2)
154
118
  end
155
119
 
156
120
  # default condition is all
157
121
  check_level = DBC::ALL unless check_level
158
122
 
159
123
  if ARGV.length > 1
160
- warn "expecting one input file, got #{ARGV.length}"
161
- exit(2)
124
+ warn "expecting one input file, got #{ARGV.length}"
125
+ exit(2)
162
126
  end
163
127
 
164
128
  unless ARGV.empty? || ARGV[0] == '-'
165
- src_file = ARGV[0]
166
- unless File.file?(src_file)
167
- warn "file does not exist: #{src_file}"
168
- exit(2)
169
- end
170
- end
171
-
172
- if dest_file
173
- if File.exists?(dest_file) and not overwrite
174
- warn "destination file '#{dest_file}' exists"
175
- exit(2)
176
- end
177
- # this is always bad - so don't allow it even if overwrite is true
178
- if src_file and File.expand_path(dest_file) == File.expand_path(src_file)
179
- warn "destination file and source file are the same"
180
- exit(2)
181
- end
182
- begin
183
- create_dest_dirs(dest_file)
184
- rescue
185
- warn "could not create destination directory: #{$?}"
186
- exit(2)
187
- end
188
- end
189
-
190
- if src_file
191
- begin
192
- text = File.open(src_file, 'r') do |in_file|
193
- in_file.read
194
- end
195
- rescue
196
- warn "could not open '#{src_file}': #{$?}"
197
- exit(2)
198
- end
199
- else
200
- text = STDIN.read
201
- end
202
-
203
- ### Compatibility with various compilers ###
204
-
205
- # the following are added to the end of the search path
206
- if search_usr_include
207
- if File.exists?('/usr/include')
208
- search_path << '/usr/include'
209
- # search for /usr/lib/gcc*/<system>/<version>/include
210
- Dir['/usr/lib/gcc*/*/*/include'].each do |gcc_inc|
211
- search_path << gcc_inc if File.directory?(gcc_inc)
212
- end
213
- else
214
- if env_include = ENV['INCLUDE']
215
- env_include.split(';').each do |p|
216
- # any anci c installation should have stdlib.h or stdio.h
217
- if File.exists?(File.join(p, 'stdlib.h'))
218
- search_path << p
219
- end
220
- end # each
221
- end
222
- end # if File.exists?('/usr/include')
129
+ settings.src_file = ARGV[0]
130
+ unless File.file?(settings.src_file)
131
+ warn "file does not exist: #{settings.src_file}"
132
+ exit(2)
133
+ end
223
134
  end
224
135
 
225
- search_path_limited = search_path.dup
226
- # Add directory of file to the begining of the search_path
227
- # search_path_limited does not include the directory of the file being parsed
228
- search_path.unshift( src_file ? File.dirname(src_file) : '.' )
229
-
230
- case RUBY_PLATFORM
231
- when /i[0-9]86|cygwin|mingw/
232
- defines << ['__i386__', nil, nil]
233
- # I have found that these setting work best with GNU C libs
234
- # experiance may vary :)
235
- defines << ['__GLIBC_HAVE_LONG_LONG', nil, '1']
236
- when /powerpc|darwin/
237
- defines << ['__ppc__', nil, nil]
238
- else
239
- warn "unrecognized platform: #{RUBY_PLATFORM}"
136
+ begin
137
+ settings.check
138
+ settings.execute
139
+ rescue
140
+ $stderr.puts $!
141
+ exit(2)
240
142
  end
241
143
 
242
- # for gcc compatibility
243
- defines << ['__asm__', ['...'], ' ']
244
- defines << ['__attribute__', ['...'], ' ']
245
- defines << ['__const__', nil, 'const']
246
- defines << ['__extension__', nil, ' ']
247
- defines << ['__inline__', nil, ' ']
248
- defines << ['__signed__', nil, 'signed']
249
- defines << ['__volatile__', nil, 'volatile']
250
-
251
- # for gcc 3.4+ compatibility
252
- defines << ['__builtin_va_list', nil, 'int *']
253
- defines << ['SHLIB_COMPAT', ['...'], '(0)']
254
-
255
- # so '##' is used to paste tokens, not '/**/'
256
- defines << ['__STDC__', nil, '1']
257
- #############################################
258
144
 
259
145
  begin
260
- if docs
261
- out_str = DBC.parse_docs(CTokenizer::Lexer.new(text, src_file))
262
- elsif preprocess_only
263
- out_str = ''
264
- preproc = Preprocessor::Parser.new( search_path,
265
- search_path_limited,
266
- text, src_file )
267
-
268
- defines.each { |d,p,v| preproc.define(d, p, v) }
269
- preproc.each do |t|
270
- out_str << t.at(1)
271
- end
272
- else
273
- if check_level == DBC::NONE
274
- out_str = text
275
- else
276
- # Cache Tokens => Preprocessor => Parse OCL => Parse C Types
277
- # Cached tokens are output, not Preprocessed tokens
278
- out_str = ''
279
- # cache statements
280
- cache = DBC::Cache.new(text, src_file)
281
- # preprocesses all tokens
282
- preproc = Preprocessor::Parser.new( search_path,
283
- search_path_limited,
284
- cache )
285
-
286
- # define tokens passed
287
- defines.each { |d| preproc.define(*d) }
288
-
289
- # extracts DBC condtions
290
- source = DBC::OCLParser.new(preproc)
291
-
292
- # extract all C Statements
293
- DBC.parse(source) do |context, f_body|
294
- stmt = cache.reset
295
- line = stmt.line
296
- stmt = stmt.cache
297
- # remove end of file token if it exists
298
- stmt.pop unless stmt.empty? or stmt.last[0]
299
-
300
- # define C types
301
- context.each do |ctxt|
302
- CType[ctxt.identifier] = ctxt if ctxt.typedef?
303
- end
304
-
305
- if f_body and preproc.base?
306
- unless context.first
307
- raise CTokenizer.error(nil, line, "unmatched braket")
308
- end
309
- out_str << expand_function( source.conditions,
310
- context.first,
311
- stmt, line, line_info )
312
- else
313
- stmt.each do |t|
314
- out_str << t.at(1)
315
- end
316
- end
317
- end # do
318
- end #if
319
- end # if
146
+ out_str = ''
147
+
148
+ if docs
149
+ out_str = DBC.parse_docs(CTokenizer::Lexer.new(
150
+ settings.source, settings.src_file))
151
+ elsif check_level == DBC::NONE
152
+ out_str = settings.source
153
+ else
154
+ parser = Caphir::EventParser.new(settings, DBC::Parser)
155
+
156
+ if settings.preprocess_only
157
+ parser.each do |t|
158
+ out_str << t
159
+ end
160
+ else
161
+ parser.each do |function|
162
+ line = parser.in_stream.last_line
163
+ stmt = parser.in_stream.reset([])
164
+
165
+ if function and parser.preproc.base?
166
+ out_str << expand_function( parser.handler.conditions,
167
+ function, stmt, line, line_info)
168
+ else
169
+ out_str << stmt.collect! { |t| t.at(1) }.join
170
+ end
171
+ end
172
+ end
173
+ end
320
174
  rescue CTokenizer::Error, CType::EvaluationError
321
- warn $!
322
- exit(-1)
175
+ warn $!
176
+ exit(-1)
323
177
  ensure
324
- if !$! or always_output
325
- if dest_file
326
- File.open(dest_file, 'w') { |f| f.write(out_str) }
327
- else
328
- $stdout.write(out_str)
329
- end
330
- end
178
+ if !$! or always_output
179
+ if settings.dest_file
180
+ File.open(settings.dest_file, 'w') { |f| f.write(out_str) }
181
+ else
182
+ $stdout.write(out_str)
183
+ end
184
+ end
331
185
  end
332
186