note

	status: "See notice at end of class.";
	Date: "$Date$";
	Revision: "$Revision$";
	Product: "Environment Converter"

class EC_PARSE

inherit
	EC_TYPES;

	EXT_INTERNAL

create
	make

feature {NONE} -- Initialization

	make
		do
			create descriptor.make
			create ecp_reference
		end

feature -- Status report

	ecp_parsed: BOOLEAN
			-- Did last operation succeeded?

	ecp_message: STRING
			-- Error message if `sql_parsed' is false
		once
			create Result.make (0)
		end

	ecp_token_array: detachable ARRAYED_LIST [TOKEN]
			-- Array of tokens

	ecp_reference: ANY
			-- One object reference

	is_descriptor_set: BOOLEAN
			-- If descriptor has been set?
		do
			Result := descriptor /= Void
		end

feature  -- Status setting

	set_descriptor (e: EC_DESCRIPTOR)
			-- Set `ecl_descriptor' with `e'.
		require
			descriptor_exits: e /= Void
		do
			descriptor := e
		end;

	ecp_parse (at: ARRAYED_LIST [TOKEN])
			-- Perform syntactical analysis on a lexical item list.
		require
			token_array_exists: at /= Void
			set: is_descriptor_set
		local
			l_descriptor: like descriptor
			l_array: like ecp_token_array
			l_context: like context_type
			l_token: TOKEN
		do
			l_array := at
			ecp_token_array := l_array;
			ecp_parsed := True;
			ecp_message.wipe_out;
			if at.is_empty then
				set_ecp_parse_error("Empty Line")
			else
				from
					ecp_current_field := 1;
					ecp_current_token := 1;
					l_token := l_array.i_th (1);
					l_descriptor := descriptor
					if attached l_descriptor.ecd_reference as l_ref then
						set_ecp_reference (l_ref)
					end
					l_context := [l_token, l_descriptor.ecd_fields.i_th (ecp_current_field), 0, 0]
				until
					not ecp_parsed or
						ecp_current_field >= l_descriptor.ecd_max_index
				loop
					l_context.ecp_field := l_descriptor.ecd_fields.i_th (ecp_current_field);
					ecp_parse_field (l_context)
					ecp_parse_separator (l_context)
					ecp_current_field := ecp_current_field +1
				end;
				l_context.ecp_field := l_descriptor.ecd_fields.i_th (ecp_current_field);
				ecp_parse_last_field (l_context)
				check_end_of_line
			end
		end;

feature {NONE} -- Status report

	context_type: TUPLE [ecp_token: TOKEN; ecp_field: EC_FIELD; ecp_current_token, ecp_current_field: INTEGER]
			-- Type of context used during parsing. It is only used for typing.
		do
			check False then
			end
		end

	descriptor: EC_DESCRIPTOR
		-- A descriptor

	ecp_current_token: INTEGER
		-- Token lexical number

	ecp_current_field: INTEGER
		-- Field number

	tmps: STRING
			-- Temporary buffer
		once
			create Result.make (0)
		end;

feature {NONE} -- Status setting

	set_ecp_reference (object: ANY)
			-- Set `ecp_reference' with `object'.
		require
			object_exists: object /= Void
		do
			ecp_reference := object
		end;

	set_ecp_parse_error (message: STRING)
			-- Set error flag, and initialize error message.
		do
			ecp_parsed := False;
			ecp_token_array := Void
			ecp_message.wipe_out;
			ecp_message.append(message)
		end;

feature {NONE} -- Implementation

	ecp_parse_separator (a_context: like context_type)
			-- Parse current token which must be the field separator.
		require
			set: is_descriptor_set
		local
			l_descriptor: like descriptor
		do
			if ecp_parsed then
				l_descriptor := descriptor
				if a_context.ecp_token.type = Field_sep_ttype
					and then a_context.ecp_token.string_value ~
					l_descriptor.field_separator.out then
					ecp_remove_word (a_context)
				else
					tmps.wipe_out;
					tmps.append("Syntax error, Bad field separator `");
					tmps.append(a_context.ecp_token.string_value);
					tmps.append("' instead of `");
					tmps.append(l_descriptor.field_separator.out);
					tmps.append("'.%N");
					set_ecp_parse_error(tmps)
				end
			end
		end;

	ecp_parse_field (a_context: like context_type)
			-- Parse an object field.
		local
		do
			if ecp_parsed then
				if a_context.ecp_field.use_label then
					ecp_parse_label_name (a_context)
					ecp_parse_label_sep (a_context)
				end;
				if a_context.ecp_field.use_value_delimiters then
					ecp_parse_left_delimiter (a_context)
				end;
				ecp_parse_value(a_context, 1);
				if a_context.ecp_field.use_value_delimiters then
					ecp_parse_right_delimiter(a_context, 1)
				end
			end
		end;

	ecp_parse_last_field (a_context: like context_type)
			-- Parse the last field of an object.
		do
			if ecp_parsed then
				if a_context.ecp_field.use_label then
					ecp_parse_label_name (a_context)
					ecp_parse_label_sep (a_context)
				end;
				if a_context.ecp_field.use_value_delimiters then
					ecp_parse_left_delimiter (a_context)
				end;
				if a_context.ecp_field.use_value_delimiters then
					ecp_parse_value(a_context, 1);
					ecp_parse_right_delimiter(a_context, 0)
				else
					ecp_parse_value(a_context, 0)
				end
			end
		end;

	ecp_parse_label_name (a_context: like context_type)
			-- Parse current token which must be an identifier
			-- matching the current field name.
		require
			set: is_descriptor_set
		local
			i: INTEGER;
			done: BOOLEAN
			l_field: EC_FIELD
			l_descriptor: like descriptor
		do
			if ecp_parsed then
				if a_context.ecp_token.type = Identifier_ttype and then a_context.ecp_token.string_value ~ a_context.ecp_field.field_name then
					ecp_remove_word (a_context)
				else
					from
						l_descriptor := descriptor
						i := 1
					until
						i > l_descriptor.ecd_max_index or done
					loop
						l_field := l_descriptor.ecd_fields.i_th (i)
						if (a_context.ecp_token.string_value ~ l_field.field_name) then
							--l_field.set_rank (ecp_current_field);
							a_context.ecp_field := l_field
							ecp_remove_word (a_context)
							done := True
						end;
						i := i + 1
					end;
					if not done then
						tmps.wipe_out;
						tmps.append("Syntax error, Field name `");
						tmps.append(a_context.ecp_token.string_value);
						tmps.append("' instead of `");
						tmps.append(a_context.ecp_field.field_name);
						tmps.append("'.%N");
						set_ecp_parse_error(tmps)
					end
				end
			end
		end;

	ecp_parse_label_sep (a_context: like context_type)
			-- Parse current token which must be an label separator
			-- matching the current field label separator.
		do
			if ecp_parsed then
				if a_context.ecp_token.type = Label_sep_ttype and then
					a_context.ecp_token.string_value ~ a_context.ecp_field.label_separator.out then
					ecp_remove_word (a_context)
				else
					tmps.wipe_out;
					tmps.append("Syntax error, Bad label separator `");
					tmps.append(a_context.ecp_token.string_value);
					tmps.append("' instead of `");
					tmps.append(a_context.ecp_field.label_separator.out);
					tmps.append("'.%N");
					set_ecp_parse_error(tmps)
				end
			end
		end;

	ecp_parse_left_delimiter (a_context: like context_type)
			-- Parse current token which must be a left delimiter
			-- matching the current field value left delim. .
		do
			if ecp_parsed then
				if a_context.ecp_token.type = Left_del_ttype and then
					a_context.ecp_token.string_value ~ a_context.ecp_field.left_delimiter.out then
					ecp_remove_word (a_context)
				else
					tmps.wipe_out;
					tmps.append("Syntax error, Bad left delimiter `");
					tmps.append(a_context.ecp_token.string_value);
					tmps.append("' instead of `");
					tmps.append(a_context.ecp_field.left_delimiter.out);
					tmps.append("'.%N");
					set_ecp_parse_error(tmps)
				end
			end
		end;

	ecp_parse_right_delimiter (a_context: like context_type; i: INTEGER)
			-- Parse current token which must be a right delimiter
			-- matching the current field value right delim.
			-- `i' is 0 if this is the last token to be read, else >0
		do
			if ecp_parsed then
				if a_context.ecp_token.type = Right_del_ttype and then
					a_context.ecp_token.string_value ~ a_context.ecp_field.right_delimiter.out then
					if i>0 then
						ecp_remove_word (a_context)
					else
						ecp_current_token := ecp_current_token+1
					end
				else
					tmps.wipe_out;
					tmps.append("Syntax error, Bad right delimiter `");
					tmps.append(a_context.ecp_token.string_value);
					tmps.append("' instead of `");
					tmps.append(a_context.ecp_field.right_delimiter.out);
					tmps.append("'.%N");
					set_ecp_parse_error(tmps)
				end
			end
		end;

	ecp_parse_value (a_context: like context_type; i:INTEGER)
			-- Parse and skip next token which
			-- will be converted in an object.
			-- `i' is 0 if this is the last token to be read, else >0
		local
			token_string, tmp_s: STRING;
			token_type, tmp_i: INTEGER;
			tmp_r: REAL;
			tmp_b ,tst: BOOLEAN
			l_field: EC_FIELD
			l_reference: like ecp_reference
		do
			if ecp_parsed then
				l_field := a_context.ecp_field
				l_reference := ecp_reference
				token_string := a_context.ecp_token.string_value.twin
				token_type := a_context.ecp_token.type;
				if token_type = l_field.field_type then
					inspect
						token_type
					when Integer_ttype then
						tmp_i := token_string.to_integer;
						tst := field_copy (l_field.field_rank, l_reference, tmp_i)
					when Real_ttype then
						tmp_r := token_string.to_real;
						tst := field_copy (l_field.field_rank, l_reference, tmp_r)
					when Boolean_ttype then
						if token_string.is_equal ("TRUE") then
							tmp_b:=True
						else
							tmp_b:=False
						end;
						tst := field_copy (l_field.field_rank, l_reference, tmp_b)
					when String_ttype then
						create tmp_s.make (0);
						tmp_s.append (token_string.substring
								(2, token_string.count-1));
						tst := field_copy (l_field.field_rank, l_reference, tmp_s)
					else
						tmps.wipe_out;
						tmps.append("Type of attribute `");
						tmps.append(l_field.field_name);
						tmps.append("' invalid with `");
						tmps.append(token_string);
						tmps.append("'.%N");
						set_ecp_parse_error (tmps)
					end;
					if ecp_parsed then
						if i>0 then
							ecp_remove_word (a_context)
						else
							ecp_current_token := ecp_current_token + 1
						end
					end
				else
					tmps.wipe_out;
					tmps.append("Type of attribute `");
					tmps.append(l_field.field_name);
					tmps.append("' invalid with `");
					tmps.append(token_string);
					tmps.append("'.%N");
					set_ecp_parse_error (tmps)
				end
			end
		end;

	ecp_remove_word (a_context: like context_type)
			-- Remove current token and checks for the
			-- next token to exist.
		do
			if attached ecp_token_array as l_array then
				ecp_current_token := ecp_current_token + 1;
				if ecp_current_token > l_array.count then
					tmps.wipe_out;
					tmps.append("Unexpected end of line after `");
					tmps.append(a_context.ecp_token.string_value);
					tmps.append("'.%N");
					set_ecp_parse_error(tmps)
				else
					a_context.ecp_token := l_array.i_th (ecp_current_token)
				end
			end
		end;

	check_end_of_line
			-- Check if the last token read was the last of the array
		do
			-- Not implemented yet
		end

invariant
	ecp_token_array_not_void: ecp_parsed = (ecp_token_array /= Void)

note
	copyright:	"Copyright (c) 1984-2014, Eiffel Software and others"
	license:	"Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
	source: "[
			Eiffel Software
			5949 Hollister Ave., Goleta, CA 93117 USA
			Telephone 805-685-1006, Fax 805-685-6869
			Website http://www.eiffel.com
			Customer support http://support.eiffel.com
		]"




end -- class EC_PARSE