note description: "Parse a query text." legal: "See notice at end of class." status: "See notice at end of class." date: "$Date$" revision: "$Revision$" class EB_QUERY_PARSER inherit ANY E_PROFILER_CONSTANTS export {NONE} all end feature -- Parsing parse (str: STRING; sqv: SHARED_QUERY_VALUES): BOOLEAN -- Parse `str', put results in `sqv' and -- return true if the query has a good syntax. require string_not_void: str /= Void string_not_empty: not str.is_empty shared_query_values_not_void: sqv /= Void do Result := real_parse (str, sqv) end feature {NONE} -- Implementation real_parse (str: STRING; sqv: SHARED_QUERY_VALUES): BOOLEAN -- Real parsing feature. local col_name, operator, value, boolean_op: STRING index, end_index: INTEGER end_of_query, error: BOOLEAN subquery: SUBQUERY sub_operator: SUBQUERY_OPERATOR do from index := 1 str.to_lower str.left_adjust str.right_adjust until error or else end_of_query loop init_expected_values --| Guillaume - 09/18/97 col_name := column_name (str, index) if col_name = Void then error := True elseif col_name.is_equal (profiler_end_of_query) then if index = 1 then -- This is an error to find "EOQ" the first time the loop is executed. -- It means the query is not valid. error := True end end_of_query := True else index := index + col_name.count index := index + white_space_length (str, index) operator := operator_str (str, index) if operator = void then error := true else index := index + operator.count index := index + white_space_length (str, index) if index <= str.count then end_index := stricly_positive_min (str.substring_index (profiler_spaced_and, index), str.substring_index (profiler_spaced_or, index), str.count) value := value_str (str, index, end_index) index := end_index else value := void end if value = Void then error := True else create subquery.make (col_name, operator, value) sqv.subqueries.extend (subquery) if index < str.count then index := index + white_space_length (str, index) boolean_op := boolean_operator (str, index) if boolean_op = void then error := True elseif boolean_op.is_equal (profiler_end_of_query) then end_of_query := True else index := index + boolean_op.count index := index + white_space_length (str, index) create sub_operator.make (boolean_op) sqv.subquery_operators.extend (sub_operator) index := index + white_space_length (str, index) end else end_of_query := True end end end end end Result := not error end init_expected_values do expects_int := True expects_real := True expects_string := True expects_bounded := True end --| Guillaume 09/18/97 column_name (str: STRING; idx: INTEGER): STRING -- Get the column name in `str' at position `idx' do if idx < str.count then if str.substring (idx, idx + (profiler_feature_name).count - 1).is_equal (profiler_feature_name) then Result := profiler_feature_name expects_real := False expects_int := False expects_bounded := False --| Guillaume - 09/18/97 elseif str.substring (idx, idx + (profiler_calls).count - 1).is_equal (profiler_calls) then Result := profiler_calls expects_string := False --| Guillaume - 09/18/97 elseif str.substring (idx, idx + (profiler_total).count - 1).is_equal (profiler_total) then Result := profiler_total expects_string := False --| Guillaume - 09/18/97 elseif str.substring (idx, idx + (profiler_self).count - 1).is_equal (profiler_self) then Result := profiler_self expects_string := False --| Guillaume - 09/18/97 elseif str.substring (idx, idx + (profiler_percentage).count - 1).is_equal (profiler_percentage) then Result := profiler_percentage expects_string := False --| Guillaume - 09/18/97 elseif str.substring (idx, idx + (profiler_descendants).count - 1).is_equal (profiler_descendants) then Result := profiler_descendants expects_string := False --| Guillaume - 09/18/97 else Result := Void end else Result := profiler_end_of_query end end operator_str (str: STRING; idx: INTEGER): STRING -- Get operator str from `str' at position `idx'. local operator: STRING do create operator.make (0) create Result.make (0) operator := str.substring (idx, idx + 1) if operator.is_equal (profiler_less_than_or_equal) or else operator.is_equal (profiler_greater_than_or_equal) or else operator.is_equal (profiler_not_equal) then Result := operator expects_bounded := false elseif operator.item (1) = '>' or else operator.item (1) = '<' or else operator.item (1) = '=' then Result.extend (operator.item (1)) expects_bounded := false elseif operator.is_equal (profiler_in) then Result := operator expects_real := false expects_int := false expects_string := false else Result := void end end --| Guillaume - 09/18/97 value_str (str: STRING; index, end_index: INTEGER): STRING -- Get value str from `str' beetween position 'index' and 'end_index' do create Result.make (0) Result := str.substring (index, end_index) Result.left_adjust Result.right_adjust if is_expected_value (Result) then Result.prune_all (' ') else Result := void end end is_expected_value (value: STRING) : BOOLEAN -- Is the 'value' string an expected value do Result := (expects_int and value.is_integer) or else (expects_int and is_computed_value (value)) or else (expects_real and value.is_real) or else (expects_real and is_computed_value (value)) or else (expects_string and not value.has (' ')) or else (expects_bounded and is_bounded (value)) end is_computed_value (value: STRING) : BOOLEAN do Result := value.is_equal (profiler_max) or else value.is_equal (profiler_min) or else value.is_equal (profiler_avg) end is_bounded (value: STRING) : BOOLEAN local range_position : INTEGER lower_value, upper_value: STRING do range_position := value.index_of ('-', 1) if range_position = 0 then Result := false else lower_value := value.substring (1, range_position - 1) upper_value := value.substring (range_position + 1, value.count) lower_value.right_adjust lower_value.left_adjust upper_value.right_adjust upper_value.left_adjust if lower_value.is_real or else lower_value.is_integer then if upper_value.is_real or else upper_value.is_integer then Result := true end else Result := false end end end --| Guillaume - 09/18/97 boolean_operator (str: STRING; idx: INTEGER): STRING -- Get boolean operator in `str' at `idx'. do if idx > str.count then Result := profiler_end_of_query elseif str [idx] = 'o' and then str [idx + 1] = 'r' then Result := profiler_or elseif str [idx] = 'a' and then str [idx + 1] = 'n' and then str [idx + 2] = 'd' then Result := profiler_and else Result := Void end end white_space_length (str: STRING; idx: INTEGER): INTEGER -- Length of white space starting at `idx' in `str' do from Result := 0 until idx + Result > str.count or else not chis_space ( str.item( idx + Result ) ) loop Result := Result + 1 end end has_range_operator (str: STRING; idx: INTEGER): BOOLEAN -- Is there a '-' at or after `idx' in `str'? do Result := str [idx] = '-' end stricly_positive_min (a, b, c : INTEGER): INTEGER -- The min of a, b and c that is not zero -- a < c and b < c do if a > 0 then if b > 0 then if a < b then Result := a else Result := b end else Result := a end else if b > 0 then Result := b else Result := c end end end feature {NONE} -- Externals chis_space (c: CHARACTER): BOOLEAN external "C [macro %"ctype.h%"] (char): EIF_CHARACTER" alias "isspace" end feature {NONE} -- Attributes expects_int, -- The expected type of the subquery 'value' is a integer expects_real, -- The expected type of the subquery 'value' is a real expects_string, -- The expected type of the subquery 'value' is a string expects_bounded: BOOLEAN; -- The expected type of the subquery 'value' is a bounded value note copyright: "Copyright (c) 1984-2021, Eiffel Software" license: "GPL version 2 (see http://www.eiffel.com/licensing/gpl.txt)" licensing_options: "http://www.eiffel.com/licensing" copying: "[ This file is part of Eiffel Software's Eiffel Development Environment. Eiffel Software's Eiffel Development Environment is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2 of the License (available at the URL listed under "license" above). Eiffel Software's Eiffel Development Environment is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Eiffel Software's Eiffel Development Environment; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ]" 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