node/lib/metasm/metasm/parse_c.rb
# parses variable/function definition/declaration/initialization
# populates scope.symbols and scope.struct
# raises on redefinitions
# returns false if no definition found
parses variable/function definition/declaration/initialization
populates scope.symbols and scope.struct
raises on redefinitions
returns false if no definition found
parses variable / function definition / declaration / initialization populates scope . symbols and scope . struct raises on redefinitions returns false if no definition found
def parse_definition(scope)
			return false if not basetype = Variable.parse_type(self, scope, true)
			tok = skipspaces
			if tok and tok.type == :punct and tok.raw == ';' and basetype.type and
					(basetype.type.kind_of? Union or basetype.type.kind_of? Enum)
				return true
			else unreadtok tok
			end
			nofunc = false
			loop do
				var = basetype.dup
				var.parse_declarator(self, scope)
				raise var.backtrace if not var.name	
				if prev = scope.symbol[var.name]
					if prev.kind_of? TypeDef and var.storage == :typedef
						check_compatible_type(var.backtrace, prev.type, var.type, true)
						puts "redefining typedef #{var.name}" if $VERBOSE
						var = prev
					elsif not prev.kind_of?(Variable) or
							prev.initializer or
							(prev.storage != :extern and prev.storage != var.storage) or
							(scope != @toplevel and prev.storage != :static)
						if prev.kind_of? ::Integer	
							prev = (scope.struct.values.grep(Enum) + scope.anonymous_enums.to_a).find { |e| e.members.index(prev) }
						end
						raise var.backtrace, "redefinition, previous is #{prev.backtrace.exception(nil).message rescue :unknown}"
					else
						check_compatible_type var.backtrace, prev.type, var.type, true
						(var.attributes ||= []).concat prev.attributes if prev.attributes
					end
				elsif var.storage == :typedef
					attrs = var.attributes
					var = TypeDef.new var.name, var.type, var.backtrace
					var.attributes = attrs if attrs
				end
				scope.statements << Declaration.new(var) unless var.kind_of? TypeDef
				raise tok || self, 'punctuation expected' if not tok = skipspaces or (tok.type != :punct and not %w[asm __asm __asm__].include? tok.raw)
				case tok.raw
				when '{'
					raise tok if nofunc or not var.kind_of? Variable or not var.type.kind_of? Function
					scope.symbol[var.name] = var
					body = var.initializer = Block.new(scope)
					var.type.args ||= []
					var.type.args.each { |v|
						if not v.name
							puts "unnamed argument in definition of #{var.name}" if $DEBUG
							next	
						end
						body.symbol[v.name] = v	
					}
					loop do
						raise tok || self, var.backtrace.exception('"}" expected for end of function') if not tok = skipspaces
						break if tok.type == :punct and tok.raw == '}'
						unreadtok tok
						if not parse_definition(body)
							body.statements << parse_statement(body, [var.type.type])
						end
					end
					if $VERBOSE and not body.statements.last.kind_of? Return and not body.statements.last.kind_of? Asm
						puts tok.exception('missing function return value').message if not var.type.type.untypedef.kind_of? BaseType or var.type.type.untypedef.name != :void
					end
					break
				when 'asm', '__asm', '__asm__'
					raise tok if nofunc or not var.kind_of? Variable or not var.type.kind_of? Function
					unused = Asm.parse(self, scope)
					puts "unsupported gcc-style __asm__ function redirect #{var.name.inspect} => #{unused.body.inspect}" if $VERBOSE
					break
				when '='
					raise tok, '"{" or ";" expected' if var.type.kind_of? Function
					raise tok, 'cannot initialize extern variable' if var.storage == :extern
					scope.symbol[var.name] = var	
					var.initializer = var.type.parse_initializer(self, scope)
					if var.initializer.kind_of?(CExpression) and (scope == @toplevel or var.storage == :static)
						raise tok, "initializer for static #{var.name} is not constant" if not var.initializer.constant?
					end
					reference_value = lambda { |e, v|
						found = false
						case e
						when Variable; found = true if e == v
						when CExpression; e.walk { |ee| found ||= reference_value[ee, v] } if e.op != :& or e.lexpr
						end
						found
					}
					raise tok, "initializer for #{var.name} is not constant (selfreference)" if reference_value[var.initializer, var]
					raise tok || self, '"," or ";" expected' if not tok = skipspaces or tok.type != :punct
				else
					scope.symbol[var.name] = var
				end
				case tok.raw
				when ','; nofunc = true
				when ';'; break
				when '}'; unreadtok(tok); break
				else raise tok, '";" or "," expected'
				end
			end
			true
		end
def parse_definition ( scope ) return false if not basetype = Variable . parse_type ( self , scope , true ) tok = skipspaces if tok and tok . type == :punct and tok . raw == ';' and basetype . type and ( basetype . type . kind_of? Union or basetype . type . kind_of? Enum ) return true else unreadtok tok end nofunc = false loop do var = basetype . dup var . parse_declarator ( self , scope ) raise var . backtrace if not var . name if prev = scope . symbol [ var . name ] if prev . kind_of? TypeDef and var . storage == :typedef check_compatible_type ( var . backtrace , prev . type , var . type , true ) puts "redefining typedef #{var.name}" if $VERBOSE var = prev elsif not prev . kind_of? ( Variable ) or prev . initializer or ( prev . storage != :extern and prev . storage != var . storage ) or ( scope != @toplevel and prev . storage != :static ) if prev . kind_of? :: Integer prev = ( scope . struct . values . grep ( Enum ) + scope . anonymous_enums . to_a ) . find { | e | e . members . index ( prev ) } end raise var . backtrace , "redefinition, previous is #{prev.backtrace.exception(nil).message rescue :unknown}" else check_compatible_type var . backtrace , prev . type , var . type , true ( var . attributes ||= [ ] ) . concat prev . attributes if prev . attributes end elsif var . storage == :typedef attrs = var . attributes var = TypeDef . new var . name , var . type , var . backtrace var . attributes = attrs if attrs end scope . statements << Declaration . new ( var ) unless var . kind_of? TypeDef raise tok || self , 'punctuation expected' if not tok = skipspaces or ( tok . type != :punct and not %w[ asm __asm __asm__ ] . include? tok . raw ) case tok . raw when '{' raise tok if nofunc or not var . kind_of? Variable or not var . type . kind_of? Function scope . symbol [ var . name ] = var body = var . initializer = Block . new ( scope ) var . type . args ||= [ ] var . type . args . each { | v | if not v . name puts "unnamed argument in definition of #{var.name}" if $DEBUG next end body . symbol [ v . name ] = v } loop do raise tok || self , var . backtrace . exception ( '"}" expected for end of function' ) if not tok = skipspaces break if tok . type == :punct and tok . raw == '}' unreadtok tok if not parse_definition ( body ) body . statements << parse_statement ( body , [ var . type . type ] ) end end if $VERBOSE and not body . statements . last . kind_of? Return and not body . statements . last . kind_of? Asm puts tok . exception ( 'missing function return value' ) . message if not var . type . type . untypedef . kind_of? BaseType or var . type . type . untypedef . name != :void end break when 'asm' , '__asm' , '__asm__' raise tok if nofunc or not var . kind_of? Variable or not var . type . kind_of? Function unused = Asm . parse ( self , scope ) puts "unsupported gcc-style __asm__ function redirect #{var.name.inspect} => #{unused.body.inspect}" if $VERBOSE break when '=' raise tok , '"{" or ";" expected' if var . type . kind_of? Function raise tok , 'cannot initialize extern variable' if var . storage == :extern scope . symbol [ var . name ] = var var . initializer = var . type . parse_initializer ( self , scope ) if var . initializer . kind_of? ( CExpression ) and ( scope == @toplevel or var . storage == :static ) raise tok , "initializer for static #{var.name} is not constant" if not var . initializer . constant? end reference_value = lambda { | e , v | found = false case e when Variable ; found = true if e == v when CExpression ; e . walk { | ee | found ||= reference_value [ ee , v ] } if e . op != :& or e . lexpr end found } raise tok , "initializer for #{var.name} is not constant (selfreference)" if reference_value [ var . initializer , var ] raise tok || self , '"," or ";" expected' if not tok = skipspaces or tok . type != :punct else scope . symbol [ var . name ] = var end case tok . raw when ',' ; nofunc = true when ';' ; break when '}' ; unreadtok ( tok ) ; break else raise tok , '";" or "," expected' end end true end
parses variable/function definition/declaration/initialization
populates scope.symbols and scope.struct
raises on redefinitions
returns false if no definition found
parses variable / function definition / declaration / initialization populates scope . symbols and scope . struct raises on redefinitions returns false if no definition found
# check struct predeclaration # barrel roll # windows.h redefines many typedefs with the same definition # enum value # function body # put func parameters in func body scope # arg redefinition is checked in parse_declarator # should raise to be compliant # XXX will need special check in stack allocator # GCC function redirection # void foo(void) __asm__("bar");  =>  when code uses 'foo', silently redirect to 'bar' instead # most of the time, 'bar' is not defined anywhere, so we support it only # to allow parsing of headers using it, hoping noone will actually use them # variable initialization # allow initializer to reference var, eg 'void *ptr = &ptr;'
scope