rucc 0.1.0
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/.gitignore +55 -0
- data/.rspec +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +46 -0
- data/LICENCE +21 -0
- data/README.md +82 -0
- data/Rakefile +2 -0
- data/Vagrantfile +10 -0
- data/bin/console +10 -0
- data/bin/rspec +2 -0
- data/bin/setup +8 -0
- data/exe/rucc +7 -0
- data/include/8cc.h +48 -0
- data/include/float.h +44 -0
- data/include/iso646.h +20 -0
- data/include/rucc.h +2 -0
- data/include/stdalign.h +11 -0
- data/include/stdarg.h +52 -0
- data/include/stdbool.h +11 -0
- data/include/stddef.h +15 -0
- data/include/stdnoreturn.h +8 -0
- data/lib/rucc.rb +8 -0
- data/lib/rucc/case.rb +22 -0
- data/lib/rucc/decl.rb +9 -0
- data/lib/rucc/enc.rb +9 -0
- data/lib/rucc/engine.rb +138 -0
- data/lib/rucc/file_io.rb +108 -0
- data/lib/rucc/file_io_list.rb +56 -0
- data/lib/rucc/gen.rb +1602 -0
- data/lib/rucc/int_evaluator.rb +114 -0
- data/lib/rucc/k.rb +73 -0
- data/lib/rucc/keyword.rb +17 -0
- data/lib/rucc/kind.rb +43 -0
- data/lib/rucc/label_gen.rb +13 -0
- data/lib/rucc/lexer.rb +40 -0
- data/lib/rucc/lexer/impl.rb +683 -0
- data/lib/rucc/lexer/preprocessor.rb +888 -0
- data/lib/rucc/lexer/preprocessor/cond_incl.rb +27 -0
- data/lib/rucc/lexer/preprocessor/constructor.rb +54 -0
- data/lib/rucc/lexer/preprocessor/pragma.rb +31 -0
- data/lib/rucc/lexer/preprocessor/special_macro.rb +110 -0
- data/lib/rucc/libc.rb +47 -0
- data/lib/rucc/m.rb +7 -0
- data/lib/rucc/macro.rb +24 -0
- data/lib/rucc/node.rb +530 -0
- data/lib/rucc/node/conv.rb +33 -0
- data/lib/rucc/op.rb +61 -0
- data/lib/rucc/operator.rb +13 -0
- data/lib/rucc/option.rb +30 -0
- data/lib/rucc/parser.rb +961 -0
- data/lib/rucc/parser/break.rb +18 -0
- data/lib/rucc/parser/builtin.rb +25 -0
- data/lib/rucc/parser/continue.rb +18 -0
- data/lib/rucc/parser/do.rb +33 -0
- data/lib/rucc/parser/ensure.rb +39 -0
- data/lib/rucc/parser/enum.rb +64 -0
- data/lib/rucc/parser/expr.rb +493 -0
- data/lib/rucc/parser/for.rb +71 -0
- data/lib/rucc/parser/func.rb +274 -0
- data/lib/rucc/parser/func_call.rb +54 -0
- data/lib/rucc/parser/goto.rb +29 -0
- data/lib/rucc/parser/if.rb +23 -0
- data/lib/rucc/parser/initializer.rb +237 -0
- data/lib/rucc/parser/label.rb +31 -0
- data/lib/rucc/parser/return.rb +16 -0
- data/lib/rucc/parser/struct_and_union.rb +280 -0
- data/lib/rucc/parser/switch.rb +117 -0
- data/lib/rucc/parser/while.rb +29 -0
- data/lib/rucc/pos.rb +11 -0
- data/lib/rucc/rmap.rb +22 -0
- data/lib/rucc/s.rb +9 -0
- data/lib/rucc/static_label_gen.rb +15 -0
- data/lib/rucc/t.rb +18 -0
- data/lib/rucc/tempname_gen.rb +14 -0
- data/lib/rucc/token.rb +114 -0
- data/lib/rucc/token_gen.rb +68 -0
- data/lib/rucc/type.rb +304 -0
- data/lib/rucc/type/check.rb +39 -0
- data/lib/rucc/type/conv.rb +29 -0
- data/lib/rucc/type_info.rb +21 -0
- data/lib/rucc/utf.rb +126 -0
- data/lib/rucc/util.rb +111 -0
- data/lib/rucc/version.rb +3 -0
- data/rucc.gemspec +38 -0
- metadata +201 -0
@@ -0,0 +1,888 @@
|
|
1
|
+
require "set"
|
2
|
+
require "rucc/m"
|
3
|
+
require "rucc/macro"
|
4
|
+
require "rucc/lexer/preprocessor/cond_incl"
|
5
|
+
require "rucc/lexer/preprocessor/constructor"
|
6
|
+
require "rucc/lexer/preprocessor/pragma"
|
7
|
+
require "rucc/lexer/preprocessor/special_macro"
|
8
|
+
|
9
|
+
module Rucc
|
10
|
+
class Lexer
|
11
|
+
class Preprocessor
|
12
|
+
include Constructor
|
13
|
+
include SpecialMacro
|
14
|
+
include Pragma
|
15
|
+
|
16
|
+
# @param [Impl] impl
|
17
|
+
def initialize(impl)
|
18
|
+
@impl = impl
|
19
|
+
@std_include_path = []
|
20
|
+
|
21
|
+
# preprocessor context
|
22
|
+
@cond_incl_stack = []
|
23
|
+
@macros = {}
|
24
|
+
@once = {}
|
25
|
+
@include_guard = {}
|
26
|
+
|
27
|
+
# warning context
|
28
|
+
# TODO(south37) Impl warnf
|
29
|
+
@enable_warning = true
|
30
|
+
|
31
|
+
# Used for __DATE__ and __TIME__
|
32
|
+
@now = Time.now
|
33
|
+
|
34
|
+
define_special_macros!
|
35
|
+
end
|
36
|
+
|
37
|
+
# Return parsed node, only used for read_constexpr
|
38
|
+
attr_writer :expr_reader
|
39
|
+
|
40
|
+
# @return [Token]
|
41
|
+
def read_token
|
42
|
+
while true
|
43
|
+
tok = read_expand
|
44
|
+
if tok.bol && Token.is_keyword?(tok, '#') && (tok.hideset.size == 0)
|
45
|
+
read_directive(tok)
|
46
|
+
next
|
47
|
+
end
|
48
|
+
Util.assert!{ !T::CPP_TOKENS.include?(tok.kind) }
|
49
|
+
return maybe_convert_keyword(tok)
|
50
|
+
end
|
51
|
+
raise "Must not reach here!"
|
52
|
+
end
|
53
|
+
|
54
|
+
# @param [Token] tok
|
55
|
+
def unget_token(tok)
|
56
|
+
@impl.unget_token(tok)
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Token]
|
60
|
+
def peek_token
|
61
|
+
r = read_token
|
62
|
+
unget_token(r)
|
63
|
+
r
|
64
|
+
end
|
65
|
+
|
66
|
+
# @param [String] path
|
67
|
+
def append_include_path(path)
|
68
|
+
@std_include_path << path
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
# @return [Token]
|
74
|
+
def read_expand
|
75
|
+
while true
|
76
|
+
tok = read_expand_newline
|
77
|
+
if tok.kind != T::NEWLINE
|
78
|
+
tok.expanded = true
|
79
|
+
return tok
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Note: This is "expand" function in the Dave Prosser's document.
|
85
|
+
# @return [Token]
|
86
|
+
def read_expand_newline
|
87
|
+
tok = @impl.lex
|
88
|
+
# NOTE: return tok if already expanded
|
89
|
+
if tok.expanded
|
90
|
+
return tok
|
91
|
+
end
|
92
|
+
|
93
|
+
if tok.kind != T::IDENT
|
94
|
+
return tok
|
95
|
+
end
|
96
|
+
name = tok.sval
|
97
|
+
macro = @macros[name]
|
98
|
+
if !macro || tok.hideset.include?(name)
|
99
|
+
return tok
|
100
|
+
end
|
101
|
+
|
102
|
+
case macro.kind
|
103
|
+
when M::OBJ
|
104
|
+
hideset = tok.hideset.dup
|
105
|
+
hideset << name
|
106
|
+
tokens = subst(macro, nil, hideset)
|
107
|
+
propagate_space(tokens, tok)
|
108
|
+
@impl.unget_all(tokens)
|
109
|
+
return read_expand
|
110
|
+
when M::FUNC
|
111
|
+
if !next?('(')
|
112
|
+
return tok
|
113
|
+
end
|
114
|
+
args = read_args(tok, macro)
|
115
|
+
rparen = peek_token
|
116
|
+
expect!(')')
|
117
|
+
hideset = ((tok.hideset & rparen.hideset) << name)
|
118
|
+
tokens = subst(macro, args, hideset)
|
119
|
+
propagate_space(tokens, tok)
|
120
|
+
@impl.unget_all(tokens)
|
121
|
+
return read_expand
|
122
|
+
when M::SPECIAL
|
123
|
+
macro.fn.call(tok)
|
124
|
+
return read_expand
|
125
|
+
else
|
126
|
+
raise "internal error"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# @param [Token] tok
|
131
|
+
# @return [Token]
|
132
|
+
def maybe_convert_keyword(tok)
|
133
|
+
return tok if tok.kind != T::IDENT
|
134
|
+
|
135
|
+
id = (K.keywords[tok.sval] || OP.operators[tok.sval])
|
136
|
+
return tok if id.nil?
|
137
|
+
|
138
|
+
r = tok.dup
|
139
|
+
r.kind = T::KEYWORD
|
140
|
+
r.id = id
|
141
|
+
r
|
142
|
+
end
|
143
|
+
|
144
|
+
# @param [Token] hash
|
145
|
+
def read_directive(hash)
|
146
|
+
tok = @impl.lex
|
147
|
+
if tok.kind == T::NEWLINE
|
148
|
+
return
|
149
|
+
end
|
150
|
+
if tok.kind == T::NUMBER
|
151
|
+
read_linemarker(tok)
|
152
|
+
return
|
153
|
+
end
|
154
|
+
if tok.kind != T::IDENT
|
155
|
+
raise "unsupported preprocessor directive: #{tok}"
|
156
|
+
end
|
157
|
+
|
158
|
+
# NOTE: only for debug
|
159
|
+
# print "#{' ' * 2 * @cond_incl_stack.size}##{tok.sval}\n"
|
160
|
+
|
161
|
+
case tok.sval
|
162
|
+
when "define" then read_define
|
163
|
+
when "elif" then read_elif(hash)
|
164
|
+
when "else" then read_else(hash)
|
165
|
+
when "endif" then read_endif(hash)
|
166
|
+
when "error" then read_error(hash)
|
167
|
+
when "if" then read_if
|
168
|
+
when "ifdef" then read_ifdef
|
169
|
+
when "ifndef" then read_ifndef
|
170
|
+
when "import" then read_include(hash, tok.file, true)
|
171
|
+
when "include" then read_include(hash, tok.file, false)
|
172
|
+
when "include_next" then read_include_next(hash, tok.file)
|
173
|
+
when "line" then read_line
|
174
|
+
when "pragma" then read_pragma
|
175
|
+
when "undef" then read_undef
|
176
|
+
when "warning" then read_warning(hash)
|
177
|
+
else
|
178
|
+
raise "unsupported preprocessor directive: #{tok}"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# GNU CPP outputs "# linenum filename flags" to preserve original
|
183
|
+
# source file information. This function reads them. Flags are ignored.
|
184
|
+
#
|
185
|
+
# @param [Token] tok
|
186
|
+
def read_linemarker(tok)
|
187
|
+
if !is_digit_sequence?(tok.sval)
|
188
|
+
Util.errort!(tok, "line number expected, but got #{tok}")
|
189
|
+
end
|
190
|
+
line = tok.sval.to_i
|
191
|
+
tok = @impl.lex
|
192
|
+
if tok.kind != T::STRING
|
193
|
+
Util.errort!(tok, "filename expected, but got #{tok}")
|
194
|
+
end
|
195
|
+
filename = tok.sval
|
196
|
+
|
197
|
+
tok = @impl.lex
|
198
|
+
while tok.kind != T::NEWLINE
|
199
|
+
tok = @impl.lex
|
200
|
+
end
|
201
|
+
file = @impl.current_file
|
202
|
+
file.line = line
|
203
|
+
file.name = filename
|
204
|
+
end
|
205
|
+
|
206
|
+
# @param [Token] tok
|
207
|
+
# @param [Macro] macro
|
208
|
+
# @return [<Token>]
|
209
|
+
def read_args(tok, macro)
|
210
|
+
if (macro.nargs == 0) && Token.is_keyword?(peek_token, ')')
|
211
|
+
# If a macro M has no parameter, argument list of M()
|
212
|
+
# is an empty list. If it has one parameter,
|
213
|
+
# argument list of M() is a list containing an empty list.
|
214
|
+
return []
|
215
|
+
end
|
216
|
+
args = do_read_args(tok, macro)
|
217
|
+
if args.size != macro.nargs
|
218
|
+
Util.errort!(tok, "macro argument number does not match");
|
219
|
+
end
|
220
|
+
args
|
221
|
+
end
|
222
|
+
|
223
|
+
# @param [Token] ident
|
224
|
+
# @param [Macro] macro
|
225
|
+
# @return [<Token>]
|
226
|
+
def do_read_args(ident, macro)
|
227
|
+
r = []
|
228
|
+
e = false
|
229
|
+
while !e
|
230
|
+
in_ellipsis = macro.is_varg && (r.size + 1 == macro.nargs)
|
231
|
+
arg, e = read_one_arg(ident, in_ellipsis)
|
232
|
+
r.push(arg)
|
233
|
+
end
|
234
|
+
if macro.is_varg && (r.size == macro.nargs - 1)
|
235
|
+
r.push([])
|
236
|
+
end
|
237
|
+
r
|
238
|
+
end
|
239
|
+
|
240
|
+
# @param [Token] ident
|
241
|
+
# @param [Boolean] readall
|
242
|
+
# @return [<<Token>, Boolean>]
|
243
|
+
def read_one_arg(ident, readall)
|
244
|
+
r = []
|
245
|
+
level = 0
|
246
|
+
while true
|
247
|
+
tok = @impl.lex
|
248
|
+
if tok.kind == T::EOF
|
249
|
+
Util.errort!(ident, "unterminated macro argument list")
|
250
|
+
end
|
251
|
+
if tok.kind == T::NEWLINE
|
252
|
+
next
|
253
|
+
end
|
254
|
+
if tok.bol && Token.is_keyword?(tok, '#')
|
255
|
+
read_directive(tok)
|
256
|
+
next
|
257
|
+
end
|
258
|
+
if (level == 0) && Token.is_keyword?(tok, ')')
|
259
|
+
unget_token(tok)
|
260
|
+
return r, true
|
261
|
+
end
|
262
|
+
if (level == 0) && Token.is_keyword?(tok, ',') && !readall
|
263
|
+
return r, false
|
264
|
+
end
|
265
|
+
if Token.is_keyword?(tok, '(')
|
266
|
+
level += 1
|
267
|
+
end
|
268
|
+
if Token.is_keyword?(tok, ')')
|
269
|
+
level -= 1
|
270
|
+
end
|
271
|
+
# C11 6.10.3p10: Within the macro argument list,
|
272
|
+
# newline is considered a normal whitespace character.
|
273
|
+
# I don't know why the standard specifies such a minor detail,
|
274
|
+
# but the difference of newline and space is observable
|
275
|
+
# if you stringize tokens using #.
|
276
|
+
if tok.bol
|
277
|
+
tok = copy_token(tok)
|
278
|
+
tok.bol = false
|
279
|
+
tok.space = true
|
280
|
+
end
|
281
|
+
r.push(tok)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
# @param [Char] id
|
286
|
+
# @return [Boolean]
|
287
|
+
def next?(id)
|
288
|
+
tok = @impl.lex
|
289
|
+
if Token.is_keyword?(tok, id)
|
290
|
+
return true
|
291
|
+
end
|
292
|
+
@impl.unget_token(tok)
|
293
|
+
false
|
294
|
+
end
|
295
|
+
|
296
|
+
# @param(return) [<Token>] tokens
|
297
|
+
# @param [Token] tmpl
|
298
|
+
def propagate_space(tokens, tmpl)
|
299
|
+
if tokens.size == 0
|
300
|
+
return
|
301
|
+
end
|
302
|
+
tok = copy_token(tokens.first)
|
303
|
+
tok.space = tmpl.space
|
304
|
+
tokens[0] = tok
|
305
|
+
end
|
306
|
+
|
307
|
+
# @param [Char] id
|
308
|
+
def expect!(id)
|
309
|
+
tok = @impl.lex
|
310
|
+
if !Token.is_keyword?(tok, id)
|
311
|
+
raise "#{id} expected, but got #{tok}"
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
# @param [Macro] macro
|
316
|
+
# @param [Array] args
|
317
|
+
# @param [Set] hideset
|
318
|
+
# @return [<Token>]
|
319
|
+
def subst(macro, args, hideset)
|
320
|
+
r = []
|
321
|
+
i = 0
|
322
|
+
len = macro.body.size
|
323
|
+
while i < len
|
324
|
+
t0 = macro.body[i]
|
325
|
+
t1 = macro.body[i + 1] # Note: nil when i == (macro.body.size - 1)
|
326
|
+
t0_param = (t0.kind == T::MACRO_PARAM)
|
327
|
+
t1_param = (t1 && t1.kind == T::MACRO_PARAM)
|
328
|
+
|
329
|
+
if Token.is_keyword?(t0, '#') && t1_param
|
330
|
+
r.push(stringize(t0, args[t1.position]))
|
331
|
+
i += 2
|
332
|
+
next
|
333
|
+
end
|
334
|
+
if Token.is_keyword?(t0, K::HASHHASH) && t1_param
|
335
|
+
arg = args[t1.position]
|
336
|
+
# [GNU] [,##__VA_ARG__] is expanded to the empty token sequence
|
337
|
+
# if __VA_ARG__ is empty. Otherwise it's expanded to
|
338
|
+
# [,<tokens in __VA_ARG__>].
|
339
|
+
if t1.is_vararg && (r.size > 0) && Token.is_keyword?(r.last, ',')
|
340
|
+
if arg.size > 0
|
341
|
+
r += arg
|
342
|
+
else
|
343
|
+
r.pop
|
344
|
+
end
|
345
|
+
elsif arg.size > 0
|
346
|
+
glue_push(r, arg.first)
|
347
|
+
arg[1..-1].each do |e|
|
348
|
+
r.push(e)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
i += 2
|
352
|
+
next
|
353
|
+
end
|
354
|
+
if Token.is_keyword?(t0, K::HASHHASH) && t1
|
355
|
+
hideset = t1.hideset
|
356
|
+
glue_push(r, t1)
|
357
|
+
i += 2
|
358
|
+
next
|
359
|
+
end
|
360
|
+
if t0_param && t1 && Token.is_keyword?(t1, K::HASHHASH)
|
361
|
+
hideset = t1.hideset
|
362
|
+
arg = args[t0.position]
|
363
|
+
if arg.size == 0
|
364
|
+
i += 2
|
365
|
+
next
|
366
|
+
else
|
367
|
+
r += arg
|
368
|
+
i += 1
|
369
|
+
next
|
370
|
+
end
|
371
|
+
end
|
372
|
+
if t0_param
|
373
|
+
arg = args[t0.position]
|
374
|
+
r += expand_all(arg, t0)
|
375
|
+
i += 1
|
376
|
+
next
|
377
|
+
end
|
378
|
+
r.push(t0)
|
379
|
+
i += 1
|
380
|
+
end
|
381
|
+
add_hide_set(r, hideset)
|
382
|
+
end
|
383
|
+
|
384
|
+
# @param(result) [<Token>] tokens
|
385
|
+
# @param [Token] tok
|
386
|
+
def glue_push(tokens, tok)
|
387
|
+
last = tokens.pop
|
388
|
+
tokens.push(glue_tokens(last, tok))
|
389
|
+
end
|
390
|
+
|
391
|
+
# @param [Token] t
|
392
|
+
# @param [Token] u
|
393
|
+
# @return [Token]
|
394
|
+
def glue_tokens(t, u)
|
395
|
+
b = "#{t}#{u}"
|
396
|
+
r = @impl.lex_string(b)
|
397
|
+
r
|
398
|
+
end
|
399
|
+
|
400
|
+
# @param [Token] tmpl
|
401
|
+
# @param [<Token>] args
|
402
|
+
def stringize(tmpl, args)
|
403
|
+
b = ""
|
404
|
+
args.each do |tok|
|
405
|
+
if b.size > 0 && tok.space
|
406
|
+
b << " "
|
407
|
+
end
|
408
|
+
b << tok.to_s
|
409
|
+
end
|
410
|
+
r = tmpl.dup
|
411
|
+
r.kind = T::STRING
|
412
|
+
r.sval = b
|
413
|
+
r.enc = ENC::NONE
|
414
|
+
r
|
415
|
+
end
|
416
|
+
|
417
|
+
# @return [<Token>]
|
418
|
+
def expand_all(tokens, tmpl)
|
419
|
+
@impl.token_buffer_stash(tokens.reverse)
|
420
|
+
r = []
|
421
|
+
while true
|
422
|
+
tok = read_expand
|
423
|
+
if tok.kind == T::EOF
|
424
|
+
break
|
425
|
+
end
|
426
|
+
r.push(tok)
|
427
|
+
end
|
428
|
+
propagate_space(r, tmpl)
|
429
|
+
@impl.token_buffer_unstash
|
430
|
+
r
|
431
|
+
end
|
432
|
+
|
433
|
+
# @param [<Token>] tokens
|
434
|
+
# @param [Set] hideset
|
435
|
+
# @return [<Token>]
|
436
|
+
def add_hide_set(tokens, hideset)
|
437
|
+
r = []
|
438
|
+
tokens.each do |token|
|
439
|
+
t = copy_token(token)
|
440
|
+
t.hideset = (t.hideset | hideset)
|
441
|
+
r.push(t)
|
442
|
+
end
|
443
|
+
r
|
444
|
+
end
|
445
|
+
|
446
|
+
# @param [Token] hash
|
447
|
+
# @param [FileIO] file
|
448
|
+
# @param [Boolean] isimport
|
449
|
+
def read_include(hash, file, isimport)
|
450
|
+
filename, std = read_cpp_header_name(hash)
|
451
|
+
expect_newline!
|
452
|
+
if filename[0] == '/'
|
453
|
+
if try_include("/", filename, isimport)
|
454
|
+
return
|
455
|
+
end
|
456
|
+
Util.errort!(hash, "cannot find header file: #{filename}")
|
457
|
+
end
|
458
|
+
if !std
|
459
|
+
dir = file.name ? File.dirname(file.name) : "."
|
460
|
+
if try_include(dir, filename, isimport)
|
461
|
+
return
|
462
|
+
end
|
463
|
+
end
|
464
|
+
@std_include_path.each do |path|
|
465
|
+
if try_include(path, filename, isimport)
|
466
|
+
return
|
467
|
+
end
|
468
|
+
end
|
469
|
+
Util.errort!(hash, "cannot find header file: #{filename}")
|
470
|
+
end
|
471
|
+
|
472
|
+
# @raise [RuntimeError]
|
473
|
+
def expect_newline!
|
474
|
+
tok = @impl.lex
|
475
|
+
if tok.kind != T::NEWLINE
|
476
|
+
raise "newline expected, but got #{tok}"
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
# @param [Token] hash
|
481
|
+
# @return [String, Boolean]
|
482
|
+
def read_cpp_header_name(hash)
|
483
|
+
# Try reading a filename using a special tokenizer for #include.
|
484
|
+
path, std = @impl.read_header_file_name
|
485
|
+
if path
|
486
|
+
return path, std
|
487
|
+
end
|
488
|
+
|
489
|
+
# If a token following #include does not start with < nor ",
|
490
|
+
# try to read the token as a regular token. Macro-expanded
|
491
|
+
# form may be a valid header file path.
|
492
|
+
tok = read_expand_newline
|
493
|
+
if tok.kind == T::NEWLINE
|
494
|
+
Util.errort!(hash, "expected filename, but got newline")
|
495
|
+
end
|
496
|
+
if tok.kind == T::STRING
|
497
|
+
std = false
|
498
|
+
return tok.sval, std
|
499
|
+
end
|
500
|
+
if !Token.is_keyword?(tok, '<')
|
501
|
+
Util.errort!(tok, "< expected, but got #{tok}")
|
502
|
+
end
|
503
|
+
tokens = []
|
504
|
+
while true
|
505
|
+
tok = read_expand_newline
|
506
|
+
if tok.kind == T::NEWLINE
|
507
|
+
Util.errort!(hash, "premature end of header name")
|
508
|
+
end
|
509
|
+
if Token.is_keyword?(tok, '>')
|
510
|
+
break
|
511
|
+
end
|
512
|
+
tokens.push(tok)
|
513
|
+
end
|
514
|
+
std = true
|
515
|
+
|
516
|
+
return tokens.join, std
|
517
|
+
end
|
518
|
+
|
519
|
+
# @param [String] dir
|
520
|
+
# @param [String] filename
|
521
|
+
# @param [Boolean] isimport
|
522
|
+
# @return [Boolean]
|
523
|
+
def try_include(dir, filename, isimport)
|
524
|
+
path = File.join(dir, filename)
|
525
|
+
if @once[path]
|
526
|
+
return true
|
527
|
+
end
|
528
|
+
if guarded?(path)
|
529
|
+
return true
|
530
|
+
end
|
531
|
+
|
532
|
+
# NOTE: file may not exist
|
533
|
+
begin
|
534
|
+
fp = File.open(path, "r")
|
535
|
+
rescue Errno::ENOENT, Errno::ENOTDIR
|
536
|
+
return false
|
537
|
+
end
|
538
|
+
|
539
|
+
if isimport
|
540
|
+
@once[path] = true
|
541
|
+
end
|
542
|
+
@impl.push_file(FileIO.new(fp, path))
|
543
|
+
true
|
544
|
+
end
|
545
|
+
|
546
|
+
CPP_TOKEN_ZERO = Token.new(T::NUMBER, sval: "0")
|
547
|
+
CPP_TOKEN_ONE = Token.new(T::NUMBER, sval: "1")
|
548
|
+
|
549
|
+
# @param [String] path
|
550
|
+
# @return [Boolean]
|
551
|
+
def guarded?(path)
|
552
|
+
guard = @include_guard[path]
|
553
|
+
r = guard && @macros[guard]
|
554
|
+
define_obj_macro("__8cc_include_guard", r ? CPP_TOKEN_ONE : CPP_TOKEN_ZERO)
|
555
|
+
r
|
556
|
+
end
|
557
|
+
|
558
|
+
def read_line
|
559
|
+
tok = read_expand_newline
|
560
|
+
if (tok.kind != T::NUMBER) || !is_digit_sequence?(tok.sval)
|
561
|
+
Util.errort!(tok, "number expected after #line, but got #{tok}")
|
562
|
+
end
|
563
|
+
line = tok.sval.to_i
|
564
|
+
tok = read_expand_newline
|
565
|
+
if tok.kind == T::STRING
|
566
|
+
filename = tok.sval
|
567
|
+
expect_newline!
|
568
|
+
elsif tok.kind != T::NEWLINE
|
569
|
+
Util.errort!(tok, "newline or a source name are expected, but got #{tok}")
|
570
|
+
end
|
571
|
+
f = @impl.current_file
|
572
|
+
f.line = line
|
573
|
+
if filename
|
574
|
+
f.name = filename
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
# @param [String] p
|
579
|
+
# @return [Boolean]
|
580
|
+
def is_digit_sequence?(p)
|
581
|
+
p.match(/^\d*$/)
|
582
|
+
end
|
583
|
+
|
584
|
+
def read_if
|
585
|
+
do_read_if(read_constexpr)
|
586
|
+
end
|
587
|
+
|
588
|
+
# @return [Boolean]
|
589
|
+
def read_constexpr
|
590
|
+
@impl.token_buffer_stash(read_intexpr_line.reverse)
|
591
|
+
Util.assert!{ !@expr_reader.nil? }
|
592
|
+
expr = @expr_reader.call
|
593
|
+
tok = @impl.lex
|
594
|
+
if tok.kind != T::EOF
|
595
|
+
Util.errort!(tok, "stray token: #{tok}")
|
596
|
+
end
|
597
|
+
@impl.token_buffer_unstash
|
598
|
+
n, _ = IntEvaluator.eval(expr)
|
599
|
+
n != 0
|
600
|
+
end
|
601
|
+
|
602
|
+
def read_ifdef
|
603
|
+
tok = @impl.lex
|
604
|
+
if tok.kind != T::IDENT
|
605
|
+
Util.errort!(tok, "identifier expected, but got #{tok}")
|
606
|
+
end
|
607
|
+
do_read_if(@macros[tok.sval])
|
608
|
+
end
|
609
|
+
|
610
|
+
def read_ifndef
|
611
|
+
tok = @impl.lex
|
612
|
+
if tok.kind != T::IDENT
|
613
|
+
Util.errort!(tok, "identifier expected, but got #{tok}")
|
614
|
+
end
|
615
|
+
expect_newline!
|
616
|
+
do_read_if(!@macros[tok.sval])
|
617
|
+
if tok.count == 2
|
618
|
+
# "ifndef" is the second token in this file.
|
619
|
+
# Prepare to detect an include guard.
|
620
|
+
ci = @cond_incl_stack.last
|
621
|
+
ci.include_guard = tok.sval
|
622
|
+
ci.file = tok.file
|
623
|
+
end
|
624
|
+
end
|
625
|
+
|
626
|
+
# @param [Boolean] istrue
|
627
|
+
def do_read_if(istrue)
|
628
|
+
@cond_incl_stack.push(make_cond_incl(istrue))
|
629
|
+
|
630
|
+
if (!istrue)
|
631
|
+
@impl.skip_cond_incl!
|
632
|
+
end
|
633
|
+
end
|
634
|
+
|
635
|
+
def read_define
|
636
|
+
name = read_ident
|
637
|
+
tok = @impl.lex
|
638
|
+
if Token.is_keyword?(tok, '(') && !tok.space
|
639
|
+
read_funclike_macro(name)
|
640
|
+
return
|
641
|
+
end
|
642
|
+
@impl.unget_token(tok)
|
643
|
+
read_obj_macro(name.sval)
|
644
|
+
end
|
645
|
+
|
646
|
+
# @param [Token] name
|
647
|
+
def read_funclike_macro(name)
|
648
|
+
param = {}
|
649
|
+
param, isvarg = read_funclike_macro_params(name)
|
650
|
+
body = read_funclike_macro_body(param)
|
651
|
+
hashhash_check!(body)
|
652
|
+
macro = make_func_macro(body, param.size, isvarg)
|
653
|
+
@macros[name.sval] = macro
|
654
|
+
end
|
655
|
+
|
656
|
+
# @param [Token] name
|
657
|
+
# @return [<Hash, Boolean>]
|
658
|
+
def read_funclike_macro_params(name)
|
659
|
+
pos = 0
|
660
|
+
param = {}
|
661
|
+
while true
|
662
|
+
tok = @impl.lex
|
663
|
+
if Token.is_keyword?(tok, ')')
|
664
|
+
return param, false
|
665
|
+
end
|
666
|
+
if pos > 0
|
667
|
+
if !Token.is_keyword?(tok, ',')
|
668
|
+
Util.errort!(tok, ", expected, but got #{tok}")
|
669
|
+
end
|
670
|
+
tok = @impl.lex
|
671
|
+
end
|
672
|
+
if tok.kind == T::NEWLINE
|
673
|
+
Util.errort!(name, "missing ')' in macro parameter list")
|
674
|
+
end
|
675
|
+
if Token.is_keyword?(tok, K::ELLIPSIS)
|
676
|
+
param["__VA_ARGS__"] = make_macro_token(pos, true)
|
677
|
+
pos += 1
|
678
|
+
expect!(')')
|
679
|
+
return param, true
|
680
|
+
end
|
681
|
+
if tok.kind != T::IDENT
|
682
|
+
Util.errort!(tok, "identifier expected, but got #{tok}")
|
683
|
+
end
|
684
|
+
arg = tok.sval
|
685
|
+
if next?(K::ELLIPSIS)
|
686
|
+
expect!(')')
|
687
|
+
param[arg] = make_macro_token(pos, true)
|
688
|
+
pos += 1
|
689
|
+
return param, true
|
690
|
+
end
|
691
|
+
param[arg] = make_macro_token(pos, false)
|
692
|
+
pos += 1
|
693
|
+
end
|
694
|
+
end
|
695
|
+
|
696
|
+
# @param [Hash] param
|
697
|
+
# @return [<Token>]
|
698
|
+
def read_funclike_macro_body(param)
|
699
|
+
r = []
|
700
|
+
while true
|
701
|
+
tok = @impl.lex
|
702
|
+
if tok.kind == T::NEWLINE
|
703
|
+
return r
|
704
|
+
end
|
705
|
+
if tok.kind == T::IDENT
|
706
|
+
subst = param[tok.sval]
|
707
|
+
if subst
|
708
|
+
subst = copy_token(subst)
|
709
|
+
subst.space = tok.space
|
710
|
+
r.push(subst)
|
711
|
+
next
|
712
|
+
end
|
713
|
+
end
|
714
|
+
r.push(tok)
|
715
|
+
end
|
716
|
+
end
|
717
|
+
|
718
|
+
# @param [String] name
|
719
|
+
def read_obj_macro(name)
|
720
|
+
body = []
|
721
|
+
while true
|
722
|
+
tok = @impl.lex
|
723
|
+
if tok.kind == T::NEWLINE
|
724
|
+
break
|
725
|
+
end
|
726
|
+
body.push(tok)
|
727
|
+
end
|
728
|
+
hashhash_check!(body)
|
729
|
+
@macros[name] = make_obj_macro(body)
|
730
|
+
end
|
731
|
+
|
732
|
+
# @param [<Token>] v
|
733
|
+
# @raise
|
734
|
+
def hashhash_check!(v)
|
735
|
+
if v.size == 0
|
736
|
+
return
|
737
|
+
end
|
738
|
+
if Token.is_keyword?(v.first, K::HASHHASH)
|
739
|
+
Util.errort!(vec_head(v), "'##' cannot appear at start of macro expansion")
|
740
|
+
end
|
741
|
+
if Token.is_keyword?(v.last, K::HASHHASH)
|
742
|
+
Util.errort!(vec_tail(v), "'##' cannot appear at end of macro expansion")
|
743
|
+
end
|
744
|
+
end
|
745
|
+
|
746
|
+
# @return [Token]
|
747
|
+
def read_ident
|
748
|
+
tok = @impl.lex
|
749
|
+
if tok.kind != T::IDENT
|
750
|
+
Util.errort!(tok, "identifier expected, but got #{tok}")
|
751
|
+
end
|
752
|
+
tok
|
753
|
+
end
|
754
|
+
|
755
|
+
# @return [<Token>]
|
756
|
+
def read_intexpr_line
|
757
|
+
r = []
|
758
|
+
while true
|
759
|
+
tok = read_expand_newline
|
760
|
+
if tok.kind == T::NEWLINE
|
761
|
+
return r
|
762
|
+
end
|
763
|
+
if Token.is_ident?(tok, "defined")
|
764
|
+
r.push(read_defined_op)
|
765
|
+
elsif tok.kind == T::IDENT
|
766
|
+
# C11 6.10.1.4 says that remaining identifiers
|
767
|
+
# should be replaced with pp-number 0.
|
768
|
+
r.push(CPP_TOKEN_ZERO)
|
769
|
+
else
|
770
|
+
r.push(tok)
|
771
|
+
end
|
772
|
+
end
|
773
|
+
end
|
774
|
+
|
775
|
+
# @return [Token]
|
776
|
+
def read_defined_op
|
777
|
+
tok = @impl.lex
|
778
|
+
if Token.is_keyword?(tok, '(')
|
779
|
+
tok = @impl.lex
|
780
|
+
expect!(')')
|
781
|
+
end
|
782
|
+
if tok.kind != T::IDENT
|
783
|
+
Util.errort!(tok, "identifier expected, but got #{tok}")
|
784
|
+
end
|
785
|
+
@macros[tok.sval] ? CPP_TOKEN_ONE : CPP_TOKEN_ZERO
|
786
|
+
end
|
787
|
+
|
788
|
+
# @param [Token] hash
|
789
|
+
def read_elif(hash)
|
790
|
+
if @cond_incl_stack.size == 0
|
791
|
+
Util.errort!(hash, "stray #elif")
|
792
|
+
end
|
793
|
+
ci = @cond_incl_stack.last
|
794
|
+
if ci.ctx == CondInclCtx::ELSE
|
795
|
+
Util.errort!(hash, "#elif after #else")
|
796
|
+
end
|
797
|
+
ci.ctx = CondInclCtx::ELIF
|
798
|
+
ci.include_guard = nil
|
799
|
+
if ci.wastrue || !read_constexpr
|
800
|
+
@impl.skip_cond_incl!
|
801
|
+
return
|
802
|
+
end
|
803
|
+
ci.wastrue = true
|
804
|
+
end
|
805
|
+
|
806
|
+
# @param [Token] hash
|
807
|
+
def read_else(hash)
|
808
|
+
if @cond_incl_stack.size == 0
|
809
|
+
Util.errort!(hash, "stray #else")
|
810
|
+
end
|
811
|
+
ci = @cond_incl_stack.last
|
812
|
+
if ci.ctx == CondInclCtx::ELSE
|
813
|
+
Util.errort!(hash, "#else appears in #else")
|
814
|
+
end
|
815
|
+
expect_newline!
|
816
|
+
ci.ctx = CondInclCtx::ELSE
|
817
|
+
ci.include_guard = nil
|
818
|
+
if ci.wastrue
|
819
|
+
@impl.skip_cond_incl!
|
820
|
+
end
|
821
|
+
end
|
822
|
+
|
823
|
+
# @param [Token] hash
|
824
|
+
def read_endif(hash)
|
825
|
+
if @cond_incl_stack.size == 0
|
826
|
+
Util.errort!(hash, "stray #endif")
|
827
|
+
end
|
828
|
+
ci = @cond_incl_stack.pop
|
829
|
+
expect_newline!
|
830
|
+
|
831
|
+
# Detect an #ifndef and #endif pair that guards the entire
|
832
|
+
# header file. Remember the macro name guarding the file
|
833
|
+
# so that we can skip the file next time.
|
834
|
+
if !ci.include_guard || ci.file != hash.file
|
835
|
+
return
|
836
|
+
end
|
837
|
+
last = skip_newlines!
|
838
|
+
if ci.file != last.file
|
839
|
+
@include_guard[ci.file.name] = ci.include_guard
|
840
|
+
end
|
841
|
+
end
|
842
|
+
|
843
|
+
# Skips all newlines and returns the first non-newline token.
|
844
|
+
#
|
845
|
+
# @return [Token]
|
846
|
+
def skip_newlines!
|
847
|
+
tok = @impl.lex
|
848
|
+
while (tok.kind == T::NEWLINE)
|
849
|
+
tok = @impl.lex
|
850
|
+
end
|
851
|
+
@impl.unget_token(tok)
|
852
|
+
tok
|
853
|
+
end
|
854
|
+
|
855
|
+
# @param [Token] hash
|
856
|
+
def read_error(hash)
|
857
|
+
Util.errort!(hash, "#error: #{read_error_message}")
|
858
|
+
end
|
859
|
+
|
860
|
+
# @param [Token] hash
|
861
|
+
def read_warning(hash)
|
862
|
+
print "#warning: #{read_error_message}\n"
|
863
|
+
# warnt(hash, "#warning: %s", read_error_message());
|
864
|
+
end
|
865
|
+
|
866
|
+
# @return [String]
|
867
|
+
def read_error_message
|
868
|
+
b = ""
|
869
|
+
while true
|
870
|
+
tok = @impl.lex
|
871
|
+
if tok.kind == T::NEWLINE
|
872
|
+
return b
|
873
|
+
end
|
874
|
+
if (b.size != 0) && tok.space
|
875
|
+
b << ' '
|
876
|
+
end
|
877
|
+
b << "#{tok}"
|
878
|
+
end
|
879
|
+
end
|
880
|
+
|
881
|
+
def read_undef
|
882
|
+
name = read_ident
|
883
|
+
expect_newline!
|
884
|
+
@macros.delete(name.sval)
|
885
|
+
end
|
886
|
+
end
|
887
|
+
end
|
888
|
+
end
|