ridl 2.2.4
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.
- 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
|