ridl 2.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +49 -0
- data/README.rdoc +42 -0
- data/lib/idl/BiDirPolicy.pidl +28 -0
- data/lib/idl/CosNaming.idl +260 -0
- data/lib/idl/IOP.pidl +98 -0
- data/lib/idl/Messaging.pidl +152 -0
- data/lib/idl/PortableServer.pidl +371 -0
- data/lib/idl/TimeBase.pidl +40 -0
- data/lib/idl/orb.idl +200 -0
- data/lib/ridl/backend.rb +124 -0
- data/lib/ridl/delegate.rb +740 -0
- data/lib/ridl/expression.rb +276 -0
- data/lib/ridl/genfile.rb +220 -0
- data/lib/ridl/node.rb +2818 -0
- data/lib/ridl/optparse_ext.rb +357 -0
- data/lib/ridl/parser.diff +42 -0
- data/lib/ridl/parser.rb +3836 -0
- data/lib/ridl/parser.ry +933 -0
- data/lib/ridl/require.rb +18 -0
- data/lib/ridl/ridl.rb +44 -0
- data/lib/ridl/runner.rb +304 -0
- data/lib/ridl/scanner.rb +1238 -0
- data/lib/ridl/type.rb +507 -0
- data/lib/ridl/version.rb +22 -0
- metadata +74 -0
data/lib/ridl/require.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#--------------------------------------------------------------------
|
2
|
+
# require.rb - Ruby IDL loader
|
3
|
+
#
|
4
|
+
# Author: Martin Corino
|
5
|
+
#
|
6
|
+
# This program is free software; you can redistribute it and/or
|
7
|
+
# modify it under the terms of the RIDL LICENSE which is
|
8
|
+
# included with this program.
|
9
|
+
#
|
10
|
+
# Copyright (c) Remedy IT Expertise BV
|
11
|
+
# Chamber of commerce Rotterdam nr.276339, The Netherlands
|
12
|
+
#--------------------------------------------------------------------
|
13
|
+
require 'ridl/version'
|
14
|
+
require 'ridl/scanner'
|
15
|
+
require 'ridl/type'
|
16
|
+
require 'ridl/node'
|
17
|
+
require 'ridl/expression'
|
18
|
+
require 'ridl/parser'
|
data/lib/ridl/ridl.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# Encoding.default_internal = 'UTF-8'
|
3
|
+
|
4
|
+
# :main: README.rdoc
|
5
|
+
|
6
|
+
#--------------------------------------------------------------------
|
7
|
+
# ridl.rb - main file for Ruby IDL compiler
|
8
|
+
#
|
9
|
+
# Author: Martin Corino
|
10
|
+
#
|
11
|
+
# This program is free software; you can redistribute it and/or
|
12
|
+
# modify it under the terms of the RIDL LICENSE which is
|
13
|
+
# included with this program.
|
14
|
+
#
|
15
|
+
# Copyright (c) Remedy IT Expertise BV
|
16
|
+
# Chamber of commerce Rotterdam nr.276339, The Netherlands
|
17
|
+
#--------------------------------------------------------------------
|
18
|
+
require 'ridl/require'
|
19
|
+
|
20
|
+
##
|
21
|
+
# RIDL is a Ruby library implementing an OMG \IDL parser/compiler
|
22
|
+
# frontend with support for pluggable (and stackable) backends.
|
23
|
+
#
|
24
|
+
# RIDL itself implements an \IDL parser (RACC based) in IDL::Parser in
|
25
|
+
# combination with IDL::Scanner, syntax tree classes under IDL::AST,
|
26
|
+
# type classes under IDL::Type and expression classes under
|
27
|
+
# IDL::Expression.
|
28
|
+
# Furthermore RIDL implements a number of support classes useful in
|
29
|
+
# the implementation of backends for RIDL.
|
30
|
+
#
|
31
|
+
# RIDL does *not* implement any standard backend to handle things like
|
32
|
+
# code generation and/or documentation generation but instead provides
|
33
|
+
# a framework for user defined pluggable backends.
|
34
|
+
# Known backends for RIDL are the R2CORBA RIDL backend and the IDL2C++11
|
35
|
+
# backend.
|
36
|
+
#
|
37
|
+
module IDL
|
38
|
+
end
|
39
|
+
|
40
|
+
# load RIDL runner/initializer
|
41
|
+
require 'ridl/runner'
|
42
|
+
|
43
|
+
# initialize RIDL
|
44
|
+
IDL.init
|
data/lib/ridl/runner.rb
ADDED
@@ -0,0 +1,304 @@
|
|
1
|
+
#--------------------------------------------------------------------
|
2
|
+
# run.rb - Standalone Ruby IDL compiler runner
|
3
|
+
#
|
4
|
+
# Author: Martin Corino
|
5
|
+
#
|
6
|
+
# This program is free software; you can redistribute it and/or
|
7
|
+
# modify it under the terms of the RIDL LICENSE which is
|
8
|
+
# included with this program.
|
9
|
+
#
|
10
|
+
# Copyright (c) Remedy IT Expertise BV
|
11
|
+
# Chamber of commerce Rotterdam nr.276339, The Netherlands
|
12
|
+
#--------------------------------------------------------------------
|
13
|
+
require 'stringio'
|
14
|
+
require 'ridl/optparse_ext'
|
15
|
+
require 'ridl/genfile'
|
16
|
+
require 'ridl/backend'
|
17
|
+
|
18
|
+
# -----------------------------------------------------------------------
|
19
|
+
|
20
|
+
$VERBOSE = $VERBOSE || ENV['RIDL_VERBOSE']
|
21
|
+
|
22
|
+
module IDL
|
23
|
+
|
24
|
+
@@embedded = false unless class_variable_defined?(:@@embedded)
|
25
|
+
@@be_name = nil unless class_variable_defined?(:@@be_name)
|
26
|
+
@@preprocessing = false
|
27
|
+
@@no_pidl = false
|
28
|
+
@@idlstack = []
|
29
|
+
@@backend = nil
|
30
|
+
@@verbose_level = 0
|
31
|
+
|
32
|
+
OPTIONS = {
|
33
|
+
:outputdir => nil,
|
34
|
+
:includepaths => [],
|
35
|
+
:verbose => 0,
|
36
|
+
:debug => false,
|
37
|
+
:namespace => nil,
|
38
|
+
:search_incpath => false,
|
39
|
+
:backend => nil,
|
40
|
+
:macros => {
|
41
|
+
:__RIDL__ => "#{RIDL_VERSION}",
|
42
|
+
:__RIDLBE__ => nil,
|
43
|
+
:__RIDLBE_VER__ => nil
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
def IDL.preprocessing?
|
48
|
+
@@preprocessing
|
49
|
+
end
|
50
|
+
|
51
|
+
def IDL.no_pidl?
|
52
|
+
@@no_pidl
|
53
|
+
end
|
54
|
+
|
55
|
+
def IDL.backend
|
56
|
+
@@backend
|
57
|
+
end
|
58
|
+
|
59
|
+
def IDL.init
|
60
|
+
unless @@embedded
|
61
|
+
# check commandline args for explicit language mapping backend
|
62
|
+
if ARGV.first =~ /^:\S+/
|
63
|
+
@@be_name = ARGV.shift.reverse.chop.reverse.to_sym
|
64
|
+
elsif ENV['RIDL_BE_SELECT'] # or from environment
|
65
|
+
@@be_name = ENV['RIDL_BE_SELECT'].to_sym
|
66
|
+
end
|
67
|
+
|
68
|
+
# add optional search paths for RIDL backends
|
69
|
+
$:.concat(ENV['RIDL_BE_PATH'].split(/:|;/)) if ENV['RIDL_BE_PATH']
|
70
|
+
|
71
|
+
# check for special bootstrapping switches
|
72
|
+
if ARGV.first == '--preprocess'
|
73
|
+
@@preprocessing = true
|
74
|
+
ARGV.shift
|
75
|
+
elsif ARGV.first == '--ignore-pidl'
|
76
|
+
@@no_pidl = true
|
77
|
+
ARGV.shift
|
78
|
+
end
|
79
|
+
end
|
80
|
+
# try to load the active laguage mapping backend
|
81
|
+
@@backend = @@be_name ? Backend.load(@@be_name) : Backend.null_be
|
82
|
+
# finalize base options
|
83
|
+
OPTIONS[:preprocess] = preprocessing?
|
84
|
+
OPTIONS[:ignore_pidl] = no_pidl?
|
85
|
+
OPTIONS[:backend] = @@backend
|
86
|
+
OPTIONS[:__RIDLBE__] = @@backend.name.to_s
|
87
|
+
OPTIONS[:__RIDLBE_VER__] = @@backend.version
|
88
|
+
end
|
89
|
+
|
90
|
+
# parse commandline arguments
|
91
|
+
#
|
92
|
+
def IDL.parse_args
|
93
|
+
script_name = File.basename($0, '.*')
|
94
|
+
if not script_name =~ /ridlc/
|
95
|
+
script_name = "ruby "+$0
|
96
|
+
end
|
97
|
+
|
98
|
+
# set up option parser with common options
|
99
|
+
opts = OptionParser.new
|
100
|
+
opts.banner = "Usage: #{script_name} [:backend] [options] [<idlfile> [<idlfile> ...]]\n\n" +
|
101
|
+
" backend\t\tSpecifies the IDL language mapping backend to use.\n"+
|
102
|
+
" \t\tDefault = :null\n\n"+
|
103
|
+
" Active language mapping = :#{IDL.backend.name}"
|
104
|
+
opts.separator ""
|
105
|
+
opts.on("-I PATH", "--include=PATH", String,
|
106
|
+
"Adds include searchpath.",
|
107
|
+
"Default: nil") { |v| OPTIONS[:includepaths] << v }
|
108
|
+
opts.on('-Dmacro=[value]', String, 'defines preprocessor macro') { |v|
|
109
|
+
name, value = v.split('=')
|
110
|
+
OPTIONS[:macros][name] = (value ? value : true)
|
111
|
+
}
|
112
|
+
opts.on("-n NAMESPACE", "--namespace=NAMESPACE", String,
|
113
|
+
"Defines rootlevel enclosing namespace.",
|
114
|
+
"Default: nil") { |v| OPTIONS[:namespace]=v }
|
115
|
+
opts.on("-v", "--verbose",
|
116
|
+
"Set verbosity level. Repeat to increment.",
|
117
|
+
"Default: 0") { |v| OPTIONS[:verbose] += 1 }
|
118
|
+
opts.on("--debug",
|
119
|
+
"Set parser debug mode. Don't do this at home!",
|
120
|
+
"Default: off") { |v| OPTIONS[:debug] = true }
|
121
|
+
opts.on('--stdidl',
|
122
|
+
'Adds include path to standard IDL files provided with RIDL.',
|
123
|
+
'Default: not set') { |v|
|
124
|
+
OPTIONS[:includepaths] << File.expand_path(File.join(File.dirname(__FILE__), '..', 'idl'))
|
125
|
+
}
|
126
|
+
opts.on("--search-includepath",
|
127
|
+
"Use include paths to find main IDL source.",
|
128
|
+
"Default: off") { |v| OPTIONS[:search_incpath]=v }
|
129
|
+
if preprocessing?
|
130
|
+
opts.on("--output=FILE", String,
|
131
|
+
"Specifies filename to generate output in.",
|
132
|
+
"Default: File.basename(idlfile, '.idl')+<postfix>+<ext>") { |v| OPTIONS[:output]=v }
|
133
|
+
end
|
134
|
+
|
135
|
+
# setup language mapping specific options
|
136
|
+
be_options = OptionList.new
|
137
|
+
@@backend.setup_be(be_options, OPTIONS)
|
138
|
+
be_options.to_option_parser(opts, OPTIONS)
|
139
|
+
|
140
|
+
opts.on('-V', "--version",
|
141
|
+
"Show version information and exit.") {
|
142
|
+
puts "RIDL compiler #{OPTIONS[:macros][:__RIDL__]}"
|
143
|
+
puts RIDL_COPYRIGHT
|
144
|
+
puts '---'
|
145
|
+
@@backend.print_version
|
146
|
+
exit
|
147
|
+
}
|
148
|
+
|
149
|
+
opts.separator ""
|
150
|
+
|
151
|
+
opts.on("-h", "--help",
|
152
|
+
"Show this help message.") { puts opts; puts; exit }
|
153
|
+
|
154
|
+
opts.parse!(ARGV)
|
155
|
+
end
|
156
|
+
|
157
|
+
def IDL.push_input(idlfile, opts)
|
158
|
+
@@idlstack << [idlfile, opts]
|
159
|
+
end
|
160
|
+
|
161
|
+
def IDL.pop_input
|
162
|
+
@@idlstack.shift
|
163
|
+
end
|
164
|
+
|
165
|
+
def IDL.peek_input
|
166
|
+
@@idlstack.first
|
167
|
+
end
|
168
|
+
|
169
|
+
def IDL.has_input?
|
170
|
+
!@@idlstack.empty?
|
171
|
+
end
|
172
|
+
|
173
|
+
# main run method
|
174
|
+
#
|
175
|
+
def IDL.run
|
176
|
+
# parse arguments
|
177
|
+
begin
|
178
|
+
parse_args
|
179
|
+
rescue ArgumentError => ex
|
180
|
+
IDL.error(ex.inspect)
|
181
|
+
IDL.error(ex.backtrace.join("\n")) if $VERBOSE
|
182
|
+
exit 1
|
183
|
+
end
|
184
|
+
|
185
|
+
verbose_level = OPTIONS[:verbose]
|
186
|
+
|
187
|
+
if preprocessing?
|
188
|
+
## PREPROCESSING
|
189
|
+
o = if OPTIONS[:output].nil?
|
190
|
+
$stdout
|
191
|
+
else
|
192
|
+
File.open(OPTIONS[:output], "w+")
|
193
|
+
end
|
194
|
+
OPTIONS[:output] = o
|
195
|
+
|
196
|
+
parser = ::IDL::Parser.new(OPTIONS)
|
197
|
+
parser.yydebug = OPTIONS[:debug]
|
198
|
+
|
199
|
+
begin
|
200
|
+
input_base = File.basename(ARGV.first)
|
201
|
+
if (input_base != ARGV.first)
|
202
|
+
OPTIONS[:includepaths] << File.dirname(ARGV.first)
|
203
|
+
end
|
204
|
+
parser.parse("#include \"#{input_base}\"")
|
205
|
+
rescue => ex
|
206
|
+
IDL.error(ex.inspect)
|
207
|
+
IDL.error(ex.backtrace.join("\n")) unless ex.is_a? IDL::ParseError
|
208
|
+
ensure
|
209
|
+
o.close
|
210
|
+
end
|
211
|
+
else
|
212
|
+
## collect input files from commandline
|
213
|
+
ARGV.each do |_arg|
|
214
|
+
_opts = OPTIONS.dup
|
215
|
+
|
216
|
+
_opts[:idlfile] = _arg
|
217
|
+
if _opts[:search_incpath]
|
218
|
+
_fname = _arg
|
219
|
+
_fpath = if File.file?(_fname) && File.readable?(_fname)
|
220
|
+
_fname
|
221
|
+
else
|
222
|
+
_fp = _opts[:includepaths].find do |_p|
|
223
|
+
_f = _p + "/" + _fname
|
224
|
+
File.file?(_f) && File.readable?(_f)
|
225
|
+
end
|
226
|
+
_opts[:outputdir] = _fp unless _fp.nil? || !_opts[:outputdir].nil?
|
227
|
+
_fp += '/' + _fname unless _fp.nil?
|
228
|
+
_fp
|
229
|
+
end
|
230
|
+
_arg = _fpath unless _fpath.nil?
|
231
|
+
end
|
232
|
+
_opts[:includepaths] << File.dirname(_arg)
|
233
|
+
|
234
|
+
_opts[:outputdir] ||= '.'
|
235
|
+
|
236
|
+
push_input(_arg, _opts)
|
237
|
+
end
|
238
|
+
|
239
|
+
## if no IDL input file specified read from STDIN
|
240
|
+
unless has_input?
|
241
|
+
_opts = OPTIONS.dup
|
242
|
+
_opts[:outputdir] ||= '.'
|
243
|
+
push_input($stdin, _opts)
|
244
|
+
end
|
245
|
+
|
246
|
+
## CODE GENERATION
|
247
|
+
while has_input?
|
248
|
+
# get input from stack
|
249
|
+
_idlfile, _opts = pop_input
|
250
|
+
|
251
|
+
_fio = if IO === _idlfile || StringIO === _idlfile
|
252
|
+
_idlfile
|
253
|
+
else
|
254
|
+
File.open(_idlfile, "r")
|
255
|
+
end
|
256
|
+
raise RuntimeError, 'cannot read from STDOUT' if $stdout == _fio
|
257
|
+
|
258
|
+
# parse IDL source
|
259
|
+
IDL.log(1, "RIDL - parsing #{IO === _idlfile ? 'from STDIN': _idlfile}")
|
260
|
+
|
261
|
+
_parser = ::IDL::Parser.new(_opts)
|
262
|
+
_parser.yydebug = _opts[:debug]
|
263
|
+
|
264
|
+
begin
|
265
|
+
_parser.parse(_fio)
|
266
|
+
rescue => ex
|
267
|
+
IDL.error(ex.inspect)
|
268
|
+
IDL.error(ex.backtrace.join("\n")) unless ex.is_a? IDL::ParseError
|
269
|
+
exit 1
|
270
|
+
ensure
|
271
|
+
_fio.close unless _fio == $stdin
|
272
|
+
end
|
273
|
+
|
274
|
+
# process parse result -> code generation
|
275
|
+
IDL.log(2, 'RIDL - starting code generation')
|
276
|
+
|
277
|
+
GenFile.transaction do
|
278
|
+
begin
|
279
|
+
@@backend.process_input(_parser, _opts)
|
280
|
+
rescue Backend::ProcessStop
|
281
|
+
IDL.log(2, "RIDL - processing #{IO === _idlfile ? 'from STDIN': _idlfile} stopped from #{$!.message}")
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end # IDL.run
|
287
|
+
|
288
|
+
def IDL.verbose_level
|
289
|
+
@@verbose_level
|
290
|
+
end
|
291
|
+
|
292
|
+
def IDL.verbose_level=(l)
|
293
|
+
@@verbose_level = l
|
294
|
+
end
|
295
|
+
|
296
|
+
def IDL.log(level, message)
|
297
|
+
STDERR.puts message if verbose_level >= level
|
298
|
+
end
|
299
|
+
|
300
|
+
def IDL.error(message)
|
301
|
+
STDERR.puts(message)
|
302
|
+
end
|
303
|
+
|
304
|
+
end
|
data/lib/ridl/scanner.rb
ADDED
@@ -0,0 +1,1238 @@
|
|
1
|
+
#--------------------------------------------------------------------
|
2
|
+
# scanner.rb - IDL scanner
|
3
|
+
#
|
4
|
+
# Author: Martin Corino
|
5
|
+
#
|
6
|
+
# This program is free software; you can redistribute it and/or
|
7
|
+
# modify it under the terms of the RIDL LICENSE which is
|
8
|
+
# included with this program.
|
9
|
+
#
|
10
|
+
# Copyright (c) Remedy IT Expertise BV
|
11
|
+
# Chamber of commerce Rotterdam nr.276339, The Netherlands
|
12
|
+
#--------------------------------------------------------------------
|
13
|
+
require 'racc/parser'
|
14
|
+
|
15
|
+
module IDL
|
16
|
+
class ParseError < StandardError
|
17
|
+
attr_reader :positions
|
18
|
+
def initialize(msg, positions)
|
19
|
+
super(msg)
|
20
|
+
@positions = positions
|
21
|
+
end
|
22
|
+
def inspect
|
23
|
+
puts self.class.name+": "+message
|
24
|
+
@positions.each { |pos|
|
25
|
+
print " "
|
26
|
+
puts pos
|
27
|
+
}
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Scanner
|
33
|
+
Position = Struct.new(nil, :name, :line, :column)
|
34
|
+
|
35
|
+
class Position
|
36
|
+
def to_s
|
37
|
+
format("%s: line %d, column %d", name.to_s, line, column)
|
38
|
+
end
|
39
|
+
def inspect
|
40
|
+
to_s
|
41
|
+
end
|
42
|
+
end ## Position
|
43
|
+
|
44
|
+
class In
|
45
|
+
def initialize(src, name = '', line = 0, column = 1)
|
46
|
+
@src, @fwd, @bwd = src, src.getc, nil
|
47
|
+
@pos = Position.new(name, line, column)
|
48
|
+
@mark = nil
|
49
|
+
end
|
50
|
+
def position
|
51
|
+
@pos
|
52
|
+
end
|
53
|
+
def column
|
54
|
+
@pos.column
|
55
|
+
end
|
56
|
+
# cursor set at last gotten character.
|
57
|
+
# ex: after initialization, position is (0,0).
|
58
|
+
def to_s; @src.to_s; end
|
59
|
+
|
60
|
+
def lookc
|
61
|
+
@fwd
|
62
|
+
end
|
63
|
+
|
64
|
+
def getc
|
65
|
+
cur = @fwd
|
66
|
+
@fwd = @src.getc unless @src.nil?
|
67
|
+
@mark << cur unless @mark.nil?
|
68
|
+
if [nil, ?\n, ?\r].include? @bwd
|
69
|
+
if @bwd == ?\r and cur == ?\n
|
70
|
+
else
|
71
|
+
@pos.line += 1
|
72
|
+
@pos.column = 1
|
73
|
+
end
|
74
|
+
else
|
75
|
+
@pos.column += 1
|
76
|
+
end
|
77
|
+
|
78
|
+
if false
|
79
|
+
if not @bwd.nil? or cur.nil? or @fwd.nil?
|
80
|
+
printf("%c(%02x), %c(%02x), %c(%02x) @(l:%d,c:%d)\n",
|
81
|
+
@bwd,@bwd,cur,cur,@fwd,@fwd, @pos.line, @pos.column)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
@bwd = cur
|
85
|
+
end
|
86
|
+
|
87
|
+
def gets
|
88
|
+
return nil if @fwd.nil?
|
89
|
+
|
90
|
+
s = ""
|
91
|
+
s << getc until [nil, ?\n, ?\r].include? lookc
|
92
|
+
s << getc while [?\n, ?\r].include? lookc
|
93
|
+
|
94
|
+
@mark << s unless @mark.nil?
|
95
|
+
s
|
96
|
+
end
|
97
|
+
alias skipc getc
|
98
|
+
|
99
|
+
def _include?(ch, *chars)
|
100
|
+
chars.each { |v|
|
101
|
+
return true if case v
|
102
|
+
when Array
|
103
|
+
_include?(ch, *v)
|
104
|
+
when Enumerable
|
105
|
+
v.include? ch
|
106
|
+
when Fixnum
|
107
|
+
v == ch
|
108
|
+
when String
|
109
|
+
v == ch
|
110
|
+
else
|
111
|
+
false
|
112
|
+
end
|
113
|
+
}
|
114
|
+
return false
|
115
|
+
end
|
116
|
+
|
117
|
+
def skipwhile(*chars, &block)
|
118
|
+
if block.nil?
|
119
|
+
block = Proc.new { |ch| _include?(ch, *chars) }
|
120
|
+
end
|
121
|
+
|
122
|
+
until (ch = lookc).nil?
|
123
|
+
break unless block.call(ch)
|
124
|
+
skipc
|
125
|
+
end
|
126
|
+
ch
|
127
|
+
end
|
128
|
+
|
129
|
+
def skipuntil(*chars, &block)
|
130
|
+
if block.nil?
|
131
|
+
block = Proc.new { |ch| _include?(ch, *chars) }
|
132
|
+
end
|
133
|
+
|
134
|
+
until (ch = lookc).nil?
|
135
|
+
break if block.call(ch)
|
136
|
+
skipc
|
137
|
+
end
|
138
|
+
ch
|
139
|
+
end
|
140
|
+
|
141
|
+
def mark(*ini)
|
142
|
+
@mark = ""
|
143
|
+
ini.each { |i|
|
144
|
+
case i
|
145
|
+
when nil
|
146
|
+
when String
|
147
|
+
@mark << i.dup
|
148
|
+
when Fixnum
|
149
|
+
@mark << i
|
150
|
+
when Array
|
151
|
+
i.each { |j| @mark << j } # array of array is not incoming.
|
152
|
+
end
|
153
|
+
}
|
154
|
+
end
|
155
|
+
|
156
|
+
def getregion
|
157
|
+
ret = @mark
|
158
|
+
@mark = nil
|
159
|
+
return ret
|
160
|
+
end
|
161
|
+
end ## of class In
|
162
|
+
|
163
|
+
class StrIStream
|
164
|
+
def initialize(src)
|
165
|
+
@src = src
|
166
|
+
@i = 0
|
167
|
+
end
|
168
|
+
def to_s
|
169
|
+
@src
|
170
|
+
end
|
171
|
+
def getc
|
172
|
+
ch = @src[@i]
|
173
|
+
@i += 1
|
174
|
+
ch
|
175
|
+
end
|
176
|
+
def close
|
177
|
+
@i = 0
|
178
|
+
end
|
179
|
+
end ## of class StrIStream
|
180
|
+
|
181
|
+
class TokenRegistry < ::Hash
|
182
|
+
def [](key)
|
183
|
+
super(::Symbol === key ? key : key.to_s.to_sym)
|
184
|
+
end
|
185
|
+
def []=(key, val)
|
186
|
+
super(::Symbol === key ? key : key.to_s.to_sym, val.to_s)
|
187
|
+
end
|
188
|
+
def has_key?(key)
|
189
|
+
super(::Symbol === key ? key : key.to_s.to_sym)
|
190
|
+
end
|
191
|
+
def delete(key)
|
192
|
+
super(::Symbol === key ? key : key.to_s.to_sym)
|
193
|
+
end
|
194
|
+
def assoc(key)
|
195
|
+
k_ = (::Symbol === key ? key : key.to_s.to_sym)
|
196
|
+
self.has_key?(k_) ? [k_, self[k_]] : nil
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
class CharRegistry
|
201
|
+
def initialize(table_)
|
202
|
+
@table = table_
|
203
|
+
end
|
204
|
+
def [](key)
|
205
|
+
key = (::Integer === key) ? key.chr.to_sym : key.to_sym
|
206
|
+
@table[key]
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# string derivative for IDL parsed identifiers able
|
211
|
+
# to carry both 'raw' IDL name as well as language mapped
|
212
|
+
# name
|
213
|
+
class Identifier < ::String
|
214
|
+
attr_reader :checked_name
|
215
|
+
def initialize(idl_id, checked_id)
|
216
|
+
super(idl_id)
|
217
|
+
@checked_name = checked_id
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
# Scanner
|
222
|
+
def initialize(src, directiver, params = {})
|
223
|
+
@includepaths = params[:includepaths] || []
|
224
|
+
@stack = []
|
225
|
+
@expansions = []
|
226
|
+
@prefix = nil
|
227
|
+
@directiver = directiver
|
228
|
+
@defined = TokenRegistry.new
|
229
|
+
# initialize with predefined macros
|
230
|
+
if params[:macros]
|
231
|
+
params[:macros].each do |(name, value)|
|
232
|
+
@defined[name] = value
|
233
|
+
end
|
234
|
+
end
|
235
|
+
@ifdef = Array.new
|
236
|
+
@ifskip = false
|
237
|
+
@ifnest = 0
|
238
|
+
i = nil
|
239
|
+
nm = ''
|
240
|
+
case src
|
241
|
+
when String
|
242
|
+
i = StrIStream.new(src)
|
243
|
+
nm = '<string>'
|
244
|
+
when File
|
245
|
+
i = src
|
246
|
+
nm = src.path
|
247
|
+
when IO
|
248
|
+
i = src
|
249
|
+
nm = '<io>'
|
250
|
+
else
|
251
|
+
parse_error "illegal type for input source: #{src.class} "
|
252
|
+
end
|
253
|
+
@in = In.new(i, nm)
|
254
|
+
@scan_comment = false # true if parsing commented annotation
|
255
|
+
@in_annotation = false # true if parsing annotation
|
256
|
+
end
|
257
|
+
def find_include(fname)
|
258
|
+
path = if File.file?(fname) && File.readable?(fname)
|
259
|
+
fname
|
260
|
+
else
|
261
|
+
fp = @includepaths.find do |p|
|
262
|
+
f = p + "/" + fname
|
263
|
+
File.file?(f) && File.readable?(f)
|
264
|
+
end
|
265
|
+
fp += '/' + fname if !fp.nil?
|
266
|
+
fp
|
267
|
+
end
|
268
|
+
end
|
269
|
+
def enter_include(src)
|
270
|
+
if @directiver.is_included?(src)
|
271
|
+
@directiver.declare_include(src)
|
272
|
+
else
|
273
|
+
fpath = find_include(src)
|
274
|
+
if fpath.nil?
|
275
|
+
parse_error "Cannot open include file '#{src}'"
|
276
|
+
end
|
277
|
+
@stack << [:include, @prefix, @ifdef, @in, @ifskip]
|
278
|
+
@prefix = nil
|
279
|
+
@ifdef = Array.new
|
280
|
+
@in = In.new(File.open(fpath, 'r'), fpath)
|
281
|
+
@directiver.enter_include(src)
|
282
|
+
@directiver.pragma_prefix(nil)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
def enter_expansion(src, define)
|
286
|
+
@stack << [:define, nil, nil, @in, nil]
|
287
|
+
@expansions << define
|
288
|
+
@in = In.new(StrIStream.new(src), @in.position.name, @in.position.line, @in.position.column)
|
289
|
+
end
|
290
|
+
def is_expanded?(define)
|
291
|
+
@expansions.include?(define)
|
292
|
+
end
|
293
|
+
def more_source?
|
294
|
+
@stack.size>0
|
295
|
+
end
|
296
|
+
def in_expansion?
|
297
|
+
more_source? and @stack.last[0] == :define
|
298
|
+
end
|
299
|
+
def leave_source()
|
300
|
+
if @stack.size>0
|
301
|
+
if @stack.last[0] == :include
|
302
|
+
@directiver.leave_include
|
303
|
+
type, @prefix, @ifdef, @in, @ifskip = @stack.pop
|
304
|
+
@directiver.pragma_prefix(@prefix)
|
305
|
+
else
|
306
|
+
type, prefix_, ifdef_, @in, elsif_ = @stack.pop
|
307
|
+
@expansions.pop
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
def do_parse?
|
312
|
+
@ifdef.empty? || @ifdef.last
|
313
|
+
end
|
314
|
+
def positions
|
315
|
+
@stack.reverse.inject(@in.nil? ? [] : [@in.position]) {|pos_arr,(type_,pfx_,ifdef_,in_,elsif_)| pos_arr << in_.position }
|
316
|
+
end
|
317
|
+
def parse_error(msg, ex = nil)
|
318
|
+
e = IDL::ParseError.new(msg, positions)
|
319
|
+
e.set_backtrace(ex.backtrace) unless ex.nil?
|
320
|
+
raise e
|
321
|
+
end
|
322
|
+
|
323
|
+
LFCR = [ (?\n), (?\r) ]
|
324
|
+
WHITESPACE = [ (?\ ), (?\t) ].concat(LFCR)
|
325
|
+
|
326
|
+
ANNOTATION = ?@
|
327
|
+
ANNOTATION_STR = '@'
|
328
|
+
|
329
|
+
BREAKCHARS = [
|
330
|
+
?(, ?), ?[, ?], ?{, ?},
|
331
|
+
?^, ?~,
|
332
|
+
?*, ?%, ?&, ?|,
|
333
|
+
?<, ?=, ?>,
|
334
|
+
?,, ?; ]
|
335
|
+
|
336
|
+
SHIFTCHARS = [ ?<, ?> ]
|
337
|
+
|
338
|
+
HEXCHARS = [(?0..?9).to_a, (?a..?f).to_a, (?A..?F).to_a].flatten
|
339
|
+
|
340
|
+
IDCHARS = [?_ , (?a..?z).to_a, (?A..?Z).to_a].flatten
|
341
|
+
|
342
|
+
ESCTBL = CharRegistry.new({
|
343
|
+
:n => ?\n, :t => ?\t, :v => ?\v, :b => ?\b,
|
344
|
+
:r => ?\r, :f => ?\f, :a => ?\a
|
345
|
+
})
|
346
|
+
|
347
|
+
KEYWORDS = [ # see 7.2.4 "Keywords" of CORBA V3.2 specification (part1) and 7.3.6 (part3; IDL3+ keywords)
|
348
|
+
"abstract",
|
349
|
+
"alias",
|
350
|
+
"any",
|
351
|
+
"attribute",
|
352
|
+
"boolean",
|
353
|
+
"case",
|
354
|
+
"char",
|
355
|
+
"component",
|
356
|
+
"connector",
|
357
|
+
"const",
|
358
|
+
"consumes",
|
359
|
+
"context",
|
360
|
+
"custom",
|
361
|
+
"default",
|
362
|
+
"double",
|
363
|
+
"exception",
|
364
|
+
"emits",
|
365
|
+
"enum",
|
366
|
+
"eventtype",
|
367
|
+
"factory",
|
368
|
+
"FALSE",
|
369
|
+
"finder",
|
370
|
+
"fixed",
|
371
|
+
"float",
|
372
|
+
"getraises",
|
373
|
+
"home",
|
374
|
+
"import",
|
375
|
+
"in",
|
376
|
+
"inout",
|
377
|
+
"interface",
|
378
|
+
"local",
|
379
|
+
"long",
|
380
|
+
"manages",
|
381
|
+
"mirrorport",
|
382
|
+
"module",
|
383
|
+
"multiple",
|
384
|
+
"native",
|
385
|
+
"Object",
|
386
|
+
"octet",
|
387
|
+
"oneway",
|
388
|
+
"out",
|
389
|
+
"port",
|
390
|
+
"porttype",
|
391
|
+
"primarykey",
|
392
|
+
"private",
|
393
|
+
"provides",
|
394
|
+
"public",
|
395
|
+
"publishes",
|
396
|
+
"raises",
|
397
|
+
"readonly",
|
398
|
+
"setraises",
|
399
|
+
"sequence",
|
400
|
+
"short",
|
401
|
+
"string",
|
402
|
+
"struct",
|
403
|
+
"supports",
|
404
|
+
"switch",
|
405
|
+
"TRUE",
|
406
|
+
"truncatable",
|
407
|
+
"typedef",
|
408
|
+
"typeid",
|
409
|
+
"typename",
|
410
|
+
"typeprefix",
|
411
|
+
"unsigned",
|
412
|
+
"union",
|
413
|
+
"uses",
|
414
|
+
"ValueBase",
|
415
|
+
"valuetype",
|
416
|
+
"void",
|
417
|
+
"wchar",
|
418
|
+
"wstring",
|
419
|
+
].inject(TokenRegistry.new) { |h,a| h[a.downcase.to_sym] = a; h }
|
420
|
+
|
421
|
+
LITERALS = [
|
422
|
+
:integer_literal,
|
423
|
+
:string_literal,
|
424
|
+
# :wide_string_literal,
|
425
|
+
:character_literal,
|
426
|
+
# :wide_character_literal,
|
427
|
+
:fixed_pt_literal,
|
428
|
+
:floating_pt_literal,
|
429
|
+
:boolean_literal ]
|
430
|
+
|
431
|
+
BOOL_LITERALS = {
|
432
|
+
'false' => false,
|
433
|
+
'true' => true
|
434
|
+
}
|
435
|
+
|
436
|
+
def is_literal?(o)
|
437
|
+
return LITERALS.include?(o)
|
438
|
+
end
|
439
|
+
|
440
|
+
def extract_annotation_value()
|
441
|
+
annotation_value = nil
|
442
|
+
token = next_token # needs '{' (array) or literal or identifier (which means nested annotation object or enum value)
|
443
|
+
if token.first == '{'
|
444
|
+
# extract array of values (literals or identifiers) separated by ','
|
445
|
+
annotation_value = []
|
446
|
+
begin
|
447
|
+
token, ann_value = extract_annotation_value()
|
448
|
+
parse_error 'invalid annotation value array' unless token.first == ',' || token.first == '}'
|
449
|
+
annotation_value << ann_value
|
450
|
+
end until token.first == '}'
|
451
|
+
token = next_token
|
452
|
+
elsif token.first == :identifier
|
453
|
+
member_annotation_id = token.last
|
454
|
+
# get nested body
|
455
|
+
token, member_annotation_body = extract_annotation()
|
456
|
+
# determin vaue type; if it has a body it is an annotation instance
|
457
|
+
if member_annotation_body
|
458
|
+
annotation_value = { member_annotation_id => member_annotation_body }
|
459
|
+
else # otherwise it is a symbolic value
|
460
|
+
annotation_value = member_annotation_id.to_sym
|
461
|
+
end
|
462
|
+
else
|
463
|
+
parse_error 'invalid annotation member' unless is_literal?(token.first)
|
464
|
+
annotation_value = token.last
|
465
|
+
token = next_token
|
466
|
+
end
|
467
|
+
return [token, annotation_value]
|
468
|
+
end
|
469
|
+
|
470
|
+
def extract_annotation()
|
471
|
+
annotation_body = nil
|
472
|
+
# next token should be '(' in case of normal/single value annotation
|
473
|
+
# or anything else in case of marker annotation
|
474
|
+
token = next_token
|
475
|
+
if token.first == '('
|
476
|
+
begin
|
477
|
+
# identifier or value (in case of single value annotation) expected
|
478
|
+
token = next_token
|
479
|
+
if token.first == ')' # marker annotation; leave body empty
|
480
|
+
annotation_body = { }
|
481
|
+
else
|
482
|
+
parse_error 'annotation member expected!' unless token.first == :identifier || is_literal?(token.first)
|
483
|
+
s1 = token.last
|
484
|
+
token = next_token # ')' (in case of single value annotation) or '='
|
485
|
+
if token.first == ')'
|
486
|
+
parse_error 'invalid annotation member' if annotation_body
|
487
|
+
annotation_body = { :value => s1 }
|
488
|
+
else
|
489
|
+
parse_error 'invalid annotation member' unless token.first == '='
|
490
|
+
token, annotation_value = extract_annotation_value()
|
491
|
+
parse_error 'invalid annotation body' unless token.first == ',' || token.first == ')'
|
492
|
+
(annotation_body ||= {})[s1] = annotation_value
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end until token.first == ')'
|
496
|
+
token = next_token # need to get next token
|
497
|
+
else
|
498
|
+
# marker annotation or symbolic value; leave body nil
|
499
|
+
end
|
500
|
+
return [token, annotation_body]
|
501
|
+
end
|
502
|
+
|
503
|
+
def parse_annotation(in_comment = false)
|
504
|
+
@in_annotation = true
|
505
|
+
@scan_comment = in_comment
|
506
|
+
begin
|
507
|
+
# parse (possibly multiple) annotation(s)
|
508
|
+
begin
|
509
|
+
# next token should be identifier (must be on same line following '@')
|
510
|
+
token = next_token
|
511
|
+
parse_error 'annotation identifier expected!' unless token.first == :identifier
|
512
|
+
annotation_id = token.last
|
513
|
+
token, annotation_body = extract_annotation()
|
514
|
+
# pass annotation to directiver for processing
|
515
|
+
@directiver.define_annotation(annotation_id, annotation_body || {})
|
516
|
+
end until token.first != ANNOTATION_STR
|
517
|
+
ensure
|
518
|
+
@in_annotation = false
|
519
|
+
@scan_comment = false
|
520
|
+
end
|
521
|
+
# check identifier for keywords
|
522
|
+
if token.first == :identifier
|
523
|
+
# keyword check
|
524
|
+
if (a = KEYWORDS.assoc(token.last)).nil?
|
525
|
+
token = [ :identifier, Identifier.new(token.last, chk_identifier(token.last)) ]
|
526
|
+
elsif token.last == a[1]
|
527
|
+
token = [ a[1], nil ]
|
528
|
+
else
|
529
|
+
parse_error "`#{token.last}' collides with a keyword `#{a[1]}'"
|
530
|
+
end
|
531
|
+
end
|
532
|
+
return token
|
533
|
+
end
|
534
|
+
|
535
|
+
def next_identifier(first = nil)
|
536
|
+
@in.mark(first)
|
537
|
+
while TRUE
|
538
|
+
case @in.lookc
|
539
|
+
when nil
|
540
|
+
break
|
541
|
+
when ?0..?9, ?a..?z, ?A..?Z, ?_
|
542
|
+
@in.skipc
|
543
|
+
else
|
544
|
+
break
|
545
|
+
end
|
546
|
+
end
|
547
|
+
s0 = @in.getregion
|
548
|
+
s1 = s0.downcase
|
549
|
+
|
550
|
+
# simple check
|
551
|
+
if (s0.length == 0)
|
552
|
+
parse_error "identifier expected!"
|
553
|
+
else
|
554
|
+
case s0[0]
|
555
|
+
when ?a..?z, ?A..?Z
|
556
|
+
when ?_ ## if starts with CORBA IDL escape => remove
|
557
|
+
s0.slice!(0)
|
558
|
+
else
|
559
|
+
parse_error "identifier must begin with alphabet character: #{s0}"
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
# preprocessor check
|
564
|
+
if @defined.has_key?(s0) and !is_expanded?(s0)
|
565
|
+
# enter expansion as new source
|
566
|
+
enter_expansion(@defined[s0], s0)
|
567
|
+
# call next_token to parse expanded source
|
568
|
+
next_token
|
569
|
+
# keyword check
|
570
|
+
elsif @in_annotation
|
571
|
+
if BOOL_LITERALS.has_key?(s1)
|
572
|
+
[ :boolean_literal, BOOL_LITERALS[s1] ]
|
573
|
+
else
|
574
|
+
[ :identifier, s0 ]
|
575
|
+
end
|
576
|
+
elsif (a = KEYWORDS.assoc(s1)).nil?
|
577
|
+
# check for language mapping keyword except when
|
578
|
+
# - this is an IDL escaped ('_' prefix) identifier
|
579
|
+
[ :identifier, Identifier.new(s0, s1[0] == ?_ ? s0 : chk_identifier(s0)) ]
|
580
|
+
elsif s0 == a[1]
|
581
|
+
[ a[1], nil ]
|
582
|
+
else
|
583
|
+
parse_error "`#{s0}' collides with a keyword `#{a[1]}'"
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
587
|
+
def next_escape
|
588
|
+
ret = 0
|
589
|
+
case (ch = @in.getc)
|
590
|
+
when nil
|
591
|
+
parse_error 'illegal escape sequence'
|
592
|
+
when ?0..?7
|
593
|
+
ret = ""
|
594
|
+
ret << ch
|
595
|
+
1.upto(2) {
|
596
|
+
ch = @in.lookc
|
597
|
+
if (?0..?7).include? ch
|
598
|
+
ret << ch
|
599
|
+
else
|
600
|
+
break
|
601
|
+
end
|
602
|
+
@in.skipc
|
603
|
+
}
|
604
|
+
ret = ret.oct
|
605
|
+
when ?x # i'm not sure '\x' should be 0 or 'x'. currently returns 0.
|
606
|
+
ret = ""
|
607
|
+
1.upto(2) {
|
608
|
+
ch = @in.lookc
|
609
|
+
if HEXCHARS.include? ch
|
610
|
+
ret << ch
|
611
|
+
else
|
612
|
+
break
|
613
|
+
end
|
614
|
+
@in.skipc
|
615
|
+
}
|
616
|
+
ret = ret.hex
|
617
|
+
when ?u
|
618
|
+
ret = ""
|
619
|
+
1.upto(4) {
|
620
|
+
ch = @in.lookc
|
621
|
+
if HEXCHARS.include? ch
|
622
|
+
ret << ch
|
623
|
+
else
|
624
|
+
break
|
625
|
+
end
|
626
|
+
@in.skipc
|
627
|
+
}
|
628
|
+
ret = ret.hex
|
629
|
+
when ?n, ?t, ?v, ?b, ?r, ?f, ?a
|
630
|
+
ret = ESCTBL[ch]
|
631
|
+
else
|
632
|
+
ret = ('' << ch).unpack('C').first
|
633
|
+
end
|
634
|
+
return ret
|
635
|
+
end
|
636
|
+
|
637
|
+
def next_escape_str(keep_type_ch = false)
|
638
|
+
ret = 0
|
639
|
+
case (ch = @in.getc)
|
640
|
+
when nil
|
641
|
+
parse_error 'illegal escape sequence'
|
642
|
+
when ?0..?7
|
643
|
+
ret = ""
|
644
|
+
ret << ch
|
645
|
+
1.upto(2) {
|
646
|
+
ch = @in.lookc
|
647
|
+
if (?0..?7).include? ch
|
648
|
+
ret << ch
|
649
|
+
else
|
650
|
+
break
|
651
|
+
end
|
652
|
+
@in.skipc
|
653
|
+
}
|
654
|
+
ret = [ :oct, ret ]
|
655
|
+
when ?x # i'm not sure '\x' should be 0 or 'x'. currently returns 0.
|
656
|
+
ret = ""
|
657
|
+
ret << ch if keep_type_ch
|
658
|
+
1.upto(2) {
|
659
|
+
ch = @in.lookc
|
660
|
+
if HEXCHARS.include? ch
|
661
|
+
ret << ch
|
662
|
+
else
|
663
|
+
break
|
664
|
+
end
|
665
|
+
@in.skipc
|
666
|
+
}
|
667
|
+
ret = [ :hex2, ret ]
|
668
|
+
when ?u
|
669
|
+
ret = ""
|
670
|
+
ret << ch if keep_type_ch
|
671
|
+
1.upto(4) {
|
672
|
+
ch = @in.lookc
|
673
|
+
if HEXCHARS.include? ch
|
674
|
+
ret << ch
|
675
|
+
else
|
676
|
+
break
|
677
|
+
end
|
678
|
+
@in.skipc
|
679
|
+
}
|
680
|
+
ret = [ :hex4, ret ]
|
681
|
+
when ?n, ?t, ?v, ?b, ?r, ?f, ?a
|
682
|
+
ret = ''
|
683
|
+
ret << ch
|
684
|
+
ret = [ :esc, ret ]
|
685
|
+
else
|
686
|
+
ret = ''
|
687
|
+
ret << ch
|
688
|
+
ret = [ :esc_ch, ch ]
|
689
|
+
end
|
690
|
+
return ret
|
691
|
+
end
|
692
|
+
|
693
|
+
def skipfloat_or_fixed
|
694
|
+
if (@in.lookc == ?.)
|
695
|
+
@in.skipc
|
696
|
+
@in.skipwhile(?0..?9)
|
697
|
+
end
|
698
|
+
if [?e, ?E].include? @in.lookc
|
699
|
+
@in.skipc
|
700
|
+
@in.skipc if [?+, ?-].include? @in.lookc
|
701
|
+
@in.skipwhile(?0..?9)
|
702
|
+
return :floating_pt_literal
|
703
|
+
elsif [?d, ?D].include? @in.lookc
|
704
|
+
@in.skipc
|
705
|
+
@in.skipc if [?+, ?-].include? @in.lookc
|
706
|
+
@in.skipwhile(?0..?9)
|
707
|
+
return :fixed_pt_literal
|
708
|
+
end
|
709
|
+
return :floating_pt_literal
|
710
|
+
end
|
711
|
+
|
712
|
+
def skipline
|
713
|
+
while TRUE
|
714
|
+
s = @in.gets
|
715
|
+
until s.chomp!.nil?; end
|
716
|
+
break unless s[s.length - 1] == ?\\
|
717
|
+
end
|
718
|
+
end
|
719
|
+
|
720
|
+
def getline
|
721
|
+
s = ""
|
722
|
+
while TRUE
|
723
|
+
ch = @in.lookc
|
724
|
+
break if ch.nil?
|
725
|
+
case
|
726
|
+
when (ch == ?\") #"
|
727
|
+
s << @in.getc # opening quote
|
728
|
+
while TRUE
|
729
|
+
if @in.lookc == ?\\
|
730
|
+
# escape sequence
|
731
|
+
s << @in.getc
|
732
|
+
esctyp, escstr = next_escape_str(true)
|
733
|
+
s << escstr
|
734
|
+
elsif @in.lookc == ?\" #"
|
735
|
+
break
|
736
|
+
elsif @in.lookc
|
737
|
+
# normal character
|
738
|
+
s << @in.getc
|
739
|
+
else
|
740
|
+
parse_error "unterminated string literal"
|
741
|
+
end
|
742
|
+
end
|
743
|
+
s << @in.getc # closing quote
|
744
|
+
when (ch == ?\') #' # quoted character
|
745
|
+
s << @in.getc # opening quote
|
746
|
+
if @in.lookc == ?\\
|
747
|
+
# escape sequence
|
748
|
+
s << @in.getc
|
749
|
+
esctyp, escstr = next_escape_str(true)
|
750
|
+
s << escstr
|
751
|
+
elsif @in.lookc && @in.lookc != ?\' #'
|
752
|
+
# normal character
|
753
|
+
s << @in.getc
|
754
|
+
end
|
755
|
+
if @in.lookc != ?\' #'
|
756
|
+
parse_error "character literal must be single character enclosed in \"'\""
|
757
|
+
end
|
758
|
+
s << @in.getc # closing quote
|
759
|
+
when LFCR.include?(ch)
|
760
|
+
@in.skipwhile() { |ch_| LFCR.include? ch_ }
|
761
|
+
break
|
762
|
+
when ch == ?/
|
763
|
+
@in.skipc
|
764
|
+
if @in.lookc == ?/
|
765
|
+
# //-style comment; skip till eol
|
766
|
+
@in.gets
|
767
|
+
break
|
768
|
+
elsif @in.lookc == ?*
|
769
|
+
# /*...*/ style comment; skip comment
|
770
|
+
ch1 = nil
|
771
|
+
@in.skipuntil { |ch_|
|
772
|
+
ch0 = ch1; ch1 = ch_
|
773
|
+
ch0 == ?* and ch1 == ?/ #
|
774
|
+
}
|
775
|
+
if @in.lookc.nil?
|
776
|
+
parse_error "cannot find comment closing brace (\'*/\'). "
|
777
|
+
end
|
778
|
+
@in.skipc
|
779
|
+
else
|
780
|
+
s << ch
|
781
|
+
end
|
782
|
+
when ch == ?\\
|
783
|
+
@in.skipc
|
784
|
+
if LFCR.include?(@in.lookc)
|
785
|
+
# line continuation
|
786
|
+
@in.skipwhile() { |ch_| LFCR.include? ch_ }
|
787
|
+
if @in.lookc.nil?
|
788
|
+
parse_error "line continuation character ('\\') not allowed as last character in file."
|
789
|
+
end
|
790
|
+
else
|
791
|
+
s << ch
|
792
|
+
end
|
793
|
+
else
|
794
|
+
@in.skipc
|
795
|
+
s << ch
|
796
|
+
end
|
797
|
+
end
|
798
|
+
s
|
799
|
+
end
|
800
|
+
|
801
|
+
def resolve_define(id, stack = [])
|
802
|
+
return id if ['true', 'false'].include?(id)
|
803
|
+
IDL.log(3,"*** RIDL - resolve_define(#{id})")
|
804
|
+
if @defined.has_key?(id)
|
805
|
+
define_ = @defined[id]
|
806
|
+
stack << id
|
807
|
+
parse_error("circular macro reference detected for [#{define_}]") if stack.include?(define_)
|
808
|
+
# resolve any nested macro definitions
|
809
|
+
define_.gsub(/(^|[\W])([A-Za-z_][\w]*)/) do |m_| "#{$1}#{resolve_define($2, stack)}" end
|
810
|
+
else
|
811
|
+
'0' # unknown id
|
812
|
+
end
|
813
|
+
end
|
814
|
+
|
815
|
+
def eval_directive(s)
|
816
|
+
IDL.log(2,"** RIDL - eval_directive(#{s})")
|
817
|
+
rc = eval(s)
|
818
|
+
case rc
|
819
|
+
when FalseClass, TrueClass
|
820
|
+
rc
|
821
|
+
when Numeric
|
822
|
+
rc != 0
|
823
|
+
else
|
824
|
+
parse_error "invalid preprocessor expression."
|
825
|
+
end
|
826
|
+
end
|
827
|
+
|
828
|
+
def parse_directive
|
829
|
+
@in.skipwhile(?\ , ?\t)
|
830
|
+
s = getline
|
831
|
+
/^(\w*)\s*/ === s
|
832
|
+
s1,s2 = $1, $' #'
|
833
|
+
|
834
|
+
if /(else|endif|elif)/ === s1
|
835
|
+
|
836
|
+
if @ifdef.empty?
|
837
|
+
parse_error "#else/#elif/#endif must not appear without preceding #if"
|
838
|
+
end
|
839
|
+
case s1
|
840
|
+
when 'else'
|
841
|
+
if @ifnest == 0
|
842
|
+
if @ifskip # true branch has already been parsed
|
843
|
+
@ifdef[@ifdef.size - 1] = false
|
844
|
+
else
|
845
|
+
@ifdef[@ifdef.size - 1] ^= true;
|
846
|
+
@ifskip = @ifdef.last
|
847
|
+
end
|
848
|
+
end
|
849
|
+
when 'endif'
|
850
|
+
if @ifnest == 0
|
851
|
+
@ifdef.pop
|
852
|
+
@ifskip = @ifdef.last
|
853
|
+
else
|
854
|
+
@ifnest -= 1
|
855
|
+
end
|
856
|
+
else
|
857
|
+
if @ifnest == 0
|
858
|
+
if @ifskip || @ifdef[@ifdef.size - 1]
|
859
|
+
# true branch has already been parsed so skip from now on
|
860
|
+
@ifdef[@ifdef.size - 1] = false
|
861
|
+
@ifskip = true
|
862
|
+
else
|
863
|
+
while s2 =~ /(^|[\W])defined\s*\(\s*(\w+)\s*\)/
|
864
|
+
def_id = $2
|
865
|
+
s2.gsub!(/(^|[\W])(defined\s*\(\s*\w+\s*\))/, '\1'+"#{@defined.has_key?(def_id).to_s}")
|
866
|
+
end
|
867
|
+
s2.gsub!(/(^|[\W])([A-Za-z_][\w]*)/) do |m_| "#{$1}#{resolve_define($2)}" end
|
868
|
+
begin
|
869
|
+
@ifdef[@ifdef.size - 1] = eval_directive(s2)
|
870
|
+
@ifskip = @ifdef[@ifdef.size - 1]
|
871
|
+
rescue IDL::ParseError
|
872
|
+
raise
|
873
|
+
rescue => ex
|
874
|
+
p ex
|
875
|
+
puts ex.backtrace.join("\n")
|
876
|
+
parse_error "error evaluating #elif"
|
877
|
+
end
|
878
|
+
end
|
879
|
+
end
|
880
|
+
end
|
881
|
+
|
882
|
+
elsif /(if|ifn?def)/ === s1
|
883
|
+
|
884
|
+
case s1
|
885
|
+
when /ifn?def/
|
886
|
+
if do_parse?
|
887
|
+
if not (/^(\w+)/ === s2)
|
888
|
+
parse_error "no #if(n)def target."
|
889
|
+
end
|
890
|
+
@ifdef.push(@defined[$1].nil? ^ (s1 == "ifdef"))
|
891
|
+
@ifskip = @ifdef.last
|
892
|
+
else
|
893
|
+
@ifnest += 1
|
894
|
+
end
|
895
|
+
|
896
|
+
when 'if'
|
897
|
+
if do_parse?
|
898
|
+
while s2 =~ /(^|[\W])defined\s*\(\s*(\w+)\s*\)/
|
899
|
+
def_id = $2
|
900
|
+
s2.gsub!(/(^|[\W])(defined\s*\(\s*\w+\s*\))/, '\1'+"#{@defined.has_key?(def_id).to_s}")
|
901
|
+
end
|
902
|
+
s2.gsub!(/(^|[\W])([A-Za-z_][\w]*)/) do |m_| "#{$1}#{resolve_define($2)}" end
|
903
|
+
begin
|
904
|
+
@ifdef.push(eval_directive(s2))
|
905
|
+
@ifskip = @ifdef.last
|
906
|
+
rescue IDL::ParseError
|
907
|
+
raise
|
908
|
+
rescue => ex
|
909
|
+
p ex
|
910
|
+
puts ex.backtrace.join("\n")
|
911
|
+
parse_error "error evaluating #if"
|
912
|
+
end
|
913
|
+
else
|
914
|
+
@ifnest += 1
|
915
|
+
end
|
916
|
+
end
|
917
|
+
|
918
|
+
elsif do_parse?
|
919
|
+
|
920
|
+
case s1
|
921
|
+
when 'pragma'
|
922
|
+
parse_pragma(s2)
|
923
|
+
|
924
|
+
when 'error'
|
925
|
+
parse_error(s2)
|
926
|
+
|
927
|
+
when 'define'
|
928
|
+
a = s2.split
|
929
|
+
a[1] = true if a[1].nil?
|
930
|
+
if a[0].nil?
|
931
|
+
parse_error "no #define target."
|
932
|
+
elsif not @defined[a[0]].nil?
|
933
|
+
parse_error "#{a[0]} is already #define-d."
|
934
|
+
end
|
935
|
+
@defined[a[0]] = a[1]
|
936
|
+
|
937
|
+
when 'undef'
|
938
|
+
@defined.delete(s2)
|
939
|
+
|
940
|
+
when 'include'
|
941
|
+
if s2[0,1] == '"' || s2[0,1] == '<'
|
942
|
+
if s2.size>2
|
943
|
+
s2.strip!
|
944
|
+
s2 = s2.slice(1..(s2.size-2))
|
945
|
+
else
|
946
|
+
s2 = ""
|
947
|
+
end
|
948
|
+
end
|
949
|
+
enter_include(s2)
|
950
|
+
|
951
|
+
when /[0-9]+/
|
952
|
+
# ignore line directive
|
953
|
+
else
|
954
|
+
parse_error "unknown directive: #{s}."
|
955
|
+
end
|
956
|
+
end
|
957
|
+
end
|
958
|
+
|
959
|
+
def parse_pragma(s)
|
960
|
+
case s
|
961
|
+
when /^ID\s+(.*)\s+"(.*)"\s*$/
|
962
|
+
@directiver.pragma_id($1.strip, $2)
|
963
|
+
when /^version\s+(.*)\s+([0-9]+)\.([0-9]+)\s*$/
|
964
|
+
@directiver.pragma_version($1.strip, $2, $3)
|
965
|
+
when /^prefix\s+"(.*)"\s*$/
|
966
|
+
@prefix = $1
|
967
|
+
@directiver.pragma_prefix(@prefix)
|
968
|
+
else
|
969
|
+
@directiver.handle_pragma(s)
|
970
|
+
end
|
971
|
+
end
|
972
|
+
|
973
|
+
def next_token
|
974
|
+
sign = nil
|
975
|
+
str = "" #initialize empty string
|
976
|
+
while TRUE
|
977
|
+
ch = @in.getc
|
978
|
+
if ch.nil?
|
979
|
+
if @ifdef.size>0 and !in_expansion?
|
980
|
+
parse_error "mismatched #if/#endif"
|
981
|
+
end
|
982
|
+
if more_source?
|
983
|
+
leave_source
|
984
|
+
next
|
985
|
+
else
|
986
|
+
return [FALSE, nil]
|
987
|
+
end
|
988
|
+
end
|
989
|
+
|
990
|
+
if WHITESPACE.include? ch
|
991
|
+
@in.skipwhile( WHITESPACE )
|
992
|
+
next
|
993
|
+
end
|
994
|
+
|
995
|
+
if str.empty? && ch == ?\#
|
996
|
+
parse_directive
|
997
|
+
next
|
998
|
+
end
|
999
|
+
unless do_parse?
|
1000
|
+
skipline
|
1001
|
+
next
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
str << ch
|
1005
|
+
case
|
1006
|
+
when BREAKCHARS.include?(ch)
|
1007
|
+
if SHIFTCHARS.include?(ch) && @in.lookc == ch
|
1008
|
+
# '<<' or '>>'
|
1009
|
+
str << @in.getc
|
1010
|
+
end
|
1011
|
+
return [str, str]
|
1012
|
+
|
1013
|
+
when ch == ANNOTATION
|
1014
|
+
if @in_annotation
|
1015
|
+
return [str, str]
|
1016
|
+
else
|
1017
|
+
return parse_annotation()
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
when ch == ?: #
|
1021
|
+
if @in.lookc == ?: #
|
1022
|
+
@in.skipc
|
1023
|
+
return ["::", "::"]
|
1024
|
+
else
|
1025
|
+
return [":", ":"]
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
when ch == ?L
|
1029
|
+
_nxtc = @in.lookc
|
1030
|
+
if _nxtc == ?\' #' #single quote, for a character literal.
|
1031
|
+
ret = 0
|
1032
|
+
@in.skipc # skip 'L'
|
1033
|
+
_nxtc = @in.lookc
|
1034
|
+
if _nxtc == ?\\
|
1035
|
+
@in.skipc
|
1036
|
+
ret = next_escape_str
|
1037
|
+
elsif _nxtc == ?\' #'
|
1038
|
+
ret = [ nil, nil ]
|
1039
|
+
else
|
1040
|
+
ret = ''
|
1041
|
+
ret << @in.getc
|
1042
|
+
ret = [ :char, ret ]
|
1043
|
+
end
|
1044
|
+
|
1045
|
+
if @in.lookc != ?\' #'
|
1046
|
+
parse_error "wide character literal must be single wide character enclosed in \"'\""
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
@in.skipc
|
1050
|
+
return [ :wide_character_literal, ret ]
|
1051
|
+
|
1052
|
+
elsif _nxtc == ?\" #" #double quote, for a string literal.
|
1053
|
+
ret = []
|
1054
|
+
chs = ''
|
1055
|
+
@in.skipc # skip 'L'
|
1056
|
+
while TRUE
|
1057
|
+
_nxtc = @in.lookc
|
1058
|
+
if _nxtc == ?\\
|
1059
|
+
@in.skipc
|
1060
|
+
ret << [:char, chs] unless chs.empty?
|
1061
|
+
chs = ''
|
1062
|
+
ret << next_escape_str
|
1063
|
+
elsif _nxtc == ?\" #"
|
1064
|
+
@in.skipc
|
1065
|
+
ret << [:char, chs] unless chs.empty?
|
1066
|
+
return [ :wide_string_literal, ret ]
|
1067
|
+
else
|
1068
|
+
chs << @in.getc
|
1069
|
+
end
|
1070
|
+
end
|
1071
|
+
|
1072
|
+
else
|
1073
|
+
return next_identifier(ch)
|
1074
|
+
end
|
1075
|
+
|
1076
|
+
when IDCHARS.include?(ch)
|
1077
|
+
return next_identifier(ch)
|
1078
|
+
|
1079
|
+
when ch == ?/ #
|
1080
|
+
_nxtc = @in.lookc
|
1081
|
+
if _nxtc == ?*
|
1082
|
+
# skip comment like a `/* ... */'
|
1083
|
+
@in.skipc # forward stream beyond `/*'
|
1084
|
+
ch1 = nil
|
1085
|
+
@in.skipuntil { |ch_|
|
1086
|
+
ch0 = ch1; ch1 = ch_
|
1087
|
+
ch0 == ?* and ch1 == ?/ #
|
1088
|
+
}
|
1089
|
+
if @in.lookc.nil?
|
1090
|
+
parse_error "cannot find comment closing brace (\'*/\'). "
|
1091
|
+
end
|
1092
|
+
@in.skipc
|
1093
|
+
str = "" # reset
|
1094
|
+
next
|
1095
|
+
|
1096
|
+
elsif _nxtc == ?/
|
1097
|
+
# skip comment like a `// ...\n'
|
1098
|
+
@in.skipc
|
1099
|
+
unless @scan_comment # scan_comment will be true when parsing commented annotations
|
1100
|
+
_nxtc = @in.lookc
|
1101
|
+
if _nxtc == ANNOTATION
|
1102
|
+
@in.skipc
|
1103
|
+
return parse_annotation(true)
|
1104
|
+
else
|
1105
|
+
@in.skipuntil(?\n, ?\r)
|
1106
|
+
end
|
1107
|
+
end
|
1108
|
+
str = "" # reset
|
1109
|
+
next
|
1110
|
+
|
1111
|
+
else
|
1112
|
+
return [ "/", "/" ]
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
when ch == ?+ || ch == ?-
|
1116
|
+
_nxtc = @in.lookc
|
1117
|
+
if (?0..?9).include? _nxtc
|
1118
|
+
sign = ch
|
1119
|
+
str = "" # reset
|
1120
|
+
next
|
1121
|
+
else
|
1122
|
+
return [str, str]
|
1123
|
+
end
|
1124
|
+
|
1125
|
+
when (?1..?9).include?(ch)
|
1126
|
+
@in.mark(sign, ch)
|
1127
|
+
sign = nil
|
1128
|
+
@in.skipwhile(?0..?9)
|
1129
|
+
num_type = ([?., ?e, ?E, ?d, ?D].include?(@in.lookc)) ? skipfloat_or_fixed : :integer_literal
|
1130
|
+
|
1131
|
+
r = @in.getregion
|
1132
|
+
|
1133
|
+
if num_type == :floating_pt_literal
|
1134
|
+
return [:floating_pt_literal, r.to_f]
|
1135
|
+
elsif num_type == :fixed_pt_literal
|
1136
|
+
return [:fixed_pt_literal, r]
|
1137
|
+
else
|
1138
|
+
return [:integer_literal, r.to_i]
|
1139
|
+
end
|
1140
|
+
|
1141
|
+
when ch == ?. #
|
1142
|
+
@in.mark(ch)
|
1143
|
+
@in.skipwhile(?0..?9)
|
1144
|
+
num_type = (?. != @in.lookc) ? skipfloat_or_fixed : nil
|
1145
|
+
s = @in.getregion
|
1146
|
+
if s == "."
|
1147
|
+
parse_error "token consisting of single dot (.) is invalid."
|
1148
|
+
end
|
1149
|
+
if num_type == :floating_pt_literal
|
1150
|
+
return [:floating_pt_literal, s.to_f]
|
1151
|
+
elsif num_type == :fixed_pt_literal
|
1152
|
+
return [:fixed_pt_literal, s]
|
1153
|
+
else
|
1154
|
+
parse_error "invalid floating point constant."
|
1155
|
+
end
|
1156
|
+
|
1157
|
+
when ch == ?0
|
1158
|
+
@in.mark(sign, ch)
|
1159
|
+
sign = nil
|
1160
|
+
|
1161
|
+
_nxtc = @in.lookc
|
1162
|
+
if _nxtc == ?x || _nxtc == ?X
|
1163
|
+
@in.skipc
|
1164
|
+
@in.skipwhile() { |ch_| HEXCHARS.include? ch_ }
|
1165
|
+
s = @in.getregion
|
1166
|
+
return [:integer_literal, s.hex]
|
1167
|
+
|
1168
|
+
else
|
1169
|
+
dec = FALSE
|
1170
|
+
@in.skipwhile(?0..?7)
|
1171
|
+
if (?8..?9).include? @in.lookc
|
1172
|
+
dec = TRUE
|
1173
|
+
@in.skipwhile(?0..?9)
|
1174
|
+
end
|
1175
|
+
|
1176
|
+
num_type = ([?., ?e, ?E, ?d, ?D].include?(@in.lookc)) ? skipfloat_or_fixed : :integer_literal
|
1177
|
+
|
1178
|
+
ret = nil
|
1179
|
+
s = @in.getregion
|
1180
|
+
if num_type == :floating_pt_literal
|
1181
|
+
ret = [:floating_pt_literal, s.to_f]
|
1182
|
+
elsif num_type == :fixed_pt_literal
|
1183
|
+
ret = [:fixed_pt_literal, s]
|
1184
|
+
elsif dec
|
1185
|
+
parse_error "decimal literal starting with '0' should be octal ('0'..'7' only): #{s}"
|
1186
|
+
else
|
1187
|
+
ret = [:integer_literal, s.oct]
|
1188
|
+
end
|
1189
|
+
return ret
|
1190
|
+
end
|
1191
|
+
|
1192
|
+
when ch == ?\' #' #single quote, for a character literal.
|
1193
|
+
ret = 0
|
1194
|
+
_nxtc = @in.lookc
|
1195
|
+
if _nxtc == ?\\
|
1196
|
+
@in.skipc
|
1197
|
+
ret = next_escape
|
1198
|
+
elsif _nxtc == ?\' #'
|
1199
|
+
ret = 0
|
1200
|
+
elsif _nxtc
|
1201
|
+
ret = ('' << @in.getc).unpack('C').first
|
1202
|
+
end
|
1203
|
+
|
1204
|
+
if @in.lookc != ?\' #'
|
1205
|
+
parse_error "character literal must be single character enclosed in \"'\""
|
1206
|
+
end
|
1207
|
+
|
1208
|
+
@in.skipc
|
1209
|
+
return [ :character_literal, ret ]
|
1210
|
+
|
1211
|
+
when ch == ?\" #" #double quote, for a string literal.
|
1212
|
+
ret = ""
|
1213
|
+
while TRUE
|
1214
|
+
_nxtc = @in.lookc
|
1215
|
+
if _nxtc == ?\\
|
1216
|
+
@in.skipc
|
1217
|
+
ret << next_escape
|
1218
|
+
elsif _nxtc == ?\" #"
|
1219
|
+
@in.skipc
|
1220
|
+
return [ :string_literal, ret ]
|
1221
|
+
elsif _nxtc
|
1222
|
+
ret << @in.getc
|
1223
|
+
else
|
1224
|
+
parse_error "unterminated string literal"
|
1225
|
+
end
|
1226
|
+
end
|
1227
|
+
|
1228
|
+
else
|
1229
|
+
parse_error 'illegal character [' << ch << ']'
|
1230
|
+
|
1231
|
+
end #of case
|
1232
|
+
|
1233
|
+
end #of while
|
1234
|
+
parse_error "unexcepted error"
|
1235
|
+
end #of method next_token
|
1236
|
+
end
|
1237
|
+
|
1238
|
+
end
|