ridl 2.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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