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.
@@ -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'
@@ -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
@@ -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
@@ -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