note
	description: "Summary description for {WRAPC_PARSER}."
	date: "$Date$"
	revision: "$Revision$"

class
	WRAPC_MACRO_PARSER


create

	make

feature {NONE} -- Initialization

	make (a_path: PATH)
		do
			path := a_path
			create last_line.make_empty
			create last_file_path.make_empty
			create constants.make_equal_caseless (10)
		end

feature -- Access

	constants: STRING_TABLE [LIST [TUPLE [constant:STRING; type: STRING; value: STRING]]]
		-- String table with header name and a list of tuples (contant and type)

feature -- Parser

	reset
		do
			error_description := Void
		end

	parse_macro
		do
			initialize
			if not has_error and then has_next_line then
				from
					next_line
				until
					has_error or else end_of_file
				loop
					parse_line
					if has_next_line then
						next_line
					end
				end
			end
			finalize
		end

feature {NONE} -- Parser Implementation

	initialize
		local
			l_file: like file
		do
			create {PLAIN_TEXT_FILE} l_file.make_with_path (path)
			if l_file.exists and then l_file.is_readable then
				l_file.open_read
				file := l_file
			else
				create error_description.make_from_string ("File is not readable or does not exist at path: " + path.out)
			end
		end

	finalize
		do
			if attached file as l_file then
				l_file.close
			end
		end

	parse_line
			-- Example
			-- #line 1 "C:\\home\\projects\\dev\\WrapC_dev\\wrap_libgit2\\library\\C\\include\\git2.h"
		local
			parser: WRAPC_MACRO_HEADER_PARSER
		do
			if not last_line.is_empty then
				skip_white_spaces
				if last_line.at (cursor) = '#' then
					next
					if is_line then
						-- mark_define
						if {PLATFORM}.is_windows then
							next
							next
							next
							next
						end
						skip_white_spaces
						skip_values
						skip_white_spaces
						if last_line.at (cursor) = '"' then
							create last_file_path.make_empty
							parse_file_path
							if not last_file_path.is_empty and then not constants.has_key (last_file_path) then
								create parser.make (create {PATH}.make_from_string (last_file_path))
								parser.parse_macro
								if not parser.constants.is_empty then
									constants.force (parser.constants, last_file_path)
								end
							end
						end
					end
				end
			end
		end

	skip_white_spaces
			-- Remove white spaces
		local
		do
			from
			until
				(last_line.at (cursor) /= ' ' and last_line.at (cursor) /= '%N' and last_line.at (cursor) /= '%R' and last_line.at (cursor) /= '%U' and last_line.at (cursor) /= '%T') or not has_next
			loop
				next
			end
		end

	parse_file_path
		do
			from
				next
			until
				(last_line.at (cursor) = '"') or not has_next
			loop
				last_file_path.append_character (last_line.at (cursor))
				next
			end
		end

	skip_values
			-- Remove values
		local
		do
			from
			until
				(last_line.at (cursor) = ' ' or last_line.at (cursor) = '%N' or last_line.at (cursor) = '%R' or last_line.at (cursor) = '%U' or last_line.at (cursor) = '%T') or not has_next
			loop
				next
			end
		end

	is_line: BOOLEAN
			-- Word at index represents define?
		do
			if {PLATFORM}.is_windows then
				Result := last_line.same_characters_general (line_id, 1, 4, cursor) -- 6 = line_id.count
			else
				Result := True
			end
		end

feature {NONE} -- Implementation

	is_value_supported: BOOLEAN

	is_defined_supported: BOOLEAN

	last_line: STRING

	last_file_path: STRING

	cursor: INTEGER


feature {NONE} -- File macro implementaetion

	next
		require
			has_next
		do
			if cursor < last_line.count then
				cursor := cursor + 1
			end
		ensure
			limits: cursor <= last_line.count
		end

	has_next: BOOLEAN
		do
			Result := cursor < last_line.count
		end

	end_of_file: BOOLEAN
		do
			if not has_error and then attached file as l_file then
				Result := l_file.end_of_file
			else
				Result := True
			end
		end

	has_next_line: BOOLEAN
		do
			Result := not end_of_file
		end

	next_line
		require
			has_next_line: has_next_line
			not_has_error: not has_error
		do
			if attached file as l_file then
				l_file.read_line
				last_line := l_file.last_string.twin
				cursor := 1
			else
				last_line := ""
				cursor := 1
			end
		end


feature {NONE} -- Implementation

	path: PATH

	file: detachable FILE

	error_description: detachable STRING

	has_error: BOOLEAN
		do
			Result := attached error_description
		end


feature {NONE} -- Constants

	line_id: STRING = "line"

end