node/lib/metasm/metasm/preprocessor.rb
def preprocessor_directive(cmd, ocmd = cmd)
		# read spaces, returns the next token
		# XXX for all commands that may change @ifelse_nesting, ensure last element is :testing to disallow any other preprocessor directive to be run in a bad environment (while looking ahead)
		skipspc = lambda {
			loop do
				tok = readtok_nopp
				break tok if not tok or tok.type != :space
			end
		}

		# XXX do not preprocess tokens when searching for :eol, it will trigger preprocessor directive detection from readtok

		eol = tok = nil
		case cmd.raw
		when 'if'
			case @ifelse_nesting.last
			when :accept, nil
				@ifelse_nesting << :testing
				raise cmd, 'expr expected' if not test = PPExpression.parse(self)
				eol = skipspc[]
				raise eol, 'pp syntax error' if eol and eol.type != :eol
				unreadtok eol
				case test.reduce
				when 0;       @ifelse_nesting[-1] = :discard
				when Integer; @ifelse_nesting[-1] = :accept
				else          @ifelse_nesting[-1] = :discard
				end
			when :discard, :discard_all
				@ifelse_nesting << :discard_all
			end

		when 'ifdef'
			case @ifelse_nesting.last
			when :accept, nil
				@ifelse_nesting << :testing
				raise eol || tok || cmd, 'pp syntax error' if not tok = skipspc[] or tok.type != :string or (eol = skipspc[] and eol.type != :eol)
				unreadtok eol
				@ifelse_nesting[-1] = (@definition[tok.raw] ? :accept : :discard)
			when :discard, :discard_all
				@ifelse_nesting << :discard_all
			end

		when 'ifndef'
			case @ifelse_nesting.last
			when :accept, nil
				@ifelse_nesting << :testing
				raise eol || tok || cmd, 'pp syntax error' if not tok = skipspc[] or tok.type != :string or (eol = skipspc[] and eol.type != :eol)
				unreadtok eol
				@ifelse_nesting[-1] = (@definition[tok.raw] ? :discard : :accept)
			when :discard, :discard_all
				@ifelse_nesting << :discard_all
			end

		when 'elif'
			case @ifelse_nesting.last
			when :accept
				@ifelse_nesting[-1] = :discard_all
			when :discard
				@ifelse_nesting[-1] = :testing
				raise cmd, 'expr expected' if not test = PPExpression.parse(self)
				raise eol, 'pp syntax error' if eol = skipspc[] and eol.type != :eol
				unreadtok eol
				case test.reduce
				when 0;       @ifelse_nesting[-1] = :discard
				when Integer; @ifelse_nesting[-1] = :accept
				else          @ifelse_nesting[-1] = :discard
				end
			when :discard_all
			else raise cmd, 'pp syntax error'
			end

		when 'else'
			@ifelse_nesting << :testing
			@ifelse_nesting.pop
			raise eol || cmd, 'pp syntax error' if @ifelse_nesting.empty? or (eol = skipspc[] and eol.type != :eol)
			unreadtok eol
			case @ifelse_nesting.last
			when :accept
				@ifelse_nesting[-1] = :discard_all
			when :discard
				@ifelse_nesting[-1] = :accept
			when :discard_all
			end

		when 'endif'
			@ifelse_nesting << :testing
			@ifelse_nesting.pop
			raise eol || cmd, 'pp syntax error' if @ifelse_nesting.empty? or (eol = skipspc[] and eol.type != :eol)
			unreadtok eol
			@ifelse_nesting.pop

		when 'define'
			return if @ifelse_nesting.last and @ifelse_nesting.last != :accept

			raise tok || cmd, 'pp syntax error' if not tok = skipspc[] or tok.type != :string
			m = Macro.new(tok)
			valid = m.parse_definition(self)
			if not defined? @defined_strong or not @defined_strong.include? tok.raw
				puts "W: pp: redefinition of #{tok.raw} at #{tok.backtrace_str},\n prev def at #{@definition[tok.raw].name.backtrace_str}" if @definition[tok.raw] and $VERBOSE and @warn_redefinition
				@definition[tok.raw] = m if valid
			end

		when 'undef'
			return if @ifelse_nesting.last and @ifelse_nesting.last != :accept

			raise eol || tok || cmd, 'pp syntax error' if not tok = skipspc[] or tok.type != :string or (eol = skipspc[] and eol.type != :eol)
			if not defined? @defined_strong or not @defined_strong.include? tok.raw
				@definition.delete tok.raw
				unreadtok eol
			end

		when 'include', 'include_next'
			return if @ifelse_nesting.last and @ifelse_nesting.last != :accept

			directive_include(cmd, skipspc)

		when 'error', 'warning'
			return if @ifelse_nesting.last and @ifelse_nesting.last != :accept
			msg = ''
			while tok = readtok_nopp and tok.type != :eol
				msg << tok.raw
			end
			unreadtok tok
			if cmd.raw == 'warning'
				puts cmd.exception("#warning#{msg}").message if $VERBOSE
			else
				raise cmd, "#error#{msg}"
			end

		when 'line'
			return if @ifelse_nesting.last and @ifelse_nesting.last != :accept

			raise tok || cmd if not tok = skipspc[] or tok.type != :string
			@lineno = Integer(tok.raw) rescue raise(tok, 'bad line number')
			raise eol if eol = skipspc[] and eol.type != :eol
			unreadtok eol

		when 'pragma'
			return if @ifelse_nesting.last and @ifelse_nesting.last != :accept

			directive_pragma(cmd, skipspc)

		else return false
		end

		# skip #ifndef'd parts of the source
		state = 1	# just seen :eol
		while @ifelse_nesting.last == :discard or @ifelse_nesting.last == :discard_all
			begin
				tok = skipspc[]
			rescue ParseError
				# react as gcc -E: <"> unterminated in #if 0 => ok, </*> unterminated => error (the " will fail at eol)
				retry
			end

			if not tok; raise ocmd, 'pp unterminated conditional'
			elsif tok.type == :eol; state = 1
			elsif state == 1 and tok.type == :punct and tok.raw == '#'; state = 2
			elsif state == 2 and tok.type == :string; state = preprocessor_directive(tok, ocmd) ? 1 : 0
			else state = 0
			end
		end

		true
	end
# handles #directives
# returns true if the command is valid
# second parameter for internal use
handles #directives
returns true if the command is valid
second parameter for internal use
handles #directives returns true if the command is valid second parameter for internal use
def preprocessor_directive(cmd, ocmd = cmd)
		skipspc = lambda {
			loop do
				tok = readtok_nopp
				break tok if not tok or tok.type != :space
			end
		}
		eol = tok = nil
		case cmd.raw
		when 'if'
			case @ifelse_nesting.last
			when :accept, nil
				@ifelse_nesting << :testing
				raise cmd, 'expr expected' if not test = PPExpression.parse(self)
				eol = skipspc[]
				raise eol, 'pp syntax error' if eol and eol.type != :eol
				unreadtok eol
				case test.reduce
				when 0;       @ifelse_nesting[-1] = :discard
				when Integer; @ifelse_nesting[-1] = :accept
				else          @ifelse_nesting[-1] = :discard
				end
			when :discard, :discard_all
				@ifelse_nesting << :discard_all
			end
		when 'ifdef'
			case @ifelse_nesting.last
			when :accept, nil
				@ifelse_nesting << :testing
				raise eol || tok || cmd, 'pp syntax error' if not tok = skipspc[] or tok.type != :string or (eol = skipspc[] and eol.type != :eol)
				unreadtok eol
				@ifelse_nesting[-1] = (@definition[tok.raw] ? :accept : :discard)
			when :discard, :discard_all
				@ifelse_nesting << :discard_all
			end
		when 'ifndef'
			case @ifelse_nesting.last
			when :accept, nil
				@ifelse_nesting << :testing
				raise eol || tok || cmd, 'pp syntax error' if not tok = skipspc[] or tok.type != :string or (eol = skipspc[] and eol.type != :eol)
				unreadtok eol
				@ifelse_nesting[-1] = (@definition[tok.raw] ? :discard : :accept)
			when :discard, :discard_all
				@ifelse_nesting << :discard_all
			end
		when 'elif'
			case @ifelse_nesting.last
			when :accept
				@ifelse_nesting[-1] = :discard_all
			when :discard
				@ifelse_nesting[-1] = :testing
				raise cmd, 'expr expected' if not test = PPExpression.parse(self)
				raise eol, 'pp syntax error' if eol = skipspc[] and eol.type != :eol
				unreadtok eol
				case test.reduce
				when 0;       @ifelse_nesting[-1] = :discard
				when Integer; @ifelse_nesting[-1] = :accept
				else          @ifelse_nesting[-1] = :discard
				end
			when :discard_all
			else raise cmd, 'pp syntax error'
			end
		when 'else'
			@ifelse_nesting << :testing
			@ifelse_nesting.pop
			raise eol || cmd, 'pp syntax error' if @ifelse_nesting.empty? or (eol = skipspc[] and eol.type != :eol)
			unreadtok eol
			case @ifelse_nesting.last
			when :accept
				@ifelse_nesting[-1] = :discard_all
			when :discard
				@ifelse_nesting[-1] = :accept
			when :discard_all
			end
		when 'endif'
			@ifelse_nesting << :testing
			@ifelse_nesting.pop
			raise eol || cmd, 'pp syntax error' if @ifelse_nesting.empty? or (eol = skipspc[] and eol.type != :eol)
			unreadtok eol
			@ifelse_nesting.pop
		when 'define'
			return if @ifelse_nesting.last and @ifelse_nesting.last != :accept
			raise tok || cmd, 'pp syntax error' if not tok = skipspc[] or tok.type != :string
			m = Macro.new(tok)
			valid = m.parse_definition(self)
			if not defined? @defined_strong or not @defined_strong.include? tok.raw
				puts "W: pp: redefinition of #{tok.raw} at #{tok.backtrace_str},\n prev def at #{@definition[tok.raw].name.backtrace_str}" if @definition[tok.raw] and $VERBOSE and @warn_redefinition
				@definition[tok.raw] = m if valid
			end
		when 'undef'
			return if @ifelse_nesting.last and @ifelse_nesting.last != :accept
			raise eol || tok || cmd, 'pp syntax error' if not tok = skipspc[] or tok.type != :string or (eol = skipspc[] and eol.type != :eol)
			if not defined? @defined_strong or not @defined_strong.include? tok.raw
				@definition.delete tok.raw
				unreadtok eol
			end
		when 'include', 'include_next'
			return if @ifelse_nesting.last and @ifelse_nesting.last != :accept
			directive_include(cmd, skipspc)
		when 'error', 'warning'
			return if @ifelse_nesting.last and @ifelse_nesting.last != :accept
			msg = ''
			while tok = readtok_nopp and tok.type != :eol
				msg << tok.raw
			end
			unreadtok tok
			if cmd.raw == 'warning'
				puts cmd.exception("#warning#{msg}").message if $VERBOSE
			else
				raise cmd, "#error#{msg}"
			end
		when 'line'
			return if @ifelse_nesting.last and @ifelse_nesting.last != :accept
			raise tok || cmd if not tok = skipspc[] or tok.type != :string
			@lineno = Integer(tok.raw) rescue raise(tok, 'bad line number')
			raise eol if eol = skipspc[] and eol.type != :eol
			unreadtok eol
		when 'pragma'
			return if @ifelse_nesting.last and @ifelse_nesting.last != :accept
			directive_pragma(cmd, skipspc)
		else return false
		end
		state = 1	
		while @ifelse_nesting.last == :discard or @ifelse_nesting.last == :discard_all
			begin
				tok = skipspc[]
			rescue ParseError
				retry
			end
			if not tok; raise ocmd, 'pp unterminated conditional'
			elsif tok.type == :eol; state = 1
			elsif state == 1 and tok.type == :punct and tok.raw == '#'; state = 2
			elsif state == 2 and tok.type == :string; state = preprocessor_directive(tok, ocmd) ? 1 : 0
			else state = 0
			end
		end
		true
	end
def preprocessor_directive ( cmd , ocmd = cmd ) skipspc = lambda { loop do tok = readtok_nopp break tok if not tok or tok . type != :space end } eol = tok = nil case cmd . raw when 'if' case @ifelse_nesting . last when :accept , nil @ifelse_nesting << :testing raise cmd , 'expr expected' if not test = PPExpression . parse ( self ) eol = skipspc [ ] raise eol , 'pp syntax error' if eol and eol . type != :eol unreadtok eol case test . reduce when 0 ; @ifelse_nesting [ - 1 ] = :discard when Integer ; @ifelse_nesting [ - 1 ] = :accept else @ifelse_nesting [ - 1 ] = :discard end when :discard , :discard_all @ifelse_nesting << :discard_all end when 'ifdef' case @ifelse_nesting . last when :accept , nil @ifelse_nesting << :testing raise eol || tok || cmd , 'pp syntax error' if not tok = skipspc [ ] or tok . type != :string or ( eol = skipspc [ ] and eol . type != :eol ) unreadtok eol @ifelse_nesting [ - 1 ] = ( @definition [ tok . raw ] ? :accept : :discard ) when :discard , :discard_all @ifelse_nesting << :discard_all end when 'ifndef' case @ifelse_nesting . last when :accept , nil @ifelse_nesting << :testing raise eol || tok || cmd , 'pp syntax error' if not tok = skipspc [ ] or tok . type != :string or ( eol = skipspc [ ] and eol . type != :eol ) unreadtok eol @ifelse_nesting [ - 1 ] = ( @definition [ tok . raw ] ? :discard : :accept ) when :discard , :discard_all @ifelse_nesting << :discard_all end when 'elif' case @ifelse_nesting . last when :accept @ifelse_nesting [ - 1 ] = :discard_all when :discard @ifelse_nesting [ - 1 ] = :testing raise cmd , 'expr expected' if not test = PPExpression . parse ( self ) raise eol , 'pp syntax error' if eol = skipspc [ ] and eol . type != :eol unreadtok eol case test . reduce when 0 ; @ifelse_nesting [ - 1 ] = :discard when Integer ; @ifelse_nesting [ - 1 ] = :accept else @ifelse_nesting [ - 1 ] = :discard end when :discard_all else raise cmd , 'pp syntax error' end when 'else' @ifelse_nesting << :testing @ifelse_nesting . pop raise eol || cmd , 'pp syntax error' if @ifelse_nesting . empty? or ( eol = skipspc [ ] and eol . type != :eol ) unreadtok eol case @ifelse_nesting . last when :accept @ifelse_nesting [ - 1 ] = :discard_all when :discard @ifelse_nesting [ - 1 ] = :accept when :discard_all end when 'endif' @ifelse_nesting << :testing @ifelse_nesting . pop raise eol || cmd , 'pp syntax error' if @ifelse_nesting . empty? or ( eol = skipspc [ ] and eol . type != :eol ) unreadtok eol @ifelse_nesting . pop when 'define' return if @ifelse_nesting . last and @ifelse_nesting . last != :accept raise tok || cmd , 'pp syntax error' if not tok = skipspc [ ] or tok . type != :string m = Macro . new ( tok ) valid = m . parse_definition ( self ) if not defined? @defined_strong or not @defined_strong . include? tok . raw puts "W: pp: redefinition of #{tok.raw} at #{tok.backtrace_str},\n prev def at #{@definition[tok.raw].name.backtrace_str}" if @definition [ tok . raw ] and $VERBOSE and @warn_redefinition @definition [ tok . raw ] = m if valid end when 'undef' return if @ifelse_nesting . last and @ifelse_nesting . last != :accept raise eol || tok || cmd , 'pp syntax error' if not tok = skipspc [ ] or tok . type != :string or ( eol = skipspc [ ] and eol . type != :eol ) if not defined? @defined_strong or not @defined_strong . include? tok . raw @definition . delete tok . raw unreadtok eol end when 'include' , 'include_next' return if @ifelse_nesting . last and @ifelse_nesting . last != :accept directive_include ( cmd , skipspc ) when 'error' , 'warning' return if @ifelse_nesting . last and @ifelse_nesting . last != :accept msg = '' while tok = readtok_nopp and tok . type != :eol msg << tok . raw end unreadtok tok if cmd . raw == 'warning' puts cmd . exception ( "#warning#{msg}" ) . message if $VERBOSE else raise cmd , "#error#{msg}" end when 'line' return if @ifelse_nesting . last and @ifelse_nesting . last != :accept raise tok || cmd if not tok = skipspc [ ] or tok . type != :string @lineno = Integer ( tok . raw ) rescue raise ( tok , 'bad line number' ) raise eol if eol = skipspc [ ] and eol . type != :eol unreadtok eol when 'pragma' return if @ifelse_nesting . last and @ifelse_nesting . last != :accept directive_pragma ( cmd , skipspc ) else return false end state = 1 while @ifelse_nesting . last == :discard or @ifelse_nesting . last == :discard_all begin tok = skipspc [ ] rescue ParseError retry end if not tok ; raise ocmd , 'pp unterminated conditional' elsif tok . type == :eol ; state = 1 elsif state == 1 and tok . type == :punct and tok . raw == '#' ; state = 2 elsif state == 2 and tok . type == :string ; state = preprocessor_directive ( tok , ocmd ) ? 1 : 0 else state = 0 end end true end
handles #directives
returns true if the command is valid
second parameter for internal use
handles #directives returns true if the command is valid second parameter for internal use
# read spaces, returns the next token # XXX for all commands that may change @ifelse_nesting, ensure last element is :testing to disallow any other preprocessor directive to be run in a bad environment (while looking ahead) # XXX do not preprocess tokens when searching for :eol, it will trigger preprocessor directive detection from readtok # skip #ifndef'd parts of the source # just seen :eol # react as gcc -E: <"> unterminated in #if 0 => ok, </*> unterminated => error (the " will fail at eol)
cmd
ocmd