metasm 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/BUGS +11 -0
- data/CREDITS +17 -0
- data/README +270 -0
- data/TODO +114 -0
- data/doc/code_organisation.txt +146 -0
- data/doc/const_missing.txt +16 -0
- data/doc/core_classes.txt +75 -0
- data/doc/feature_list.txt +53 -0
- data/doc/index.txt +59 -0
- data/doc/install_notes.txt +170 -0
- data/doc/style.css +3 -0
- data/doc/use_cases.txt +18 -0
- data/lib/metasm.rb +80 -0
- data/lib/metasm/arm.rb +12 -0
- data/lib/metasm/arm/debug.rb +39 -0
- data/lib/metasm/arm/decode.rb +167 -0
- data/lib/metasm/arm/encode.rb +77 -0
- data/lib/metasm/arm/main.rb +75 -0
- data/lib/metasm/arm/opcodes.rb +177 -0
- data/lib/metasm/arm/parse.rb +130 -0
- data/lib/metasm/arm/render.rb +55 -0
- data/lib/metasm/compile_c.rb +1457 -0
- data/lib/metasm/dalvik.rb +8 -0
- data/lib/metasm/dalvik/decode.rb +196 -0
- data/lib/metasm/dalvik/main.rb +60 -0
- data/lib/metasm/dalvik/opcodes.rb +366 -0
- data/lib/metasm/decode.rb +213 -0
- data/lib/metasm/decompile.rb +2659 -0
- data/lib/metasm/disassemble.rb +2068 -0
- data/lib/metasm/disassemble_api.rb +1280 -0
- data/lib/metasm/dynldr.rb +1329 -0
- data/lib/metasm/encode.rb +333 -0
- data/lib/metasm/exe_format/a_out.rb +194 -0
- data/lib/metasm/exe_format/autoexe.rb +82 -0
- data/lib/metasm/exe_format/bflt.rb +189 -0
- data/lib/metasm/exe_format/coff.rb +455 -0
- data/lib/metasm/exe_format/coff_decode.rb +901 -0
- data/lib/metasm/exe_format/coff_encode.rb +1078 -0
- data/lib/metasm/exe_format/dex.rb +457 -0
- data/lib/metasm/exe_format/dol.rb +145 -0
- data/lib/metasm/exe_format/elf.rb +923 -0
- data/lib/metasm/exe_format/elf_decode.rb +979 -0
- data/lib/metasm/exe_format/elf_encode.rb +1375 -0
- data/lib/metasm/exe_format/macho.rb +827 -0
- data/lib/metasm/exe_format/main.rb +228 -0
- data/lib/metasm/exe_format/mz.rb +164 -0
- data/lib/metasm/exe_format/nds.rb +172 -0
- data/lib/metasm/exe_format/pe.rb +437 -0
- data/lib/metasm/exe_format/serialstruct.rb +246 -0
- data/lib/metasm/exe_format/shellcode.rb +114 -0
- data/lib/metasm/exe_format/xcoff.rb +167 -0
- data/lib/metasm/gui.rb +23 -0
- data/lib/metasm/gui/cstruct.rb +373 -0
- data/lib/metasm/gui/dasm_coverage.rb +199 -0
- data/lib/metasm/gui/dasm_decomp.rb +369 -0
- data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
- data/lib/metasm/gui/dasm_graph.rb +1354 -0
- data/lib/metasm/gui/dasm_hex.rb +543 -0
- data/lib/metasm/gui/dasm_listing.rb +599 -0
- data/lib/metasm/gui/dasm_main.rb +906 -0
- data/lib/metasm/gui/dasm_opcodes.rb +291 -0
- data/lib/metasm/gui/debug.rb +1228 -0
- data/lib/metasm/gui/gtk.rb +884 -0
- data/lib/metasm/gui/qt.rb +495 -0
- data/lib/metasm/gui/win32.rb +3004 -0
- data/lib/metasm/gui/x11.rb +621 -0
- data/lib/metasm/ia32.rb +14 -0
- data/lib/metasm/ia32/compile_c.rb +1523 -0
- data/lib/metasm/ia32/debug.rb +193 -0
- data/lib/metasm/ia32/decode.rb +1167 -0
- data/lib/metasm/ia32/decompile.rb +564 -0
- data/lib/metasm/ia32/encode.rb +314 -0
- data/lib/metasm/ia32/main.rb +233 -0
- data/lib/metasm/ia32/opcodes.rb +872 -0
- data/lib/metasm/ia32/parse.rb +327 -0
- data/lib/metasm/ia32/render.rb +91 -0
- data/lib/metasm/main.rb +1193 -0
- data/lib/metasm/mips.rb +11 -0
- data/lib/metasm/mips/compile_c.rb +7 -0
- data/lib/metasm/mips/decode.rb +253 -0
- data/lib/metasm/mips/encode.rb +51 -0
- data/lib/metasm/mips/main.rb +72 -0
- data/lib/metasm/mips/opcodes.rb +443 -0
- data/lib/metasm/mips/parse.rb +51 -0
- data/lib/metasm/mips/render.rb +43 -0
- data/lib/metasm/os/gnu_exports.rb +270 -0
- data/lib/metasm/os/linux.rb +1112 -0
- data/lib/metasm/os/main.rb +1686 -0
- data/lib/metasm/os/remote.rb +527 -0
- data/lib/metasm/os/windows.rb +2027 -0
- data/lib/metasm/os/windows_exports.rb +745 -0
- data/lib/metasm/parse.rb +876 -0
- data/lib/metasm/parse_c.rb +3938 -0
- data/lib/metasm/pic16c/decode.rb +42 -0
- data/lib/metasm/pic16c/main.rb +17 -0
- data/lib/metasm/pic16c/opcodes.rb +68 -0
- data/lib/metasm/ppc.rb +11 -0
- data/lib/metasm/ppc/decode.rb +264 -0
- data/lib/metasm/ppc/decompile.rb +251 -0
- data/lib/metasm/ppc/encode.rb +51 -0
- data/lib/metasm/ppc/main.rb +129 -0
- data/lib/metasm/ppc/opcodes.rb +410 -0
- data/lib/metasm/ppc/parse.rb +52 -0
- data/lib/metasm/preprocessor.rb +1277 -0
- data/lib/metasm/render.rb +130 -0
- data/lib/metasm/sh4.rb +8 -0
- data/lib/metasm/sh4/decode.rb +336 -0
- data/lib/metasm/sh4/main.rb +292 -0
- data/lib/metasm/sh4/opcodes.rb +381 -0
- data/lib/metasm/x86_64.rb +12 -0
- data/lib/metasm/x86_64/compile_c.rb +1025 -0
- data/lib/metasm/x86_64/debug.rb +59 -0
- data/lib/metasm/x86_64/decode.rb +268 -0
- data/lib/metasm/x86_64/encode.rb +264 -0
- data/lib/metasm/x86_64/main.rb +135 -0
- data/lib/metasm/x86_64/opcodes.rb +118 -0
- data/lib/metasm/x86_64/parse.rb +68 -0
- data/misc/bottleneck.rb +61 -0
- data/misc/cheader-findpppath.rb +58 -0
- data/misc/hexdiff.rb +74 -0
- data/misc/hexdump.rb +55 -0
- data/misc/metasm-all.rb +13 -0
- data/misc/objdiff.rb +47 -0
- data/misc/objscan.rb +40 -0
- data/misc/pdfparse.rb +661 -0
- data/misc/ppc_pdf2oplist.rb +192 -0
- data/misc/tcp_proxy_hex.rb +84 -0
- data/misc/txt2html.rb +440 -0
- data/samples/a.out.rb +31 -0
- data/samples/asmsyntax.rb +77 -0
- data/samples/bindiff.rb +555 -0
- data/samples/compilation-steps.rb +49 -0
- data/samples/cparser_makestackoffset.rb +55 -0
- data/samples/dasm-backtrack.rb +38 -0
- data/samples/dasmnavig.rb +318 -0
- data/samples/dbg-apihook.rb +228 -0
- data/samples/dbghelp.rb +143 -0
- data/samples/disassemble-gui.rb +102 -0
- data/samples/disassemble.rb +133 -0
- data/samples/dump_upx.rb +95 -0
- data/samples/dynamic_ruby.rb +1929 -0
- data/samples/elf_list_needed.rb +46 -0
- data/samples/elf_listexports.rb +33 -0
- data/samples/elfencode.rb +25 -0
- data/samples/exeencode.rb +128 -0
- data/samples/factorize-headers-elfimports.rb +77 -0
- data/samples/factorize-headers-peimports.rb +109 -0
- data/samples/factorize-headers.rb +43 -0
- data/samples/gdbclient.rb +583 -0
- data/samples/generate_libsigs.rb +102 -0
- data/samples/hotfix_gtk_dbg.rb +59 -0
- data/samples/install_win_env.rb +78 -0
- data/samples/lindebug.rb +924 -0
- data/samples/linux_injectsyscall.rb +95 -0
- data/samples/machoencode.rb +31 -0
- data/samples/metasm-shell.rb +91 -0
- data/samples/pe-hook.rb +69 -0
- data/samples/pe-ia32-cpuid.rb +203 -0
- data/samples/pe-mips.rb +35 -0
- data/samples/pe-shutdown.rb +78 -0
- data/samples/pe-testrelocs.rb +51 -0
- data/samples/pe-testrsrc.rb +24 -0
- data/samples/pe_listexports.rb +31 -0
- data/samples/peencode.rb +19 -0
- data/samples/peldr.rb +494 -0
- data/samples/preprocess-flatten.rb +19 -0
- data/samples/r0trace.rb +308 -0
- data/samples/rubstop.rb +399 -0
- data/samples/scan_pt_gnu_stack.rb +54 -0
- data/samples/scanpeexports.rb +62 -0
- data/samples/shellcode-c.rb +40 -0
- data/samples/shellcode-dynlink.rb +146 -0
- data/samples/source.asm +34 -0
- data/samples/struct_offset.rb +47 -0
- data/samples/testpe.rb +32 -0
- data/samples/testraw.rb +45 -0
- data/samples/win32genloader.rb +132 -0
- data/samples/win32hooker-advanced.rb +169 -0
- data/samples/win32hooker.rb +96 -0
- data/samples/win32livedasm.rb +33 -0
- data/samples/win32remotescan.rb +133 -0
- data/samples/wintrace.rb +92 -0
- data/tests/all.rb +8 -0
- data/tests/dasm.rb +39 -0
- data/tests/dynldr.rb +35 -0
- data/tests/encodeddata.rb +132 -0
- data/tests/ia32.rb +82 -0
- data/tests/mips.rb +116 -0
- data/tests/parse_c.rb +239 -0
- data/tests/preprocessor.rb +269 -0
- data/tests/x86_64.rb +62 -0
- metadata +255 -0
@@ -0,0 +1,3938 @@
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
3
|
+
#
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
5
|
+
|
6
|
+
|
7
|
+
require 'metasm/main'
|
8
|
+
require 'metasm/preprocessor'
|
9
|
+
|
10
|
+
module Metasm
|
11
|
+
# c parser
|
12
|
+
# inspired from http://www.math.grin.edu/~stone/courses/languages/C-syntax.xhtml
|
13
|
+
module C
|
14
|
+
Keyword = %w[struct union enum if else for while do switch goto
|
15
|
+
register extern auto static typedef const volatile
|
16
|
+
void int float double char signed unsigned long short
|
17
|
+
case continue break return default __attribute__
|
18
|
+
asm __asm __asm__ sizeof typeof
|
19
|
+
__declspec __cdecl __stdcall __fastcall __noreturn
|
20
|
+
inline __inline __inline__ __volatile__
|
21
|
+
__int8 __int16 __int32 __int64
|
22
|
+
__builtin_offsetof
|
23
|
+
].inject({}) { |h, w| h.update w => true }
|
24
|
+
|
25
|
+
class Statement
|
26
|
+
end
|
27
|
+
|
28
|
+
module Typed # allows quick testing whether an object is an CExpr or a Variable
|
29
|
+
end
|
30
|
+
|
31
|
+
class Block < Statement
|
32
|
+
attr_accessor :symbol # hash name => Type/Variable/enum value
|
33
|
+
attr_accessor :struct # hash name => Struct/Union/Enum
|
34
|
+
attr_accessor :outer # parent block
|
35
|
+
attr_accessor :statements # array of Statement/Declaration
|
36
|
+
attr_accessor :anonymous_enums # array of anonymous Enum
|
37
|
+
|
38
|
+
def initialize(outer, statements=[], symbol={}, struct={})
|
39
|
+
@outer = outer
|
40
|
+
@statements = statements
|
41
|
+
@symbol = symbol
|
42
|
+
@struct = struct
|
43
|
+
end
|
44
|
+
|
45
|
+
def struct_ancestors
|
46
|
+
@outer ? @outer.struct_ancestors.merge(@struct) : @struct
|
47
|
+
end
|
48
|
+
|
49
|
+
def symbol_ancestors
|
50
|
+
@outer ? @outer.symbol_ancestors.merge(@symbol) : @symbol
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
module Attributes
|
55
|
+
attr_accessor :attributes
|
56
|
+
|
57
|
+
DECLSPECS = %w[cdecl stdcall fastcall inline naked thiscall noreturn]
|
58
|
+
|
59
|
+
# parses a sequence of __attribute__((anything)) into self.attributes (array of string)
|
60
|
+
def parse_attributes(parser, allow_declspec = false)
|
61
|
+
while tok = parser.skipspaces and tok.type == :string
|
62
|
+
case keyword = tok.raw
|
63
|
+
when '__attribute__', '__declspec' # synonymous: __attribute__((foo)) == __declspec(foo)
|
64
|
+
raise tok || parser if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '('
|
65
|
+
raise tok || parser if keyword == '__attribute__' and (not tok = parser.skipspaces or tok.type != :punct or tok.raw != '(')
|
66
|
+
nest = 0
|
67
|
+
attrib = ''
|
68
|
+
loop do
|
69
|
+
raise parser if not tok = parser.skipspaces
|
70
|
+
if tok.type == :punct and tok.raw == ')'
|
71
|
+
if nest == 0
|
72
|
+
raise tok || parser if keyword == '__attribute__' and (not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')')
|
73
|
+
break
|
74
|
+
else
|
75
|
+
nest -= 1
|
76
|
+
end
|
77
|
+
elsif tok.type == :punct and tok.raw == '('
|
78
|
+
nest += 1
|
79
|
+
elsif nest == 0 and tok.type == :punct and tok.raw == ','
|
80
|
+
raise tok || parser if not allow_declspec and DECLSPECS.include? attrib
|
81
|
+
add_attribute attrib
|
82
|
+
attrib = ''
|
83
|
+
next
|
84
|
+
end
|
85
|
+
attrib << tok.raw
|
86
|
+
end
|
87
|
+
raise tok || parser, "attr #{attrib.inspect} not allowed here" if not allow_declspec and DECLSPECS.include? attrib
|
88
|
+
else
|
89
|
+
if allow_declspec and DECLSPECS.include? keyword.gsub('_', '')
|
90
|
+
attrib = keyword.gsub('_', '')
|
91
|
+
else break
|
92
|
+
end
|
93
|
+
end
|
94
|
+
add_attribute(attrib)
|
95
|
+
end
|
96
|
+
parser.unreadtok tok
|
97
|
+
end
|
98
|
+
|
99
|
+
# checks if the object has an attribute in its attribute list
|
100
|
+
def has_attribute(attr)
|
101
|
+
attributes.to_a.include? attr
|
102
|
+
end
|
103
|
+
|
104
|
+
# adds an attribute to the object attribute list if it is not already in it
|
105
|
+
def add_attribute(attr)
|
106
|
+
(@attributes ||= []) << attr if not has_attribute(attr)
|
107
|
+
end
|
108
|
+
|
109
|
+
# checks if the object has an attributes a la __attribute__((attr(stuff))), returns 'stuff' (raw, no split on ',' or anything)
|
110
|
+
def has_attribute_var(attr)
|
111
|
+
$1 if attributes.to_a.find { |a| a =~ /^#{attr}\((.*)\)$/ }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class Type
|
116
|
+
include Attributes
|
117
|
+
attr_accessor :qualifier # const volatile
|
118
|
+
|
119
|
+
def pointer? ; false end
|
120
|
+
def arithmetic? ; false end
|
121
|
+
def integral? ; false end
|
122
|
+
def float? ; false end
|
123
|
+
def void? ; false end
|
124
|
+
def base ; self end
|
125
|
+
def untypedef ; self end
|
126
|
+
|
127
|
+
def parse_initializer(parser, scope)
|
128
|
+
raise parser, 'expr expected' if not ret = CExpression.parse(parser, scope, false)
|
129
|
+
p, i = pointer?, integral?
|
130
|
+
r = ret.reduce(parser) if p or i
|
131
|
+
if (not p and not i) or (i and not r.kind_of? ::Integer) or (p and r != 0)
|
132
|
+
parser.check_compatible_type(parser, ret.type, self)
|
133
|
+
end
|
134
|
+
ret
|
135
|
+
end
|
136
|
+
|
137
|
+
def parse_initializer_designator(parser, scope, value, idx, root=true)
|
138
|
+
if not root and (not nt = parser.skipspaces or nt.type != :punct or nt.raw != '=')
|
139
|
+
raise nt || parser, '"=" expected'
|
140
|
+
end
|
141
|
+
value[idx] = parse_initializer(parser, scope)
|
142
|
+
idx + 1
|
143
|
+
end
|
144
|
+
end
|
145
|
+
class BaseType < Type
|
146
|
+
attr_accessor :name # :int :long :longlong :short :double :longdouble :float :char :void :__int8/16/32/64
|
147
|
+
attr_accessor :specifier # sign specifier only
|
148
|
+
|
149
|
+
def arithmetic? ; @name != :void end
|
150
|
+
def integral? ; [:char, :short, :int, :long, :longlong, :ptr,
|
151
|
+
:__int8, :__int16, :__int32, :__int64].include? @name end
|
152
|
+
def signed? ; specifier != :unsigned end
|
153
|
+
def float? ; [:float, :double, :longdouble].include? @name end
|
154
|
+
def void? ; @name == :void end
|
155
|
+
def align(parser) @name == :double ? 4 : parser.typesize[@name] end
|
156
|
+
|
157
|
+
def initialize(name, *specs)
|
158
|
+
@name = name
|
159
|
+
specs.each { |s|
|
160
|
+
case s
|
161
|
+
when :const, :volatile; (@qualifier ||= []) << s
|
162
|
+
when :signed, :unsigned; @specifier = s
|
163
|
+
when nil
|
164
|
+
else raise "internal error, got #{name.inspect} #{specs.inspect}"
|
165
|
+
end
|
166
|
+
}
|
167
|
+
end
|
168
|
+
|
169
|
+
def ==(o)
|
170
|
+
o.object_id == self.object_id or
|
171
|
+
(o.class == self.class and o.name == self.name and o.specifier == self.specifier and o.attributes == self.attributes)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
class TypeDef < Type
|
175
|
+
attr_accessor :name
|
176
|
+
attr_accessor :type
|
177
|
+
attr_accessor :backtrace
|
178
|
+
|
179
|
+
def initialize(name, type, backtrace)
|
180
|
+
@name, @type, @backtrace = name, type, backtrace
|
181
|
+
end
|
182
|
+
|
183
|
+
def parse_initializer(parser, scope)
|
184
|
+
@type.parse_initializer(parser, scope)
|
185
|
+
end
|
186
|
+
|
187
|
+
def pointer? ; @type.pointer? end
|
188
|
+
def arithmetic? ; @type.arithmetic? end
|
189
|
+
def integral? ; @type.integral? end
|
190
|
+
def signed? ; @type.signed? end # relevant only if integral? returns true
|
191
|
+
def float? ; @type.float? end
|
192
|
+
def void? ; @type.void? end
|
193
|
+
def untypedef ; @type.untypedef end
|
194
|
+
def align(parser) @type.align(parser) end # XXX __attribute__ ?
|
195
|
+
def pointed ; @type.pointed end
|
196
|
+
end
|
197
|
+
class Function < Type
|
198
|
+
attr_accessor :type # return type
|
199
|
+
attr_accessor :args # [name, Variable]
|
200
|
+
attr_accessor :varargs # true/false
|
201
|
+
|
202
|
+
def initialize(type=nil, args=nil)
|
203
|
+
@type = type
|
204
|
+
@args = args if args
|
205
|
+
end
|
206
|
+
|
207
|
+
def base ; @type.base ; end
|
208
|
+
end
|
209
|
+
class Union < Type
|
210
|
+
attr_accessor :members # [Variable]
|
211
|
+
attr_accessor :bits # [bits] or nil
|
212
|
+
attr_accessor :name
|
213
|
+
attr_accessor :backtrace
|
214
|
+
|
215
|
+
attr_accessor :fldoffset, :fldbitoffset, :fldlist
|
216
|
+
|
217
|
+
def align(parser) @members.to_a.map { |m| m.type.align(parser) }.max end
|
218
|
+
|
219
|
+
# there is only one instance of a given named struct per parser
|
220
|
+
# so we just compare struct names here
|
221
|
+
# for comparison between parsers, see #compare_deep
|
222
|
+
def ==(o)
|
223
|
+
o.object_id == self.object_id or
|
224
|
+
(o.class == self.class and o.name == self.name and ((o.name and true) or compare_deep(o)))
|
225
|
+
end
|
226
|
+
|
227
|
+
# compare to another structure, comparing members recursively (names and type)
|
228
|
+
# returns true if the self is same as o
|
229
|
+
def compare_deep(o, seen = [])
|
230
|
+
return true if o.object_id == self.object_id
|
231
|
+
return if o.class != self.class or o.name != self.name or o.attributes != self.attributes
|
232
|
+
o.members.to_a.zip(self.members.to_a).each { |om, sm|
|
233
|
+
return if om.name != sm.name
|
234
|
+
return if om.type != sm.type
|
235
|
+
if om.type.pointer?
|
236
|
+
ot = om.type
|
237
|
+
st = sm.type
|
238
|
+
500.times { # limit for selfpointers (shouldnt happen)
|
239
|
+
break if not ot.pointer?
|
240
|
+
ot = ot.pointed.untypedef
|
241
|
+
st = st.pointed.untypedef
|
242
|
+
}
|
243
|
+
if ot.kind_of?(C::Union) and ot.name and not seen.include?(ot)
|
244
|
+
return if not st.compare_deep(ot, seen+[ot])
|
245
|
+
end
|
246
|
+
end
|
247
|
+
}
|
248
|
+
true
|
249
|
+
end
|
250
|
+
|
251
|
+
def findmember(name, igncase=false)
|
252
|
+
raise parser, 'undefined structure' if not @members
|
253
|
+
return @fldlist[name] if fldlist and @fldlist[name]
|
254
|
+
|
255
|
+
name = name.downcase if igncase
|
256
|
+
if m = @members.find { |m_| (n = m_.name) and (igncase ? n.downcase : n) == name }
|
257
|
+
return m
|
258
|
+
else
|
259
|
+
@members.each { |m_|
|
260
|
+
if t = m_.type.untypedef and t.kind_of? Union and mm = t.findmember(name, igncase)
|
261
|
+
return mm
|
262
|
+
end
|
263
|
+
}
|
264
|
+
end
|
265
|
+
|
266
|
+
nil
|
267
|
+
end
|
268
|
+
|
269
|
+
def offsetof(parser, name)
|
270
|
+
raise parser, 'undefined structure' if not @members
|
271
|
+
update_member_cache(parser) if not fldlist
|
272
|
+
return 0 if @fldlist[name]
|
273
|
+
|
274
|
+
if name.kind_of?(Variable)
|
275
|
+
return 0 if @members.include? name
|
276
|
+
raise ParseError, 'unknown union member'
|
277
|
+
end
|
278
|
+
|
279
|
+
raise parser, 'unknown union member' if not findmember(name)
|
280
|
+
|
281
|
+
@members.find { |m|
|
282
|
+
m.type.untypedef.kind_of? Union and m.type.untypedef.findmember(name)
|
283
|
+
}.type.untypedef.offsetof(parser, name)
|
284
|
+
end
|
285
|
+
|
286
|
+
def bitoffsetof(parser, name)
|
287
|
+
raise parser, 'undefined structure' if not @members
|
288
|
+
update_member_cache(parser) if not fldlist
|
289
|
+
return if @fldlist[name] or @members.include?(name)
|
290
|
+
raise parser, 'undefined union' if not @members
|
291
|
+
raise parser, 'unknown union member' if not findmember(name)
|
292
|
+
|
293
|
+
@members.find { |m|
|
294
|
+
m.type.untypedef.kind_of? Union and m.type.untypedef.findmember(name)
|
295
|
+
}.type.untypedef.bitoffsetof(parser, name)
|
296
|
+
end
|
297
|
+
|
298
|
+
def parse_members(parser, scope)
|
299
|
+
@fldlist = nil if fldlist # invalidate fld offset cache
|
300
|
+
@members = []
|
301
|
+
# parse struct/union members in definition
|
302
|
+
loop do
|
303
|
+
raise parser if not tok = parser.skipspaces
|
304
|
+
break if tok.type == :punct and tok.raw == '}'
|
305
|
+
parser.unreadtok tok
|
306
|
+
|
307
|
+
raise tok, 'invalid struct member type' if not basetype = Variable.parse_type(parser, scope)
|
308
|
+
loop do
|
309
|
+
member = basetype.dup
|
310
|
+
member.parse_declarator(parser, scope)
|
311
|
+
member.type.length ||= 0 if member.type.kind_of?(Array) # struct { char blarg[]; };
|
312
|
+
raise member.backtrace, 'member redefinition' if member.name and @members.find { |m| m.name == member.name }
|
313
|
+
@members << member
|
314
|
+
|
315
|
+
raise tok || parser if not tok = parser.skipspaces or tok.type != :punct
|
316
|
+
|
317
|
+
if tok.raw == ':' # bits
|
318
|
+
raise tok, 'bad type for bitslice' if not member.type.integral?
|
319
|
+
bits = nil
|
320
|
+
raise tok, "bad bit count #{bits.inspect}" if not bits = CExpression.parse(parser, scope, false) or
|
321
|
+
not bits.constant? or !(bits = bits.reduce(parser)).kind_of? ::Integer
|
322
|
+
#raise tok, 'need more bits' if bits > 8*parser.sizeof(member)
|
323
|
+
# WORD wReserved:17; => yay windows.h
|
324
|
+
(@bits ||= [])[@members.length-1] = bits
|
325
|
+
raise tok || parser, '"," or ";" expected' if not tok = parser.skipspaces or tok.type != :punct
|
326
|
+
end
|
327
|
+
|
328
|
+
case tok.raw
|
329
|
+
when ';'; break
|
330
|
+
when ','
|
331
|
+
when '}'; parser.unreadtok(tok); break
|
332
|
+
else raise tok, '"," or ";" expected'
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
parse_attributes(parser)
|
337
|
+
end
|
338
|
+
|
339
|
+
# updates the @fldoffset / @fldbitoffset hash storing the offset of members
|
340
|
+
def update_member_cache(parser)
|
341
|
+
@fldlist = {}
|
342
|
+
@members.to_a.each { |m|
|
343
|
+
@fldlist[m.name] = m if m.name
|
344
|
+
}
|
345
|
+
end
|
346
|
+
|
347
|
+
def parse_initializer(parser, scope)
|
348
|
+
if tok = parser.skipspaces and tok.type == :punct and tok.raw == '{'
|
349
|
+
# struct x toto = { 1, .4, .member[0][6].bla = 12 };
|
350
|
+
raise tok, 'undefined struct' if not @members
|
351
|
+
ret = []
|
352
|
+
if tok = parser.skipspaces and (tok.type != :punct or tok.raw != '}')
|
353
|
+
parser.unreadtok tok
|
354
|
+
idx = 0
|
355
|
+
loop do
|
356
|
+
idx = parse_initializer_designator(parser, scope, ret, idx, true)
|
357
|
+
raise tok || parser, '"," or "}" expected' if not tok = parser.skipspaces or tok.type != :punct or (tok.raw != '}' and tok.raw != ',')
|
358
|
+
break if tok.raw == '}'
|
359
|
+
raise tok, 'struct is smaller than that' if idx >= @members.length
|
360
|
+
end
|
361
|
+
end
|
362
|
+
ret
|
363
|
+
else
|
364
|
+
parser.unreadtok tok
|
365
|
+
super(parser, scope)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
# parses a designator+initializer eg '.toto = 4' or '.tutu[42][12].bla = 16' or (root ? '4' : '=4')
|
370
|
+
def parse_initializer_designator(parser, scope, value, idx, root=true)
|
371
|
+
if nt = parser.skipspaces and nt.type == :punct and nt.raw == '.' and
|
372
|
+
nnt = parser.skipspaces and nnt.type == :string and
|
373
|
+
findmember(nnt.raw)
|
374
|
+
raise nnt, 'unhandled indirect initializer' if not nidx = @members.index(@members.find { |m| m.name == nnt.raw }) # TODO
|
375
|
+
if not root
|
376
|
+
value[idx] ||= [] # AryRecorder may change [] to AryRec.new, can't do v = v[i] ||= []
|
377
|
+
value = value[idx]
|
378
|
+
end
|
379
|
+
idx = nidx
|
380
|
+
@members[idx].type.untypedef.parse_initializer_designator(parser, scope, value, idx, false)
|
381
|
+
else
|
382
|
+
parser.unreadtok nnt
|
383
|
+
if root
|
384
|
+
parser.unreadtok nt
|
385
|
+
value[idx] = @members[idx].type.parse_initializer(parser, scope)
|
386
|
+
else
|
387
|
+
raise nt || parser, '"=" expected' if not nt or nt.type != :punct or nt.raw != '='
|
388
|
+
value[idx] = parse_initializer(parser, scope)
|
389
|
+
end
|
390
|
+
end
|
391
|
+
idx + 1
|
392
|
+
end
|
393
|
+
end
|
394
|
+
class Struct < Union
|
395
|
+
attr_accessor :pack
|
396
|
+
|
397
|
+
def align(parser) [@members.to_a.map { |m| m.type.align(parser) }.max || 1, (pack || 8)].min end
|
398
|
+
|
399
|
+
def offsetof(parser, name)
|
400
|
+
raise parser, 'undefined structure' if not @members
|
401
|
+
update_member_cache(parser) if not fldlist
|
402
|
+
return @fldoffset[name] if @fldoffset[name]
|
403
|
+
|
404
|
+
# this is almost never reached, only for <struct>.offsetof(anonymoussubstructmembername)
|
405
|
+
raise parser, 'unknown structure member' if (name.kind_of?(::String) ? !findmember(name) : !@members.include?(name))
|
406
|
+
|
407
|
+
indirect = true if name.kind_of?(::String) and not @fldlist[name]
|
408
|
+
|
409
|
+
al = align(parser)
|
410
|
+
off = 0
|
411
|
+
bit_off = 0
|
412
|
+
isz = nil
|
413
|
+
|
414
|
+
@members.each_with_index { |m, i|
|
415
|
+
if bits and b = @bits[i]
|
416
|
+
if not isz
|
417
|
+
mal = [m.type.align(parser), al].min
|
418
|
+
off = (off + mal - 1) / mal * mal
|
419
|
+
end
|
420
|
+
isz = parser.sizeof(m)
|
421
|
+
if b == 0 or (bit_off > 0 and bit_off + b > 8*isz)
|
422
|
+
bit_off = 0
|
423
|
+
mal = [m.type.align(parser), al].min
|
424
|
+
off = (off + isz + mal - 1) / mal * mal
|
425
|
+
end
|
426
|
+
break if m.name == name or m == name
|
427
|
+
bit_off += b
|
428
|
+
else
|
429
|
+
if isz
|
430
|
+
off += isz
|
431
|
+
bit_off = 0
|
432
|
+
isz = nil
|
433
|
+
end
|
434
|
+
mal = [m.type.align(parser), al].min
|
435
|
+
off = (off + mal - 1) / mal * mal
|
436
|
+
if m.name == name or m == name
|
437
|
+
break
|
438
|
+
elsif indirect and m.type.untypedef.kind_of? Union and m.type.untypedef.findmember(name)
|
439
|
+
off += m.type.untypedef.offsetof(parser, name)
|
440
|
+
break
|
441
|
+
else
|
442
|
+
off += parser.sizeof(m)
|
443
|
+
end
|
444
|
+
end
|
445
|
+
}
|
446
|
+
off
|
447
|
+
end
|
448
|
+
|
449
|
+
# returns the [bitoffset, bitlength] of the field if it is a bitfield
|
450
|
+
# this should be added to the offsetof(field)
|
451
|
+
def bitoffsetof(parser, name)
|
452
|
+
raise parser, 'undefined structure' if not @members
|
453
|
+
update_member_cache(parser) if not fldlist
|
454
|
+
return @fldbitoffset[name] if fldbitoffset and @fldbitoffset[name]
|
455
|
+
return if @fldlist[name] or @members.include?(name)
|
456
|
+
raise parser, 'undefined union' if not @members
|
457
|
+
raise parser, 'unknown union member' if not findmember(name)
|
458
|
+
|
459
|
+
@members.find { |m|
|
460
|
+
m.type.untypedef.kind_of? Union and m.type.untypedef.findmember(name)
|
461
|
+
}.type.untypedef.bitoffsetof(parser, name)
|
462
|
+
end
|
463
|
+
|
464
|
+
def parse_members(parser, scope)
|
465
|
+
super(parser, scope)
|
466
|
+
|
467
|
+
if has_attribute 'packed'
|
468
|
+
@pack = 1
|
469
|
+
elsif p = has_attribute_var('pack')
|
470
|
+
@pack = p[/\d+/].to_i
|
471
|
+
raise parser, "illegal struct pack(#{p})" if @pack == 0
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
# updates the @fldoffset / @fldbitoffset hash storing the offset of members
|
476
|
+
def update_member_cache(parser)
|
477
|
+
super(parser)
|
478
|
+
|
479
|
+
@fldoffset = {}
|
480
|
+
@fldbitoffset = {} if fldbitoffset
|
481
|
+
|
482
|
+
al = align(parser)
|
483
|
+
off = 0
|
484
|
+
bit_off = 0
|
485
|
+
isz = nil
|
486
|
+
|
487
|
+
@members.each_with_index { |m, i|
|
488
|
+
if bits and b = @bits[i]
|
489
|
+
if not isz
|
490
|
+
mal = [m.type.align(parser), al].min
|
491
|
+
off = (off + mal - 1) / mal * mal
|
492
|
+
end
|
493
|
+
isz = parser.sizeof(m)
|
494
|
+
if b == 0 or (bit_off > 0 and bit_off + b > 8*isz)
|
495
|
+
bit_off = 0
|
496
|
+
mal = [m.type.align(parser), al].min
|
497
|
+
off = (off + isz + mal - 1) / mal * mal
|
498
|
+
end
|
499
|
+
if m.name
|
500
|
+
@fldoffset[m.name] = off
|
501
|
+
@fldbitoffset ||= {}
|
502
|
+
@fldbitoffset[m.name] = [bit_off, b]
|
503
|
+
end
|
504
|
+
bit_off += b
|
505
|
+
else
|
506
|
+
if isz
|
507
|
+
off += isz
|
508
|
+
bit_off = 0
|
509
|
+
isz = nil
|
510
|
+
end
|
511
|
+
mal = [m.type.align(parser), al].min
|
512
|
+
off = (off + mal - 1) / mal * mal
|
513
|
+
@fldoffset[m.name] = off if m.name
|
514
|
+
off += parser.sizeof(m)
|
515
|
+
end
|
516
|
+
}
|
517
|
+
end
|
518
|
+
end
|
519
|
+
class Enum < Type
|
520
|
+
# name => value
|
521
|
+
attr_accessor :members
|
522
|
+
attr_accessor :name
|
523
|
+
attr_accessor :backtrace
|
524
|
+
|
525
|
+
def align(parser) BaseType.new(:int).align(parser) end
|
526
|
+
|
527
|
+
def arithmetic?; true end
|
528
|
+
def integral?; true end
|
529
|
+
def signed?; false end
|
530
|
+
|
531
|
+
def parse_members(parser, scope)
|
532
|
+
val = -1
|
533
|
+
@members = {}
|
534
|
+
loop do
|
535
|
+
raise parser if not tok = parser.skipspaces
|
536
|
+
break if tok.type == :punct and tok.raw == '}'
|
537
|
+
|
538
|
+
name = tok.raw
|
539
|
+
raise tok, 'bad enum name' if tok.type != :string or Keyword[name] or (?0..?9).include?(name[0])
|
540
|
+
|
541
|
+
raise parser if not tok = parser.skipspaces
|
542
|
+
if tok.type == :punct and tok.raw == '='
|
543
|
+
raise tok || parser if not val = CExpression.parse(parser, scope, false) or not val = val.reduce(parser) or not tok = parser.skipspaces
|
544
|
+
else
|
545
|
+
val += 1
|
546
|
+
end
|
547
|
+
raise tok, "enum value #{name} redefinition" if scope.symbol[name] and scope.symbol[name] != val
|
548
|
+
@members[name] = val
|
549
|
+
scope.symbol[name] = val
|
550
|
+
|
551
|
+
if tok.type == :punct and tok.raw == '}'
|
552
|
+
break
|
553
|
+
elsif tok.type == :punct and tok.raw == ','
|
554
|
+
else raise tok, '"," or "}" expected'
|
555
|
+
end
|
556
|
+
end
|
557
|
+
parse_attributes(parser)
|
558
|
+
end
|
559
|
+
|
560
|
+
end
|
561
|
+
class Pointer < Type
|
562
|
+
attr_accessor :type
|
563
|
+
|
564
|
+
def initialize(type=nil)
|
565
|
+
@type = type
|
566
|
+
end
|
567
|
+
|
568
|
+
def pointer? ; true ; end
|
569
|
+
def arithmetic? ; true ; end
|
570
|
+
def base ; @type.base ; end
|
571
|
+
def align(parser) BaseType.new(:ptr).align(parser) end
|
572
|
+
def pointed ; @type end
|
573
|
+
|
574
|
+
def ==(o)
|
575
|
+
o.class == self.class and o.type == self.type
|
576
|
+
end
|
577
|
+
end
|
578
|
+
class Array < Pointer
|
579
|
+
attr_accessor :length
|
580
|
+
|
581
|
+
def initialize(type=nil, length=nil)
|
582
|
+
super(type)
|
583
|
+
@length = length if length
|
584
|
+
end
|
585
|
+
|
586
|
+
def align(parser) @type.align(parser) end
|
587
|
+
|
588
|
+
def parse_initializer(parser, scope)
|
589
|
+
raise parser, 'cannot initialize dynamic array' if @length.kind_of? CExpression
|
590
|
+
if tok = parser.skipspaces and tok.type == :punct and tok.raw == '{'
|
591
|
+
# struct x foo[] = { { 4 }, [12].tutu = 2 };
|
592
|
+
ret = []
|
593
|
+
if tok = parser.skipspaces and (tok.type != :punct or tok.raw != '}')
|
594
|
+
parser.unreadtok tok
|
595
|
+
idx = 0
|
596
|
+
loop do
|
597
|
+
idx = parse_initializer_designator(parser, scope, ret, idx, true)
|
598
|
+
raise tok || parser, '"," or "}" expected' if not tok = parser.skipspaces or tok.type != :punct or (tok.raw != '}' and tok.raw != ',')
|
599
|
+
break if tok.raw == '}'
|
600
|
+
# allow int x[] = {1, 2, 3, };
|
601
|
+
break if tok = parser.skipspaces and tok.type == :punct and tok.raw == '}'
|
602
|
+
parser.unreadtok tok
|
603
|
+
raise tok, 'array is smaller than that' if length and idx >= @length
|
604
|
+
end
|
605
|
+
end
|
606
|
+
ret
|
607
|
+
else
|
608
|
+
parser.unreadtok tok
|
609
|
+
i = super(parser, scope)
|
610
|
+
if i.kind_of? CExpression and not i.op and i.rexpr.kind_of? String and @length and i.rexpr.length > @length
|
611
|
+
puts tok.exception("initializer is too long (#{i.rexpr.length} for #@length)").message if $VERBOSE
|
612
|
+
i.rexpr = i.rexpr[0, @length]
|
613
|
+
end
|
614
|
+
i
|
615
|
+
end
|
616
|
+
end
|
617
|
+
|
618
|
+
# this class is a hack to support [1 ... 4] array initializer
|
619
|
+
# it stores the effects of subsequent initializers (eg [1 ... 4].toto[48].bla[2 ... 57] = 12)
|
620
|
+
# which are later played back on the range
|
621
|
+
class AryRecorder
|
622
|
+
attr_accessor :log
|
623
|
+
def initialize
|
624
|
+
@log = []
|
625
|
+
end
|
626
|
+
|
627
|
+
def []=(idx, val)
|
628
|
+
val = self.class.new if val == []
|
629
|
+
@log[idx] = val
|
630
|
+
end
|
631
|
+
|
632
|
+
def [](idx)
|
633
|
+
@log[idx]
|
634
|
+
end
|
635
|
+
|
636
|
+
def playback_idx(i)
|
637
|
+
case v = @log[i]
|
638
|
+
when self.class; v.playback
|
639
|
+
else v
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
643
|
+
def playback(ary=[])
|
644
|
+
@log.each_with_index { |v, i| ary[i] = playback_idx(i) }
|
645
|
+
ary
|
646
|
+
end
|
647
|
+
end
|
648
|
+
|
649
|
+
# parses a designator+initializer eg '[12] = 4' or '[42].bla = 16' or '[3 ... 8] = 28'
|
650
|
+
def parse_initializer_designator(parser, scope, value, idx, root=true)
|
651
|
+
# root = true for 1st invocation (x = { 4 }) => immediate value allowed
|
652
|
+
# or false for recursive invocations (x = { .y = 4 }) => need '=' sign before immediate
|
653
|
+
if nt = parser.skipspaces and nt.type == :punct and nt.raw == '['
|
654
|
+
if not root
|
655
|
+
value[idx] ||= [] # AryRecorder may change [] to AryRec.new, can't do v = v[i] ||= []
|
656
|
+
value = value[idx]
|
657
|
+
end
|
658
|
+
raise nt, 'const expected' if not idx = CExpression.parse(parser, scope) or not idx.constant? or not idx = idx.reduce(parser) or not idx.kind_of? ::Integer
|
659
|
+
nt = parser.skipspaces
|
660
|
+
if nt and nt.type == :punct and nt.raw == '.' # range
|
661
|
+
raise nt || parser, '".." expected' if not nt = parser.skipspaces or nt.type != :punct or nt.raw != '.'
|
662
|
+
raise nt || parser, '"." expected' if not nt = parser.skipspaces or nt.type != :punct or nt.raw != '.'
|
663
|
+
raise nt, 'const expected' if not eidx = CExpression.parse(parser, scope) or not eidx.constant? or not eidx = eidx.reduce(parser) or not eidx.kind_of? ::Integer
|
664
|
+
raise nt, 'bad range' if eidx < idx
|
665
|
+
nt = parser.skipspaces
|
666
|
+
realvalue = value
|
667
|
+
value = AryRecorder.new
|
668
|
+
end
|
669
|
+
raise nt || parser, '"]" expected' if not nt or nt.type != :punct or nt.raw != ']'
|
670
|
+
raise nt, 'array is smaller than that' if length and (eidx||idx) >= @length
|
671
|
+
@type.untypedef.parse_initializer_designator(parser, scope, value, idx, false)
|
672
|
+
if eidx
|
673
|
+
(idx..eidx).each { |i| realvalue[i] = value.playback_idx(idx) }
|
674
|
+
idx = eidx # next default value = eidx+1 (eg int x[] = { [1 ... 3] = 4, 5 } => x[4] = 5)
|
675
|
+
end
|
676
|
+
else
|
677
|
+
if root
|
678
|
+
parser.unreadtok nt
|
679
|
+
value[idx] = @type.parse_initializer(parser, scope)
|
680
|
+
else
|
681
|
+
raise nt || parser, '"=" expected' if not nt or nt.type != :punct or nt.raw != '='
|
682
|
+
value[idx] = parse_initializer(parser, scope)
|
683
|
+
end
|
684
|
+
end
|
685
|
+
idx + 1
|
686
|
+
end
|
687
|
+
end
|
688
|
+
|
689
|
+
class Variable
|
690
|
+
include Attributes
|
691
|
+
include Typed
|
692
|
+
|
693
|
+
attr_accessor :type
|
694
|
+
attr_accessor :initializer # CExpr / Block (for Functions)
|
695
|
+
attr_accessor :name
|
696
|
+
attr_accessor :storage # auto register static extern typedef
|
697
|
+
attr_accessor :backtrace # definition backtrace info (the name token)
|
698
|
+
|
699
|
+
def initialize(name=nil, type=nil)
|
700
|
+
@name, @type = name, type
|
701
|
+
end
|
702
|
+
end
|
703
|
+
|
704
|
+
# found in a block's Statements, used to know the initialization order
|
705
|
+
# eg { int i; i = 4; struct foo { int k; } toto = {i}; }
|
706
|
+
class Declaration
|
707
|
+
attr_accessor :var
|
708
|
+
def initialize(var)
|
709
|
+
@var = var
|
710
|
+
end
|
711
|
+
end
|
712
|
+
|
713
|
+
class If < Statement
|
714
|
+
attr_accessor :test # expression
|
715
|
+
attr_accessor :bthen, :belse # statements
|
716
|
+
def initialize(test, bthen, belse=nil)
|
717
|
+
@test = test
|
718
|
+
@bthen = bthen
|
719
|
+
@belse = belse if belse
|
720
|
+
end
|
721
|
+
|
722
|
+
def self.parse(parser, scope, nest)
|
723
|
+
tok = nil
|
724
|
+
raise tok || self, '"(" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '('
|
725
|
+
raise tok, 'expr expected' if not expr = CExpression.parse(parser, scope) or not expr.type.arithmetic?
|
726
|
+
raise tok || self, '")" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
|
727
|
+
bthen = parser.parse_statement scope, nest
|
728
|
+
if tok = parser.skipspaces and tok.type == :string and tok.raw == 'else'
|
729
|
+
belse = parser.parse_statement scope, nest
|
730
|
+
else
|
731
|
+
parser.unreadtok tok
|
732
|
+
end
|
733
|
+
|
734
|
+
new expr, bthen, belse
|
735
|
+
end
|
736
|
+
end
|
737
|
+
class For < Statement
|
738
|
+
attr_accessor :init, :test, :iter # CExpressions, init may be Block
|
739
|
+
attr_accessor :body
|
740
|
+
def initialize(init, test, iter, body)
|
741
|
+
@init, @test, @iter, @body = init, test, iter, body
|
742
|
+
end
|
743
|
+
|
744
|
+
def self.parse(parser, scope, nest)
|
745
|
+
tok = nil
|
746
|
+
raise tok || parser, '"(" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '('
|
747
|
+
init = forscope = Block.new(scope)
|
748
|
+
if not parser.parse_definition(forscope)
|
749
|
+
forscope = scope
|
750
|
+
init = CExpression.parse(parser, forscope)
|
751
|
+
raise tok || parser, '";" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ';'
|
752
|
+
end
|
753
|
+
test = CExpression.parse(parser, forscope)
|
754
|
+
raise tok || parser, '";" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ';'
|
755
|
+
raise tok, 'bad test expression in for loop' if test and not test.type.arithmetic?
|
756
|
+
iter = CExpression.parse(parser, forscope)
|
757
|
+
raise tok || parser, '")" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
|
758
|
+
|
759
|
+
new init, test, iter, parser.parse_statement(forscope, nest + [:loop])
|
760
|
+
end
|
761
|
+
end
|
762
|
+
class While < Statement
|
763
|
+
attr_accessor :test
|
764
|
+
attr_accessor :body
|
765
|
+
|
766
|
+
def initialize(test, body)
|
767
|
+
@test = test
|
768
|
+
@body = body
|
769
|
+
end
|
770
|
+
|
771
|
+
def self.parse(parser, scope, nest)
|
772
|
+
tok = nil
|
773
|
+
raise tok || parser, '"(" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '('
|
774
|
+
raise tok, 'expr expected' if not expr = CExpression.parse(parser, scope) or not expr.type.arithmetic?
|
775
|
+
raise tok || parser, '")" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
|
776
|
+
|
777
|
+
new expr, parser.parse_statement(scope, nest + [:loop])
|
778
|
+
end
|
779
|
+
end
|
780
|
+
class DoWhile < While
|
781
|
+
def self.parse(parser, scope, nest)
|
782
|
+
body = parser.parse_statement(scope, nest + [:loop])
|
783
|
+
tok = nil
|
784
|
+
raise tok || parser, '"while" expected' if not tok = parser.skipspaces or tok.type != :string or tok.raw != 'while'
|
785
|
+
raise tok || parser, '"(" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '('
|
786
|
+
raise tok, 'expr expected' if not expr = CExpression.parse(parser, scope) or not expr.type.arithmetic?
|
787
|
+
raise tok || parser, '")" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
|
788
|
+
parser.checkstatementend(tok)
|
789
|
+
|
790
|
+
new expr, body
|
791
|
+
end
|
792
|
+
end
|
793
|
+
class Switch < Statement
|
794
|
+
attr_accessor :test, :body
|
795
|
+
|
796
|
+
def initialize(test, body)
|
797
|
+
@test = test
|
798
|
+
@body = body
|
799
|
+
end
|
800
|
+
|
801
|
+
def self.parse(parser, scope, nest)
|
802
|
+
raise tok || parser, '"(" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '('
|
803
|
+
raise tok, 'expr expected' if not expr = CExpression.parse(parser, scope) or not expr.type.integral?
|
804
|
+
raise tok || parser, '")" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
|
805
|
+
|
806
|
+
new expr, parser.parse_statement(scope, nest + [:switch])
|
807
|
+
end
|
808
|
+
end
|
809
|
+
|
810
|
+
class Continue < Statement
|
811
|
+
end
|
812
|
+
class Break < Statement
|
813
|
+
end
|
814
|
+
class Goto < Statement
|
815
|
+
attr_accessor :target
|
816
|
+
def initialize(target)
|
817
|
+
@target = target
|
818
|
+
end
|
819
|
+
end
|
820
|
+
class Return < Statement
|
821
|
+
attr_accessor :value
|
822
|
+
def initialize(value)
|
823
|
+
@value = value
|
824
|
+
end
|
825
|
+
end
|
826
|
+
class Label < Statement
|
827
|
+
attr_accessor :name
|
828
|
+
attr_accessor :statement
|
829
|
+
def initialize(name, statement=nil)
|
830
|
+
@name, @statement = name, statement
|
831
|
+
end
|
832
|
+
end
|
833
|
+
class Case < Label
|
834
|
+
attr_accessor :expr, :exprup # exprup if range, expr may be 'default'
|
835
|
+
def initialize(expr, exprup, statement)
|
836
|
+
@expr, @statement = expr, statement
|
837
|
+
@exprup = exprup if exprup
|
838
|
+
end
|
839
|
+
|
840
|
+
def self.parse(parser, scope, nest)
|
841
|
+
raise parser, 'invalid case' if not expr = CExpression.parse(parser, scope) or not expr.constant? or not expr.type.integral?
|
842
|
+
raise tok || parser, '":" or "..." expected' if not tok = parser.skipspaces or tok.type != :punct or (tok.raw != ':' and tok.raw != '.')
|
843
|
+
if tok.raw == '.'
|
844
|
+
raise tok || parser, '".." expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '.'
|
845
|
+
raise tok || parser, '"." expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '.'
|
846
|
+
raise tok, 'invalid case range' if not exprup = CExpression.parse(parser, scope) or not exprup.constant? or not exprup.type.integral?
|
847
|
+
raise tok || parser, '":" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ':'
|
848
|
+
end
|
849
|
+
body = parser.parse_statement scope, nest
|
850
|
+
new expr, exprup, body
|
851
|
+
end
|
852
|
+
end
|
853
|
+
|
854
|
+
# inline asm statement
|
855
|
+
class Asm < Statement
|
856
|
+
include Attributes
|
857
|
+
attr_accessor :body # asm source (::String)
|
858
|
+
attr_accessor :output, :input, :clobber # I/O, gcc-style (::Array)
|
859
|
+
attr_accessor :backtrace # body Token
|
860
|
+
attr_accessor :volatile
|
861
|
+
|
862
|
+
def initialize(body, backtrace, output=nil, input=nil, clobber=nil, volatile=nil)
|
863
|
+
@body, @backtrace, @output, @input, @clobber, @volatile = body, backtrace, output, input, clobber, volatile
|
864
|
+
end
|
865
|
+
|
866
|
+
def self.parse(parser, scope)
|
867
|
+
if tok = parser.skipspaces and tok.type == :string and (tok.raw == 'volatile' or tok.raw == '__volatile__')
|
868
|
+
volatile = true
|
869
|
+
tok = parser.skipspaces
|
870
|
+
end
|
871
|
+
if not tok or tok.type != :punct or tok.raw != '('
|
872
|
+
# detect MS-style inline asm: "__asm .* __asm .*" or "asm { [\s.]* }"
|
873
|
+
ftok = tok
|
874
|
+
body = ''
|
875
|
+
if tok.type == :punct and tok.raw == '{'
|
876
|
+
loop do
|
877
|
+
raise ftok, 'unterminated asm block' if not tok = parser.lexer.readtok
|
878
|
+
break if tok.type == :punct and tok.raw == '}'
|
879
|
+
case tok.type
|
880
|
+
when :space; body << ' '
|
881
|
+
when :eol; body << "\n"
|
882
|
+
when :punct; body << tok.raw
|
883
|
+
when :quoted; body << tok.value.inspect # concat adjacent c strings
|
884
|
+
when :string
|
885
|
+
body << \
|
886
|
+
case tok.raw
|
887
|
+
when 'asm', '__asm', '__asm__'; "\n"
|
888
|
+
when '_emit'; 'db'
|
889
|
+
else tok.raw
|
890
|
+
end
|
891
|
+
end
|
892
|
+
end
|
893
|
+
# allow shell-style heredoc: asm <<EOS\n<asm>\nEOS
|
894
|
+
elsif tok.type == :punct and tok.raw == '<'
|
895
|
+
raise ftok, 'bad asm heredoc' if not tok = parser.lexer.readtok or tok.type != :punct or tok.raw != '<'
|
896
|
+
delimiter = parser.lexer.readtok
|
897
|
+
if delimiter.type == :punct and delimiter.raw == '-'
|
898
|
+
skipspc = true
|
899
|
+
delimiter = parser.lexer.readtok
|
900
|
+
end
|
901
|
+
raise ftok, 'bad asm heredoc delim' if delimiter.type != :string or not tok = parser.lexer.readtok or tok.type != :eol
|
902
|
+
nl = true
|
903
|
+
loop do
|
904
|
+
raise ftok, 'unterminated heredoc' if not tok = parser.lexer.readtok
|
905
|
+
break if nl and tok.raw == delimiter.raw
|
906
|
+
raw = tok.raw
|
907
|
+
raw = "\n" if skipspc and tok.type == :eol
|
908
|
+
body << raw
|
909
|
+
nl = (tok.type == :eol and (raw[-1] == ?\n or raw[-1] == ?\r))
|
910
|
+
end
|
911
|
+
# MS single-instr: asm inc eax;
|
912
|
+
# also allow asm "foo bar\nbaz";
|
913
|
+
else
|
914
|
+
parser.lexer.unreadtok tok
|
915
|
+
loop do
|
916
|
+
break if not tok = parser.lexer.readtok or tok.type == :eol
|
917
|
+
case tok.type
|
918
|
+
when :space; body << ' '
|
919
|
+
when :punct
|
920
|
+
case tok.raw
|
921
|
+
when '}'
|
922
|
+
parser.lexer.unreadtok tok
|
923
|
+
break
|
924
|
+
else body << tok.raw
|
925
|
+
end
|
926
|
+
when :quoted; body << (body.empty? ? tok.value : tok.value.inspect) # asm "pop\nret" VS asm add al, 'z'
|
927
|
+
when :string
|
928
|
+
body << \
|
929
|
+
case tok.raw
|
930
|
+
when 'asm', '__asm', '__asm__'; "\n"
|
931
|
+
when '_emit'; 'db'
|
932
|
+
else tok.raw
|
933
|
+
end
|
934
|
+
end
|
935
|
+
end
|
936
|
+
end
|
937
|
+
return new(body, ftok, nil, nil, nil, volatile)
|
938
|
+
end
|
939
|
+
raise tok || parser, '"(" expected' if not tok or tok.type != :punct or tok.raw != '('
|
940
|
+
raise tok || parser, 'qstring expected' if not tok = parser.skipspaces or tok.type != :quoted
|
941
|
+
body = tok
|
942
|
+
ret = new body.value, body
|
943
|
+
tok = parser.skipspaces
|
944
|
+
raise tok || parser, '":" or ")" expected' if not tok or tok.type != :punct or (tok.raw != ':' and tok.raw != ')')
|
945
|
+
|
946
|
+
if tok.raw == ':'
|
947
|
+
ret.output = []
|
948
|
+
raise parser if not tok = parser.skipspaces
|
949
|
+
while tok.type == :quoted
|
950
|
+
type = tok.value
|
951
|
+
raise tok, 'expr expected' if not var = CExpression.parse_value(parser, scope)
|
952
|
+
ret.output << [type, var]
|
953
|
+
raise tok || parser, '":" or "," or ")" expected' if not tok = parser.skipspaces or tok.type != :punct or (tok.raw != ',' and tok.raw != ')' and tok.raw != ':')
|
954
|
+
break if tok.raw == ':' or tok.raw == ')'
|
955
|
+
raise tok || parser, 'qstring expected' if not tok = parser.skipspaces or tok.type != :quoted
|
956
|
+
end
|
957
|
+
end
|
958
|
+
if tok.raw == ':'
|
959
|
+
ret.input = []
|
960
|
+
raise parser if not tok = parser.skipspaces
|
961
|
+
while tok.type == :quoted
|
962
|
+
type = tok.value
|
963
|
+
raise tok, 'expr expected' if not var = CExpression.parse_value(parser, scope)
|
964
|
+
ret.input << [type, var]
|
965
|
+
raise tok || parser, '":" or "," or ")" expected' if not tok = parser.skipspaces or tok.type != :punct or (tok.raw != ',' and tok.raw != ')' and tok.raw != ':')
|
966
|
+
break if tok.raw == ':' or tok.raw == ')'
|
967
|
+
raise tok || parser, 'qstring expected' if not tok = parser.skipspaces or tok.type != :quoted
|
968
|
+
end
|
969
|
+
end
|
970
|
+
if tok.raw == ':'
|
971
|
+
ret.clobber = []
|
972
|
+
raise parser if not tok = parser.skipspaces
|
973
|
+
while tok.type == :quoted
|
974
|
+
ret.clobber << tok.value
|
975
|
+
raise tok || parser, '"," or ")" expected' if not tok = parser.skipspaces or tok.type != :punct or (tok.raw != ',' and tok.raw != ')')
|
976
|
+
break if tok.raw == ')'
|
977
|
+
raise tok || parser, 'qstring expected' if not tok = parser.skipspaces or tok.type != :quoted
|
978
|
+
end
|
979
|
+
end
|
980
|
+
raise tok || parser, '")" expected' if not tok or tok.type != :punct or tok.raw != ')'
|
981
|
+
ret.parse_attributes(parser)
|
982
|
+
parser.checkstatementend(tok)
|
983
|
+
ret
|
984
|
+
end
|
985
|
+
end
|
986
|
+
|
987
|
+
class CExpression < Statement
|
988
|
+
include Typed
|
989
|
+
|
990
|
+
# may be :,, :., :'->', :funcall (function, [arglist]), :[] (array indexing), nil (cast)
|
991
|
+
attr_accessor :op
|
992
|
+
# nil/CExpr/Variable/Label/::String( = :quoted/struct member name)/::Integer/::Float/Block
|
993
|
+
attr_accessor :lexpr, :rexpr
|
994
|
+
# a Type
|
995
|
+
attr_accessor :type
|
996
|
+
def initialize(l, o, r, t)
|
997
|
+
raise "invalid CExpr #{[l, o, r, t].inspect}" if (o and not o.kind_of? ::Symbol) or not t.kind_of? Type
|
998
|
+
@lexpr, @op, @rexpr, @type = l, o, r, t
|
999
|
+
end
|
1000
|
+
|
1001
|
+
# overwrites @lexpr @op @rexpr @type from the arg
|
1002
|
+
def replace(o)
|
1003
|
+
@lexpr, @op, @rexpr, @type = o.lexpr, o.op, o.rexpr, o.type
|
1004
|
+
self
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
# deep copy of the object
|
1008
|
+
# recurses only within CExpressions, anything else is copied by reference
|
1009
|
+
def deep_dup
|
1010
|
+
n = dup
|
1011
|
+
n.lexpr = n.lexpr.deep_dup if n.lexpr.kind_of? CExpression
|
1012
|
+
n.rexpr = n.rexpr.deep_dup if n.rexpr.kind_of? CExpression
|
1013
|
+
n.rexpr = n.rexpr.map { |e| e.kind_of?(CExpression) ? e.deep_dup : e } if n.rexpr.kind_of? ::Array
|
1014
|
+
n
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
# recursive constructor with automatic type inference
|
1018
|
+
# e.g. CExpression[foo, :+, [:*, bar]]
|
1019
|
+
# assumes root args are correctly typed (eg *foo => foo must be a pointer)
|
1020
|
+
# take care to use [int] with immediates, e.g. CExpression[foo, :+, [2]]
|
1021
|
+
# CExpr[some_cexpr] returns some_cexpr
|
1022
|
+
def self.[](*args)
|
1023
|
+
# sub-arrays in args are to be passed to self.[] recursively (syntaxic sugar)
|
1024
|
+
splat = lambda { |e| e.kind_of?(::Array) ? self[*e] : e }
|
1025
|
+
|
1026
|
+
args.shift while args.first == nil # CExpr[nil, :&, bla] => CExpr[:&, bla]
|
1027
|
+
|
1028
|
+
case args.length
|
1029
|
+
when 4
|
1030
|
+
op = args[1]
|
1031
|
+
if op == :funcall or op == :'?:'
|
1032
|
+
x2 = args[2].map { |a| splat[a] } if args[2]
|
1033
|
+
else
|
1034
|
+
x2 = splat[args[2]]
|
1035
|
+
end
|
1036
|
+
new(splat[args[0]], op, x2, args[3])
|
1037
|
+
when 3
|
1038
|
+
op = args[1]
|
1039
|
+
x1 = splat[args[0]]
|
1040
|
+
if op == :funcall or op == :'?:'
|
1041
|
+
x2 = args[2].map { |a| splat[a] } if args[2]
|
1042
|
+
else
|
1043
|
+
x2 = splat[args[2]]
|
1044
|
+
end
|
1045
|
+
|
1046
|
+
case op
|
1047
|
+
when :funcall
|
1048
|
+
rt = x1.type.untypedef
|
1049
|
+
rt = rt.type.untypedef if rt.pointer?
|
1050
|
+
new(x1, op, x2, rt.type)
|
1051
|
+
when :[]; new(x1, op, x2, x1.type.untypedef.type)
|
1052
|
+
when :+; new(x1, op, x2, (x2.type.pointer? ? x2.type : x1.type))
|
1053
|
+
when :-; new(x1, op, x2, ((x1.type.pointer? and x2.type.pointer?) ? BaseType.new(:int) : x2.type.pointer? ? x2.type : x1.type))
|
1054
|
+
when :'&&', :'||', :==, :'!=', :>, :<, :<=, :>=; new(x1, op, x2, BaseType.new(:int))
|
1055
|
+
when :'.', :'->'
|
1056
|
+
t = x1.type.untypedef
|
1057
|
+
t = t.type.untypedef if op == :'->' and x1.type.pointer?
|
1058
|
+
raise "parse error: #{t} has no member #{x2}" if not t.kind_of? Union or not m = t.findmember(x2)
|
1059
|
+
new(x1, op, x2, m.type)
|
1060
|
+
when :'?:'; new(x1, op, x2, x2[0].type)
|
1061
|
+
when :','; new(x1, op, x2, x2.type)
|
1062
|
+
else new(x1, op, x2, x1.type)
|
1063
|
+
end
|
1064
|
+
when 2
|
1065
|
+
x0 = splat[args[0]]
|
1066
|
+
x1 = splat[args[1]]
|
1067
|
+
x0, x1 = x1, x0 if x0.kind_of? Type
|
1068
|
+
if x1.kind_of? Type; new(nil, nil, x0, x1) # (cast)r
|
1069
|
+
elsif x0 == :*; new(nil, x0, x1, x1.type.untypedef.type) # *r
|
1070
|
+
elsif x0 == :& and x1.kind_of? CExpression and x1.type.kind_of? C::Array; new(nil, nil, x1, Pointer.new(x1.type.type))
|
1071
|
+
elsif x0 == :&; new(nil, x0, x1, Pointer.new(x1.type)) # &r
|
1072
|
+
elsif x0 == :'!'; new(nil, x0, x1, BaseType.new(:int)) # &r
|
1073
|
+
elsif x1.kind_of? ::Symbol; new(x0, x1, nil, x0.type) # l++
|
1074
|
+
else new(nil, x0, x1, x1.type) # +r
|
1075
|
+
end
|
1076
|
+
when 1
|
1077
|
+
x = splat[args[0]]
|
1078
|
+
case x
|
1079
|
+
when CExpression; x
|
1080
|
+
when ::Integer; new(nil, nil, x, BaseType.new(:int)) # XXX range => __int64 ?
|
1081
|
+
when ::Float; new(nil, nil, x, BaseType.new(:double))
|
1082
|
+
when ::String; new(nil, nil, x, Pointer.new(BaseType.new(:char)))
|
1083
|
+
else new(nil, nil, x, x.type)
|
1084
|
+
end
|
1085
|
+
else raise "parse error CExpr[*#{args.inspect}]"
|
1086
|
+
end
|
1087
|
+
end
|
1088
|
+
end
|
1089
|
+
|
1090
|
+
|
1091
|
+
class Parser
|
1092
|
+
# creates a new CParser, parses all top-level statements
|
1093
|
+
def self.parse(text)
|
1094
|
+
new.parse text
|
1095
|
+
end
|
1096
|
+
|
1097
|
+
# parses the current lexer content (or the text arg) for toplevel definitions
|
1098
|
+
def parse(text=nil, filename='<unk>', lineno=1)
|
1099
|
+
@lexer.feed text, filename, lineno if text
|
1100
|
+
nil while not @lexer.eos? and (parse_definition(@toplevel) or parse_toplevel_statement(@toplevel))
|
1101
|
+
raise @lexer.readtok || self, 'invalid definition' if not @lexer.eos?
|
1102
|
+
sanity_checks
|
1103
|
+
self
|
1104
|
+
end
|
1105
|
+
|
1106
|
+
# parses a C file
|
1107
|
+
def parse_file(file)
|
1108
|
+
parse(File.read(file), file)
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
attr_accessor :lexer, :toplevel, :typesize, :pragma_pack
|
1112
|
+
attr_accessor :endianness
|
1113
|
+
attr_accessor :allow_bad_c
|
1114
|
+
# allowed arguments: ExeFormat, CPU, Preprocessor, Symbol (for the data model)
|
1115
|
+
def initialize(*args)
|
1116
|
+
model = args.grep(Symbol).first || :ilp32
|
1117
|
+
lexer = args.grep(Preprocessor).first || Preprocessor.new
|
1118
|
+
exe = args.grep(ExeFormat).first
|
1119
|
+
cpu = args.grep(CPU).first
|
1120
|
+
cpu ||= exe.cpu if exe
|
1121
|
+
@lexer = lexer
|
1122
|
+
@prev_pragma_callback = @lexer.pragma_callback
|
1123
|
+
@lexer.pragma_callback = lambda { |tok| parse_pragma_callback(tok) }
|
1124
|
+
@toplevel = Block.new(nil)
|
1125
|
+
@unreadtoks = []
|
1126
|
+
@endianness = cpu ? cpu.endianness : :big
|
1127
|
+
@typesize = { :void => 1, :__int8 => 1, :__int16 => 2, :__int32 => 4, :__int64 => 8,
|
1128
|
+
:char => 1, :float => 4, :double => 8, :longdouble => 12 }
|
1129
|
+
send model
|
1130
|
+
cpu.tune_cparser(self) if cpu
|
1131
|
+
exe.tune_cparser(self) if exe
|
1132
|
+
end
|
1133
|
+
|
1134
|
+
def ilp16
|
1135
|
+
@typesize.update :short => 2, :ptr => 2,
|
1136
|
+
:int => 2, :long => 4, :longlong => 4
|
1137
|
+
end
|
1138
|
+
|
1139
|
+
def lp32
|
1140
|
+
@typesize.update :short => 2, :ptr => 4,
|
1141
|
+
:int => 2, :long => 4, :longlong => 8
|
1142
|
+
end
|
1143
|
+
def ilp32
|
1144
|
+
@typesize.update :short => 2, :ptr => 4,
|
1145
|
+
:int => 4, :long => 4, :longlong => 8
|
1146
|
+
end
|
1147
|
+
|
1148
|
+
def llp64
|
1149
|
+
@typesize.update :short => 2, :ptr => 8,
|
1150
|
+
:int => 4, :long => 4, :longlong => 8
|
1151
|
+
end
|
1152
|
+
def lp64
|
1153
|
+
@typesize.update :short => 2, :ptr => 8,
|
1154
|
+
:int => 4, :long => 8, :longlong => 8
|
1155
|
+
end
|
1156
|
+
def ilp64
|
1157
|
+
@typesize.update :short => 2, :ptr => 8,
|
1158
|
+
:int => 8, :long => 8, :longlong => 8
|
1159
|
+
end
|
1160
|
+
|
1161
|
+
def parse_pragma_callback(otok)
|
1162
|
+
case otok.raw
|
1163
|
+
when 'pack'
|
1164
|
+
nil while lp = @lexer.readtok and lp.type == :space
|
1165
|
+
nil while rp = @lexer.readtok and rp.type == :space
|
1166
|
+
if not rp or rp.type != :punct or rp.raw != ')'
|
1167
|
+
v1 = rp
|
1168
|
+
nil while rp = @lexer.readtok and rp.type == :space
|
1169
|
+
end
|
1170
|
+
if rp and rp.type == :punct and rp.raw == ','
|
1171
|
+
nil while v2 = @lexer.readtok and v2.type == :space
|
1172
|
+
nil while rp = @lexer.readtok and rp.type == :space
|
1173
|
+
end
|
1174
|
+
raise otok if not rp or lp.type != :punct or rp.type != :punct or lp.raw != '(' or rp.raw != ')'
|
1175
|
+
raise otok if (v1 and v1.type != :string) or (v2 and (v2.type != :string or v2.raw =~ /[^\d]/))
|
1176
|
+
if not v1
|
1177
|
+
@pragma_pack = nil
|
1178
|
+
elsif v1.raw == 'push'
|
1179
|
+
@pragma_pack_stack ||= []
|
1180
|
+
@pragma_pack_stack << pragma_pack
|
1181
|
+
@pragma_pack = v2.raw.to_i if v2
|
1182
|
+
raise v2, 'bad pack value' if pragma_pack == 0
|
1183
|
+
elsif v1.raw == 'pop'
|
1184
|
+
@pragma_pack_stack ||= []
|
1185
|
+
raise v1, 'pack stack empty' if @pragma_pack_stack.empty?
|
1186
|
+
@pragma_pack = @pragma_pack_stack.pop
|
1187
|
+
@pragma_pack = v2.raw.to_i if v2 and v2.raw # #pragma pack(pop, 4) => pop stack, but use 4 as pack value (imho)
|
1188
|
+
raise v2, 'bad pack value' if @pragma_pack == 0
|
1189
|
+
elsif v1.raw =~ /^\d+$/
|
1190
|
+
raise v2, '2nd arg unexpected' if v2
|
1191
|
+
@pragma_pack = v1.raw.to_i
|
1192
|
+
raise v1, 'bad pack value' if @pragma_pack == 0
|
1193
|
+
else raise otok
|
1194
|
+
end
|
1195
|
+
# the caller checks for :eol
|
1196
|
+
when 'warning'
|
1197
|
+
if $DEBUG
|
1198
|
+
@prev_pragma_callback[otok]
|
1199
|
+
else
|
1200
|
+
# silent discard
|
1201
|
+
nil while tok = @lexer.readtok_nopp and tok.type != :eol
|
1202
|
+
@lexer.unreadtok tok
|
1203
|
+
end
|
1204
|
+
when 'prepare_visualstudio'
|
1205
|
+
prepare_visualstudio
|
1206
|
+
when 'prepare_gcc'
|
1207
|
+
prepare_gcc
|
1208
|
+
when 'data_model' # XXX use carefully, should be the very first thing parsed
|
1209
|
+
nil while lp = @lexer.readtok and lp.type == :space
|
1210
|
+
if lp.type != :string or lp.raw !~ /^s?[il]?lp(16|32|64)$/ or not respond_to? lp.raw
|
1211
|
+
raise lp, "invalid data model (use lp32/lp64/llp64/ilp64)"
|
1212
|
+
else
|
1213
|
+
send lp.raw
|
1214
|
+
end
|
1215
|
+
else @prev_pragma_callback[otok]
|
1216
|
+
end
|
1217
|
+
end
|
1218
|
+
|
1219
|
+
def prepare_visualstudio
|
1220
|
+
@lexer.define_weak('_WIN32')
|
1221
|
+
@lexer.define_weak('_WIN32_WINNT', 0x500)
|
1222
|
+
@lexer.define_weak('_INTEGRAL_MAX_BITS', 64)
|
1223
|
+
@lexer.define_weak('__w64')
|
1224
|
+
@lexer.define_weak('_cdecl', '__cdecl') # typo ? seen in winreg.h
|
1225
|
+
@lexer.define_weak('_fastcall', '__fastcall') # typo ? seen in ntddk.h
|
1226
|
+
@lexer.define_weak('_MSC_VER', 1300) # handle '#pragma once' and _declspec(noreturn)
|
1227
|
+
@lexer.define_weak('__forceinline', '__inline')
|
1228
|
+
@lexer.define_weak('__ptr32') # needed with msc_ver 1300, don't understand their use
|
1229
|
+
@lexer.define_weak('__ptr64')
|
1230
|
+
end
|
1231
|
+
|
1232
|
+
def prepare_gcc
|
1233
|
+
@lexer.define_weak('__GNUC__', 2) # otherwise __attribute__ is defined to void..
|
1234
|
+
@lexer.define_weak('__STDC__')
|
1235
|
+
@lexer.define_weak('__const', 'const')
|
1236
|
+
@lexer.define_weak('__signed', 'signed')
|
1237
|
+
@lexer.define_weak('__signed__', 'signed')
|
1238
|
+
@lexer.define_weak('__volatile', 'volatile')
|
1239
|
+
if not @lexer.definition['__builtin_constant_p']
|
1240
|
+
# magic macro to check if its arg is an immediate value
|
1241
|
+
@lexer.define_weak('__builtin_constant_p', '0')
|
1242
|
+
@lexer.definition['__builtin_constant_p'].args = [Preprocessor::Token.new([])]
|
1243
|
+
end
|
1244
|
+
@lexer.nodefine_strong('alloca') # TODO __builtin_alloca
|
1245
|
+
@lexer.hooked_include['stddef.h'] = <<EOH
|
1246
|
+
/* simplified, define all at first invocation. may break things... */
|
1247
|
+
#undef __need_ptrdiff_t
|
1248
|
+
#undef __need_size_t
|
1249
|
+
#undef __need_wint_t
|
1250
|
+
#undef __need_wchar_t
|
1251
|
+
#undef __need_NULL
|
1252
|
+
#undef NULL
|
1253
|
+
#if !defined (_STDDEF_H)
|
1254
|
+
#define _STDDEF_H
|
1255
|
+
#define __PTRDIFF_TYPE__ long int
|
1256
|
+
typedef __PTRDIFF_TYPE__ ptrdiff_t;
|
1257
|
+
#define __SIZE_TYPE__ long unsigned int
|
1258
|
+
typedef __SIZE_TYPE__ size_t;
|
1259
|
+
#define __WINT_TYPE__ unsigned int
|
1260
|
+
typedef __WINT_TYPE__ wint_t;
|
1261
|
+
#define __WCHAR_TYPE__ int
|
1262
|
+
typedef __WCHAR_TYPE__ wchar_t;
|
1263
|
+
#define NULL 0
|
1264
|
+
#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
|
1265
|
+
#endif
|
1266
|
+
EOH
|
1267
|
+
# TODO va_args
|
1268
|
+
@lexer.hooked_include['stdarg.h'] = <<EOH
|
1269
|
+
// TODO
|
1270
|
+
typedef void* __gnuc_va_list;
|
1271
|
+
/*
|
1272
|
+
typedef void* va_list;
|
1273
|
+
#define va_start(v, l)
|
1274
|
+
#define va_end(v)
|
1275
|
+
#define va_arg(v, l)
|
1276
|
+
#define va_copy(d, s)
|
1277
|
+
*/
|
1278
|
+
EOH
|
1279
|
+
@lexer.hooked_include['limits.h'] = <<EOH
|
1280
|
+
#define CHAR_BIT 8
|
1281
|
+
#define SCHAR_MIN (-128)
|
1282
|
+
#define SCHAR_MAX 127
|
1283
|
+
#define UCHAR_MAX 255
|
1284
|
+
#ifdef __CHAR_UNSIGNED__
|
1285
|
+
# define CHAR_MIN 0
|
1286
|
+
# define CHAR_MAX UCHAR_MAX
|
1287
|
+
#else
|
1288
|
+
# define CHAR_MIN SCHAR_MIN
|
1289
|
+
# define CHAR_MAX SCHAR_MAX
|
1290
|
+
#endif
|
1291
|
+
#define UINT_MAX #{(1 << (8*@typesize[:int]))-1}U
|
1292
|
+
#define INT_MAX (UINT_MAX >> 1)
|
1293
|
+
#define INT_MIN (-INT_MAX - 1)
|
1294
|
+
#define ULONG_MAX #{(1 << (8*@typesize[:long]))-1}UL
|
1295
|
+
#define LONG_MAX (ULONG_MAX >> 1L)
|
1296
|
+
#define LONG_MIN (-LONG_MAX - 1L)
|
1297
|
+
EOH
|
1298
|
+
end
|
1299
|
+
|
1300
|
+
# C sanity checks
|
1301
|
+
def sanity_checks
|
1302
|
+
return if not $VERBOSE
|
1303
|
+
# TODO
|
1304
|
+
end
|
1305
|
+
|
1306
|
+
# checks that the types are compatible (variable predeclaration, function argument..)
|
1307
|
+
# strict = false for func call/assignment (eg char compatible with int -- but int is incompatible with char)
|
1308
|
+
# output warnings only
|
1309
|
+
def check_compatible_type(tok, oldtype, newtype, strict = false, checked = [])
|
1310
|
+
return if not $VERBOSE
|
1311
|
+
oldtype = oldtype.untypedef
|
1312
|
+
newtype = newtype.untypedef
|
1313
|
+
oldtype = BaseType.new(:int) if oldtype.kind_of? Enum
|
1314
|
+
newtype = BaseType.new(:int) if newtype.kind_of? Enum
|
1315
|
+
|
1316
|
+
puts tok.exception('type qualifier mismatch').message if oldtype.qualifier.to_a.uniq.length > newtype.qualifier.to_a.uniq.length
|
1317
|
+
|
1318
|
+
# avoid infinite recursion
|
1319
|
+
return if checked.include? oldtype
|
1320
|
+
checked = checked + [oldtype]
|
1321
|
+
|
1322
|
+
begin
|
1323
|
+
case newtype
|
1324
|
+
when Function
|
1325
|
+
raise tok, 'type error' if not oldtype.kind_of? Function
|
1326
|
+
check_compatible_type tok, oldtype.type, newtype.type, strict, checked
|
1327
|
+
if oldtype.args and newtype.args
|
1328
|
+
if oldtype.args.length != newtype.args.length or
|
1329
|
+
oldtype.varargs != newtype.varargs
|
1330
|
+
raise tok, 'type error'
|
1331
|
+
end
|
1332
|
+
oldtype.args.zip(newtype.args) { |oa, na|
|
1333
|
+
# begin ; rescue ParseError: raise $!.message + "in parameter #{oa.name}" end
|
1334
|
+
check_compatible_type tok, oa.type, na.type, strict, checked
|
1335
|
+
}
|
1336
|
+
end
|
1337
|
+
when Pointer
|
1338
|
+
if oldtype.kind_of? BaseType and oldtype.integral?
|
1339
|
+
puts tok.exception('making pointer from integer without a cast').message
|
1340
|
+
return
|
1341
|
+
end
|
1342
|
+
raise tok, 'type error' if not oldtype.kind_of? Pointer
|
1343
|
+
hasvoid = true if (t = newtype.type.untypedef).kind_of? BaseType and t.name == :void
|
1344
|
+
hasvoid = true if (t = oldtype.type.untypedef).kind_of? BaseType and t.name == :void # struct foo *f = NULL;
|
1345
|
+
if strict and not hasvoid
|
1346
|
+
check_compatible_type tok, oldtype.type, newtype.type, strict, checked
|
1347
|
+
end
|
1348
|
+
when Union
|
1349
|
+
raise tok, 'type error' if not oldtype.class == newtype.class
|
1350
|
+
if oldtype.members and newtype.members
|
1351
|
+
if oldtype.members.length != newtype.members.length
|
1352
|
+
raise tok, 'bad member count'
|
1353
|
+
end
|
1354
|
+
oldtype.members.zip(newtype.members) { |om, nm|
|
1355
|
+
# raise tok if om.name and nm.name and om.name != nm.name # don't care
|
1356
|
+
check_compatible_type tok, om.type, nm.type, strict, checked
|
1357
|
+
}
|
1358
|
+
end
|
1359
|
+
when BaseType
|
1360
|
+
raise tok, 'type error' if not oldtype.kind_of? BaseType
|
1361
|
+
if strict
|
1362
|
+
if oldtype.name != newtype.name or
|
1363
|
+
oldtype.specifier != newtype.specifier
|
1364
|
+
raise tok, 'type error'
|
1365
|
+
end
|
1366
|
+
else
|
1367
|
+
raise tok, 'type error' if @typesize[newtype.name] == 0 and @typesize[oldtype.name] > 0
|
1368
|
+
puts tok.exception('type size mismatch, may lose bits').message if @typesize[oldtype.name] > @typesize[newtype.name]
|
1369
|
+
puts tok.exception('sign mismatch').message if oldtype.specifier != newtype.specifier and @typesize[newtype.name] == @typesize[oldtype.name]
|
1370
|
+
end
|
1371
|
+
end
|
1372
|
+
rescue ParseError
|
1373
|
+
raise $! if checked.length != 1 # bubble up
|
1374
|
+
oname = (oldtype.to_s rescue oldtype.class.name)
|
1375
|
+
nname = (newtype.to_s rescue newtype.class.name)
|
1376
|
+
puts $!.message + " incompatible type #{oname} to #{nname}"
|
1377
|
+
end
|
1378
|
+
end
|
1379
|
+
|
1380
|
+
# allows 'raise self'
|
1381
|
+
def exception(msg='EOF unexpected')
|
1382
|
+
@lexer.exception msg
|
1383
|
+
end
|
1384
|
+
|
1385
|
+
# reads a token, convert 'L"foo"' to a :quoted
|
1386
|
+
def readtok_longstr
|
1387
|
+
if t = @lexer.readtok and t.type == :string and t.raw == 'L' and
|
1388
|
+
nt = @lexer.readtok and nt.type == :quoted and nt.raw[0] == ?"
|
1389
|
+
nt.raw[0, 0] = 'L'
|
1390
|
+
nt
|
1391
|
+
elsif t and t.type == :punct and t.raw == '/' and
|
1392
|
+
# nt has not been read
|
1393
|
+
nt = @lexer.readtok and nt.type == :punct and nt.raw == '/'
|
1394
|
+
# windows.h has a #define some_type_name /##/, and VS interprets this as a comment..
|
1395
|
+
puts @lexer.exception('#defined //').message if $VERBOSE
|
1396
|
+
t = @lexer.readtok while t and t.type != :eol
|
1397
|
+
t
|
1398
|
+
else
|
1399
|
+
@lexer.unreadtok nt
|
1400
|
+
t
|
1401
|
+
end
|
1402
|
+
end
|
1403
|
+
private :readtok_longstr
|
1404
|
+
|
1405
|
+
# reads a token from self.lexer
|
1406
|
+
# concatenates strings, merges spaces/eol to ' ', handles wchar strings, allows $@_ in :string
|
1407
|
+
def readtok
|
1408
|
+
if not t = @unreadtoks.pop
|
1409
|
+
return if not t = readtok_longstr
|
1410
|
+
case t.type
|
1411
|
+
when :space, :eol
|
1412
|
+
# merge consecutive :space/:eol
|
1413
|
+
t = t.dup
|
1414
|
+
t.type = :space
|
1415
|
+
t.raw = ' '
|
1416
|
+
nil while nt = @lexer.readtok and (nt.type == :eol or nt.type == :space)
|
1417
|
+
@lexer.unreadtok nt
|
1418
|
+
|
1419
|
+
when :quoted
|
1420
|
+
# merge consecutive :quoted
|
1421
|
+
t = t.dup
|
1422
|
+
while nt = readtok_longstr
|
1423
|
+
case nt.type
|
1424
|
+
when :quoted
|
1425
|
+
if t.raw[0] == ?" and nt.raw[0, 2] == 'L"'
|
1426
|
+
# ensure wide prefix is set
|
1427
|
+
t.raw[0, 0] = 'L'
|
1428
|
+
end
|
1429
|
+
t.raw << ' ' << nt.raw
|
1430
|
+
t.value << nt.value
|
1431
|
+
when :space, :eol
|
1432
|
+
else break
|
1433
|
+
end
|
1434
|
+
end
|
1435
|
+
@lexer.unreadtok nt
|
1436
|
+
else
|
1437
|
+
if (t.type == :punct and (t.raw == '_' or t.raw == '@' or t.raw == '$')) or t.type == :string
|
1438
|
+
t = t.dup
|
1439
|
+
t.type = :string
|
1440
|
+
nt = nil
|
1441
|
+
t.raw << nt.raw while nt = @lexer.readtok and ((nt.type == :punct and (nt.raw == '_' or nt.raw == '@' or nt.raw == '$')) or nt.type == :string)
|
1442
|
+
@lexer.unreadtok nt
|
1443
|
+
end
|
1444
|
+
end
|
1445
|
+
end
|
1446
|
+
t
|
1447
|
+
end
|
1448
|
+
|
1449
|
+
def eos?
|
1450
|
+
@unreadtoks.empty? and @lexer.eos?
|
1451
|
+
end
|
1452
|
+
|
1453
|
+
def unreadtok(tok)
|
1454
|
+
@unreadtoks << tok if tok
|
1455
|
+
end
|
1456
|
+
|
1457
|
+
# returns the next non-space/non-eol token
|
1458
|
+
def skipspaces
|
1459
|
+
nil while t = readtok and t.type == :space
|
1460
|
+
t
|
1461
|
+
end
|
1462
|
+
|
1463
|
+
# checks that we are at the end of a statement, ie an ';' character (consumed), or a '}' (not consumed)
|
1464
|
+
# otherwise, raise either the given token or self.
|
1465
|
+
def checkstatementend(tok=nil)
|
1466
|
+
raise tok || self, '";" expected' if not tok = skipspaces or tok.type != :punct or (tok.raw != ';' and tok.raw != '}')
|
1467
|
+
unreadtok tok if tok.raw == '}'
|
1468
|
+
end
|
1469
|
+
|
1470
|
+
# returns the size of a type in bytes
|
1471
|
+
def sizeof(var, type=nil)
|
1472
|
+
var, type = nil, var if var.kind_of? Type and not type
|
1473
|
+
type ||= var.type
|
1474
|
+
# XXX double-check class apparition order ('when' checks inheritance)
|
1475
|
+
case type
|
1476
|
+
when Array
|
1477
|
+
case type.length
|
1478
|
+
when nil
|
1479
|
+
if var.kind_of? CExpression and not var.lexpr and not var.op and var.rexpr.kind_of? Variable
|
1480
|
+
var = var.rexpr
|
1481
|
+
end
|
1482
|
+
raise self, 'unknown array size' if not var.kind_of? Variable or not var.initializer
|
1483
|
+
init = var.initializer
|
1484
|
+
init = init.rexpr if init.kind_of? C::CExpression and not init.op and init.rexpr.kind_of? ::String
|
1485
|
+
case init
|
1486
|
+
when ::String; sizeof(nil, type.type) * (init.length + 1)
|
1487
|
+
when ::Array
|
1488
|
+
v = init.compact.first
|
1489
|
+
v ? (sizeof(nil, type.type) * init.length) : 0
|
1490
|
+
else sizeof(init)
|
1491
|
+
end
|
1492
|
+
when ::Integer; type.length * sizeof(type.type)
|
1493
|
+
when CExpression
|
1494
|
+
len = type.length.reduce(self)
|
1495
|
+
raise self, 'unknown array size' if not len.kind_of? ::Integer
|
1496
|
+
len * sizeof(type)
|
1497
|
+
else raise self, 'unknown array size'
|
1498
|
+
end
|
1499
|
+
when Pointer
|
1500
|
+
if var.kind_of? CExpression and not var.op and var.rexpr.kind_of? ::String
|
1501
|
+
# sizeof("lolz") => 5
|
1502
|
+
sizeof(nil, type.type) * (var.rexpr.length + 1)
|
1503
|
+
else
|
1504
|
+
@typesize[:ptr]
|
1505
|
+
end
|
1506
|
+
when Function
|
1507
|
+
# raise
|
1508
|
+
1 # gcc
|
1509
|
+
when BaseType
|
1510
|
+
@typesize[type.name]
|
1511
|
+
when Enum
|
1512
|
+
@typesize[:int]
|
1513
|
+
when Struct
|
1514
|
+
raise self, "unknown structure size #{type.name}" if not type.members
|
1515
|
+
al = type.align(self)
|
1516
|
+
lm = type.members.last
|
1517
|
+
lm ? (type.offsetof(self, lm) + sizeof(lm) + al - 1) / al * al : 0
|
1518
|
+
when Union
|
1519
|
+
raise self, "unknown structure size #{type.name}" if not type.members
|
1520
|
+
type.members.map { |m| sizeof(m) }.max || 0
|
1521
|
+
when TypeDef
|
1522
|
+
sizeof(var, type.type)
|
1523
|
+
end
|
1524
|
+
end
|
1525
|
+
|
1526
|
+
# parses variable/function definition/declaration/initialization
|
1527
|
+
# populates scope.symbols and scope.struct
|
1528
|
+
# raises on redefinitions
|
1529
|
+
# returns false if no definition found
|
1530
|
+
def parse_definition(scope)
|
1531
|
+
return false if not basetype = Variable.parse_type(self, scope, true)
|
1532
|
+
|
1533
|
+
# check struct predeclaration
|
1534
|
+
tok = skipspaces
|
1535
|
+
if tok and tok.type == :punct and tok.raw == ';' and basetype.type and
|
1536
|
+
(basetype.type.kind_of? Union or basetype.type.kind_of? Enum)
|
1537
|
+
return true
|
1538
|
+
else unreadtok tok
|
1539
|
+
end
|
1540
|
+
|
1541
|
+
nofunc = false
|
1542
|
+
loop do
|
1543
|
+
var = basetype.dup
|
1544
|
+
var.parse_declarator(self, scope)
|
1545
|
+
|
1546
|
+
raise var.backtrace if not var.name # barrel roll
|
1547
|
+
|
1548
|
+
if prev = scope.symbol[var.name]
|
1549
|
+
if prev.kind_of? TypeDef and var.storage == :typedef
|
1550
|
+
check_compatible_type(var.backtrace, prev.type, var.type, true)
|
1551
|
+
# windows.h redefines many typedefs with the same definition
|
1552
|
+
puts "redefining typedef #{var.name}" if $VERBOSE
|
1553
|
+
var = prev
|
1554
|
+
elsif not prev.kind_of?(Variable) or
|
1555
|
+
prev.initializer or
|
1556
|
+
(prev.storage != :extern and prev.storage != var.storage) or
|
1557
|
+
(scope != @toplevel and prev.storage != :static)
|
1558
|
+
if prev.kind_of? ::Integer # enum value
|
1559
|
+
prev = (scope.struct.values.grep(Enum) + scope.anonymous_enums.to_a).find { |e| e.members.index(prev) }
|
1560
|
+
end
|
1561
|
+
raise var.backtrace, "redefinition, previous is #{prev.backtrace.exception(nil).message rescue :unknown}"
|
1562
|
+
else
|
1563
|
+
check_compatible_type var.backtrace, prev.type, var.type, true
|
1564
|
+
(var.attributes ||= []).concat prev.attributes if prev.attributes
|
1565
|
+
end
|
1566
|
+
elsif var.storage == :typedef
|
1567
|
+
attrs = var.attributes
|
1568
|
+
var = TypeDef.new var.name, var.type, var.backtrace
|
1569
|
+
var.attributes = attrs if attrs
|
1570
|
+
end
|
1571
|
+
scope.statements << Declaration.new(var) unless var.kind_of? TypeDef
|
1572
|
+
|
1573
|
+
raise tok || self, 'punctuation expected' if not tok = skipspaces or (tok.type != :punct and not %w[asm __asm __asm__].include? tok.raw)
|
1574
|
+
|
1575
|
+
case tok.raw
|
1576
|
+
when '{'
|
1577
|
+
# function body
|
1578
|
+
raise tok if nofunc or not var.kind_of? Variable or not var.type.kind_of? Function
|
1579
|
+
scope.symbol[var.name] = var
|
1580
|
+
body = var.initializer = Block.new(scope)
|
1581
|
+
var.type.args ||= []
|
1582
|
+
var.type.args.each { |v|
|
1583
|
+
# put func parameters in func body scope
|
1584
|
+
# arg redefinition is checked in parse_declarator
|
1585
|
+
if not v.name
|
1586
|
+
puts "unnamed argument in definition of #{var.name}" if $DEBUG
|
1587
|
+
next # should raise to be compliant
|
1588
|
+
end
|
1589
|
+
body.symbol[v.name] = v # XXX will need special check in stack allocator
|
1590
|
+
}
|
1591
|
+
|
1592
|
+
loop do
|
1593
|
+
raise tok || self, var.backtrace.exception('"}" expected for end of function') if not tok = skipspaces
|
1594
|
+
break if tok.type == :punct and tok.raw == '}'
|
1595
|
+
unreadtok tok
|
1596
|
+
if not parse_definition(body)
|
1597
|
+
body.statements << parse_statement(body, [var.type.type])
|
1598
|
+
end
|
1599
|
+
end
|
1600
|
+
if $VERBOSE and not body.statements.last.kind_of? Return and not body.statements.last.kind_of? Asm
|
1601
|
+
puts tok.exception('missing function return value').message if not var.type.type.untypedef.kind_of? BaseType or var.type.type.untypedef.name != :void
|
1602
|
+
end
|
1603
|
+
break
|
1604
|
+
when 'asm', '__asm', '__asm__'
|
1605
|
+
# GCC function redirection
|
1606
|
+
# void foo(void) __asm__("bar"); => when code uses 'foo', silently redirect to 'bar' instead
|
1607
|
+
raise tok if nofunc or not var.kind_of? Variable or not var.type.kind_of? Function
|
1608
|
+
# most of the time, 'bar' is not defined anywhere, so we support it only
|
1609
|
+
# to allow parsing of headers using it, hoping noone will actually use them
|
1610
|
+
unused = Asm.parse(self, scope)
|
1611
|
+
puts "unsupported gcc-style __asm__ function redirect #{var.name.inspect} => #{unused.body.inspect}" if $VERBOSE
|
1612
|
+
break
|
1613
|
+
when '='
|
1614
|
+
# variable initialization
|
1615
|
+
raise tok, '"{" or ";" expected' if var.type.kind_of? Function
|
1616
|
+
raise tok, 'cannot initialize extern variable' if var.storage == :extern
|
1617
|
+
scope.symbol[var.name] = var # allow initializer to reference var, eg 'void *ptr = &ptr;'
|
1618
|
+
var.initializer = var.type.parse_initializer(self, scope)
|
1619
|
+
if var.initializer.kind_of?(CExpression) and (scope == @toplevel or var.storage == :static)
|
1620
|
+
raise tok, "initializer for static #{var.name} is not constant" if not var.initializer.constant?
|
1621
|
+
end
|
1622
|
+
reference_value = lambda { |e, v|
|
1623
|
+
found = false
|
1624
|
+
case e
|
1625
|
+
when Variable; found = true if e == v
|
1626
|
+
when CExpression; e.walk { |ee| found ||= reference_value[ee, v] } if e.op != :& or e.lexpr
|
1627
|
+
end
|
1628
|
+
found
|
1629
|
+
}
|
1630
|
+
raise tok, "initializer for #{var.name} is not constant (selfreference)" if reference_value[var.initializer, var]
|
1631
|
+
raise tok || self, '"," or ";" expected' if not tok = skipspaces or tok.type != :punct
|
1632
|
+
else
|
1633
|
+
scope.symbol[var.name] = var
|
1634
|
+
end
|
1635
|
+
|
1636
|
+
case tok.raw
|
1637
|
+
when ','; nofunc = true
|
1638
|
+
when ';'; break
|
1639
|
+
when '}'; unreadtok(tok); break
|
1640
|
+
else raise tok, '";" or "," expected'
|
1641
|
+
end
|
1642
|
+
end
|
1643
|
+
true
|
1644
|
+
end
|
1645
|
+
|
1646
|
+
# parses toplevel statements, return nil if none found
|
1647
|
+
# toplevel statements are ';' and 'asm <..>'
|
1648
|
+
def parse_toplevel_statement(scope)
|
1649
|
+
if tok = skipspaces and tok.type == :punct and tok.raw == ';'
|
1650
|
+
true
|
1651
|
+
elsif tok and tok.type == :punct and tok.raw == '{'
|
1652
|
+
raise tok || self, '"}" expected' if not tok = skipspaces or tok.type != :punct or tok.raw != '}'
|
1653
|
+
true
|
1654
|
+
elsif tok and tok.type == :string and %w[asm __asm __asm__].include? tok.raw
|
1655
|
+
scope.statements << Asm.parse(self, scope)
|
1656
|
+
true
|
1657
|
+
end
|
1658
|
+
end
|
1659
|
+
|
1660
|
+
# returns a statement or raise
|
1661
|
+
def parse_statement(scope, nest)
|
1662
|
+
raise self, 'statement expected' if not tok = skipspaces
|
1663
|
+
|
1664
|
+
if tok.type == :punct and tok.raw == '{'
|
1665
|
+
body = Block.new scope
|
1666
|
+
loop do
|
1667
|
+
raise tok || self, '"}" expected' if not tok = skipspaces
|
1668
|
+
break if tok.type == :punct and tok.raw == '}'
|
1669
|
+
unreadtok tok
|
1670
|
+
if not parse_definition(body)
|
1671
|
+
body.statements << parse_statement(body, nest)
|
1672
|
+
end
|
1673
|
+
end
|
1674
|
+
return body
|
1675
|
+
elsif tok.type == :punct and tok.raw == ';'
|
1676
|
+
return Block.new(scope)
|
1677
|
+
elsif tok.type != :string
|
1678
|
+
unreadtok tok
|
1679
|
+
raise tok, 'expr expected' if not expr = CExpression.parse(self, scope)
|
1680
|
+
checkstatementend(tok)
|
1681
|
+
|
1682
|
+
if $VERBOSE and not nest.include?(:expression) and (expr.op or not expr.type.untypedef.kind_of? BaseType or expr.type.untypedef.name != :void) and CExpression.constant?(expr)
|
1683
|
+
puts tok.exception("statement with no effect : #{expr}").message
|
1684
|
+
end
|
1685
|
+
return expr
|
1686
|
+
end
|
1687
|
+
|
1688
|
+
case tok.raw
|
1689
|
+
when 'if'
|
1690
|
+
If.parse self, scope, nest
|
1691
|
+
when 'while'
|
1692
|
+
While.parse self, scope, nest
|
1693
|
+
when 'do'
|
1694
|
+
DoWhile.parse self, scope, nest
|
1695
|
+
when 'for'
|
1696
|
+
For.parse self, scope, nest
|
1697
|
+
when 'switch'
|
1698
|
+
Switch.parse self, scope, nest
|
1699
|
+
when 'goto'
|
1700
|
+
raise tok || self, 'label expected' if not tok = skipspaces or tok.type != :string
|
1701
|
+
name = tok.raw
|
1702
|
+
checkstatementend(tok)
|
1703
|
+
Goto.new name
|
1704
|
+
when 'return'
|
1705
|
+
expr = CExpression.parse(self, scope) # nil allowed
|
1706
|
+
p, i = nest[0].pointer?, nest[0].integral? if expr
|
1707
|
+
r = expr.reduce(self) if p or i
|
1708
|
+
if (not p and not i) or (i and not r.kind_of? ::Integer) or (p and r != 0)
|
1709
|
+
check_compatible_type(tok, (expr ? expr.type : BaseType.new(:void)), nest[0])
|
1710
|
+
end
|
1711
|
+
checkstatementend(tok)
|
1712
|
+
Return.new expr
|
1713
|
+
when 'case'
|
1714
|
+
raise tok, 'case out of switch' if not nest.include? :switch
|
1715
|
+
Case.parse self, scope, nest
|
1716
|
+
when 'default'
|
1717
|
+
raise tok || self, '":" expected' if not tok = skipspaces or tok.type != :punct or tok.raw != ':'
|
1718
|
+
raise tok, 'case out of switch' if not nest.include? :switch
|
1719
|
+
Case.new 'default', nil, parse_statement(scope, nest)
|
1720
|
+
when 'continue'
|
1721
|
+
checkstatementend(tok)
|
1722
|
+
raise tok, 'continue out of loop' if not nest.include? :loop
|
1723
|
+
Continue.new
|
1724
|
+
when 'break'
|
1725
|
+
checkstatementend(tok)
|
1726
|
+
raise tok, 'break out of loop' if not nest.include? :loop and not nest.include? :switch
|
1727
|
+
Break.new
|
1728
|
+
when 'asm', '__asm', '__asm__'
|
1729
|
+
Asm.parse self, scope
|
1730
|
+
else
|
1731
|
+
if ntok = skipspaces and ntok.type == :punct and ntok.raw == ':'
|
1732
|
+
begin
|
1733
|
+
st = parse_statement(scope, nest)
|
1734
|
+
rescue ParseError
|
1735
|
+
puts "label without statement, #{$!.message}" if $VERBOSE
|
1736
|
+
end
|
1737
|
+
Label.new tok.raw, st
|
1738
|
+
else
|
1739
|
+
unreadtok ntok
|
1740
|
+
unreadtok tok
|
1741
|
+
raise tok, 'expr expected' if not expr = CExpression.parse(self, scope)
|
1742
|
+
checkstatementend(tok)
|
1743
|
+
|
1744
|
+
if $VERBOSE and not nest.include?(:expression) and (expr.op or not expr.type.untypedef.kind_of? BaseType or expr.type.untypedef.name != :void) and CExpression.constant?(expr)
|
1745
|
+
puts tok.exception("statement with no effect : #{expr}").message
|
1746
|
+
end
|
1747
|
+
expr
|
1748
|
+
end
|
1749
|
+
end
|
1750
|
+
end
|
1751
|
+
|
1752
|
+
# check if a macro definition has a numeric value
|
1753
|
+
# returns this value or nil
|
1754
|
+
def macro_numeric(m)
|
1755
|
+
d = @lexer.definition[m]
|
1756
|
+
return if not d.kind_of? Preprocessor::Macro or d.args or d.varargs
|
1757
|
+
# filter metasm-defined vars (eg __PE__ / _M_IX86)
|
1758
|
+
return if not d.name or not bt = d.name.backtrace or (bt[0][0] != ?" and bt[0][0] != ?<)
|
1759
|
+
raise 'cannot macro_numeric with unparsed data' if not eos?
|
1760
|
+
@lexer.feed m
|
1761
|
+
if e = CExpression.parse(self, Block.new(@toplevel)) and eos?
|
1762
|
+
v = e.reduce(self)
|
1763
|
+
return v if v.kind_of? ::Numeric
|
1764
|
+
end
|
1765
|
+
readtok until eos?
|
1766
|
+
nil
|
1767
|
+
rescue ParseError
|
1768
|
+
readtok until eos?
|
1769
|
+
nil
|
1770
|
+
end
|
1771
|
+
|
1772
|
+
# returns all numeric constants defined with their value, either macros or enums
|
1773
|
+
def numeric_constants
|
1774
|
+
ret = []
|
1775
|
+
# macros
|
1776
|
+
@lexer.definition.each_key { |k|
|
1777
|
+
if v = macro_numeric(k)
|
1778
|
+
ret << [k, v]
|
1779
|
+
end
|
1780
|
+
}
|
1781
|
+
# enums
|
1782
|
+
@toplevel.symbol.each { |k, v|
|
1783
|
+
ret << [k, v] if v.kind_of? ::Numeric
|
1784
|
+
}
|
1785
|
+
ret
|
1786
|
+
end
|
1787
|
+
end
|
1788
|
+
|
1789
|
+
class Variable
|
1790
|
+
# parses a variable basetype/qualifier/(storage if allow_value), returns a new variable of this type
|
1791
|
+
# populates scope.struct
|
1792
|
+
def self.parse_type(parser, scope, allow_value = false)
|
1793
|
+
var = new
|
1794
|
+
qualifier = []
|
1795
|
+
tok = nil
|
1796
|
+
loop do
|
1797
|
+
var.parse_attributes(parser, true)
|
1798
|
+
break if not tok = parser.skipspaces
|
1799
|
+
if tok.type != :string
|
1800
|
+
parser.unreadtok tok
|
1801
|
+
break
|
1802
|
+
end
|
1803
|
+
|
1804
|
+
case tok.raw
|
1805
|
+
when 'const', 'volatile'
|
1806
|
+
qualifier << tok.raw.to_sym
|
1807
|
+
next
|
1808
|
+
when 'register', 'auto', 'static', 'typedef', 'extern'
|
1809
|
+
raise tok, 'storage specifier not allowed here' if not allow_value
|
1810
|
+
raise tok, 'multiple storage class' if var.storage
|
1811
|
+
var.storage = tok.raw.to_sym
|
1812
|
+
next
|
1813
|
+
when 'struct'
|
1814
|
+
var.type = Struct.new
|
1815
|
+
var.type.pack = parser.pragma_pack if parser.pragma_pack
|
1816
|
+
var.parse_type_struct(parser, scope)
|
1817
|
+
when 'union'
|
1818
|
+
var.type = Union.new
|
1819
|
+
var.parse_type_struct(parser, scope)
|
1820
|
+
when 'enum'
|
1821
|
+
var.type = Enum.new
|
1822
|
+
var.parse_type_struct(parser, scope)
|
1823
|
+
when 'typeof'
|
1824
|
+
if ntok = parser.skipspaces and ntok.type == :punct and ntok.raw == '('
|
1825
|
+
# check type
|
1826
|
+
if v = parse_type(parser, scope)
|
1827
|
+
v.parse_declarator(parser, scope)
|
1828
|
+
raise tok if v.name != false
|
1829
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
|
1830
|
+
else
|
1831
|
+
raise tok, 'expr expected' if not v = CExpression.parse(parser, scope)
|
1832
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
|
1833
|
+
end
|
1834
|
+
else
|
1835
|
+
parser.unreadtok ntok
|
1836
|
+
raise tok, 'expr expected' if not v = CExpression.parse_value(parser, scope)
|
1837
|
+
end
|
1838
|
+
var.type = v.type # TypeDef.new('typeof', v.type, tok)
|
1839
|
+
when 'long', 'short', 'signed', 'unsigned', 'int', 'char', 'float', 'double',
|
1840
|
+
'void', '__int8', '__int16', '__int32', '__int64',
|
1841
|
+
'intptr_t', 'uintptr_t'
|
1842
|
+
parser.unreadtok tok
|
1843
|
+
var.parse_type_base(parser, scope)
|
1844
|
+
else
|
1845
|
+
if type = scope.symbol_ancestors[tok.raw] and type.kind_of? TypeDef
|
1846
|
+
var.type = type.dup
|
1847
|
+
else
|
1848
|
+
parser.unreadtok tok
|
1849
|
+
end
|
1850
|
+
end
|
1851
|
+
|
1852
|
+
break
|
1853
|
+
end
|
1854
|
+
|
1855
|
+
if not var.type
|
1856
|
+
raise tok || parser, 'bad type name' if not qualifier.empty? or var.storage
|
1857
|
+
nil
|
1858
|
+
else
|
1859
|
+
var.type.qualifier = var.type.qualifier.to_a | qualifier if not qualifier.empty?
|
1860
|
+
var.type.parse_attributes(parser, true)
|
1861
|
+
var
|
1862
|
+
end
|
1863
|
+
end
|
1864
|
+
|
1865
|
+
# parses a structure/union/enum declaration
|
1866
|
+
def parse_type_struct(parser, scope)
|
1867
|
+
@type.parse_attributes(parser)
|
1868
|
+
if tok = parser.skipspaces and tok.type == :punct and tok.raw == '{'
|
1869
|
+
# anonymous struct, ok
|
1870
|
+
@type.backtrace = tok
|
1871
|
+
if @type.kind_of? Enum
|
1872
|
+
(scope.anonymous_enums ||= []) << @type
|
1873
|
+
end
|
1874
|
+
elsif tok and tok.type == :string
|
1875
|
+
name = tok.raw
|
1876
|
+
raise tok, 'bad struct name' if Keyword[name] or (?0..?9).include?(name[0])
|
1877
|
+
@type.backtrace = tok
|
1878
|
+
@type.name = name
|
1879
|
+
@type.parse_attributes(parser)
|
1880
|
+
raise parser if not ntok = parser.skipspaces
|
1881
|
+
if ntok.type != :punct or ntok.raw != '{'
|
1882
|
+
raise tok, "struct/union confusion" if scope.struct[name] and scope.struct[name].class != @type.class
|
1883
|
+
# variable declaration
|
1884
|
+
parser.unreadtok ntok
|
1885
|
+
if ntok.type == :punct and ntok.raw == ';'
|
1886
|
+
# struct predeclaration
|
1887
|
+
# allow redefinition
|
1888
|
+
@type = scope.struct[name] ||= @type
|
1889
|
+
else
|
1890
|
+
# check that the structure exists
|
1891
|
+
struct = scope.struct_ancestors[name]
|
1892
|
+
# allow incomplete types, usage as var type will raise later
|
1893
|
+
struct = scope.struct[name] = @type if not struct
|
1894
|
+
raise tok, 'unknown struct' if struct.class != @type.class
|
1895
|
+
(struct.attributes ||= []).concat @type.attributes if @type.attributes
|
1896
|
+
(struct.qualifier ||= []).concat @type.qualifier if @type.qualifier # XXX const struct foo bar => bar is const, not foo...
|
1897
|
+
@type = struct
|
1898
|
+
end
|
1899
|
+
return
|
1900
|
+
end
|
1901
|
+
if scope.struct[name] and scope.struct[name].members
|
1902
|
+
# redefinition of an existing struct, save for later comparison
|
1903
|
+
oldstruct = scope.struct[name]
|
1904
|
+
raise tok, "struct/union confusion" if oldstruct.class != @type.class
|
1905
|
+
elsif struct = scope.struct[name]
|
1906
|
+
raise tok, "struct/union confusion" if struct.class != @type.class
|
1907
|
+
(struct.attributes ||= []).concat @type.attributes if @type.attributes
|
1908
|
+
(struct.qualifier ||= []).concat @type.qualifier if @type.qualifier
|
1909
|
+
struct.backtrace = @type.backtrace
|
1910
|
+
struct.name = @type.name
|
1911
|
+
@type = struct
|
1912
|
+
else
|
1913
|
+
scope.struct[name] = @type
|
1914
|
+
end
|
1915
|
+
else
|
1916
|
+
raise tok || parser, 'struct name or "{" expected'
|
1917
|
+
end
|
1918
|
+
|
1919
|
+
@type.parse_members(parser, scope)
|
1920
|
+
|
1921
|
+
if oldstruct
|
1922
|
+
if not @type.compare_deep(oldstruct)
|
1923
|
+
raise tok, "conflicting struct redefinition (old at #{oldstruct.backtrace.exception(nil).message rescue :unknown})"
|
1924
|
+
end
|
1925
|
+
@type = oldstruct
|
1926
|
+
end
|
1927
|
+
end
|
1928
|
+
|
1929
|
+
# parses int/long int/long long/double etc
|
1930
|
+
def parse_type_base(parser, scope)
|
1931
|
+
specifier = []
|
1932
|
+
qualifier = []
|
1933
|
+
name = :int
|
1934
|
+
tok = nil
|
1935
|
+
loop do
|
1936
|
+
raise parser if not tok = parser.skipspaces
|
1937
|
+
if tok.type != :string
|
1938
|
+
parser.unreadtok tok
|
1939
|
+
break
|
1940
|
+
end
|
1941
|
+
case tok.raw
|
1942
|
+
when 'const', 'volatile'
|
1943
|
+
qualifier << tok.raw.to_sym
|
1944
|
+
when 'long', 'short', 'signed', 'unsigned'
|
1945
|
+
specifier << tok.raw.to_sym
|
1946
|
+
when 'int', 'char', 'void', 'float', 'double', '__int8', '__int16', '__int32', '__int64'
|
1947
|
+
name = tok.raw.to_sym
|
1948
|
+
break
|
1949
|
+
when 'intptr_t', 'uintptr_t'
|
1950
|
+
name = :ptr
|
1951
|
+
specifier << :unsigned if tok.raw == 'uintptr_t'
|
1952
|
+
break
|
1953
|
+
else
|
1954
|
+
parser.unreadtok tok
|
1955
|
+
break
|
1956
|
+
end
|
1957
|
+
end
|
1958
|
+
|
1959
|
+
case name
|
1960
|
+
when :double # long double
|
1961
|
+
if specifier == [:long]
|
1962
|
+
name = :longdouble
|
1963
|
+
specifier.clear
|
1964
|
+
elsif not specifier.empty?
|
1965
|
+
raise tok || parser, 'invalid specifier list'
|
1966
|
+
end
|
1967
|
+
when :int # short, long, long long X signed, unsigned
|
1968
|
+
# Array#count not available on old ruby (eg 1.8.4), so use ary.len - (ary-stuff).len
|
1969
|
+
specifier = specifier - [:long] + [:longlong] if specifier.length - (specifier-[:long]).length == 2
|
1970
|
+
if specifier.length - (specifier-[:signed, :unsigned]).length > 1 or specifier.length - (specifier-[:short, :long, :longlong]).length > 1
|
1971
|
+
raise tok || parser, 'invalid specifier list'
|
1972
|
+
else
|
1973
|
+
name = (specifier & [:longlong, :long, :short])[0] || :int
|
1974
|
+
specifier -= [:longlong, :long, :short]
|
1975
|
+
end
|
1976
|
+
specifier.delete :signed # default
|
1977
|
+
when :char # signed, unsigned
|
1978
|
+
# signed char != char and unsigned char != char
|
1979
|
+
if (specifier & [:signed, :unsigned]).length > 1 or (specifier & [:short, :long]).length > 0
|
1980
|
+
raise tok || parser, 'invalid specifier list'
|
1981
|
+
end
|
1982
|
+
when :__int8, :__int16, :__int32, :__int64, :ptr
|
1983
|
+
if (specifier & [:signed, :unsigned]).length > 1 or (specifier & [:short, :long]).length > 0
|
1984
|
+
raise tok || parser, 'invalid specifier list'
|
1985
|
+
end
|
1986
|
+
specifier.delete :signed # default
|
1987
|
+
else # none
|
1988
|
+
raise tok || parser, 'invalid type' if not specifier.empty?
|
1989
|
+
end
|
1990
|
+
|
1991
|
+
@type = BaseType.new(name, *specifier)
|
1992
|
+
@type.qualifier = qualifier if not qualifier.empty?
|
1993
|
+
end
|
1994
|
+
|
1995
|
+
# updates @type and @name, parses pointer/arrays/function declarations
|
1996
|
+
# parses anonymous declarators (@name will be false)
|
1997
|
+
# the caller is responsible for detecting redefinitions
|
1998
|
+
# scope used only in CExpression.parse for array sizes and function prototype argument types
|
1999
|
+
# rec for internal use only
|
2000
|
+
def parse_declarator(parser, scope, rec = false)
|
2001
|
+
parse_attributes(parser, true)
|
2002
|
+
tok = parser.skipspaces
|
2003
|
+
# read upto name
|
2004
|
+
if tok and tok.type == :punct and tok.raw == '*'
|
2005
|
+
ptr = Pointer.new
|
2006
|
+
ptr.parse_attributes(parser)
|
2007
|
+
while ntok = parser.skipspaces and ntok.type == :string
|
2008
|
+
case ntok.raw
|
2009
|
+
when 'const', 'volatile'
|
2010
|
+
(ptr.qualifier ||= []) << ntok.raw.to_sym
|
2011
|
+
ptr.parse_attributes(parser)
|
2012
|
+
else break
|
2013
|
+
end
|
2014
|
+
end
|
2015
|
+
parser.unreadtok ntok
|
2016
|
+
parse_declarator(parser, scope, true)
|
2017
|
+
t = self
|
2018
|
+
t = t.type while t.type and (t.type.kind_of?(Pointer) or t.type.kind_of?(Function))
|
2019
|
+
ptr.type = t.type
|
2020
|
+
t.type = ptr
|
2021
|
+
if t.kind_of? Function and ptr.attributes
|
2022
|
+
@attributes ||= []
|
2023
|
+
@attributes |= ptr.attributes
|
2024
|
+
ptr.attributes = nil
|
2025
|
+
end
|
2026
|
+
return
|
2027
|
+
elsif tok and tok.type == :punct and tok.raw == '('
|
2028
|
+
parse_declarator(parser, scope, true)
|
2029
|
+
raise tok || parser, '")" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
|
2030
|
+
elsif tok and tok.type == :string
|
2031
|
+
case tok.raw
|
2032
|
+
when 'const', 'volatile'
|
2033
|
+
(@type.qualifier ||= []) << tok.raw.to_sym
|
2034
|
+
return parse_declarator(parser, scope, rec)
|
2035
|
+
when 'register', 'auto', 'static', 'typedef', 'extern'
|
2036
|
+
raise tok, 'multiple storage class' if storage
|
2037
|
+
@storage = tok.raw.to_sym
|
2038
|
+
puts tok.exception('misplaced storage specifier').message if $VERBOSE
|
2039
|
+
return parse_declarator(parser, scope, rec)
|
2040
|
+
end
|
2041
|
+
raise tok if name or name == false
|
2042
|
+
raise tok, 'bad var name' if Keyword[tok.raw] or (?0..?9).include?(tok.raw[0])
|
2043
|
+
@name = tok.raw
|
2044
|
+
@backtrace = tok
|
2045
|
+
parse_attributes(parser, true)
|
2046
|
+
else
|
2047
|
+
# unnamed
|
2048
|
+
raise tok || parser if name or name == false
|
2049
|
+
@name = false
|
2050
|
+
@backtrace = tok
|
2051
|
+
parser.unreadtok tok
|
2052
|
+
parse_attributes(parser, true)
|
2053
|
+
end
|
2054
|
+
parse_declarator_postfix(parser, scope)
|
2055
|
+
if not rec
|
2056
|
+
raise @backtrace, 'void type is invalid' if name and (t = @type.untypedef).kind_of? BaseType and
|
2057
|
+
t.name == :void and storage != :typedef
|
2058
|
+
raise @backtrace, "incomplete type #{@type.name}" if (@type.kind_of? Union or @type.kind_of? Enum) and
|
2059
|
+
not @type.members and storage != :typedef and storage != :extern # gcc uses an undefined extern struct just to cast it later (_IO_FILE_plus)
|
2060
|
+
end
|
2061
|
+
end
|
2062
|
+
|
2063
|
+
# parses array/function type
|
2064
|
+
def parse_declarator_postfix(parser, scope)
|
2065
|
+
if tok = parser.skipspaces and tok.type == :punct and tok.raw == '['
|
2066
|
+
# array indexing
|
2067
|
+
idx = CExpression.parse(parser, scope) # may be nil
|
2068
|
+
if idx and (scope == parser.toplevel or storage == :static)
|
2069
|
+
raise tok, 'array size is not constant' if not idx.constant?
|
2070
|
+
idx = idx.reduce(parser)
|
2071
|
+
elsif idx and nidx = idx.reduce(parser) and nidx.kind_of? ::Integer
|
2072
|
+
idx = nidx
|
2073
|
+
end
|
2074
|
+
t = self
|
2075
|
+
t = t.type while t.type and (t.type.kind_of?(Pointer) or t.type.kind_of?(Function))
|
2076
|
+
t.type = Array.new t.type
|
2077
|
+
t.type.length = idx
|
2078
|
+
raise tok || parser, '"]" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ']'
|
2079
|
+
parse_attributes(parser) # should be type.attrs, but this is should be more compiler-compatible
|
2080
|
+
elsif tok and tok.type == :punct and tok.raw == '('
|
2081
|
+
# function prototype
|
2082
|
+
# void __attribute__((noreturn)) func() => attribute belongs to func
|
2083
|
+
if @type and @type.attributes
|
2084
|
+
@attributes ||= []
|
2085
|
+
@attributes |= @type.attributes
|
2086
|
+
@type.attributes = nil
|
2087
|
+
end
|
2088
|
+
t = self
|
2089
|
+
t = t.type while t.type and (t.type.kind_of?(Pointer) or t.type.kind_of?(Function))
|
2090
|
+
t.type = Function.new t.type
|
2091
|
+
if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
|
2092
|
+
parser.unreadtok tok
|
2093
|
+
t.type.args = []
|
2094
|
+
oldstyle = false # int func(a, b) int a; double b; { stuff; }
|
2095
|
+
loop do
|
2096
|
+
raise parser if not tok = parser.skipspaces
|
2097
|
+
if tok.type == :punct and tok.raw == '.' # variadic function
|
2098
|
+
raise parser, '".." expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '.'
|
2099
|
+
raise parser, '"." expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '.'
|
2100
|
+
raise parser, '")" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
|
2101
|
+
t.type.varargs = true
|
2102
|
+
break
|
2103
|
+
elsif tok.type == :string and tok.raw == 'register'
|
2104
|
+
storage = :register
|
2105
|
+
else
|
2106
|
+
parser.unreadtok tok
|
2107
|
+
end
|
2108
|
+
|
2109
|
+
if oldstyle or not v = Variable.parse_type(parser, scope)
|
2110
|
+
raise tok if not @name # no oldstyle in casts
|
2111
|
+
tok = parser.skipspaces
|
2112
|
+
oldstyle ||= [tok] # arg to raise() later
|
2113
|
+
oldstyle << tok.raw
|
2114
|
+
else
|
2115
|
+
v.storage = storage if storage
|
2116
|
+
v.parse_declarator(parser, scope)
|
2117
|
+
v.type = Pointer.new(v.type.type) if v.type.kind_of? Array
|
2118
|
+
v.type = Pointer.new(v.type) if v.type.kind_of? Function
|
2119
|
+
|
2120
|
+
t.type.args << v if not v.type.untypedef.kind_of? BaseType or v.type.untypedef.name != :void
|
2121
|
+
end
|
2122
|
+
|
2123
|
+
if tok = parser.skipspaces and tok.type == :punct and tok.raw == ','
|
2124
|
+
raise tok, '")" expected' if v and t.type.args.last != v # last arg of type :void
|
2125
|
+
elsif tok and tok.type == :punct and tok.raw == ')'
|
2126
|
+
break
|
2127
|
+
else raise tok || parser, '"," or ")" expected'
|
2128
|
+
end
|
2129
|
+
end
|
2130
|
+
if oldstyle
|
2131
|
+
parse_attributes(parser, true)
|
2132
|
+
ra = oldstyle.shift
|
2133
|
+
while t.type.args.compact.length != oldstyle.length
|
2134
|
+
raise ra, "invalid prototype" if not vb = Variable.parse_type(parser, scope)
|
2135
|
+
loop do
|
2136
|
+
v = vb.dup
|
2137
|
+
v.parse_declarator(parser, scope)
|
2138
|
+
v.type = Pointer.new(v.type.type) if v.type.kind_of? Array
|
2139
|
+
v.type = Pointer.new(v.type) if v.type.kind_of? Function
|
2140
|
+
raise parser, "unknown arg #{v.name.inspect}" if not i = oldstyle.index(v.name)
|
2141
|
+
t.type.args[i] = v
|
2142
|
+
raise parser, '"," or ";" expected' if not tok = parser.skipspaces or tok.type != :punct or (tok.raw != ';' and tok.raw != ',')
|
2143
|
+
break if tok.raw == ';'
|
2144
|
+
end
|
2145
|
+
end
|
2146
|
+
parse_attributes(parser, true)
|
2147
|
+
raise parser, '"{" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '{'
|
2148
|
+
parser.unreadtok tok
|
2149
|
+
end
|
2150
|
+
namedargs = t.type.args.map { |a| a.name }.compact - [false]
|
2151
|
+
raise tok, "duplicate argument name #{namedargs.find { |a| namedargs.index(a) != namedargs.rindex(a) }.inspect}" if namedargs.length != namedargs.uniq.length
|
2152
|
+
end
|
2153
|
+
parse_attributes(parser, true) # should be type.attrs, but this should be more existing-compiler-compatible
|
2154
|
+
else
|
2155
|
+
parser.unreadtok tok
|
2156
|
+
return
|
2157
|
+
end
|
2158
|
+
parse_declarator_postfix(parser, scope)
|
2159
|
+
end
|
2160
|
+
end
|
2161
|
+
|
2162
|
+
class Variable
|
2163
|
+
def ===(o)
|
2164
|
+
self == o or (o.class == CExpression and not o.op and o.rexpr == self)
|
2165
|
+
end
|
2166
|
+
end
|
2167
|
+
|
2168
|
+
class CExpression
|
2169
|
+
def self.lvalue?(e)
|
2170
|
+
e.kind_of?(self) ? e.lvalue? : (e.kind_of? Variable and e.name)
|
2171
|
+
end
|
2172
|
+
def lvalue?
|
2173
|
+
case @op
|
2174
|
+
when :*; true if not @lexpr
|
2175
|
+
when :'[]', :'.', :'->'; true
|
2176
|
+
when nil # cast
|
2177
|
+
CExpression.lvalue?(@rexpr)
|
2178
|
+
else false
|
2179
|
+
end
|
2180
|
+
end
|
2181
|
+
|
2182
|
+
def self.constant?(e)
|
2183
|
+
e.kind_of?(self) ? e.constant? : true
|
2184
|
+
end
|
2185
|
+
def constant?
|
2186
|
+
# gcc considers '1, 2' not constant
|
2187
|
+
if [:',', :funcall, :'=', :'--', :'++', :'+=', :'-=', :'*=', :'/=', :'>>=', :'<<=', :'&=', :'|=', :'^=', :'%=', :'->', :'[]'].include?(@op)
|
2188
|
+
false
|
2189
|
+
elsif @op == :'*' and not @lexpr; false
|
2190
|
+
elsif not @lexpr and not @op and @rexpr.kind_of? Block; false
|
2191
|
+
else
|
2192
|
+
out = true
|
2193
|
+
walk { |e| break out = false if not CExpression.constant?(e) }
|
2194
|
+
out
|
2195
|
+
end
|
2196
|
+
end
|
2197
|
+
|
2198
|
+
def self.reduce(parser, e)
|
2199
|
+
e.kind_of?(self) ? e.reduce(parser) : e
|
2200
|
+
end
|
2201
|
+
def reduce(parser)
|
2202
|
+
# parser used for arithmetic overflows (need basic type sizes)
|
2203
|
+
case @op
|
2204
|
+
when :'&&'
|
2205
|
+
case l = CExpression.reduce(parser, @lexpr)
|
2206
|
+
when 0; 0
|
2207
|
+
when ::Integer
|
2208
|
+
case r = CExpression.reduce(parser, @rexpr)
|
2209
|
+
when 0; 0
|
2210
|
+
when ::Integer; 1
|
2211
|
+
else CExpression.new(l, @op, r, @type)
|
2212
|
+
end
|
2213
|
+
else CExpression.new(l, @op, @rexpr, @type)
|
2214
|
+
end
|
2215
|
+
when :'||'
|
2216
|
+
case l = CExpression.reduce(parser, @lexpr)
|
2217
|
+
when 0
|
2218
|
+
case r = CExpression.reduce(parser, @rexpr)
|
2219
|
+
when 0; 0
|
2220
|
+
when ::Integer; 1
|
2221
|
+
else CExpression.new(l, @op, r, @type)
|
2222
|
+
end
|
2223
|
+
when ::Integer; 1
|
2224
|
+
else CExpression.new(l, @op, @rexpr, @type)
|
2225
|
+
end
|
2226
|
+
when :'!'
|
2227
|
+
case r = CExpression.reduce(parser, @rexpr)
|
2228
|
+
when 0; 1
|
2229
|
+
when ::Integer; 0
|
2230
|
+
else CExpression.new(nil, @op, r, @type)
|
2231
|
+
end
|
2232
|
+
when :'!=', :'==', :'<', :'>', :'>=', :'<='
|
2233
|
+
l = CExpression.reduce(parser, @lexpr)
|
2234
|
+
r = CExpression.reduce(parser, @rexpr)
|
2235
|
+
if l.kind_of?(::Integer) and r.kind_of?(::Integer)
|
2236
|
+
if @op == :'!='; l != r ? 1 : 0
|
2237
|
+
else l.send(@op, r) ? 1 : 0
|
2238
|
+
end
|
2239
|
+
else
|
2240
|
+
l = CExpression.new(nil, nil, l, BaseType.new(:int)) if l.kind_of? ::Integer
|
2241
|
+
r = CExpression.new(nil, nil, r, BaseType.new(:int)) if r.kind_of? ::Integer
|
2242
|
+
CExpression.new(l, @op, r, @type)
|
2243
|
+
end
|
2244
|
+
when :'.'
|
2245
|
+
le = CExpression.reduce(parser, @lexpr)
|
2246
|
+
if le.kind_of? Variable and le.initializer.kind_of? ::Array
|
2247
|
+
midx = le.type.members.index(le.type.findmember(@rexpr))
|
2248
|
+
CExpression.reduce(parser, le.initializer[midx] || 0)
|
2249
|
+
else
|
2250
|
+
CExpression.new(le, @op, @rexpr, @type)
|
2251
|
+
end
|
2252
|
+
when :'?:'
|
2253
|
+
case c = CExpression.reduce(parser, @lexpr)
|
2254
|
+
when 0; CExpression.reduce(parser, @rexpr[0])
|
2255
|
+
when ::Integer; CExpression.reduce(parser, @rexpr[1])
|
2256
|
+
else CExpression.new(c, @op, @rexpr, @type)
|
2257
|
+
end
|
2258
|
+
when :'+', :'-', :'*', :'/', :'^', :'%', :'&', :'|', :'>>', :'<<', :'~', nil
|
2259
|
+
t = @type.untypedef
|
2260
|
+
case t
|
2261
|
+
when BaseType
|
2262
|
+
when Pointer; return self if @op
|
2263
|
+
else
|
2264
|
+
return @rexpr if not @op and not @lexpr and @rexpr.kind_of? Variable and @rexpr.type == @type
|
2265
|
+
return self # raise parser, 'not arithmetic type'
|
2266
|
+
end
|
2267
|
+
|
2268
|
+
# compute value
|
2269
|
+
r = CExpression.reduce(parser, @rexpr)
|
2270
|
+
ret = \
|
2271
|
+
if not @lexpr
|
2272
|
+
# unary
|
2273
|
+
case @op
|
2274
|
+
when :'+', nil, :'-', :'~'
|
2275
|
+
return CExpression.new(nil, @op, r, @type) if not r.kind_of? ::Numeric
|
2276
|
+
case @op
|
2277
|
+
when :'-'; -r
|
2278
|
+
when :'~'; ~r
|
2279
|
+
else r
|
2280
|
+
end
|
2281
|
+
else return CExpression.new(nil, @op, r, @type)
|
2282
|
+
end
|
2283
|
+
else
|
2284
|
+
l = CExpression.reduce(parser, @lexpr)
|
2285
|
+
if not l.kind_of?(::Numeric) or not r.kind_of?(::Numeric)
|
2286
|
+
l = CExpression.new(nil, nil, l, BaseType.new(:int)) if l.kind_of? ::Integer
|
2287
|
+
r = CExpression.new(nil, nil, r, BaseType.new(:int)) if r.kind_of? ::Integer
|
2288
|
+
return CExpression.new(l, @op, r, @type)
|
2289
|
+
end
|
2290
|
+
l.send(@op, r)
|
2291
|
+
end
|
2292
|
+
|
2293
|
+
# overflow
|
2294
|
+
tn = (t.pointer? ? :ptr : t.name)
|
2295
|
+
case tn
|
2296
|
+
when :char, :short, :int, :long, :ptr, :longlong, :__int8, :__int16, :__int32, :__int64
|
2297
|
+
max = 1 << (8*parser.typesize[tn])
|
2298
|
+
ret = ret.to_i & (max-1)
|
2299
|
+
if not t.pointer? and t.specifier != :unsigned and (ret & (max >> 1)) > 0 # char == unsigned char
|
2300
|
+
ret - max
|
2301
|
+
else
|
2302
|
+
ret
|
2303
|
+
end
|
2304
|
+
when :float, :double, :longdouble
|
2305
|
+
ret.to_f # TODO
|
2306
|
+
end
|
2307
|
+
when :funcall
|
2308
|
+
l = CExpression.reduce(parser, @lexpr)
|
2309
|
+
r = @rexpr.map { |rr|
|
2310
|
+
rr = CExpression.reduce(parser, rr)
|
2311
|
+
rr = CExpression.new(nil, nil, rr, BaseType.new(:int)) if rr.kind_of? ::Integer
|
2312
|
+
rr
|
2313
|
+
}
|
2314
|
+
CExpression.new(l, @op, r, @type)
|
2315
|
+
else
|
2316
|
+
l = CExpression.reduce(parser, @lexpr) if @lexpr
|
2317
|
+
r = CExpression.reduce(parser, @rexpr) if @rexpr
|
2318
|
+
l = CExpression.new(nil, nil, l, BaseType.new(:int)) if l.kind_of? ::Integer
|
2319
|
+
r = CExpression.new(nil, nil, r, BaseType.new(:int)) if r.kind_of? ::Integer
|
2320
|
+
CExpression.new(l, @op, r, @type)
|
2321
|
+
end
|
2322
|
+
end
|
2323
|
+
|
2324
|
+
def ==(o)
|
2325
|
+
o.object_id == self.object_id or
|
2326
|
+
(self.class == o.class and op == o.op and lexpr == o.lexpr and rexpr == o.rexpr)
|
2327
|
+
end
|
2328
|
+
|
2329
|
+
def ===(o)
|
2330
|
+
(self.class == o.class and op == o.op and lexpr === o.lexpr and rexpr === o.rexpr) or
|
2331
|
+
(o.class == Variable and not @op and @rexpr == o)
|
2332
|
+
end
|
2333
|
+
|
2334
|
+
NegateOp = { :== => :'!=', :'!=' => :==, :> => :<=, :>= => :<, :< => :>=, :<= => :> }
|
2335
|
+
# returns a CExpr negating this one (eg 'x' => '!x', 'a > b' => 'a <= b'...)
|
2336
|
+
def self.negate(e)
|
2337
|
+
e.kind_of?(self) ? e.negate : CExpression[:'!', e]
|
2338
|
+
end
|
2339
|
+
def negate
|
2340
|
+
if @op == :'!'
|
2341
|
+
CExpression[@rexpr]
|
2342
|
+
elsif nop = NegateOp[@op]
|
2343
|
+
if nop == :== and @rexpr.kind_of? CExpression and not @rexpr.op and @rexpr.rexpr == 0 and
|
2344
|
+
@lexpr.kind_of? CExpression and [:==, :'!=', :>, :<, :>=, :<=, :'!'].include? @lexpr.op
|
2345
|
+
# (a > b) != 0 => (a > b)
|
2346
|
+
CExpression[@lexpr]
|
2347
|
+
else
|
2348
|
+
CExpression.new(@lexpr, nop, @rexpr, @type)
|
2349
|
+
end
|
2350
|
+
elsif nop = { :'||' => :'&&', :'&&' => :'||' }[@op]
|
2351
|
+
CExpression.new(CExpression.negate(@lexpr), nop, CExpression.negate(@rexpr), @type)
|
2352
|
+
else
|
2353
|
+
CExpression[:'!', self]
|
2354
|
+
end
|
2355
|
+
end
|
2356
|
+
|
2357
|
+
def walk
|
2358
|
+
case @op
|
2359
|
+
when :funcall, :'?:'
|
2360
|
+
yield @lexpr
|
2361
|
+
@rexpr.each { |arg| yield arg }
|
2362
|
+
when :'->', :'.'
|
2363
|
+
yield @lexpr
|
2364
|
+
else
|
2365
|
+
yield @lexpr if @lexpr
|
2366
|
+
yield @rexpr if @rexpr
|
2367
|
+
end
|
2368
|
+
end
|
2369
|
+
|
2370
|
+
def complexity
|
2371
|
+
cx = 1
|
2372
|
+
walk { |e| cx += e.complexity if e.kind_of?(CExpression) }
|
2373
|
+
cx
|
2374
|
+
end
|
2375
|
+
|
2376
|
+
RIGHTASSOC = [:'=', :'+=', :'-=', :'*=', :'/=', :'%=', :'&=',
|
2377
|
+
:'|=', :'^=', :'<<=', :'>>=', :'?:'
|
2378
|
+
].inject({}) { |h, op| h.update op => true }
|
2379
|
+
|
2380
|
+
# key = operator, value = hash regrouping operators of lower precedence
|
2381
|
+
# funcall/array index/member dereference/sizeof are handled in parse_value
|
2382
|
+
OP_PRIO = [[:','], [:'?:'], [:'=', :'+=', :'-=', :'*=', :'/=',
|
2383
|
+
:'%=', :'&=', :'|=', :'^=', :'<<=', :'>>='], [:'||'],
|
2384
|
+
[:'&&'], [:|], [:^], [:&], [:'==', :'!='],
|
2385
|
+
[:'<', :'>', :'<=', :'>='], [:<<, :>>], [:+, :-],
|
2386
|
+
[:*, :/, :%], ].inject({}) { |h, oplist|
|
2387
|
+
lessprio = h.keys.inject({}) { |hh, op| hh.update op => true }
|
2388
|
+
oplist.each { |op| lessprio.update op => true } if RIGHTASSOC[oplist.first]
|
2389
|
+
oplist.each { |op| h[op] = lessprio }
|
2390
|
+
h }
|
2391
|
+
|
2392
|
+
class << self
|
2393
|
+
# reads a binary operator from the parser, returns the corresponding symbol or nil
|
2394
|
+
def readop(parser)
|
2395
|
+
if not op = parser.skipspaces or op.type != :punct
|
2396
|
+
parser.unreadtok op
|
2397
|
+
return
|
2398
|
+
end
|
2399
|
+
|
2400
|
+
case op.raw
|
2401
|
+
when '>', '<', '|', '&' # << >> || &&
|
2402
|
+
if ntok = parser.readtok and ntok.type == :punct and ntok.raw == op.raw
|
2403
|
+
op.raw << ntok.raw
|
2404
|
+
else
|
2405
|
+
parser.unreadtok ntok
|
2406
|
+
end
|
2407
|
+
when '!' # != (mandatory)
|
2408
|
+
if not ntok = parser.readtok or ntok.type != :punct and ntok.raw != '='
|
2409
|
+
parser.unreadtok op
|
2410
|
+
return
|
2411
|
+
end
|
2412
|
+
op.raw << ntok.raw
|
2413
|
+
when '+', '-', '*', '/', '%', '^', '=', ',', '?', ':', '>>', '<<', '||', '&&',
|
2414
|
+
'+=','-=','*=','/=','%=','^=','==','&=','|=','!=' # ok
|
2415
|
+
else # bad
|
2416
|
+
parser.unreadtok op
|
2417
|
+
return
|
2418
|
+
end
|
2419
|
+
|
2420
|
+
# may be followed by '='
|
2421
|
+
case op.raw
|
2422
|
+
when '+', '-', '*', '/', '%', '^', '&', '|', '>>', '<<', '<', '>', '='
|
2423
|
+
if ntok = parser.readtok and ntok.type == :punct and ntok.raw == '='
|
2424
|
+
op.raw << ntok.raw
|
2425
|
+
else
|
2426
|
+
parser.unreadtok ntok
|
2427
|
+
end
|
2428
|
+
end
|
2429
|
+
|
2430
|
+
op.value = op.raw.to_sym
|
2431
|
+
op
|
2432
|
+
end
|
2433
|
+
|
2434
|
+
# parse sizeof offsetof float immediate etc into tok.value
|
2435
|
+
def parse_intfloat(parser, scope, tok)
|
2436
|
+
if tok.type == :string and not tok.value
|
2437
|
+
case tok.raw
|
2438
|
+
when 'sizeof'
|
2439
|
+
if ntok = parser.skipspaces and ntok.type == :punct and ntok.raw == '('
|
2440
|
+
# check type
|
2441
|
+
if v = Variable.parse_type(parser, scope)
|
2442
|
+
v.parse_declarator(parser, scope)
|
2443
|
+
raise tok if v.name != false
|
2444
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
|
2445
|
+
else
|
2446
|
+
raise tok, 'expr expected' if not v = parse(parser, scope)
|
2447
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
|
2448
|
+
end
|
2449
|
+
else
|
2450
|
+
parser.unreadtok ntok
|
2451
|
+
raise tok, 'expr expected' if not v = parse_value(parser, scope)
|
2452
|
+
end
|
2453
|
+
tok.value = parser.sizeof(v)
|
2454
|
+
return
|
2455
|
+
when '__builtin_offsetof'
|
2456
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != '('
|
2457
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :string or ntok.raw != 'struct'
|
2458
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :string
|
2459
|
+
raise tok, 'unknown structure' if not struct = scope.struct_ancestors[ntok.raw] or not struct.kind_of? Union or not struct.members
|
2460
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ','
|
2461
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :string
|
2462
|
+
tok.value = struct.offsetof(parser, ntok.raw)
|
2463
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
|
2464
|
+
return
|
2465
|
+
end
|
2466
|
+
end
|
2467
|
+
|
2468
|
+
Expression.parse_num_value(parser, tok)
|
2469
|
+
end
|
2470
|
+
|
2471
|
+
# returns the next value from parser (parenthesised expression, immediate, variable, unary operators)
|
2472
|
+
def parse_value(parser, scope)
|
2473
|
+
return if not tok = parser.skipspaces
|
2474
|
+
case tok.type
|
2475
|
+
when :string
|
2476
|
+
parse_intfloat(parser, scope, tok)
|
2477
|
+
val = tok.value || tok.raw
|
2478
|
+
if val.kind_of? ::String
|
2479
|
+
raise tok, 'undefined variable' if not val = scope.symbol_ancestors[val]
|
2480
|
+
end
|
2481
|
+
case val
|
2482
|
+
when Type
|
2483
|
+
raise tok, 'invalid variable'
|
2484
|
+
when Variable
|
2485
|
+
val = parse_value_postfix(parser, scope, val)
|
2486
|
+
when ::Float
|
2487
|
+
# parse suffix
|
2488
|
+
type = :double
|
2489
|
+
if (?0..?9).include?(tok.raw[0])
|
2490
|
+
case tok.raw.downcase[-1]
|
2491
|
+
when ?l; type = :longdouble
|
2492
|
+
when ?f; type = :float
|
2493
|
+
end
|
2494
|
+
end
|
2495
|
+
val = CExpression[val, BaseType.new(type)]
|
2496
|
+
|
2497
|
+
when ::Integer
|
2498
|
+
# parse suffix
|
2499
|
+
# XXX 010h ?
|
2500
|
+
type = :int
|
2501
|
+
specifier = []
|
2502
|
+
if (?0..?9).include?(tok.raw[0])
|
2503
|
+
suffix = tok.raw.downcase[-3, 3] || tok.raw.downcase[-2, 2] || tok.raw.downcase[-1, 1] # short string
|
2504
|
+
specifier << :unsigned if suffix.include?('u') # XXX or tok.raw.downcase[1] == ?x
|
2505
|
+
type = :longlong if suffix.count('l') == 2
|
2506
|
+
type = :long if suffix.count('l') == 1
|
2507
|
+
end
|
2508
|
+
val = CExpression[val, BaseType.new(type, *specifier)]
|
2509
|
+
else raise parser, "internal error #{val.inspect}"
|
2510
|
+
end
|
2511
|
+
|
2512
|
+
when :quoted
|
2513
|
+
if tok.raw[0] == ?'
|
2514
|
+
raise tok, 'invalid character constant' if not [1, 2, 4, 8].include? tok.value.length # TODO 0fill
|
2515
|
+
val = CExpression[Expression.decode_imm(tok.value, tok.value.length, :big), BaseType.new(:int)]
|
2516
|
+
else
|
2517
|
+
val = CExpression[tok.value, Pointer.new(BaseType.new(tok.raw[0, 2] == 'L"' ? :short : :char))]
|
2518
|
+
val = parse_value_postfix(parser, scope, val)
|
2519
|
+
end
|
2520
|
+
|
2521
|
+
when :punct
|
2522
|
+
case tok.raw
|
2523
|
+
when '('
|
2524
|
+
ntok = nil
|
2525
|
+
# check type casting
|
2526
|
+
if v = Variable.parse_type(parser, scope)
|
2527
|
+
v.parse_declarator(parser, scope)
|
2528
|
+
(v.type.attributes ||= []).concat v.attributes if v.attributes
|
2529
|
+
raise tok, 'bad cast' if v.name != false
|
2530
|
+
raise ntok || tok, 'no ")" found' if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
|
2531
|
+
raise ntok, 'expr expected' if not val = parse_value(parser, scope) # parses postfix too
|
2532
|
+
#raise ntok, 'unable to cast a struct' if val.type.untypedef.kind_of? Union
|
2533
|
+
val = CExpression[[val], v.type]
|
2534
|
+
# check compound statement expression
|
2535
|
+
elsif ntok = parser.skipspaces and ntok.type == :punct and ntok.raw == '{'
|
2536
|
+
parser.unreadtok ntok
|
2537
|
+
blk = parser.parse_statement(scope, [:expression]) # XXX nesting ?
|
2538
|
+
raise ntok || tok, 'no ")" found' if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
|
2539
|
+
type = blk.statements.last.kind_of?(CExpression) ? blk.statements.last.type : BaseType.new(:void)
|
2540
|
+
val = CExpression[blk, type]
|
2541
|
+
else
|
2542
|
+
parser.unreadtok ntok
|
2543
|
+
if not val = parse(parser, scope)
|
2544
|
+
parser.unreadtok tok
|
2545
|
+
return
|
2546
|
+
end
|
2547
|
+
raise ntok || tok, 'no ")" found' if not ntok = parser.readtok or ntok.type != :punct or ntok.raw != ')'
|
2548
|
+
val = parse_value_postfix(parser, scope, val)
|
2549
|
+
end
|
2550
|
+
when '.' # float
|
2551
|
+
parse_intfloat(parser, scope, tok)
|
2552
|
+
if not tok.value
|
2553
|
+
parser.unreadtok tok
|
2554
|
+
return
|
2555
|
+
end
|
2556
|
+
val = tok.value || tok.raw
|
2557
|
+
type = :double
|
2558
|
+
case tok.raw.downcase[-1]
|
2559
|
+
when ?l; type = :longdouble
|
2560
|
+
when ?f; type = :float
|
2561
|
+
end
|
2562
|
+
val = CExpression.new[val, BaseType.new(type)]
|
2563
|
+
|
2564
|
+
when '+', '-', '&', '!', '~', '*', '--', '++', '&&'
|
2565
|
+
# unary prefix
|
2566
|
+
# may have been read ahead
|
2567
|
+
|
2568
|
+
raise parser if not ntok = parser.readtok
|
2569
|
+
# check for -- ++ &&
|
2570
|
+
if ntok.type == :punct and ntok.raw == tok.raw and %w[+ - &].include?(tok.raw)
|
2571
|
+
tok.raw << ntok.raw
|
2572
|
+
else
|
2573
|
+
parser.unreadtok ntok
|
2574
|
+
end
|
2575
|
+
|
2576
|
+
case tok.raw
|
2577
|
+
when '&'
|
2578
|
+
val = parse_value(parser, scope)
|
2579
|
+
if val.kind_of? CExpression and val.op == :& and not val.lexpr and
|
2580
|
+
(val.rexpr.kind_of? Variable or val.rexpr.kind_of? CExpression) and val.rexpr.type.kind_of? Function
|
2581
|
+
# &&function == &function
|
2582
|
+
elsif (val.kind_of? CExpression or val.kind_of? Variable) and val.type.kind_of? Array
|
2583
|
+
# &ary = ary
|
2584
|
+
else
|
2585
|
+
raise parser, "invalid lvalue #{val}" if not CExpression.lvalue?(val) and not parser.allow_bad_c
|
2586
|
+
raise val.backtrace, 'cannot take addr of register' if val.kind_of? Variable and val.storage == :register and not parser.allow_bad_c
|
2587
|
+
val = CExpression.new(nil, tok.raw.to_sym, val, Pointer.new(val.type))
|
2588
|
+
end
|
2589
|
+
when '++', '--'
|
2590
|
+
val = parse_value(parser, scope)
|
2591
|
+
raise parser, "invalid lvalue #{val}" if not CExpression.lvalue?(val) and not parser.allow_bad_c
|
2592
|
+
val = CExpression.new(nil, tok.raw.to_sym, val, val.type)
|
2593
|
+
when '&&'
|
2594
|
+
raise tok, 'label name expected' if not val = parser.skipspaces or val.type != :string
|
2595
|
+
val = CExpression.new(nil, nil, Label.new(val.raw, nil), Pointer.new(BaseType.new(:void)))
|
2596
|
+
when '*'
|
2597
|
+
raise tok, 'expr expected' if not val = parse_value(parser, scope)
|
2598
|
+
raise tok, 'not a pointer' if not val.type.pointer? and not parser.allow_bad_c
|
2599
|
+
newtype = val.type.pointer? ? val.type.pointed : BaseType.new(:int)
|
2600
|
+
if not newtype.untypedef.kind_of? Function # *fptr == fptr
|
2601
|
+
val = CExpression.new(nil, tok.raw.to_sym, val, newtype)
|
2602
|
+
end
|
2603
|
+
when '~', '!', '+', '-'
|
2604
|
+
raise tok, 'expr expected' if not val = parse_value(parser, scope)
|
2605
|
+
raise tok, 'type not arithmetic' if not val.type.arithmetic? and not parser.allow_bad_c
|
2606
|
+
val = CExpression.new(nil, tok.raw.to_sym, val, val.type)
|
2607
|
+
val.type = BaseType.new(:int) if tok.raw == '!'
|
2608
|
+
else raise tok, 'internal error'
|
2609
|
+
end
|
2610
|
+
else
|
2611
|
+
parser.unreadtok tok
|
2612
|
+
return
|
2613
|
+
end
|
2614
|
+
else
|
2615
|
+
parser.unreadtok tok
|
2616
|
+
return
|
2617
|
+
end
|
2618
|
+
|
2619
|
+
if val.kind_of? Variable and val.type.kind_of? Function
|
2620
|
+
# void (*bla)() = printf; => ...= &printf;
|
2621
|
+
val = CExpression[:&, val]
|
2622
|
+
end
|
2623
|
+
|
2624
|
+
val
|
2625
|
+
end
|
2626
|
+
|
2627
|
+
# parse postfix forms (postincrement, array index, struct member dereference)
|
2628
|
+
def parse_value_postfix(parser, scope, val)
|
2629
|
+
tok = parser.skipspaces
|
2630
|
+
nval = \
|
2631
|
+
if tok and tok.type == :punct
|
2632
|
+
case tok.raw
|
2633
|
+
when '+', '++', '-', '--', '->'
|
2634
|
+
ntok = parser.readtok
|
2635
|
+
if (tok.raw == '+' or tok.raw == '-') and ntok and ntok.type == :punct and
|
2636
|
+
(ntok.raw == tok.raw or (tok.raw == '-' and ntok.raw == '>'))
|
2637
|
+
tok.raw << ntok.raw
|
2638
|
+
else
|
2639
|
+
parser.unreadtok ntok
|
2640
|
+
end
|
2641
|
+
case tok.raw
|
2642
|
+
when '+', '-'
|
2643
|
+
nil
|
2644
|
+
when '++', '--'
|
2645
|
+
raise parser, "#{val}: invalid lvalue" if not CExpression.lvalue?(val)
|
2646
|
+
CExpression.new(val, tok.raw.to_sym, nil, val.type)
|
2647
|
+
when '->'
|
2648
|
+
# XXX allow_bad_c..
|
2649
|
+
raise tok, "#{val}: not a pointer" if not val.type.pointer?
|
2650
|
+
type = val.type.pointed.untypedef
|
2651
|
+
raise tok, "#{val}: bad pointer" if not type.kind_of? Union
|
2652
|
+
raise tok, "#{val}: incomplete type" if not type.members
|
2653
|
+
raise tok, "#{val}: invalid member" if not tok = parser.skipspaces or tok.type != :string or not m = type.findmember(tok.raw)
|
2654
|
+
CExpression.new(val, :'->', tok.raw, m.type)
|
2655
|
+
end
|
2656
|
+
when '.'
|
2657
|
+
type = val.type.untypedef
|
2658
|
+
if not ntok = parser.skipspaces or ntok.type != :string or not type.kind_of? Union
|
2659
|
+
parser.unreadtok ntok
|
2660
|
+
nil
|
2661
|
+
else
|
2662
|
+
raise ntok, "#{val}: incomplete type" if not type.members
|
2663
|
+
raise ntok, "#{val}: invalid member" if not m = type.findmember(ntok.raw)
|
2664
|
+
CExpression.new(val, :'.', ntok.raw, m.type)
|
2665
|
+
end
|
2666
|
+
when '['
|
2667
|
+
raise tok, "#{val}: index expected" if not idx = parse(parser, scope)
|
2668
|
+
val, idx = idx, val if not val.type.pointer? # fake support of "4[tab]"
|
2669
|
+
raise tok, "#{val}: not a pointer" if not val.type.pointer?
|
2670
|
+
raise tok, "#{val}: invalid index" if not idx.type.integral?
|
2671
|
+
raise tok, "#{val}: get perpendicular ! (elsewhere)" if idx.kind_of?(CExpression) and idx.op == :','
|
2672
|
+
raise tok || parser, "']' expected" if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ']'
|
2673
|
+
type = val.type.untypedef.type
|
2674
|
+
# TODO boundscheck (and become king of the universe)
|
2675
|
+
CExpression.new(val, :'[]', idx, type)
|
2676
|
+
when '('
|
2677
|
+
type = val.type.untypedef
|
2678
|
+
type = type.type.untypedef if type.kind_of? Pointer
|
2679
|
+
raise tok, "#{val}: not a function" if not type.kind_of? Function
|
2680
|
+
|
2681
|
+
args = []
|
2682
|
+
loop do
|
2683
|
+
a = parse(parser, scope, false)
|
2684
|
+
break if not a
|
2685
|
+
args << a
|
2686
|
+
if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ','
|
2687
|
+
parser.unreadtok ntok
|
2688
|
+
break
|
2689
|
+
end
|
2690
|
+
end
|
2691
|
+
raise ntok || parser, "#{val}: ')' expected" if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
|
2692
|
+
|
2693
|
+
type.args ||= []
|
2694
|
+
raise tok, "#{val}: bad argument count: #{args.length} for #{type.args.length}" if (type.varargs ? (args.length < type.args.length) : (args.length != type.args.length))
|
2695
|
+
type.args.zip(args) { |ta, a|
|
2696
|
+
p, i = ta.type.pointer?, ta.type.integral?
|
2697
|
+
r = a.reduce(parser) if p or i
|
2698
|
+
if (not p and not i) or (i and not r.kind_of? ::Integer) or (p and r != 0)
|
2699
|
+
tok = tok.dup ; tok.raw = a.to_s
|
2700
|
+
parser.check_compatible_type(tok, a.type, ta.type)
|
2701
|
+
end
|
2702
|
+
}
|
2703
|
+
CExpression.new(val, :funcall, args, type.type)
|
2704
|
+
end
|
2705
|
+
end
|
2706
|
+
|
2707
|
+
if nval
|
2708
|
+
parse_value_postfix(parser, scope, nval)
|
2709
|
+
else
|
2710
|
+
parser.unreadtok tok
|
2711
|
+
val
|
2712
|
+
end
|
2713
|
+
end
|
2714
|
+
|
2715
|
+
def parse(parser, scope, allow_coma = true)
|
2716
|
+
opstack = []
|
2717
|
+
stack = []
|
2718
|
+
|
2719
|
+
popstack = lambda {
|
2720
|
+
r, l = stack.pop, stack.pop
|
2721
|
+
case op = opstack.pop
|
2722
|
+
when :'?:'
|
2723
|
+
stack << CExpression.new(stack.pop, op, [l, r], l.type)
|
2724
|
+
when :','
|
2725
|
+
stack << CExpression.new(l, op, r, r.type)
|
2726
|
+
when :'='
|
2727
|
+
parser.check_compatible_type(parser, r.type, l.type)
|
2728
|
+
stack << CExpression.new(l, op, r, l.type)
|
2729
|
+
when :'&&', :'||'
|
2730
|
+
stack << CExpression.new(l, op, r, BaseType.new(:int))
|
2731
|
+
else
|
2732
|
+
# XXX struct == struct ?
|
2733
|
+
raise parser, "invalid type #{l.type} #{l} for #{op.inspect}" if not l.type.arithmetic? and not parser.allow_bad_c
|
2734
|
+
raise parser, "invalid type #{r.type} #{r} for #{op.inspect}" if not r.type.arithmetic? and not parser.allow_bad_c
|
2735
|
+
|
2736
|
+
if l.type.pointer? and r.type.pointer?
|
2737
|
+
type = \
|
2738
|
+
case op
|
2739
|
+
when :'-'; BaseType.new(:long) # addr_t or sumthin ?
|
2740
|
+
when :'-='; l.type
|
2741
|
+
when :'>', :'>=', :'<', :'<=', :'==', :'!='; BaseType.new(:long)
|
2742
|
+
else raise parser, "cannot do #{op.inspect} on pointers" unless parser.allow_bad_c ; l.type
|
2743
|
+
end
|
2744
|
+
elsif l.type.pointer? or r.type.pointer?
|
2745
|
+
puts parser.exception("should not #{op.inspect} a pointer").message if $VERBOSE and not [:'+', :'-', :'=', :'+=', :'-=', :==, :'!='].include? op
|
2746
|
+
type = l.type.pointer? ? l.type : r.type
|
2747
|
+
else
|
2748
|
+
# yay integer promotion
|
2749
|
+
lt = l.type.untypedef
|
2750
|
+
rt = r.type.untypedef
|
2751
|
+
if (t = lt).name == :longdouble or (t = rt).name == :longdouble or
|
2752
|
+
(t = lt).name == :double or (t = rt).name == :double or
|
2753
|
+
(t = lt).name == :float or (t = rt).name == :float
|
2754
|
+
# long double > double > float ...
|
2755
|
+
type = t
|
2756
|
+
elsif true
|
2757
|
+
# custom integer rules based on type sizes
|
2758
|
+
lts = parser.typesize[lt.name]
|
2759
|
+
rts = parser.typesize[rt.name]
|
2760
|
+
its = parser.typesize[:int]
|
2761
|
+
if not lts or not rts
|
2762
|
+
type = BaseType.new(:int)
|
2763
|
+
elsif lts > rts and lts >= its
|
2764
|
+
type = lt
|
2765
|
+
elsif rts > lts and rts >= its
|
2766
|
+
type = rt
|
2767
|
+
elsif lts == rts and lts >= its
|
2768
|
+
type = lt
|
2769
|
+
type = rt if rt.specifier == :unsigned
|
2770
|
+
else
|
2771
|
+
type = BaseType.new(:int)
|
2772
|
+
end
|
2773
|
+
# end of custom rules
|
2774
|
+
elsif ((t = lt).name == :long and t.specifier == :unsigned) or
|
2775
|
+
((t = rt).name == :long and t.specifier == :unsigned)
|
2776
|
+
# ... ulong ...
|
2777
|
+
type = t
|
2778
|
+
elsif (lt.name == :long and rt.name == :int and rt.specifier == :unsigned) or
|
2779
|
+
(rt.name == :long and lt.name == :int and lt.specifier == :unsigned)
|
2780
|
+
# long+uint => ulong
|
2781
|
+
type = BaseType.new(:long, :unsigned)
|
2782
|
+
elsif (t = lt).name == :long or (t = rt).name == :long or
|
2783
|
+
((t = lt).name == :int and t.specifier == :unsigned) or
|
2784
|
+
((t = rt).name == :int and t.specifier == :unsigned)
|
2785
|
+
# ... long > uint ...
|
2786
|
+
type = t
|
2787
|
+
else
|
2788
|
+
# int
|
2789
|
+
type = BaseType.new(:int)
|
2790
|
+
end
|
2791
|
+
end
|
2792
|
+
|
2793
|
+
case op
|
2794
|
+
when :'>', :'>=', :'<', :'<=', :'==', :'!='
|
2795
|
+
# cast both sides
|
2796
|
+
l = CExpression[l, type] if l.type != type
|
2797
|
+
r = CExpression[r, type] if r.type != type
|
2798
|
+
stack << CExpression.new(l, op, r, BaseType.new(:int))
|
2799
|
+
else
|
2800
|
+
# promote result
|
2801
|
+
stack << CExpression.new(l, op, r, type)
|
2802
|
+
end
|
2803
|
+
end
|
2804
|
+
}
|
2805
|
+
|
2806
|
+
return if not e = parse_value(parser, scope)
|
2807
|
+
|
2808
|
+
stack << e
|
2809
|
+
|
2810
|
+
while op = readop(parser)
|
2811
|
+
case op.value
|
2812
|
+
when :'?'
|
2813
|
+
# a, b ? c, d : e, f == a, (b ? (c, d) : e), f
|
2814
|
+
until opstack.empty? or OP_PRIO[opstack.last][:'?:']
|
2815
|
+
popstack[]
|
2816
|
+
end
|
2817
|
+
stack << parse(parser, scope)
|
2818
|
+
raise op || parser, '":" expected' if not op = readop(parser) or op.value != :':'
|
2819
|
+
op = op.dup
|
2820
|
+
op.value = :'?:'
|
2821
|
+
when :':'
|
2822
|
+
parser.unreadtok op
|
2823
|
+
break
|
2824
|
+
else
|
2825
|
+
if not allow_coma and op.value == :','
|
2826
|
+
parser.unreadtok op
|
2827
|
+
break
|
2828
|
+
end
|
2829
|
+
until opstack.empty? or OP_PRIO[op.value][opstack.last]
|
2830
|
+
popstack[]
|
2831
|
+
end
|
2832
|
+
end
|
2833
|
+
|
2834
|
+
raise op, 'need rhs' if not e = parse_value(parser, scope)
|
2835
|
+
stack << e
|
2836
|
+
opstack << op.value
|
2837
|
+
end
|
2838
|
+
|
2839
|
+
until opstack.empty?
|
2840
|
+
popstack[]
|
2841
|
+
end
|
2842
|
+
|
2843
|
+
CExpression[stack.first]
|
2844
|
+
end
|
2845
|
+
end
|
2846
|
+
end
|
2847
|
+
|
2848
|
+
|
2849
|
+
|
2850
|
+
#
|
2851
|
+
# AllocCStruct: ruby interpreter memory-mapped structure
|
2852
|
+
#
|
2853
|
+
|
2854
|
+
# this maps a C structure from a C parser to a ruby String
|
2855
|
+
# struct members are accessed through obj['fldname']
|
2856
|
+
# obj.fldname is an alias
|
2857
|
+
class AllocCStruct
|
2858
|
+
# str is a reference to the underlying ruby String
|
2859
|
+
# stroff is the offset from the start of this string (non-nul for nested structs)
|
2860
|
+
# cp is a reference to the C::Parser
|
2861
|
+
# struct to the C::Union/Struct/Array
|
2862
|
+
# sizeof is the byte size of the C struct
|
2863
|
+
attr_accessor :str, :stroff, :cp, :struct
|
2864
|
+
attr_writer :sizeof
|
2865
|
+
def initialize(cp, struct, str=nil, stroff=0)
|
2866
|
+
@cp, @struct = cp, struct
|
2867
|
+
@str = str || [0].pack('C')*sizeof
|
2868
|
+
@stroff = stroff
|
2869
|
+
end
|
2870
|
+
|
2871
|
+
def sizeof
|
2872
|
+
@sizeof ||= @cp.sizeof(@struct)
|
2873
|
+
end
|
2874
|
+
|
2875
|
+
def [](*a)
|
2876
|
+
if @struct.kind_of? C::Array and a.length == 1 and @struct.length and a[0].kind_of? Integer
|
2877
|
+
i = a[0]
|
2878
|
+
raise "#{i} out of bounds 0...#{@struct.length}" if i < 0 or i >= @struct.length
|
2879
|
+
off = @stroff + i*@cp.sizeof(@struct.type)
|
2880
|
+
return @cp.decode_c_value(@str, @struct.type, off)
|
2881
|
+
end
|
2882
|
+
|
2883
|
+
return @str[@stroff..-1][*a] if a.length != 1
|
2884
|
+
a = a.first
|
2885
|
+
return @str[@stroff..-1][a] if not a.kind_of? Symbol and not a.kind_of? String and not a.kind_of? C::Variable
|
2886
|
+
f = a
|
2887
|
+
raise "#{a.inspect} not a member" if not f.kind_of? C::Variable and not f = @struct.findmember(a.to_s, true)
|
2888
|
+
a = f.name || f
|
2889
|
+
off = @stroff + @struct.offsetof(@cp, a)
|
2890
|
+
if bf = @struct.bitoffsetof(@cp, a)
|
2891
|
+
ft = C::BaseType.new((bf[0] + bf[1] > 32) ? :__int64 : :__int32)
|
2892
|
+
v = @cp.decode_c_value(@str, ft, off)
|
2893
|
+
(v >> bf[0]) & ((1 << bf[1])-1)
|
2894
|
+
else
|
2895
|
+
@cp.decode_c_value(@str, f, off)
|
2896
|
+
end
|
2897
|
+
end
|
2898
|
+
|
2899
|
+
def []=(*a)
|
2900
|
+
if @struct.kind_of? C::Array and a.length == 2 and @struct.length and a[0].kind_of? Integer
|
2901
|
+
i = a[0]
|
2902
|
+
raise "#{i} out of bounds 0...#{@struct.length}" if i < 0 or i >= @struct.length
|
2903
|
+
off = @stroff + i*@cp.sizeof(@struct.type)
|
2904
|
+
val = @cp.encode_c_value(@struct.type, a[1])
|
2905
|
+
@str[off, val.length] = val
|
2906
|
+
return
|
2907
|
+
end
|
2908
|
+
|
2909
|
+
if not a.first.kind_of? Symbol and not a.first.kind_of? String and not a.first.kind_of? C::Variable
|
2910
|
+
# patch @str[@stroff..-1] like a string
|
2911
|
+
# so we must find the intended start offset, and add @stroff to it
|
2912
|
+
if @stroff != 0
|
2913
|
+
case a.first
|
2914
|
+
when Range
|
2915
|
+
if a.first.begin >= 0
|
2916
|
+
a[0] = ::Range.new(a[0].begin+@stroff, a[0].end+@stroff, a[0].exclude_end?)
|
2917
|
+
else raise 'no can do, use positive index'
|
2918
|
+
end
|
2919
|
+
when Integer
|
2920
|
+
if a.first >= 0
|
2921
|
+
a[0] += @stroff
|
2922
|
+
else raise 'no can do, use positive index'
|
2923
|
+
end
|
2924
|
+
else raise 'no can do'
|
2925
|
+
end
|
2926
|
+
end
|
2927
|
+
|
2928
|
+
return @str.send(:'[]=', *a) # XXX *should* work...
|
2929
|
+
end
|
2930
|
+
|
2931
|
+
a, val = a
|
2932
|
+
raise "#{a.inspect} not a struct member" if not a.kind_of? C::Variable and not f = @struct.findmember(a.to_s, true)
|
2933
|
+
a = f.name if a.kind_of? String or a.kind_of? Symbol
|
2934
|
+
val = sizeof if val == :size
|
2935
|
+
off = @stroff + @struct.offsetof(@cp, a)
|
2936
|
+
|
2937
|
+
if bf = @struct.bitoffsetof(@cp, a)
|
2938
|
+
raise "only Integers supported in bitfield #{a}, got #{val.inspect}" if not val.kind_of?(::Integer)
|
2939
|
+
# struct { int i:8; }; => size 8 or 32 ?
|
2940
|
+
ft = C::BaseType.new((bf[0] + bf[1] > 32) ? :__int64 : :__int32)
|
2941
|
+
mask = ((1 << bf[1]) - 1) << bf[0]
|
2942
|
+
preval = @cp.decode_c_value(@str, ft, off)
|
2943
|
+
val = (preval & ~mask) | ((val << bf[0]) & mask)
|
2944
|
+
f = ft
|
2945
|
+
end
|
2946
|
+
|
2947
|
+
val = @cp.encode_c_value(f, val)
|
2948
|
+
@str[off, val.length] = val
|
2949
|
+
end
|
2950
|
+
|
2951
|
+
# virtual accessors to members
|
2952
|
+
# struct.foo is aliased to struct['foo'],
|
2953
|
+
# struct.foo = 42 aliased to struct['foo'] = 42
|
2954
|
+
def method_missing(on, *a)
|
2955
|
+
n = on.to_s
|
2956
|
+
if n[-1] == ?=
|
2957
|
+
send :[]=, n[0...-1], *a
|
2958
|
+
else
|
2959
|
+
super(on, *a) if not @struct.kind_of?(C::Union) or not @struct.findmember(n, true)
|
2960
|
+
send :[], n, *a
|
2961
|
+
end
|
2962
|
+
end
|
2963
|
+
|
2964
|
+
def to_s(off=nil, maxdepth=500)
|
2965
|
+
return '{ /* ... */ }' if maxdepth <= 0
|
2966
|
+
str = ['']
|
2967
|
+
if @struct.kind_of?(C::Array)
|
2968
|
+
str.last << "#{@struct.type} x[#{@struct.length}] = " if not off
|
2969
|
+
mlist = (0...@struct.length)
|
2970
|
+
fldoff = mlist.inject({}) { |h, i| h.update i => i*@cp.sizeof(@struct.type) }
|
2971
|
+
elsif @struct.kind_of?(C::Struct)
|
2972
|
+
str.last << "struct #{@struct.name || '_'} x = " if not off
|
2973
|
+
@struct.update_member_cache(@cp) if not @struct.fldlist
|
2974
|
+
fldoff = @struct.fldoffset
|
2975
|
+
fbo = @struct.fldbitoffset || {}
|
2976
|
+
mlist = @struct.members.map { |m| m.name || m }
|
2977
|
+
else
|
2978
|
+
str.last << "union #{@struct.name || '_'} x = " if not off
|
2979
|
+
mlist = @struct.members.map { |m| m.name || m }
|
2980
|
+
end
|
2981
|
+
str.last << '{'
|
2982
|
+
mlist.each { |k|
|
2983
|
+
if k.kind_of? Variable # anonymous member
|
2984
|
+
curoff = off.to_i + @struct.offsetof(@cp, k)
|
2985
|
+
val = self[k]
|
2986
|
+
k = '?'
|
2987
|
+
else
|
2988
|
+
curoff = off.to_i + (fldoff ? fldoff[k].to_i : 0)
|
2989
|
+
val = self[k]
|
2990
|
+
end
|
2991
|
+
if val.kind_of?(::Integer)
|
2992
|
+
if val >= 0x100
|
2993
|
+
val = '0x%X, // +%x' % [val, curoff]
|
2994
|
+
elsif val <= -0x100
|
2995
|
+
val = '-0x%X, // +%x' % [-val, curoff]
|
2996
|
+
else
|
2997
|
+
val = '%d, // +%x' % [val, curoff]
|
2998
|
+
end
|
2999
|
+
elsif val.kind_of? AllocCStruct
|
3000
|
+
val = val.to_s(curoff, maxdepth-1)
|
3001
|
+
elsif not val
|
3002
|
+
val = 'NULL, // +%x' % curoff # pointer with NULL value
|
3003
|
+
else
|
3004
|
+
val = val.to_s.sub(/$/, ', // +%x' % curoff)
|
3005
|
+
end
|
3006
|
+
val = val.gsub("\n", "\n\t")
|
3007
|
+
str << "\t#{k.kind_of?(::Integer) ? "[#{k}]" : ".#{k}"} = #{val}"
|
3008
|
+
}
|
3009
|
+
str << '}'
|
3010
|
+
str.last << (off ? ',' : ';')
|
3011
|
+
str.join("\n")
|
3012
|
+
end
|
3013
|
+
|
3014
|
+
def to_array
|
3015
|
+
raise NoMethodError, "Not an Array" if not @struct.kind_of?(C::Array)
|
3016
|
+
ary = []
|
3017
|
+
@struct.length.times { |i| ary << self[i] }
|
3018
|
+
ary
|
3019
|
+
end
|
3020
|
+
|
3021
|
+
def to_strz
|
3022
|
+
raise NoMethodError, "Not an Array" if not @struct.kind_of?(C::Array)
|
3023
|
+
a = to_array
|
3024
|
+
a[a.index(0)..-1] = [] if a.index(0)
|
3025
|
+
a.pack('C*')
|
3026
|
+
end
|
3027
|
+
end
|
3028
|
+
|
3029
|
+
class Parser
|
3030
|
+
# find a Struct/Union object from a struct name/typedef name
|
3031
|
+
# raises if it cant find it
|
3032
|
+
def find_c_struct(structname)
|
3033
|
+
structname = structname.to_s if structname.kind_of?(::Symbol)
|
3034
|
+
if structname.kind_of?(::String) and not struct = @toplevel.struct[structname]
|
3035
|
+
struct = @toplevel.symbol[structname]
|
3036
|
+
raise "unknown struct #{structname.inspect}" if not struct
|
3037
|
+
struct = struct.type.untypedef
|
3038
|
+
struct = struct.pointed while struct.pointer?
|
3039
|
+
raise "unknown struct #{structname.inspect}" if not struct.kind_of? C::Union
|
3040
|
+
end
|
3041
|
+
struct = structname if structname.kind_of? C::Union
|
3042
|
+
raise "unknown struct #{structname.inspect}" if not struct.kind_of? C::Union
|
3043
|
+
struct
|
3044
|
+
end
|
3045
|
+
|
3046
|
+
# find a C::Type (struct/union/typedef/basetype) from a string
|
3047
|
+
def find_c_type(typename)
|
3048
|
+
typename = typename.to_s if typename.kind_of? ::Symbol
|
3049
|
+
if typename.kind_of?(::String) and not type = @toplevel.struct[typename]
|
3050
|
+
if type = @toplevel.symbol[typename]
|
3051
|
+
type = type.type.untypedef
|
3052
|
+
else
|
3053
|
+
begin
|
3054
|
+
lexer.feed(typename)
|
3055
|
+
b = C::Block.new(@toplevel)
|
3056
|
+
var = Variable.parse_type(self, b)
|
3057
|
+
var.parse_declarator(self, b)
|
3058
|
+
type = var.type
|
3059
|
+
rescue
|
3060
|
+
end
|
3061
|
+
end
|
3062
|
+
end
|
3063
|
+
type = typename if typename.kind_of?(C::Type)
|
3064
|
+
raise "unknown type #{typename.inspect}" if not type.kind_of? C::Type
|
3065
|
+
type
|
3066
|
+
end
|
3067
|
+
|
3068
|
+
# allocate a new AllocCStruct from the struct/struct typedef name of the current toplevel
|
3069
|
+
# optionally populate the fields using the 'values' hash
|
3070
|
+
def alloc_c_struct(structname, values=nil)
|
3071
|
+
struct = find_c_struct(structname)
|
3072
|
+
st = AllocCStruct.new(self, struct)
|
3073
|
+
values.each { |k, v| st[k] = v } if values
|
3074
|
+
st
|
3075
|
+
end
|
3076
|
+
|
3077
|
+
# parse a given String as an AllocCStruct
|
3078
|
+
# offset is an optionnal offset from the string start
|
3079
|
+
# modification to the structure will modify the underlying string
|
3080
|
+
def decode_c_struct(structname, str, offset=0)
|
3081
|
+
struct = find_c_struct(structname)
|
3082
|
+
AllocCStruct.new(self, struct, str, offset)
|
3083
|
+
end
|
3084
|
+
|
3085
|
+
# allocate an array of types
|
3086
|
+
# init is either the length of the array, or an array of initial values
|
3087
|
+
def alloc_c_ary(typename, init=1)
|
3088
|
+
type = find_c_type(typename)
|
3089
|
+
len = init.kind_of?(Integer) ? init : init.length
|
3090
|
+
struct = C::Array.new(type, len)
|
3091
|
+
st = AllocCStruct.new(self, struct)
|
3092
|
+
if init.kind_of?(::Array)
|
3093
|
+
init.each_with_index { |v, i|
|
3094
|
+
st[i] = v
|
3095
|
+
}
|
3096
|
+
end
|
3097
|
+
st
|
3098
|
+
end
|
3099
|
+
|
3100
|
+
# "cast" a string to C::Array
|
3101
|
+
def decode_c_ary(typename, len, str, offset=0)
|
3102
|
+
type = find_c_type(typename)
|
3103
|
+
struct = C::Array.new(type, len)
|
3104
|
+
AllocCStruct.new(self, struct, str, offset)
|
3105
|
+
end
|
3106
|
+
|
3107
|
+
# convert (pack) a ruby value into a C buffer
|
3108
|
+
# packs integers, converts Strings to their C pointer (using DynLdr)
|
3109
|
+
def encode_c_value(type, val)
|
3110
|
+
type = type.type if type.kind_of? Variable
|
3111
|
+
|
3112
|
+
case val
|
3113
|
+
when nil; val = 0
|
3114
|
+
when ::Integer
|
3115
|
+
when ::String
|
3116
|
+
val = DynLdr.str_ptr(val)
|
3117
|
+
when ::Hash
|
3118
|
+
type = type.pointed while type.pointer?
|
3119
|
+
raise "need a struct ptr for #{type} #{val.inspect}" if not type.kind_of? Union
|
3120
|
+
buf = alloc_c_struct(type, val)
|
3121
|
+
val.instance_variable_set('@rb2c', buf) # GC trick
|
3122
|
+
val = buf
|
3123
|
+
when ::Proc
|
3124
|
+
val = DynLdr.convert_rb2c(type, val) # allocate a DynLdr callback
|
3125
|
+
when AllocCStruct
|
3126
|
+
val = DynLdr.str_ptr(val.str) + val.stroff
|
3127
|
+
#when ::Float # TODO
|
3128
|
+
else raise "TODO #{val.inspect}"
|
3129
|
+
end
|
3130
|
+
|
3131
|
+
val = Expression.encode_immediate(val, sizeof(type), @endianness) if val.kind_of?(::Integer)
|
3132
|
+
|
3133
|
+
val
|
3134
|
+
end
|
3135
|
+
|
3136
|
+
def decode_c_value(str, type, off=0)
|
3137
|
+
type = type.type if type.kind_of? Variable
|
3138
|
+
type = type.untypedef
|
3139
|
+
if type.kind_of? C::Union or type.kind_of? C::Array
|
3140
|
+
return AllocCStruct.new(self, type, str, off)
|
3141
|
+
end
|
3142
|
+
val = Expression.decode_immediate(str, sizeof(type), @endianness, off)
|
3143
|
+
val = Expression.make_signed(val, sizeof(type)*8) if type.integral? and type.signed?
|
3144
|
+
val = nil if val == 0 and type.pointer?
|
3145
|
+
val
|
3146
|
+
end
|
3147
|
+
end
|
3148
|
+
|
3149
|
+
|
3150
|
+
#
|
3151
|
+
# Dumper : objects => C source
|
3152
|
+
#
|
3153
|
+
|
3154
|
+
class Parser
|
3155
|
+
# returns a big string containing all definitions from headers used in the source (including macros)
|
3156
|
+
def factorize(*a)
|
3157
|
+
factorize_init
|
3158
|
+
parse(*a)
|
3159
|
+
raise @lexer.readtok || self, 'eof expected' if not @lexer.eos?
|
3160
|
+
factorize_final
|
3161
|
+
end
|
3162
|
+
|
3163
|
+
def factorize_init
|
3164
|
+
@lexer.traced_macros = []
|
3165
|
+
end
|
3166
|
+
|
3167
|
+
def factorize_final
|
3168
|
+
# now find all types/defs not coming from the standard headers
|
3169
|
+
# all
|
3170
|
+
all = @toplevel.struct.values + @toplevel.symbol.values
|
3171
|
+
all -= all.grep(::Integer) # Enum values
|
3172
|
+
|
3173
|
+
# list of definitions of user-defined objects
|
3174
|
+
userdefined = all.find_all { |t|
|
3175
|
+
t.backtrace.backtrace.grep(::String).grep(/^</).empty?
|
3176
|
+
}
|
3177
|
+
|
3178
|
+
@toplevel.statements.clear # don't want all Declarations
|
3179
|
+
|
3180
|
+
# a macro is fine too
|
3181
|
+
@lexer.dump_macros(@lexer.traced_macros, false) + "\n\n" +
|
3182
|
+
dump_definitions(userdefined, userdefined)
|
3183
|
+
end
|
3184
|
+
|
3185
|
+
# returns a big string representing the definitions of all terms appearing in +list+, excluding +exclude+
|
3186
|
+
# includes dependencies
|
3187
|
+
def dump_definitions(list, exclude=[])
|
3188
|
+
# recurse all dependencies
|
3189
|
+
todo_rndr = {}
|
3190
|
+
todo_deps = {}
|
3191
|
+
list.each { |t|
|
3192
|
+
todo_rndr[t], todo_deps[t] = t.dump_def(@toplevel)
|
3193
|
+
}
|
3194
|
+
# c.toplevel.anonymous_enums.to_a.each { |t| todo_rndr[t], todo_deps[t] = t.dump_def(c.toplevel) }
|
3195
|
+
while !(ar = (todo_deps.values.flatten - todo_deps.keys)).empty?
|
3196
|
+
ar.each { |t|
|
3197
|
+
todo_rndr[t], todo_deps[t] = t.dump_def(@toplevel)
|
3198
|
+
}
|
3199
|
+
end
|
3200
|
+
exclude.each { |t| todo_deps.delete t ; todo_rndr.delete t }
|
3201
|
+
todo_deps.each_key { |t| todo_deps[t] -= exclude }
|
3202
|
+
|
3203
|
+
all = @toplevel.struct.values + @toplevel.symbol.values
|
3204
|
+
all -= all.grep(::Integer) # Enum values
|
3205
|
+
|
3206
|
+
r, dep = @toplevel.dump_reorder(all, todo_rndr, todo_deps)
|
3207
|
+
r.join("\n")
|
3208
|
+
end
|
3209
|
+
|
3210
|
+
# returns a string containing the C definition(s) of toplevel functions, with their dependencies
|
3211
|
+
def dump_definition(*funcnames)
|
3212
|
+
oldst = @toplevel.statements
|
3213
|
+
@toplevel.statements = []
|
3214
|
+
dump_definitions(funcnames.map { |f| @toplevel.symbol[f] })
|
3215
|
+
ensure
|
3216
|
+
@toplevel.statements = oldst
|
3217
|
+
end
|
3218
|
+
|
3219
|
+
def to_s
|
3220
|
+
@toplevel.dump(nil)[0].join("\n")
|
3221
|
+
end
|
3222
|
+
end
|
3223
|
+
|
3224
|
+
class Statement
|
3225
|
+
def self.dump(e, scope, r=[''], dep=[])
|
3226
|
+
case e
|
3227
|
+
when nil; r.last << ';'
|
3228
|
+
when Block
|
3229
|
+
r.last << ' ' if not r.last.empty?
|
3230
|
+
r.last << '{'
|
3231
|
+
tr, dep = e.dump(scope, [''], dep)
|
3232
|
+
tr.pop if tr.last.empty?
|
3233
|
+
r.concat tr.map { |s| Case.dump_indent(s) }
|
3234
|
+
(r.last[-1] == ?{ ? r.last : r) << '}'
|
3235
|
+
else
|
3236
|
+
tr, dep = e.dump(scope, [''], dep)
|
3237
|
+
r.concat tr.map { |s| Case.dump_indent(s) }
|
3238
|
+
end
|
3239
|
+
[r, dep]
|
3240
|
+
end
|
3241
|
+
|
3242
|
+
def to_s
|
3243
|
+
dump(Block.new(nil))[0].join(' ')
|
3244
|
+
end
|
3245
|
+
end
|
3246
|
+
|
3247
|
+
class Block
|
3248
|
+
def to_s() dump(nil)[0].join("\n") end
|
3249
|
+
|
3250
|
+
# return array of c source lines and array of dependencies (objects)
|
3251
|
+
def dump(scp, r=[''], dep=[])
|
3252
|
+
mydefs = @symbol.values.grep(TypeDef) + @struct.values + anonymous_enums.to_a
|
3253
|
+
todo_rndr = {}
|
3254
|
+
todo_deps = {}
|
3255
|
+
mydefs.each { |t| # filter out Enum values
|
3256
|
+
todo_rndr[t], todo_deps[t] = t.dump_def(self)
|
3257
|
+
}
|
3258
|
+
r, dep = dump_reorder(mydefs, todo_rndr, todo_deps, r, dep)
|
3259
|
+
dep -= @symbol.values + @struct.values
|
3260
|
+
[r, dep]
|
3261
|
+
end
|
3262
|
+
|
3263
|
+
def dump_reorder(mydefs, todo_rndr, todo_deps, r=[''], dep=[])
|
3264
|
+
val = todo_deps.values.flatten.uniq
|
3265
|
+
dep |= val
|
3266
|
+
dep -= mydefs | todo_deps.keys
|
3267
|
+
todo_deps.each { |k, v| v.delete k }
|
3268
|
+
ext = val - mydefs
|
3269
|
+
if ext.length > todo_deps.length
|
3270
|
+
todo_deps.each_key { |k| todo_deps[k] = todo_deps[k] & mydefs }
|
3271
|
+
else
|
3272
|
+
ext.each { |k| todo_deps.each_value { |v| v.delete k } }
|
3273
|
+
end
|
3274
|
+
|
3275
|
+
# predeclare structs involved in cyclic dependencies
|
3276
|
+
dep_cycle = lambda { |ary|
|
3277
|
+
# sexyness inside (c)
|
3278
|
+
deps = todo_deps[ary.last]
|
3279
|
+
if deps.include? ary.first; ary
|
3280
|
+
elsif (deps-ary).find { |d| deps = dep_cycle[ary + [d]] }; deps
|
3281
|
+
end
|
3282
|
+
}
|
3283
|
+
todo_rndr.keys.grep(Union).find_all { |t| t.name }.sort_by { |t| t.name }.each { |t|
|
3284
|
+
oldc = nil
|
3285
|
+
while c = dep_cycle[[t]]
|
3286
|
+
break if oldc == c
|
3287
|
+
r << "#{t.kind_of?(Struct) ? 'struct' : 'union'} #{t.name};" if not oldc
|
3288
|
+
oldc = c
|
3289
|
+
c.each { |s|
|
3290
|
+
# XXX struct z { struct a* }; struct a { void (*foo)(struct z); };
|
3291
|
+
todo_deps[s].delete t unless s.kind_of? Union and
|
3292
|
+
s.members.find { |sm| sm.type.untypedef == t }
|
3293
|
+
}
|
3294
|
+
end
|
3295
|
+
}
|
3296
|
+
|
3297
|
+
loop do
|
3298
|
+
break if todo_rndr.empty?
|
3299
|
+
todo_now = todo_deps.keys.find_all { |k| todo_deps[k].empty? }
|
3300
|
+
if todo_now.empty?
|
3301
|
+
r << '// dependency problem, this may not compile'
|
3302
|
+
todo_now = todo_deps.keys
|
3303
|
+
end
|
3304
|
+
todo_now.sort_by { |k| k.name || '0' }.each { |k|
|
3305
|
+
if k.kind_of? Variable and k.type.kind_of? Function and k.initializer
|
3306
|
+
r << ''
|
3307
|
+
r.concat todo_rndr.delete(k)
|
3308
|
+
else
|
3309
|
+
r.pop if r.last == ''
|
3310
|
+
r.concat todo_rndr.delete(k)
|
3311
|
+
r.last << ';'
|
3312
|
+
end
|
3313
|
+
todo_deps.delete k
|
3314
|
+
}
|
3315
|
+
todo_deps.each_key { |k| todo_deps[k] -= todo_now }
|
3316
|
+
r << '' << '' << ''
|
3317
|
+
end
|
3318
|
+
|
3319
|
+
@statements.each { |s|
|
3320
|
+
r << '' if not r.last.empty?
|
3321
|
+
if s.kind_of? Block
|
3322
|
+
r, dep = Statement.dump(s, self, r, dep)
|
3323
|
+
else
|
3324
|
+
r, dep = s.dump(self, r, dep)
|
3325
|
+
end
|
3326
|
+
}
|
3327
|
+
|
3328
|
+
[r, dep]
|
3329
|
+
end
|
3330
|
+
end
|
3331
|
+
class Declaration
|
3332
|
+
def dump(scope, r=[''], dep=[])
|
3333
|
+
tr, dep = @var.dump_def(scope, [''], dep)
|
3334
|
+
if @var.kind_of? Variable and @var.type.kind_of? Function and @var.initializer
|
3335
|
+
r << ''
|
3336
|
+
r.concat tr
|
3337
|
+
else
|
3338
|
+
r.pop if r.last == ''
|
3339
|
+
r.concat tr
|
3340
|
+
r.last << ';'
|
3341
|
+
end
|
3342
|
+
[r, dep]
|
3343
|
+
end
|
3344
|
+
|
3345
|
+
def to_s
|
3346
|
+
dump(Block.new(nil))[0].join(' ')
|
3347
|
+
end
|
3348
|
+
end
|
3349
|
+
module Attributes
|
3350
|
+
def dump_attributes
|
3351
|
+
if attributes
|
3352
|
+
(attributes - DECLSPECS).map { |a| " __attribute__((#{a}))" }.join
|
3353
|
+
else ''
|
3354
|
+
end
|
3355
|
+
end
|
3356
|
+
def dump_attributes_pre
|
3357
|
+
if attributes
|
3358
|
+
(attributes & DECLSPECS).map { |a| "__#{a} " }.join
|
3359
|
+
else ''
|
3360
|
+
end
|
3361
|
+
end
|
3362
|
+
end
|
3363
|
+
class Variable
|
3364
|
+
def dump(scope, r=[''], dep=[])
|
3365
|
+
if name
|
3366
|
+
dep |= [scope.symbol_ancestors[@name]]
|
3367
|
+
r.last << @name
|
3368
|
+
end
|
3369
|
+
[r, dep]
|
3370
|
+
end
|
3371
|
+
def dump_def(scope, r=[''], dep=[], skiptype=false)
|
3372
|
+
# int a=1, b=2;
|
3373
|
+
r.last << dump_attributes_pre
|
3374
|
+
if not skiptype
|
3375
|
+
r.last << @storage.to_s << ' ' if storage
|
3376
|
+
r, dep = @type.base.dump(scope, r, dep)
|
3377
|
+
r.last << ' ' if name
|
3378
|
+
end
|
3379
|
+
r, dep = @type.dump_declarator([(name ? @name.dup : '') << dump_attributes], scope, r, dep)
|
3380
|
+
|
3381
|
+
if initializer
|
3382
|
+
r.last << ' = ' if not @type.kind_of?(Function)
|
3383
|
+
r, dep = @type.dump_initializer(@initializer, scope, r, dep)
|
3384
|
+
end
|
3385
|
+
[r, dep]
|
3386
|
+
end
|
3387
|
+
|
3388
|
+
def to_s
|
3389
|
+
dump(Block.new(nil))[0].join(' ')
|
3390
|
+
end
|
3391
|
+
end
|
3392
|
+
class Type
|
3393
|
+
def dump_initializer(init, scope, r=[''], dep=[])
|
3394
|
+
case init
|
3395
|
+
when ::Numeric
|
3396
|
+
r.last << init.to_s
|
3397
|
+
[r, dep]
|
3398
|
+
when ::Array
|
3399
|
+
r.last << init.inspect
|
3400
|
+
[r, dep]
|
3401
|
+
else init.dump_inner(scope, r, dep)
|
3402
|
+
end
|
3403
|
+
end
|
3404
|
+
|
3405
|
+
def dump_declarator(decl, scope, r=[''], dep=[])
|
3406
|
+
r.last << decl.shift
|
3407
|
+
r.concat decl
|
3408
|
+
[r, dep]
|
3409
|
+
end
|
3410
|
+
|
3411
|
+
def dump_def(*a)
|
3412
|
+
dump(*a)
|
3413
|
+
end
|
3414
|
+
|
3415
|
+
def dump_cast(scope, r=[''], dep=[])
|
3416
|
+
r.last << '('
|
3417
|
+
r.last << dump_attributes_pre if not kind_of? TypeDef
|
3418
|
+
r, dep = base.dump(scope, r, dep)
|
3419
|
+
r, dep = dump_declarator([kind_of?(TypeDef) ? '' : dump_attributes], scope, r, dep)
|
3420
|
+
r.last << ')'
|
3421
|
+
[r, dep]
|
3422
|
+
end
|
3423
|
+
|
3424
|
+
def to_s
|
3425
|
+
dump_cast(Block.new(nil))[0].join(' ')
|
3426
|
+
end
|
3427
|
+
end
|
3428
|
+
class Pointer
|
3429
|
+
def dump_declarator(decl, scope, r=[''], dep=[])
|
3430
|
+
d = decl[0]
|
3431
|
+
decl[0] = '*'
|
3432
|
+
decl[0] << ' ' << @qualifier.map { |q| q.to_s }.join(' ') << ' ' if qualifier
|
3433
|
+
decl[0] << d
|
3434
|
+
if @type.kind_of? Function or @type.kind_of? Array
|
3435
|
+
decl[0] = '(' << decl[0]
|
3436
|
+
decl.last << ')'
|
3437
|
+
end
|
3438
|
+
@type.dump_declarator(decl, scope, r, dep)
|
3439
|
+
end
|
3440
|
+
end
|
3441
|
+
class Array
|
3442
|
+
def dump_declarator(decl, scope, r=[''], dep=[])
|
3443
|
+
decl.last << '()' if decl.last.empty?
|
3444
|
+
decl.last << '['
|
3445
|
+
decl, dep = CExpression.dump(@length, scope, decl, dep) if length
|
3446
|
+
decl.last << ']'
|
3447
|
+
@type.dump_declarator(decl, scope, r, dep)
|
3448
|
+
end
|
3449
|
+
def dump_initializer(init, scope, r=[''], dep=[])
|
3450
|
+
return super(init, scope, r, dep) if not init.kind_of? ::Array
|
3451
|
+
r.last << '{ '
|
3452
|
+
showname = false
|
3453
|
+
init.each_with_index { |v, i|
|
3454
|
+
if not v
|
3455
|
+
showname = true
|
3456
|
+
next
|
3457
|
+
end
|
3458
|
+
r.last << ', ' if r.last[-2, 2] != '{ '
|
3459
|
+
rt = ['']
|
3460
|
+
if showname
|
3461
|
+
showname = false
|
3462
|
+
rt << "[#{i}] = "
|
3463
|
+
end
|
3464
|
+
rt, dep = @type.dump_initializer(v, scope, rt, dep)
|
3465
|
+
r.last << rt.shift
|
3466
|
+
r.concat rt.map { |s| "\t" << s }
|
3467
|
+
}
|
3468
|
+
r.last << ' }'
|
3469
|
+
[r, dep]
|
3470
|
+
end
|
3471
|
+
end
|
3472
|
+
class Function
|
3473
|
+
def dump_declarator(decl, scope, r=[''], dep=[])
|
3474
|
+
decl.last << '()' if decl.last.empty?
|
3475
|
+
decl.last << '('
|
3476
|
+
if args
|
3477
|
+
@args.each { |arg|
|
3478
|
+
decl.last << ', ' if decl.last[-1] != ?(
|
3479
|
+
decl, dep = arg.dump_def(scope, decl, dep)
|
3480
|
+
}
|
3481
|
+
if varargs
|
3482
|
+
decl.last << ', ' if decl.last[-1] != ?(
|
3483
|
+
decl.last << '...'
|
3484
|
+
else
|
3485
|
+
decl.last << 'void' if @args.empty?
|
3486
|
+
end
|
3487
|
+
end
|
3488
|
+
decl.last << ')'
|
3489
|
+
@type.dump_declarator(decl, scope, r, dep)
|
3490
|
+
end
|
3491
|
+
|
3492
|
+
def dump_initializer(init, scope, r=[''], dep=[])
|
3493
|
+
Statement.dump(init, scope, r << '', dep)
|
3494
|
+
end
|
3495
|
+
end
|
3496
|
+
class BaseType
|
3497
|
+
def dump(scope, r=[''], dep=[])
|
3498
|
+
r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier
|
3499
|
+
r.last << @specifier.to_s << ' ' if specifier and @name != :ptr
|
3500
|
+
r.last << case @name
|
3501
|
+
when :longlong; 'long long'
|
3502
|
+
when :longdouble; 'long double'
|
3503
|
+
when :ptr; specifier == :unsigned ? 'uintptr_t' : 'intptr_t'
|
3504
|
+
else @name.to_s
|
3505
|
+
end
|
3506
|
+
[r, dep]
|
3507
|
+
end
|
3508
|
+
end
|
3509
|
+
class TypeDef
|
3510
|
+
def dump(scope, r=[''], dep=[])
|
3511
|
+
r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier
|
3512
|
+
r.last << @name
|
3513
|
+
dep |= [scope.symbol_ancestors[@name]]
|
3514
|
+
[r, dep]
|
3515
|
+
end
|
3516
|
+
|
3517
|
+
def dump_def(scope, r=[''], dep=[])
|
3518
|
+
r.last << 'typedef '
|
3519
|
+
r.last << dump_attributes_pre
|
3520
|
+
r, dep = @type.base.dump(scope, r, dep)
|
3521
|
+
r.last << ' '
|
3522
|
+
@type.dump_declarator([(name ? @name.dup : '') << dump_attributes], scope, r, dep)
|
3523
|
+
end
|
3524
|
+
|
3525
|
+
def dump_initializer(init, scope, r=[''], dep=[])
|
3526
|
+
@type.dump_initializer(init, scope, r, dep)
|
3527
|
+
end
|
3528
|
+
end
|
3529
|
+
class Union
|
3530
|
+
def dump(scope, r=[''], dep=[])
|
3531
|
+
if name
|
3532
|
+
r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier
|
3533
|
+
r.last << self.class.name.downcase[/(?:.*::)?(.*)/, 1] << ' ' << @name
|
3534
|
+
dep |= [scope.struct_ancestors[@name]]
|
3535
|
+
[r, dep]
|
3536
|
+
else
|
3537
|
+
dump_def(scope, r, dep)
|
3538
|
+
end
|
3539
|
+
end
|
3540
|
+
|
3541
|
+
def dump_def(scope, r=[''], dep=[])
|
3542
|
+
r << ''
|
3543
|
+
r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier
|
3544
|
+
r.last << self.class.name.downcase[/(?:.*::)?(.*)/, 1]
|
3545
|
+
r.last << ' ' << @name if name
|
3546
|
+
if members
|
3547
|
+
r.last << ' {'
|
3548
|
+
@members.each_with_index { |m,i|
|
3549
|
+
tr, dep = m.dump_def(scope, [''], dep)
|
3550
|
+
tr.last << ':' << @bits[i].to_s if bits and @bits[i]
|
3551
|
+
tr.last << ';'
|
3552
|
+
r.concat tr.map { |s| "\t" << s }
|
3553
|
+
}
|
3554
|
+
r << '}'
|
3555
|
+
end
|
3556
|
+
r.last << dump_attributes
|
3557
|
+
[r, dep]
|
3558
|
+
end
|
3559
|
+
|
3560
|
+
def dump_initializer(init, scope, r=[''], dep=[])
|
3561
|
+
return super(init, scope, r, dep) if not init.kind_of? ::Array
|
3562
|
+
r.last << '{ '
|
3563
|
+
showname = false
|
3564
|
+
@members.zip(init) { |m, i|
|
3565
|
+
if not i
|
3566
|
+
showname = true
|
3567
|
+
next
|
3568
|
+
end
|
3569
|
+
r.last << ', ' if r.last[-2, 2] != '{ '
|
3570
|
+
rt = ['']
|
3571
|
+
if showname
|
3572
|
+
showname = false
|
3573
|
+
rt << ".#{m.name} = "
|
3574
|
+
end
|
3575
|
+
rt, dep = m.type.dump_initializer(i, scope, rt, dep)
|
3576
|
+
r.last << rt.shift
|
3577
|
+
r.concat rt.map { |s| "\t" << s }
|
3578
|
+
}
|
3579
|
+
r.last << ' }'
|
3580
|
+
[r, dep]
|
3581
|
+
end
|
3582
|
+
end
|
3583
|
+
class Struct
|
3584
|
+
def dump_def(scope, r=[''], dep=[])
|
3585
|
+
if pack
|
3586
|
+
r, dep = super(scope, r, dep)
|
3587
|
+
r.last <<
|
3588
|
+
if @pack == 1; (has_attribute('packed') or has_attribute_var('pack')) ? '' : " __attribute__((packed))"
|
3589
|
+
else has_attribute_var('pack') ? '' : " __attribute__((pack(#@pack)))"
|
3590
|
+
end
|
3591
|
+
[r, dep]
|
3592
|
+
else
|
3593
|
+
super(scope, r, dep)
|
3594
|
+
end
|
3595
|
+
end
|
3596
|
+
end
|
3597
|
+
class Enum
|
3598
|
+
def dump(scope, r=[''], dep=[])
|
3599
|
+
if name
|
3600
|
+
r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier
|
3601
|
+
r.last << 'enum ' << @name
|
3602
|
+
dep |= [scope.struct_ancestors[@name]]
|
3603
|
+
[r, dep]
|
3604
|
+
else
|
3605
|
+
dump_def(scope, r, dep)
|
3606
|
+
end
|
3607
|
+
end
|
3608
|
+
|
3609
|
+
def dump_def(scope, r=[''], dep=[])
|
3610
|
+
r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier
|
3611
|
+
r.last << 'enum'
|
3612
|
+
r.last << ' ' << @name if name
|
3613
|
+
if members
|
3614
|
+
r.last << ' { '
|
3615
|
+
val = -1
|
3616
|
+
@members.sort_by { |m, v| v }.each { |m, v|
|
3617
|
+
r.last << ', ' if r.last[-2, 2] != '{ '
|
3618
|
+
r.last << m
|
3619
|
+
if v != (val += 1)
|
3620
|
+
val = v
|
3621
|
+
r.last << ' = ' << val.to_s
|
3622
|
+
end
|
3623
|
+
}
|
3624
|
+
r.last << ' }'
|
3625
|
+
end
|
3626
|
+
[r, dep]
|
3627
|
+
end
|
3628
|
+
|
3629
|
+
def dump_initializer(init, scope, r=[''], dep=[])
|
3630
|
+
if members and (
|
3631
|
+
k = @members.index(init) or
|
3632
|
+
(init.kind_of? CExpression and not init.op and k = @members.index(init.rexpr))
|
3633
|
+
)
|
3634
|
+
r.last << k
|
3635
|
+
dep |= [scope.struct_ancestors[@name]]
|
3636
|
+
[r, dep]
|
3637
|
+
else super(init, scope, r, dep)
|
3638
|
+
end
|
3639
|
+
end
|
3640
|
+
end
|
3641
|
+
class If
|
3642
|
+
def dump(scope, r=[''], dep=[])
|
3643
|
+
r.last << 'if ('
|
3644
|
+
r, dep = CExpression.dump(@test, scope, r, dep)
|
3645
|
+
r.last << ')'
|
3646
|
+
r, dep = Statement.dump(@bthen, scope, r, dep)
|
3647
|
+
if belse
|
3648
|
+
@bthen.kind_of?(Block) ? (r.last << ' else') : (r << 'else')
|
3649
|
+
if @belse.kind_of? If
|
3650
|
+
# skip indent
|
3651
|
+
r.last << ' '
|
3652
|
+
r, dep = @belse.dump(scope, r, dep)
|
3653
|
+
else
|
3654
|
+
r, dep = Statement.dump(@belse, scope, r, dep)
|
3655
|
+
end
|
3656
|
+
end
|
3657
|
+
[r, dep]
|
3658
|
+
end
|
3659
|
+
end
|
3660
|
+
class For
|
3661
|
+
def dump(scope, r=[''], dep=[])
|
3662
|
+
r.last << 'for ('
|
3663
|
+
if @init.kind_of? Block
|
3664
|
+
scope = @init
|
3665
|
+
skiptype = false
|
3666
|
+
@init.symbol.each_value { |s|
|
3667
|
+
r.last << ', ' if skiptype
|
3668
|
+
r, dep = s.dump_def(scope, r, dep, skiptype)
|
3669
|
+
skiptype = true
|
3670
|
+
}
|
3671
|
+
else
|
3672
|
+
r, dep = CExpression.dump(@init, scope, r, dep)
|
3673
|
+
end
|
3674
|
+
r.last << ' ' if @init
|
3675
|
+
r.last << ';'
|
3676
|
+
r.last << ' ' if @test
|
3677
|
+
r, dep = CExpression.dump(@test, scope, r, dep)
|
3678
|
+
r.last << ' ' if @test
|
3679
|
+
r.last << ';'
|
3680
|
+
r.last << ' ' if @iter
|
3681
|
+
r, dep = CExpression.dump(@iter, scope, r, dep)
|
3682
|
+
r.last << ')'
|
3683
|
+
Statement.dump(@body, scope, r, dep)
|
3684
|
+
end
|
3685
|
+
end
|
3686
|
+
class While
|
3687
|
+
def dump(scope, r=[''], dep=[])
|
3688
|
+
r.last << 'while ('
|
3689
|
+
r, dep = CExpression.dump(@test, scope, r, dep)
|
3690
|
+
r.last << ')'
|
3691
|
+
Statement.dump(@body, scope, r, dep)
|
3692
|
+
end
|
3693
|
+
end
|
3694
|
+
class DoWhile
|
3695
|
+
def dump(scope, r=[''], dep=[])
|
3696
|
+
r.last << 'do'
|
3697
|
+
r, dep = Statement.dump(@body, scope, r, dep)
|
3698
|
+
@body.kind_of?(Block) ? (r.last << ' while (') : (r << 'while (')
|
3699
|
+
r, dep = CExpression.dump(@test, scope, r, dep)
|
3700
|
+
r.last << ');'
|
3701
|
+
[r, dep]
|
3702
|
+
end
|
3703
|
+
end
|
3704
|
+
class Switch
|
3705
|
+
def dump(scope, r=[''], dep=[])
|
3706
|
+
r.last << 'switch ('
|
3707
|
+
r, dep = CExpression.dump(@test, scope, r, dep)
|
3708
|
+
r.last << ')'
|
3709
|
+
r.last << ' {' if @body.kind_of? Block
|
3710
|
+
tr, dep = @body.dump(scope, [''], dep)
|
3711
|
+
r.concat tr.map { |s| Case.dump_indent(s, true) }
|
3712
|
+
r << '}' if @body.kind_of? Block
|
3713
|
+
[r, dep]
|
3714
|
+
end
|
3715
|
+
end
|
3716
|
+
class Continue
|
3717
|
+
def dump(scope, r=[''], dep=[])
|
3718
|
+
r.last << 'continue;'
|
3719
|
+
[r, dep]
|
3720
|
+
end
|
3721
|
+
end
|
3722
|
+
class Break
|
3723
|
+
def dump(scope, r=[''], dep=[])
|
3724
|
+
r.last << 'break;'
|
3725
|
+
[r, dep]
|
3726
|
+
end
|
3727
|
+
end
|
3728
|
+
class Goto
|
3729
|
+
def dump(scope, r=[''], dep=[])
|
3730
|
+
r.last << "goto #@target;"
|
3731
|
+
[r, dep]
|
3732
|
+
end
|
3733
|
+
end
|
3734
|
+
class Return
|
3735
|
+
def dump(scope, r=[''], dep=[])
|
3736
|
+
r.last << 'return '
|
3737
|
+
r, dep = CExpression.dump(@value, scope, r, dep)
|
3738
|
+
r.last.chop! if r.last[-1] == ?\ # the space character
|
3739
|
+
r.last << ';'
|
3740
|
+
[r, dep]
|
3741
|
+
end
|
3742
|
+
end
|
3743
|
+
class Case
|
3744
|
+
def dump(scope, r=[''], dep=[])
|
3745
|
+
case @expr
|
3746
|
+
when 'default'
|
3747
|
+
r.last << @expr
|
3748
|
+
else
|
3749
|
+
r.last << 'case '
|
3750
|
+
r, dep = CExpression.dump(@expr, scope, r, dep)
|
3751
|
+
if exprup
|
3752
|
+
r.last << ' ... '
|
3753
|
+
r, dep = CExpression.dump(@exprup, scope, r, dep)
|
3754
|
+
end
|
3755
|
+
end
|
3756
|
+
r.last << ':'
|
3757
|
+
dump_inner(scope, r, dep)
|
3758
|
+
end
|
3759
|
+
|
3760
|
+
def self.dump_indent(s, short=false)
|
3761
|
+
case s
|
3762
|
+
when /^(case|default)\W/; (short ? ' ' : "\t") << s
|
3763
|
+
when /^\s+(case|default)\W/; "\t" << s
|
3764
|
+
when /:$/; s
|
3765
|
+
else "\t" << s
|
3766
|
+
end
|
3767
|
+
end
|
3768
|
+
end
|
3769
|
+
class Label
|
3770
|
+
def dump(scope, r=[''], dep=[])
|
3771
|
+
r.last << @name << ':'
|
3772
|
+
dump_inner(scope, r, dep)
|
3773
|
+
end
|
3774
|
+
def dump_inner(scope, r=[''], dep=[])
|
3775
|
+
if not @statement; [r, dep]
|
3776
|
+
elsif @statement.kind_of? Block; Statement.dump(@statement, scope, r, dep)
|
3777
|
+
else @statement.dump(scope, r << '', dep)
|
3778
|
+
end
|
3779
|
+
end
|
3780
|
+
end
|
3781
|
+
class Asm
|
3782
|
+
def dump(scope, r=[''], dep=[])
|
3783
|
+
r.last << 'asm '
|
3784
|
+
r.last << 'volatile ' if @volatile
|
3785
|
+
r.last << '('
|
3786
|
+
r.last << @body.inspect
|
3787
|
+
if @output or @input or @clobber
|
3788
|
+
if @output and @output != []
|
3789
|
+
# TODO
|
3790
|
+
r << ': /* todo */'
|
3791
|
+
elsif (@input and @input != []) or (@clobber and @clobber != [])
|
3792
|
+
r.last << ' :'
|
3793
|
+
end
|
3794
|
+
end
|
3795
|
+
if @input or @clobber
|
3796
|
+
if @input and @input != []
|
3797
|
+
# TODO
|
3798
|
+
r << ': /* todo */'
|
3799
|
+
elsif @clobber and @clobber != []
|
3800
|
+
r.last << ' :'
|
3801
|
+
end
|
3802
|
+
end
|
3803
|
+
if @clobber and @clobber != []
|
3804
|
+
r << (': ' << @clobber.map { |c| c.inspect }.join(', '))
|
3805
|
+
end
|
3806
|
+
r.last << ');'
|
3807
|
+
[r, dep]
|
3808
|
+
end
|
3809
|
+
end
|
3810
|
+
class CExpression
|
3811
|
+
def self.dump(e, scope, r=[''], dep=[], brace = false)
|
3812
|
+
if $DEBUG
|
3813
|
+
brace = false
|
3814
|
+
case e
|
3815
|
+
when CExpression, Variable
|
3816
|
+
r, dep = e.type.dump_cast(scope, r, dep)
|
3817
|
+
end
|
3818
|
+
r.last << '('
|
3819
|
+
end
|
3820
|
+
r, dep = \
|
3821
|
+
case e
|
3822
|
+
when ::Numeric; r.last << e.to_s ; [r, dep]
|
3823
|
+
when ::String; r.last << e.inspect ; [r, dep]
|
3824
|
+
when CExpression; e.dump_inner(scope, r, dep, brace)
|
3825
|
+
when Variable; e.dump(scope, r, dep)
|
3826
|
+
when nil; [r, dep]
|
3827
|
+
else raise 'wtf?' + e.inspect
|
3828
|
+
end
|
3829
|
+
if $DEBUG
|
3830
|
+
r.last << ')'
|
3831
|
+
end
|
3832
|
+
[r, dep]
|
3833
|
+
end
|
3834
|
+
|
3835
|
+
def dump(scope, r=[''], dep=[])
|
3836
|
+
r, dep = dump_inner(scope, r, dep)
|
3837
|
+
r.last << ';'
|
3838
|
+
[r, dep]
|
3839
|
+
end
|
3840
|
+
|
3841
|
+
def dump_inner(scope, r=[''], dep=[], brace = false)
|
3842
|
+
r.last << '(' if brace and @op != :'->' and @op != :'.' and @op != :'[]' and (@op or @rexpr.kind_of? CExpression)
|
3843
|
+
if not @lexpr
|
3844
|
+
if not @op
|
3845
|
+
case @rexpr
|
3846
|
+
when ::Numeric
|
3847
|
+
if @rexpr < 0
|
3848
|
+
r.last << ?-
|
3849
|
+
re = -@rexpr
|
3850
|
+
else
|
3851
|
+
re = @rexpr
|
3852
|
+
end
|
3853
|
+
if re >= 0x1000
|
3854
|
+
r.last << ("0x%X" % re)
|
3855
|
+
else
|
3856
|
+
r.last << re.to_s
|
3857
|
+
end
|
3858
|
+
if @type.kind_of? BaseType
|
3859
|
+
r.last << 'U' if @type.specifier == :unsigned
|
3860
|
+
case @type.name
|
3861
|
+
when :longlong, :__int64; r.last << 'LL'
|
3862
|
+
when :long, :longdouble; r.last << 'L'
|
3863
|
+
when :float; r.last << 'F'
|
3864
|
+
end
|
3865
|
+
end
|
3866
|
+
when ::String
|
3867
|
+
r.last << 'L' if @type.kind_of? Pointer and @type.type.kind_of? BaseType and @type.type.name == :short
|
3868
|
+
r.last << @rexpr.inspect
|
3869
|
+
when CExpression # cast
|
3870
|
+
r, dep = @type.dump_cast(scope, r, dep)
|
3871
|
+
r, dep = CExpression.dump(@rexpr, scope, r, dep, true)
|
3872
|
+
when Variable
|
3873
|
+
r, dep = @rexpr.dump(scope, r, dep)
|
3874
|
+
when Block
|
3875
|
+
r.last << '('
|
3876
|
+
r, dep = Statement.dump(@rexpr, scope, r, dep)
|
3877
|
+
r.last << ' )'
|
3878
|
+
when Label
|
3879
|
+
r.last << '&&' << @rexpr.name
|
3880
|
+
else raise "wtf? #{inspect}"
|
3881
|
+
end
|
3882
|
+
else
|
3883
|
+
r.last << @op.to_s
|
3884
|
+
r, dep = CExpression.dump(@rexpr, scope, r, dep, (@rexpr.kind_of? C::CExpression and @rexpr.lexpr))
|
3885
|
+
end
|
3886
|
+
elsif not @rexpr
|
3887
|
+
r, dep = CExpression.dump(@lexpr, scope, r, dep)
|
3888
|
+
r.last << @op.to_s
|
3889
|
+
else
|
3890
|
+
case @op
|
3891
|
+
when :'->', :'.'
|
3892
|
+
r, dep = CExpression.dump(@lexpr, scope, r, dep, true)
|
3893
|
+
r.last << @op.to_s << @rexpr
|
3894
|
+
when :'[]'
|
3895
|
+
r, dep = CExpression.dump(@lexpr, scope, r, dep, true)
|
3896
|
+
r.last << '['
|
3897
|
+
l = lexpr if lexpr.kind_of? Variable
|
3898
|
+
l = lexpr.lexpr.type.untypedef.findmember(lexpr.rexpr) if lexpr.kind_of? CExpression and lexpr.op == :'.'
|
3899
|
+
l = lexpr.lexpr.type.pointed.untypedef.findmember(lexpr.rexpr) if lexpr.kind_of? CExpression and lexpr.op == :'->'
|
3900
|
+
# honor __attribute__((indexenum(enumname)))
|
3901
|
+
if l and l.attributes and rexpr.kind_of? CExpression and not rexpr.op and rexpr.rexpr.kind_of? ::Integer and
|
3902
|
+
n = l.has_attribute_var('indexenum') and enum = scope.struct_ancestors[n] and i = enum.members.index(rexpr.rexpr)
|
3903
|
+
r.last << i
|
3904
|
+
dep |= [enum]
|
3905
|
+
else
|
3906
|
+
r, dep = CExpression.dump(@rexpr, scope, r, dep)
|
3907
|
+
end
|
3908
|
+
r.last << ']'
|
3909
|
+
when :funcall
|
3910
|
+
r, dep = CExpression.dump(@lexpr, scope, r, dep, true)
|
3911
|
+
r.last << '('
|
3912
|
+
@rexpr.each { |arg|
|
3913
|
+
r.last << ', ' if r.last[-1] != ?(
|
3914
|
+
r, dep = CExpression.dump(arg, scope, r, dep)
|
3915
|
+
}
|
3916
|
+
r.last << ')'
|
3917
|
+
when :'?:'
|
3918
|
+
r, dep = CExpression.dump(@lexpr, scope, r, dep, true)
|
3919
|
+
r.last << ' ? '
|
3920
|
+
r, dep = CExpression.dump(@rexpr[0], scope, r, dep, true)
|
3921
|
+
r.last << ' : '
|
3922
|
+
r, dep = CExpression.dump(@rexpr[1], scope, r, dep, true)
|
3923
|
+
else
|
3924
|
+
r, dep = CExpression.dump(@lexpr, scope, r, dep, (@lexpr.kind_of? CExpression and @lexpr.lexpr and @lexpr.op != @op))
|
3925
|
+
r.last << ' ' << @op.to_s << ' '
|
3926
|
+
r, dep = CExpression.dump(@rexpr, scope, r, dep, (@rexpr.kind_of? CExpression and @rexpr.lexpr and @rexpr.op != @op and @rexpr.op != :funcall))
|
3927
|
+
end
|
3928
|
+
end
|
3929
|
+
r.last << ')' if brace and @op != :'->' and @op != :'.' and @op != :'[]' and (@op or @rexpr.kind_of? CExpression)
|
3930
|
+
[r, dep]
|
3931
|
+
end
|
3932
|
+
|
3933
|
+
def to_s
|
3934
|
+
dump_inner(Block.new(nil))[0].join(' ')
|
3935
|
+
end
|
3936
|
+
end
|
3937
|
+
end
|
3938
|
+
end
|