key = operator, value = hash regrouping operators of lower precedence funcall/array index/member dereference/sizeof are handled in ::parse_value
nil/CExpr/Variable/Label/::String( = :quoted/struct member name)/::Integer/::Float/Block
may be :,, :., :'->', :funcall (function, [arglist]), :[] (array indexing), nil (cast)
nil/CExpr/Variable/Label/::String( = :quoted/struct member name)/::Integer/::Float/Block
a Type
recursive constructor with automatic type inference e.g. CExpression[foo, :+, [:*, bar]] assumes root args are correctly typed (eg *foo => foo must be a pointer) take care to use [int] with immediates, e.g. CExpression[foo, :+, [2]] CExpr returns some_cexpr
# File metasm/parse_c.rb, line 1091 def self.[](*args) # sub-arrays in args are to be passed to self.[] recursively (syntaxic sugar) splat = lambda { |e| e.kind_of?(::Array) ? self[*e] : e } args.shift while args.first == nil # CExpr[nil, :&, bla] => CExpr[:&, bla] case args.length when 4 op = args[1] if op == :funcall or op == :'?:' x2 = args[2].map { |a| splat[a] } if args[2] else x2 = splat[args[2]] end new(splat[args[0]], op, x2, args[3]) when 3 op = args[1] x1 = splat[args[0]] if op == :funcall or op == :'?:' x2 = args[2].map { |a| splat[a] } if args[2] else x2 = splat[args[2]] end case op when :funcall rt = x1.type.untypedef rt = rt.type.untypedef if rt.pointer? new(x1, op, x2, rt.type) when :[]; new(x1, op, x2, x1.type.untypedef.type) when :+; new(x1, op, x2, (x2.type.pointer? ? x2.type : x1.type)) when :-; new(x1, op, x2, ((x1.type.pointer? and x2.type.pointer?) ? BaseType.new(:int) : x2.type.pointer? ? x2.type : x1.type)) when :'&&', :'||', :==, :'!=', :>, :<, :<=, :>=; new(x1, op, x2, BaseType.new(:int)) when :'.', :'->' t = x1.type.untypedef t = t.type.untypedef if op == :'->' and x1.type.pointer? raise "parse error: #{t} has no member #{x2}" if not t.kind_of? Union or not m = t.findmember(x2) new(x1, op, x2, m.type) when :'?:'; new(x1, op, x2, x2[0].type) when :','; new(x1, op, x2, x2.type) else new(x1, op, x2, x1.type) end when 2 x0 = splat[args[0]] x1 = splat[args[1]] x0, x1 = x1, x0 if x0.kind_of? Type if x1.kind_of? Type; new(nil, nil, x0, x1) # (cast)r elsif x0 == :*; new(nil, x0, x1, x1.type.untypedef.type) # *r elsif x0 == :& and x1.kind_of? CExpression and x1.type.kind_of? C::Array; new(nil, nil, x1, Pointer.new(x1.type.type)) elsif x0 == :&; new(nil, x0, x1, Pointer.new(x1.type)) # &r elsif x0 == :'!'; new(nil, x0, x1, BaseType.new(:int)) # &r elsif x1.kind_of? ::Symbol; new(x0, x1, nil, x0.type) # l++ else new(nil, x0, x1, x1.type) # +r end when 1 x = splat[args[0]] case x when CExpression; x when ::Integer; new(nil, nil, x, BaseType.new(:int)) # XXX range => __int64 ? when ::Float; new(nil, nil, x, BaseType.new(:double)) when ::String; new(nil, nil, x, Pointer.new(BaseType.new(:char))) else new(nil, nil, x, x.type) end else raise "parse error CExpr[*#{args.inspect}]" end end
# File metasm/parse_c.rb, line 2267 def self.constant?(e) e.kind_of?(self) ? e.constant? : true end
# File metasm/parse_c.rb, line 3916 def self.dump(e, scope, r=[''], dep=[], brace = false) if $DEBUG brace = false case e when CExpression, Variable r, dep = e.type.dump_cast(scope, r, dep) end r.last << '(' end r, dep = case e when ::Numeric; r.last << e.to_s ; [r, dep] when ::String; r.last << string_inspect(e) ; [r, dep] when CExpression; e.dump_inner(scope, r, dep, brace) when Variable; e.dump(scope, r, dep) when nil; [r, dep] else raise 'wtf?' + e.inspect end if $DEBUG r.last << ')' end [r, dep] end
# File metasm/parse_c.rb, line 2254 def self.lvalue?(e) e.kind_of?(self) ? e.lvalue? : (e.kind_of? Variable and e.name) end
returns a CExpr negating this one (eg 'x' => '!x', 'a > b' => 'a <= b'…)
# File metasm/parse_c.rb, line 2422 def self.negate(e) e.kind_of?(self) ? e.negate : CExpression[:'!', e] end
# File metasm/parse_c.rb, line 1065 def initialize(l, o, r, t) raise "invalid CExpr #{[l, o, r, t].inspect}" if (o and not o.kind_of? ::Symbol) or not t.kind_of? Type @lexpr, @op, @rexpr, @type = l, o, r, t end
# File metasm/parse_c.rb, line 2903 def parse(parser, scope, allow_coma = true) opstack = [] stack = [] return if not e = parse_value(parser, scope) stack << e while op = readop(parser) case op.value when :'?' # a, b ? c, d : e, f == a, (b ? (c, d) : e), f until opstack.empty? or OP_PRIO[opstack.last][:'?:'] parse_popstack(parser, stack, opstack) end stack << parse(parser, scope) raise op || parser, '":" expected' if not op = readop(parser) or op.value != :':' op = op.dup op.value = :'?:' when :':' parser.unreadtok op break else if not allow_coma and op.value == :',' parser.unreadtok op break end until opstack.empty? or OP_PRIO[op.value][opstack.last] parse_popstack(parser, stack, opstack) end end raise op, 'need rhs' if not e = parse_value(parser, scope) stack << e opstack << op.value end until opstack.empty? parse_popstack(parser, stack, opstack) end CExpression[stack.first] end
parse sizeof offsetof float immediate etc into tok.value
# File metasm/parse_c.rb, line 2521 def parse_intfloat(parser, scope, tok) if tok.type == :string and not tok.value case tok.raw when 'sizeof' if ntok = parser.skipspaces and ntok.type == :punct and ntok.raw == '(' # check type if v = Variable.parse_type(parser, scope) v.parse_declarator(parser, scope) raise tok if v.name != false raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')' else raise tok, 'expr expected' if not v = parse(parser, scope) raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')' end else parser.unreadtok ntok raise tok, 'expr expected' if not v = parse_value(parser, scope) end tok.value = parser.sizeof(v) return when '__builtin_offsetof' raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != '(' raise tok if not ntok = parser.skipspaces or ntok.type != :string or ntok.raw != 'struct' raise tok if not ntok = parser.skipspaces or ntok.type != :string raise tok, 'unknown structure' if not struct = scope.struct_ancestors[ntok.raw] or not struct.kind_of? Union or not struct.members raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ',' raise tok if not ntok = parser.skipspaces or ntok.type != :string tok.value = struct.offsetof(parser, ntok.raw) raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')' return end end Expression.parse_num_value(parser, tok) end
# File metasm/parse_c.rb, line 2803 def parse_popstack(parser, stack, opstack) r = stack.pop l = stack.pop case op = opstack.pop when :'?:' stack << CExpression.new(stack.pop, op, [l, r], l.type) when :',' stack << CExpression.new(l, op, r, r.type) when :'=' unless r.kind_of?(CExpression) and not r.lexpr and r.type.kind_of?(BaseType) and ((not r.op and r.rexpr.kind_of?(Integer)) or (r.op == :- and r.rexpr.kind_of?(CExpression) and not r.rexpr.op and not r.rexpr.lexpr and r.rexpr.rexpr.kind_of?(Integer))) and l.kind_of?(Typed) and (l.type.untypedef.kind_of?(BaseType) or (l.type.untypedef.kind_of?(Pointer) and r.rexpr == 0)) # avoid useless warnings on unsigned foo = -1 / void *foo = 0 parser.check_compatible_type(parser, r.type, l.type) end if l.kind_of?(Typed) and (lt = l.type.untypedef).kind_of?(BaseType) and r.kind_of?(Typed) and (rt = r.type.untypedef).kind_of?(BaseType) and lt.specifier != :unsigned and rt.specifier == :unsigned and parser.typesize[lt.name] > parser.typesize[rt.name] # (int32)i = (uchar)255 => 255, not -1 r = CExpression.new(nil, nil, r, BaseType.new(lt.name, :unsigned)) end stack << CExpression.new(l, op, r, l.type) when :'&&', :'||' stack << CExpression.new(l, op, r, BaseType.new(:int)) else # XXX struct == struct ? raise parser, "invalid type #{l.type} #{l} for #{op.inspect}" if not l.type.arithmetic? and not parser.allow_bad_c raise parser, "invalid type #{r.type} #{r} for #{op.inspect}" if not r.type.arithmetic? and not parser.allow_bad_c if l.type.pointer? and r.type.pointer? type = case op when :'-'; BaseType.new(:long) # addr_t or sumthin ? when :'-='; l.type when :'>', :'>=', :'<', :'<=', :'==', :'!='; BaseType.new(:long) else raise parser, "cannot do #{op.inspect} on pointers" unless parser.allow_bad_c ; l.type end elsif l.type.pointer? or r.type.pointer? puts parser.exception("should not #{op.inspect} a pointer").message if $VERBOSE and not [:'+', :'-', :'=', :'+=', :'-=', :==, :'!='].include? op type = l.type.pointer? ? l.type : r.type elsif RIGHTASSOC[op] and op != :'?:' # += etc type = l.type else # yay integer promotion lt = l.type.untypedef rt = r.type.untypedef if (t = lt).name == :longdouble or (t = rt).name == :longdouble or (t = lt).name == :double or (t = rt).name == :double or (t = lt).name == :float or (t = rt).name == :float # long double > double > float ... type = t elsif true # custom integer rules based on type sizes lts = parser.typesize[lt.name] rts = parser.typesize[rt.name] its = parser.typesize[:int] if not lts or not rts type = BaseType.new(:int) elsif lts > rts and lts >= its type = lt elsif rts > lts and rts >= its type = rt elsif lts == rts and lts >= its type = lt type = rt if rt.specifier == :unsigned else type = BaseType.new(:int) end # end of custom rules elsif ((t = lt).name == :long and t.specifier == :unsigned) or ((t = rt).name == :long and t.specifier == :unsigned) # ... ulong ... type = t elsif (lt.name == :long and rt.name == :int and rt.specifier == :unsigned) or (rt.name == :long and lt.name == :int and lt.specifier == :unsigned) # long+uint => ulong type = BaseType.new(:long, :unsigned) elsif (t = lt).name == :long or (t = rt).name == :long or ((t = lt).name == :int and t.specifier == :unsigned) or ((t = rt).name == :int and t.specifier == :unsigned) # ... long > uint ... type = t else # int type = BaseType.new(:int) end end case op when :'>', :'>=', :'<', :'<=', :'==', :'!=' # cast both sides l = CExpression[l, type] if l.type != type r = CExpression[r, type] if r.type != type stack << CExpression.new(l, op, r, BaseType.new(:int)) else # promote result stack << CExpression.new(l, op, r, type) end end end
returns the next value from parser (parenthesised expression, immediate, variable, unary operators)
# File metasm/parse_c.rb, line 2558 def parse_value(parser, scope) return if not tok = parser.skipspaces case tok.type when :string parse_intfloat(parser, scope, tok) val = tok.value || tok.raw if val.kind_of? ::String raise tok, 'undefined variable' if not val = scope.symbol_ancestors[val] end case val when Type raise tok, 'invalid variable' when Variable val = parse_value_postfix(parser, scope, val) when ::Float # parse suffix type = :double if (0..9).include?(tok.raw[0]) case tok.raw.downcase[-1] when l; type = :longdouble when f; type = :float end end val = CExpression[val, BaseType.new(type)] when ::Integer # parse suffix # XXX 010h ? type = :int specifier = [] if (0..9).include?(tok.raw[0]) suffix = tok.raw.downcase[-3, 3] || tok.raw.downcase[-2, 2] || tok.raw.downcase[-1, 1] # short string specifier << :unsigned if suffix.include?('u') # XXX or tok.raw.downcase[1] == ?x type = :longlong if suffix.count('l') == 2 type = :long if suffix.count('l') == 1 end val = CExpression[val, BaseType.new(type, *specifier)] val = parse_value_postfix(parser, scope, val) else raise parser, "internal error #{val.inspect}" end when :quoted if tok.raw[0] == ' raise tok, 'invalid character constant' if not [1, 2, 4, 8].include? tok.value.length # TODO 0fill val = CExpression[Expression.decode_imm(tok.value, tok.value.length, :big), BaseType.new(:int)] val = parse_value_postfix(parser, scope, val) else val = CExpression[tok.value, Pointer.new(BaseType.new(tok.raw[0, 2] == 'L"' ? :short : :char))] val = parse_value_postfix(parser, scope, val) end when :punct case tok.raw when '(' ntok = nil # check type casting if v = Variable.parse_type(parser, scope) v.parse_declarator(parser, scope) (v.type.attributes ||= []).concat v.attributes if v.attributes raise tok, 'bad cast' if v.name != false raise ntok || tok, 'no ")" found' if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')' raise ntok, 'expr expected' if not val = parse_value(parser, scope) # parses postfix too #raise ntok, 'unable to cast a struct' if val.type.untypedef.kind_of? Union val = CExpression[[val], v.type] # check compound statement expression elsif ntok = parser.skipspaces and ntok.type == :punct and ntok.raw == '{' parser.unreadtok ntok blk = parser.parse_statement(scope, [:expression]) # XXX nesting ? raise ntok || tok, 'no ")" found' if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')' type = blk.statements.last.kind_of?(CExpression) ? blk.statements.last.type : BaseType.new(:void) val = CExpression[blk, type] else parser.unreadtok ntok if not val = parse(parser, scope) parser.unreadtok tok return end raise ntok || tok, 'no ")" found' if not ntok = parser.readtok or ntok.type != :punct or ntok.raw != ')' val = parse_value_postfix(parser, scope, val) end when '.' # float parse_intfloat(parser, scope, tok) if not tok.value parser.unreadtok tok return end val = tok.value || tok.raw type = :double case tok.raw.downcase[-1] when l; type = :longdouble when f; type = :float end val = CExpression.new[val, BaseType.new(type)] when '+', '-', '&', '!', '~', '*', '--', '++', '&&' # unary prefix # may have been read ahead raise parser if not ntok = parser.readtok # check for -- ++ && if ntok.type == :punct and ntok.raw == tok.raw and %w[+ - &].include?(tok.raw) tok.raw << ntok.raw else parser.unreadtok ntok end case tok.raw when '&' val = parse_value(parser, scope) if val.kind_of? CExpression and val.op == :& and not val.lexpr and (val.rexpr.kind_of? Variable or val.rexpr.kind_of? CExpression) and val.rexpr.type.kind_of? Function # &&function == &function elsif (val.kind_of? CExpression or val.kind_of? Variable) and val.type.kind_of? Array # &ary = ary else raise parser, "invalid lvalue #{val}" if not CExpression.lvalue?(val) and not parser.allow_bad_c raise val.backtrace, 'cannot take addr of register' if val.kind_of? Variable and val.storage == :register and not parser.allow_bad_c val = CExpression.new(nil, tok.raw.to_sym, val, Pointer.new(val.type)) end when '++', '--' val = parse_value(parser, scope) raise parser, "invalid lvalue #{val}" if not CExpression.lvalue?(val) and not parser.allow_bad_c val = CExpression.new(nil, tok.raw.to_sym, val, val.type) when '&&' raise tok, 'label name expected' if not val = parser.skipspaces or val.type != :string val = CExpression.new(nil, nil, Label.new(val.raw, nil), Pointer.new(BaseType.new(:void))) when '*' raise tok, 'expr expected' if not val = parse_value(parser, scope) raise tok, 'not a pointer' if not val.type.pointer? and not parser.allow_bad_c newtype = val.type.pointer? ? val.type.pointed : BaseType.new(:int) if not newtype.untypedef.kind_of? Function # *fptr == fptr val = CExpression.new(nil, tok.raw.to_sym, val, newtype) end when '~', '!', '+', '-' raise tok, 'expr expected' if not val = parse_value(parser, scope) raise tok, 'type not arithmetic' if not val.type.arithmetic? and not parser.allow_bad_c val = CExpression.new(nil, tok.raw.to_sym, val, val.type) val.type = BaseType.new(:int) if tok.raw == '!' else raise tok, 'internal error' end else parser.unreadtok tok return end else parser.unreadtok tok return end if val.kind_of? Variable and val.type.kind_of? Function # void (*bla)() = printf; => ...= &printf; val = CExpression[:&, val] end val end
parse postfix forms (postincrement, array index, struct member dereference)
# File metasm/parse_c.rb, line 2716 def parse_value_postfix(parser, scope, val) tok = parser.skipspaces nval = if tok and tok.type == :punct case tok.raw when '+', '++', '-', '--', '->' ntok = parser.readtok if (tok.raw == '+' or tok.raw == '-') and ntok and ntok.type == :punct and (ntok.raw == tok.raw or (tok.raw == '-' and ntok.raw == '>')) tok.raw << ntok.raw else parser.unreadtok ntok end case tok.raw when '+', '-' nil when '++', '--' raise parser, "#{val}: invalid lvalue" if not CExpression.lvalue?(val) CExpression.new(val, tok.raw.to_sym, nil, val.type) when '->' # XXX allow_bad_c.. raise tok, "#{val}: not a pointer" if not val.type.pointer? type = val.type.pointed.untypedef raise tok, "#{val}: bad pointer" if not type.kind_of? Union raise tok, "#{val}: incomplete type" if not type.members raise tok, "#{val}: invalid member" if not tok = parser.skipspaces or tok.type != :string or not m = type.findmember(tok.raw) CExpression.new(val, :'->', tok.raw, m.type) end when '.' type = val.type.untypedef if not ntok = parser.skipspaces or ntok.type != :string or not type.kind_of? Union parser.unreadtok ntok nil else raise ntok, "#{val}: incomplete type" if not type.members raise ntok, "#{val}: invalid member" if not m = type.findmember(ntok.raw) CExpression.new(val, :'.', ntok.raw, m.type) end when '[' raise tok, "#{val}: index expected" if not idx = parse(parser, scope) val, idx = idx, val if not val.type.pointer? # fake support of "4[tab]" raise tok, "#{val}: not a pointer" if not val.type.pointer? raise tok, "#{val}: invalid index" if not idx.type.integral? raise tok, "#{val}: get perpendicular ! (elsewhere)" if idx.kind_of?(CExpression) and idx.op == :',' raise tok || parser, "']' expected" if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ']' type = val.type.untypedef.type # TODO boundscheck (and become king of the universe) CExpression.new(val, :'[]', idx, type) when '(' type = val.type.untypedef type = type.type.untypedef if type.kind_of? Pointer raise tok, "#{val}: not a function" if not type.kind_of? Function args = [] loop do a = parse(parser, scope, false) break if not a args << a if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ',' parser.unreadtok ntok break end end raise ntok || parser, "#{val}: ')' expected" if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')' type.args ||= [] 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)) type.args.zip(args) { |ta, a| p, i = ta.type.pointer?, ta.type.integral? r = a.reduce(parser) if p or i if (not p and not i) or (i and not r.kind_of? ::Integer) or (p and r != 0) tok = tok.dup ; tok.raw = a.to_s parser.check_compatible_type(tok, a.type, ta.type) end } CExpression.new(val, :funcall, args, type.type) end end if nval parse_value_postfix(parser, scope, nval) else parser.unreadtok tok val end end
# File metasm/compile_c.rb, line 1008 def self.precompile_inner(compiler, scope, expr, nested = true) case expr when CExpression; expr.precompile_inner(compiler, scope, nested) else expr end end
changes obj.type to a precompiled type keeps struct/union, change everything else to __intd except Arrays if declaration is true (need to know variable allocation sizes etc) returns the type
# File metasm/compile_c.rb, line 969 def self.precompile_type(compiler, scope, obj, declaration = false) case t = obj.type.untypedef when BaseType case t.name when :void when :float, :double, :longdouble else t = BaseType.new("__int#{compiler.typesize[t.name]*8}".to_sym, t.specifier) end when Array if declaration; precompile_type(compiler, scope, t, declaration) else t = BaseType.new("__int#{compiler.typesize[:ptr]*8}".to_sym, :unsigned) end when Pointer if t.type.untypedef.kind_of? Function precompile_type(compiler, scope, t, declaration) else t = BaseType.new("__int#{compiler.typesize[:ptr]*8}".to_sym, :unsigned) end when Enum; t = BaseType.new("__int#{compiler.typesize[:int]*8}".to_sym) when Function precompile_type(compiler, scope, t) t.args ||= [] t.args.each { |a| precompile_type(compiler, scope, a) } when Union if declaration and t.members and not t.name # anonymous struct t.members.each { |a| precompile_type(compiler, scope, a, true) } end else raise 'bad type ' + t.inspect end (t.qualifier ||= []).concat obj.type.qualifier if obj.type.qualifier and t != obj.type (t.attributes ||= []).concat obj.type.attributes if obj.type.attributes and t != obj.type while obj.type.kind_of? TypeDef obj.type = obj.type.type (t.qualifier ||= []).concat obj.type.qualifier if obj.type.qualifier and t != obj.type (t.attributes ||= []).concat obj.type.attributes if obj.type.attributes and t != obj.type end obj.type = t end
reads a binary operator from the parser, returns the corresponding symbol or nil
# File metasm/parse_c.rb, line 2480 def readop(parser) if not op = parser.skipspaces or op.type != :punct parser.unreadtok op return end case op.raw when '>', '<', '|', '&' # << >> || && if ntok = parser.readtok and ntok.type == :punct and ntok.raw == op.raw op.raw << ntok.raw else parser.unreadtok ntok end when '!' # != (mandatory) if not ntok = parser.readtok or ntok.type != :punct and ntok.raw != '=' parser.unreadtok op return end op.raw << ntok.raw when '+', '-', '*', '/', '%', '^', '=', ',', '?', ':', '>>', '<<', '||', '&&', '+=','-=','*=','/=','%=','^=','==','&=','|=','!=' # ok else # bad parser.unreadtok op return end # may be followed by '=' case op.raw when '+', '-', '*', '/', '%', '^', '&', '|', '>>', '<<', '<', '>', '=' if ntok = parser.readtok and ntok.type == :punct and ntok.raw == '=' op.raw << ntok.raw else parser.unreadtok ntok end end op.value = op.raw.to_sym op end
# File metasm/parse_c.rb, line 2283 def self.reduce(parser, e) e.kind_of?(self) ? e.reduce(parser) : e end
# File metasm/parse_c.rb, line 3911 def self.string_inspect(s) # keep all ascii printable except \ and " '"' + s.gsub(/[^ !\x23-\x5b\x5d-\x7e]/) { |o| '\x' + o.unpack('H*').first } + '"' end
# File metasm/parse_c.rb, line 2410 def ==(o) o.object_id == self.object_id or (self.class == o.class and op == o.op and lexpr == o.lexpr and rexpr == o.rexpr) end
# File metasm/parse_c.rb, line 2415 def ===(o) (self.class == o.class and op == o.op and lexpr === o.lexpr and rexpr === o.rexpr) or (o.class == Variable and not @op and @rexpr == o) end
# File metasm/parse_c.rb, line 2456 def complexity cx = 1 walk { |e| cx += e.complexity if e.kind_of?(CExpression) } cx end
# File metasm/parse_c.rb, line 2270 def constant? # gcc considers '1, 2' not constant if [:',', :funcall, :'=', :'--', :'++', :'+=', :'-=', :'*=', :'/=', :'>>=', :'<<=', :'&=', :'|=', :'^=', :'%=', :'->', :'[]'].include?(@op) false elsif @op == :'*' and not @lexpr; false elsif not @lexpr and not @op and @rexpr.kind_of? Block; false else out = true walk { |e| break out = false if not CExpression.constant?(e) } out end end
deep copy of the object recurses only within CExpressions, anything else is copied by reference
# File metasm/parse_c.rb, line 1078 def deep_dup n = dup n.lexpr = n.lexpr.deep_dup if n.lexpr.kind_of? CExpression n.rexpr = n.rexpr.deep_dup if n.rexpr.kind_of? CExpression n.rexpr = n.rexpr.map { |e| e.kind_of?(CExpression) ? e.deep_dup : e } if n.rexpr.kind_of? ::Array n end
# File metasm/parse_c.rb, line 3940 def dump(scope, r=[''], dep=[]) r, dep = dump_inner(scope, r, dep) r.last << ';' [r, dep] end
# File metasm/parse_c.rb, line 3946 def dump_inner(scope, r=[''], dep=[], brace = false) r.last << '(' if brace and @op != :'->' and @op != :'.' and @op != :'[]' and (@op or @rexpr.kind_of? CExpression) if not @lexpr if not @op case @rexpr when ::Numeric if @rexpr < 0 r.last << - re = -@rexpr else re = @rexpr end if re >= 0x1000 r.last << ("0x%X" % re) else r.last << re.to_s end if @type.kind_of? BaseType r.last << 'U' if @type.specifier == :unsigned case @type.name when :longlong, :__int64; r.last << 'LL' when :long, :longdouble; r.last << 'L' when :float; r.last << 'F' end end when ::String r.last << 'L' if @type.kind_of? Pointer and @type.type.kind_of? BaseType and @type.type.name == :short r.last << CExpression.string_inspect(@rexpr) when CExpression # cast r, dep = @type.dump_cast(scope, r, dep) r, dep = CExpression.dump(@rexpr, scope, r, dep, true) when Variable r, dep = @rexpr.dump(scope, r, dep) when Block r.last << '(' r, dep = Statement.dump(@rexpr, scope, r, dep) r.last << ' )' when Label r.last << '&&' << @rexpr.name else raise "wtf? #{inspect}" end else r.last << @op.to_s r, dep = CExpression.dump(@rexpr, scope, r, dep, (@rexpr.kind_of? C::CExpression and @rexpr.lexpr)) end elsif not @rexpr r, dep = CExpression.dump(@lexpr, scope, r, dep) r.last << @op.to_s else case @op when :'->', :'.' r, dep = CExpression.dump(@lexpr, scope, r, dep, true) r.last << @op.to_s << @rexpr when :'[]' r, dep = CExpression.dump(@lexpr, scope, r, dep, true) r.last << '[' l = lexpr if lexpr.kind_of? Variable l = lexpr.lexpr.type.untypedef.findmember(lexpr.rexpr) if lexpr.kind_of? CExpression and lexpr.op == :'.' l = lexpr.lexpr.type.pointed.untypedef.findmember(lexpr.rexpr) if lexpr.kind_of? CExpression and lexpr.op == :'->' # honor __attribute__((indexenum(enumname))) if l and l.attributes and rexpr.kind_of? CExpression and not rexpr.op and rexpr.rexpr.kind_of? ::Integer and n = l.has_attribute_var('indexenum') and enum = scope.struct_ancestors[n] and i = enum.members.index(rexpr.rexpr) r.last << i dep |= [enum] else r, dep = CExpression.dump(@rexpr, scope, r, dep) end r.last << ']' when :funcall r, dep = CExpression.dump(@lexpr, scope, r, dep, true) r.last << '(' @rexpr.each { |arg| r.last << ', ' if r.last[-1] != ( r, dep = CExpression.dump(arg, scope, r, dep) } r.last << ')' when :'?:' r, dep = CExpression.dump(@lexpr, scope, r, dep, true) r.last << ' ? ' r, dep = CExpression.dump(@rexpr[0], scope, r, dep, true) r.last << ' : ' r, dep = CExpression.dump(@rexpr[1], scope, r, dep, true) else r, dep = CExpression.dump(@lexpr, scope, r, dep, (@lexpr.kind_of? CExpression and @lexpr.lexpr and @lexpr.op != @op)) r.last << ' ' << @op.to_s << ' ' r, dep = CExpression.dump(@rexpr, scope, r, dep, (@rexpr.kind_of? CExpression and @rexpr.lexpr and @rexpr.op != @op and @rexpr.op != :funcall)) end end r.last << ')' if brace and @op != :'->' and @op != :'.' and @op != :'[]' and (@op or @rexpr.kind_of? CExpression) [r, dep] end
# File metasm/parse_c.rb, line 2257 def lvalue? case @op when :*; true if not @lexpr when :'[]', :'.', :'->'; true when nil # cast CExpression.lvalue?(@rexpr) else false end end
# File metasm/parse_c.rb, line 2425 def negate if @op == :'!' CExpression[@rexpr] elsif nop = NegateOp[@op] if nop == :== and @rexpr.kind_of? CExpression and not @rexpr.op and @rexpr.rexpr == 0 and @lexpr.kind_of? CExpression and [:==, :'!=', :>, :<, :>=, :<=, :'!'].include? @lexpr.op # (a > b) != 0 => (a > b) CExpression[@lexpr] else CExpression.new(@lexpr, nop, @rexpr, @type) end elsif nop = { :'||' => :'&&', :'&&' => :'||' }[@op] CExpression.new(CExpression.negate(@lexpr), nop, CExpression.negate(@rexpr), @type) else CExpression[:'!', self] end end
# File metasm/compile_c.rb, line 960 def precompile(compiler, scope) i = precompile_inner(compiler, scope, false) scope.statements << i if i end
returns a new CExpression with simplified self.type, computes structure offsets turns char[]/float immediates to reference to anonymised const TODO 'a = b += c' => 'b += c; a = b' (use nested argument) TODO handle ::precompile_inner return nil TODO struct.bits
# File metasm/compile_c.rb, line 1020 def precompile_inner(compiler, scope, nested = true) case @op when :'.' # a.b => (&a)->b lexpr = CExpression.precompile_inner(compiler, scope, @lexpr) ll = lexpr ll = lexpr.rexpr while ll.kind_of? CExpression and not ll.op if ll.kind_of? CExpression and ll.op == :'*' and not ll.lexpr # do not change lexpr.rexpr.type directly to a pointer, might retrigger (ptr+imm) => (ptr + imm*sizeof(*ptr)) @lexpr = CExpression.new(nil, nil, ll.rexpr, Pointer.new(lexpr.type)) else @lexpr = CExpression.new(nil, :'&', lexpr, Pointer.new(lexpr.type)) end @op = :'->' precompile_inner(compiler, scope) when :'->' # a->b => *(a + off(b)) struct = @lexpr.type.untypedef.type.untypedef lexpr = CExpression.precompile_inner(compiler, scope, @lexpr) @lexpr = nil @op = nil if struct.kind_of? Union and (off = struct.offsetof(compiler, @rexpr)) != 0 off = CExpression.new(nil, nil, off, BaseType.new(:int, :unsigned)) @rexpr = CExpression.new(lexpr, :'+', off, lexpr.type) # ensure the (ptr + value) is not expanded to (ptr + value * sizeof(*ptr)) CExpression.precompile_type(compiler, scope, @rexpr) else # union or 1st struct member @rexpr = lexpr end if @type.kind_of? Array # Array member type is already an address else @rexpr = CExpression.new(nil, :*, @rexpr, @rexpr.type) end precompile_inner(compiler, scope) when :'[]' rexpr = CExpression.precompile_inner(compiler, scope, @rexpr) if rexpr.kind_of? CExpression and not rexpr.op and rexpr.rexpr == 0 @rexpr = @lexpr else @rexpr = CExpression.new(@lexpr, :'+', rexpr, @lexpr.type) end @op = :'*' @lexpr = nil precompile_inner(compiler, scope) when :'?:' # cannot precompile in place, a conditionnal expression may have a coma: must turn into If if @lexpr.kind_of? CExpression @lexpr = @lexpr.precompile_inner(compiler, scope) if not @lexpr.lexpr and not @lexpr.op and @lexpr.rexpr.kind_of? ::Numeric if @lexpr.rexpr == 0 e = @rexpr[1] else e = @rexpr[0] end e = CExpression.new(nil, nil, e, e.type) if not e.kind_of? CExpression return e.precompile_inner(compiler, scope) end end raise 'conditional in toplevel' if scope == compiler.toplevel # just in case var = Variable.new var.storage = :register var.name = compiler.new_label('ternary') var.type = @rexpr[0].type CExpression.precompile_type(compiler, scope, var) Declaration.new(var).precompile(compiler, scope) If.new(@lexpr, CExpression.new(var, :'=', @rexpr[0], var.type), CExpression.new(var, :'=', @rexpr[1], var.type)).precompile(compiler, scope) @lexpr = nil @op = nil @rexpr = var precompile_inner(compiler, scope) when :'&&' if scope == compiler.toplevel @lexpr = CExpression.precompile_inner(compiler, scope, @lexpr) @rexpr = CExpression.precompile_inner(compiler, scope, @rexpr) CExpression.precompile_type(compiler, scope, self) self else var = Variable.new var.storage = :register var.name = compiler.new_label('and') var.type = @type CExpression.precompile_type(compiler, scope, var) var.initializer = CExpression.new(nil, nil, 0, var.type) Declaration.new(var).precompile(compiler, scope) l = @lexpr.kind_of?(CExpression) ? @lexpr : CExpression.new(nil, nil, @lexpr, @lexpr.type) r = @rexpr.kind_of?(CExpression) ? @rexpr : CExpression.new(nil, nil, @rexpr, @rexpr.type) If.new(l, If.new(r, CExpression.new(var, :'=', CExpression.new(nil, nil, 1, var.type), var.type))).precompile(compiler, scope) @lexpr = nil @op = nil @rexpr = var precompile_inner(compiler, scope) end when :'||' if scope == compiler.toplevel @lexpr = CExpression.precompile_inner(compiler, scope, @lexpr) @rexpr = CExpression.precompile_inner(compiler, scope, @rexpr) CExpression.precompile_type(compiler, scope, self) self else var = Variable.new var.storage = :register var.name = compiler.new_label('or') var.type = @type CExpression.precompile_type(compiler, scope, var) var.initializer = CExpression.new(nil, nil, 1, var.type) Declaration.new(var).precompile(compiler, scope) l = @lexpr.kind_of?(CExpression) ? @lexpr : CExpression.new(nil, nil, @lexpr, @lexpr.type) l = CExpression.new(nil, :'!', l, var.type) r = @rexpr.kind_of?(CExpression) ? @rexpr : CExpression.new(nil, nil, @rexpr, @rexpr.type) r = CExpression.new(nil, :'!', r, var.type) If.new(l, If.new(r, CExpression.new(var, :'=', CExpression.new(nil, nil, 0, var.type), var.type))).precompile(compiler, scope) @lexpr = nil @op = nil @rexpr = var precompile_inner(compiler, scope) end when :funcall if @lexpr.kind_of? Variable and @lexpr.type.kind_of? Function and @lexpr.attributes and @lexpr.attributes.include? 'inline' and @lexpr.initializer # TODO check recursive call (direct or indirect) raise 'inline varargs unsupported' if @lexpr.type.varargs rtype = @lexpr.type.type.untypedef if not rtype.kind_of? BaseType or rtype.name != :void rval = Variable.new rval.name = compiler.new_label('inline_return') rval.type = @lexpr.type.type Declaration.new(rval).precompile(compiler, scope) end inline_label = {} locals = @lexpr.type.args.zip(@rexpr).inject({}) { |h, (fa, a)| h.update fa => CExpression.new(nil, nil, a, fa.type).precompile_inner(compiler, scope) } copy_inline_ce = lambda { |ce| case ce when CExpression; CExpression.new(copy_inline_ce[ce.lexpr], ce.op, copy_inline_ce[ce.rexpr], ce.type) when Variable; locals[ce] || ce when ::Array; ce.map { |e_| copy_inline_ce[e_] } else ce end } copy_inline = lambda { |stmt, scp| case stmt when Block b = Block.new(scp) stmt.statements.each { |s| s = copy_inline[s, b] b.statements << s if s } b when If; If.new(copy_inline_ce[stmt.test], copy_inline[stmt.bthen, scp]) # re-precompile ? when Label; Label.new(inline_label[stmt.name] ||= compiler.new_label('inline_'+stmt.name)) when Goto; Goto.new(inline_label[stmt.target] ||= compiler.new_label('inline_'+stmt.target)) when Return; CExpression.new(rval, :'=', copy_inline_ce[stmt.value], rval.type).precompile_inner(compiler, scp) if stmt.value when CExpression; copy_inline_ce[stmt] when Declaration nv = stmt.var.dup if nv.type.kind_of? Array and nv.type.length.kind_of? CExpression nv.type = Array.new(nv.type.type, copy_inline_ce[nv.type.length]) # XXX nested dynamic? end locals[stmt.var] = nv scp.symbol[nv.name] = nv Declaration.new(nv) else raise 'unexpected inline statement ' + stmt.inspect end } scope.statements << copy_inline[@lexpr.initializer, scope] # body already precompiled CExpression.new(nil, nil, rval, rval.type).precompile_inner(compiler, scope) elsif @type.kind_of? Union var = Variable.new var.name = compiler.new_label('return_struct') var.type = @type Declaration.new(var).precompile(compiler, scope) @rexpr.unshift CExpression.new(nil, :&, var, Pointer.new(var.type)) var2 = Variable.new var2.name = compiler.new_label('return_struct_ptr') var2.type = Pointer.new(@type) var2.storage = :register CExpression.precompile_type(compiler, scope, var2) Declaration.new(var2).precompile(compiler, scope) @type = var2.type CExpression.new(var2, :'=', self, var2.type).precompile(compiler, scope) CExpression.new(nil, :'*', var2, var.type).precompile_inner(compiler, scope) else t = @lexpr.type.untypedef t = t.type.untypedef if t.pointer? @lexpr = CExpression.precompile_inner(compiler, scope, @lexpr) types = t.args.map { |a| a.type } # cast args to func prototype @rexpr.map! { |e_| (types.empty? ? e_ : CExpression.new(nil, nil, e_, types.shift)).precompile_inner(compiler, scope) } CExpression.precompile_type(compiler, scope, self) self end when :',' lexpr = @lexpr.kind_of?(CExpression) ? @lexpr : CExpression.new(nil, nil, @lexpr, @lexpr.type) rexpr = @rexpr.kind_of?(CExpression) ? @rexpr : CExpression.new(nil, nil, @rexpr, @rexpr.type) lexpr.precompile(compiler, scope) rexpr.precompile_inner(compiler, scope) when :'!' CExpression.precompile_type(compiler, scope, self) if @rexpr.kind_of?(CExpression) case @rexpr.op when :'<', :'>', :'<=', :'>=', :'==', :'!=' @op = { :'<' => :'>=', :'>' => :'<=', :'<=' => :'>', :'>=' => :'<', :'==' => :'!=', :'!=' => :'==' }[@rexpr.op] @lexpr = @rexpr.lexpr @rexpr = @rexpr.rexpr precompile_inner(compiler, scope) when :'&&', :'||' @op = { :'&&' => :'||', :'||' => :'&&' }[@rexpr.op] @lexpr = CExpression.new(nil, :'!', @rexpr.lexpr, @type) @rexpr = CExpression.new(nil, :'!', @rexpr.rexpr, @type) precompile_inner(compiler, scope) when :'!' if @rexpr.rexpr.kind_of? CExpression @op = nil @rexpr = @rexpr.rexpr else @op = :'!=' @lexpr = @rexpr.rexpr @rexpr = CExpression.new(nil, nil, 0, @lexpr.type) end precompile_inner(compiler, scope) else @rexpr = CExpression.precompile_inner(compiler, scope, @rexpr) self end else @rexpr = CExpression.precompile_inner(compiler, scope, @rexpr) self end when :'++', :'--' if not @rexpr var = Variable.new var.storage = :register var.name = compiler.new_label('postincrement') var.type = @type Declaration.new(var).precompile(compiler, scope) CExpression.new(var, :'=', @lexpr, @type).precompile(compiler, scope) CExpression.new(nil, @op, @lexpr, @type).precompile(compiler, scope) @lexpr = nil @op = nil @rexpr = var precompile_inner(compiler, scope) elsif @type.pointer? and compiler.sizeof(nil, @type.untypedef.type.untypedef) != 1 # ++ptr => ptr += sizeof(*ptr) (done in += precompiler) @op = { :'++' => :'+=', :'--' => :'-=' }[@op] @lexpr = @rexpr @rexpr = CExpression.new(nil, nil, 1, BaseType.new(:ptr, :unsigned)) precompile_inner(compiler, scope) else CExpression.precompile_type(compiler, scope, self) @rexpr = CExpression.precompile_inner(compiler, scope, @rexpr) self end when :'=' # handle structure assignment/array assignment case @lexpr.type.untypedef when Union # rexpr may be a :funcall @rexpr = CExpression.precompile_inner(compiler, scope, @rexpr) @lexpr.type.untypedef.members.zip(@rexpr.type.untypedef.members) { |m1, m2| # assume m1 and m2 are compatible v1 = CExpression.new(@lexpr, :'.', m1.name, m1.type) v2 = CExpression.new(@rexpr, :'.', m2.name, m1.type) CExpression.new(v1, :'=', v2, v1.type).precompile(compiler, scope) } # (foo = bar).toto @op = nil @rexpr = @lexpr @lexpr = nil @type = @rexpr.type precompile_inner(compiler, scope) if nested when Array if not len = @lexpr.type.untypedef.length @rexpr = CExpression.precompile_inner(compiler, scope, @rexpr) # char toto[] = "bla" if @rexpr.kind_of? CExpression and not @rexpr.lexpr and not @rexpr.op and @rexpr.rexpr.kind_of? Variable and @rexpr.rexpr.type.kind_of? Array len = @rexpr.rexpr.type.length end end raise 'array initializer with no length !' if not len # TODO optimize... len.times { |i| i = CExpression.new(nil, nil, i, BaseType.new(:long, :unsigned)) v1 = CExpression.new(@lexpr, :'[]', i, @lexpr.type.untypedef.type) v2 = CExpression.new(@rexpr, :'[]', i, v1.type) CExpression.new(v1, :'=', v2, v1.type).precompile(compiler, scope) } @op = nil @rexpr = @lexpr @lexpr = nil @type = @rexpr.type precompile_inner(compiler, scope) if nested else @lexpr = CExpression.precompile_inner(compiler, scope, @lexpr) @rexpr = CExpression.precompile_inner(compiler, scope, @rexpr) CExpression.precompile_type(compiler, scope, self) self end when nil case @rexpr when Block # compound statements raise 'compound statement in toplevel' if scope == compiler.toplevel # just in case var = Variable.new var.storage = :register var.name = compiler.new_label('compoundstatement') var.type = @type CExpression.precompile_type(compiler, scope, var) Declaration.new(var).precompile(compiler, scope) if @rexpr.statements.last.kind_of? CExpression @rexpr.statements[-1] = CExpression.new(var, :'=', @rexpr.statements[-1], var.type) @rexpr.precompile(compiler, scope) end @rexpr = var precompile_inner(compiler, scope) when ::String # char[] immediate v = Variable.new v.storage = :static v.name = 'char_' + @rexpr.tr('^a-zA-Z', '')[0, 8] v.type = Array.new(@type.type) v.type.length = @rexpr.length + 1 v.type.type.qualifier = [:const] v.initializer = CExpression.new(nil, nil, @rexpr, @type) Declaration.new(v).precompile(compiler, scope) @rexpr = v precompile_inner(compiler, scope) when ::Float # float immediate v = Variable.new v.storage = :static v.name = @type.untypedef.name.to_s v.type = @type v.type.qualifier = [:const] v.initializer = CExpression.new(nil, nil, @rexpr, @type) Declaration.new(v).precompile(compiler, scope) @rexpr = CExpression.new(nil, :'*', v, v.type) precompile_inner(compiler, scope) when CExpression # simplify casts CExpression.precompile_type(compiler, scope, self) # propagate type first so that __uint64 foo() { return -1 } => 0xffffffffffffffff @rexpr.type = @type if @rexpr.kind_of? CExpression and @rexpr.op == :- and not @rexpr.lexpr and @type.kind_of? BaseType and @type.name == :__int64 # XXX kill me @rexpr = @rexpr.precompile_inner(compiler, scope) if @type.kind_of? BaseType and @rexpr.type.kind_of? BaseType if @rexpr.type == @type # noop cast @lexpr, @op, @rexpr = @rexpr.lexpr, @rexpr.op, @rexpr.rexpr elsif not @rexpr.op and @type.integral? and @rexpr.type.integral? if @rexpr.rexpr.kind_of? ::Numeric and (val = reduce(compiler)).kind_of? ::Numeric @rexpr = val elsif compiler.typesize[@type.name] < compiler.typesize[@rexpr.type.name] # (char)(short)(int)(long)foo => (char)foo @rexpr = @rexpr.rexpr end end end self else CExpression.precompile_type(compiler, scope, self) self end else # int+ptr => ptr+int if @op == :+ and @lexpr and @lexpr.type.integral? and @rexpr.type.pointer? @rexpr, @lexpr = @lexpr, @rexpr end # handle pointer + 2 == ((char *)pointer) + 2*sizeof(*pointer) if @rexpr and [:'+', :'+=', :'-', :'-='].include? @op and @type.pointer? and @rexpr.type.integral? sz = compiler.sizeof(nil, @type.untypedef.type.untypedef) if sz != 1 sz = CExpression.new(nil, nil, sz, @rexpr.type) @rexpr = CExpression.new(@rexpr, :'*', sz, @rexpr.type) end end # type promotion => cast case @op when :+, :-, :*, :/, :&, :|, :^, :% if @lexpr if @lexpr.type != @type @lexpr = CExpression.new(nil, nil, @lexpr, @lexpr.type) if not @lexpr.kind_of? CExpression @lexpr = CExpression.new(nil, nil, @lexpr, @type) end if @rexpr.type != @type @rexpr = CExpression.new(nil, nil, @rexpr, @rexpr.type) if not @rexpr.kind_of? CExpression @rexpr = CExpression.new(nil, nil, @rexpr, @type) end end when :>>, :<< # char => int if @lexpr.type != @type @lexpr = CExpression.new(nil, nil, @lexpr, @lexpr.type) if not @lexpr.kind_of? CExpression @lexpr = CExpression.new(nil, nil, @lexpr, @type) end when :'+=', :'-=', :'*=', :'/=', :'&=', :'|=', :'^=', :'%=' if @rexpr.type != @lexpr.type @rexpr = CExpression.new(nil, nil, @rexpr, @rexpr.type) if not @rexpr.kind_of? CExpression @rexpr = CExpression.new(nil, nil, @rexpr, @type) end end @lexpr = CExpression.precompile_inner(compiler, scope, @lexpr) @rexpr = CExpression.precompile_inner(compiler, scope, @rexpr) if @op == :'&' and not @lexpr rr = @rexpr rr = rr.rexpr while rr.kind_of? CExpression and not rr.op if rr.kind_of? CExpression and rr.op == :'*' and not rr.lexpr @lexpr = nil @op = nil @rexpr = rr.rexpr return precompile_inner(compiler, scope) elsif rr != @rexpr @rexpr = rr return precompile_inner(compiler, scope) end end CExpression.precompile_type(compiler, scope, self) isnumeric = lambda { |e_| e_.kind_of?(::Numeric) or (e_.kind_of? CExpression and not e_.lexpr and not e_.op and e_.rexpr.kind_of? ::Numeric) } # calc numeric # XXX do not simplify operations involving variables (for type overflow etc) if isnumeric[@rexpr] and (not @lexpr or isnumeric[@lexpr]) and (val = reduce(compiler)).kind_of? ::Numeric @lexpr = nil @op = nil @rexpr = val end self end end
# File metasm/parse_c.rb, line 2286 def reduce(parser) # parser used for arithmetic overflows (need basic type sizes) case @op when :'&&' case l = CExpression.reduce(parser, @lexpr) when 0; 0 when ::Integer case r = CExpression.reduce(parser, @rexpr) when 0; 0 when ::Integer; 1 else CExpression.new(l, @op, r, @type) end else CExpression.new(l, @op, @rexpr, @type) end when :'||' case l = CExpression.reduce(parser, @lexpr) when 0 case r = CExpression.reduce(parser, @rexpr) when 0; 0 when ::Integer; 1 else CExpression.new(l, @op, r, @type) end when ::Integer; 1 else CExpression.new(l, @op, @rexpr, @type) end when :'!' case r = CExpression.reduce(parser, @rexpr) when 0; 1 when ::Integer; 0 else CExpression.new(nil, @op, r, @type) end when :'!=', :'==', :'<', :'>', :'>=', :'<=' l = CExpression.reduce(parser, @lexpr) r = CExpression.reduce(parser, @rexpr) if l.kind_of?(::Integer) and r.kind_of?(::Integer) if @op == :'!='; l != r ? 1 : 0 else l.send(@op, r) ? 1 : 0 end else l = CExpression.new(nil, nil, l, BaseType.new(:int)) if l.kind_of? ::Integer r = CExpression.new(nil, nil, r, BaseType.new(:int)) if r.kind_of? ::Integer CExpression.new(l, @op, r, @type) end when :'.' le = CExpression.reduce(parser, @lexpr) if le.kind_of? Variable and le.initializer.kind_of? ::Array t = le.type.untypedef midx = t.members.index(t.findmember(@rexpr)) CExpression.reduce(parser, le.initializer[midx] || 0) else CExpression.new(le, @op, @rexpr, @type) end when :'?:' case c = CExpression.reduce(parser, @lexpr) when 0; CExpression.reduce(parser, @rexpr[0]) when ::Integer; CExpression.reduce(parser, @rexpr[1]) else CExpression.new(c, @op, @rexpr, @type) end when :'+', :'-', :'*', :'/', :'^', :'%', :'&', :'|', :'>>', :'<<', :'~', nil t = @type.untypedef case t when BaseType when Pointer; return self if @op else return @rexpr if not @op and not @lexpr and @rexpr.kind_of? Variable and @rexpr.type == @type return self # raise parser, 'not arithmetic type' end # compute value r = CExpression.reduce(parser, @rexpr) ret = if not @lexpr # unary case @op when :'+', nil, :'-', :'~' return CExpression.new(nil, @op, r, @type) if not r.kind_of? ::Numeric case @op when :'-'; -r when :'~'; ~r else r end else return CExpression.new(nil, @op, r, @type) end else l = CExpression.reduce(parser, @lexpr) if not l.kind_of?(::Numeric) or not r.kind_of?(::Numeric) l = CExpression.new(nil, nil, l, BaseType.new(:int)) if l.kind_of? ::Integer r = CExpression.new(nil, nil, r, BaseType.new(:int)) if r.kind_of? ::Integer return CExpression.new(l, @op, r, @type) end l.send(@op, r) end # overflow tn = (t.pointer? ? :ptr : t.name) case tn when :char, :short, :int, :long, :ptr, :longlong, :__int8, :__int16, :__int32, :__int64 max = 1 << (8*parser.typesize[tn]) ret = ret.to_i & (max-1) if not t.pointer? and t.specifier != :unsigned and (ret & (max >> 1)) > 0 # char == unsigned char ret - max else ret end when :float, :double, :longdouble ret.to_f # TODO end when :funcall l = CExpression.reduce(parser, @lexpr) r = @rexpr.map { |rr| rr = CExpression.reduce(parser, rr) rr = CExpression.new(nil, nil, rr, BaseType.new(:int)) if rr.kind_of? ::Integer rr } CExpression.new(l, @op, r, @type) else l = CExpression.reduce(parser, @lexpr) if @lexpr r = CExpression.reduce(parser, @rexpr) if @rexpr l = CExpression.new(nil, nil, l, BaseType.new(:int)) if l.kind_of? ::Integer r = CExpression.new(nil, nil, r, BaseType.new(:int)) if r.kind_of? ::Integer CExpression.new(l, @op, r, @type) end end
overwrites @lexpr @op @rexpr @type from the arg
# File metasm/parse_c.rb, line 1071 def replace(o) @lexpr, @op, @rexpr, @type = o.lexpr, o.op, o.rexpr, o.type self end
# File metasm/parse_c.rb, line 4038 def to_s dump_inner(Block.new(nil))[0].join(' ') end
# File metasm/parse_c.rb, line 2443 def walk case @op when :funcall, :'?:' yield @lexpr @rexpr.each { |arg| yield arg } when :'->', :'.' yield @lexpr else yield @lexpr if @lexpr yield @rexpr if @rexpr end end