(********************************************************************)
(*                                                                  *)
(*  idxarray.s7i  Support for arrays with non-integer index         *)
(*  Copyright (C) 1989 - 2012  Thomas Mertes                        *)
(*                                                                  *)
(*  This file is part of the Seed7 Runtime Library.                 *)
(*                                                                  *)
(*  The Seed7 Runtime Library is free software; you can             *)
(*  redistribute it and/or modify it under the terms of the GNU     *)
(*  Lesser General Public License as published by the Free Software *)
(*  Foundation; either version 2.1 of the License, or (at your      *)
(*  option) any later version.                                      *)
(*                                                                  *)
(*  The Seed7 Runtime Library 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 Lesser General Public License for more    *)
(*  details.                                                        *)
(*                                                                  *)
(*  You should have received a copy of the GNU Lesser General       *)
(*  Public License along with this program; if not, write to the    *)
(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
(*                                                                  *)
(********************************************************************)


(**
 *  Abstract data type, describing resizable arrays with ''indexType'' index.
 *)
const func type: array [ (in type: indexType) ] (in type: baseType) is func
  result
    var type: arrayType is void;
  local
    var type: stdArrayType is void;
    var type: tupleType is void;
  begin
    arrayType := get_type(getfunc(array [ (attr indexType) ] (attr baseType)));
    if arrayType = void then
      global
      arrayType := newtype;
      IN_PARAM_IS_REFERENCE(arrayType);
      stdArrayType := array baseType;
      tupleType := tuple(baseType);
      const type: array [ (attr indexType) ] (attr baseType)                  is arrayType;
      const type: base_type (attr arrayType)                                  is baseType;

      const proc: (ref arrayType: dest) ::= (in arrayType: source)            is action "ARR_CREATE";
      const proc: destroy (ref arrayType: aValue)                             is action "ARR_DESTR";
      const proc: (inout arrayType: dest) := (in arrayType: source)           is action "ARR_CPY";

      (**
       *  Append the array ''extension'' to the array ''arr''.
       *  @exception MEMORY_ERROR Not enough memory for the concatenated
       *             array.
       *)
      const proc: (inout arrayType: arr) &:= (in arrayType: extension)        is action "ARR_APPEND";

      (**
       *  Append the given ''element'' to the array ''arr''.
       *  @exception MEMORY_ERROR Not enough memory for the concatenated
       *             array.
       *)
      const proc: (inout arrayType: arr) &:= (in baseType: element)           is action "ARR_PUSH";

      (**
       *  Concatenate two arrays.
       *  @return the result of the concatenation.
       *)
      const func arrayType: (in arrayType: arr1) & (in arrayType: arr2)       is action "ARR_CAT";

      (**
       *  Determine the length of the array ''arr''.
       *   length(['a'] (1, 2, 3))  returns  3
       *  @return the length of the array.
       *)
      const func integer: length (in arrayType: arr)                          is action "ARR_LNG";

      (**
       *  Minimum index of array ''arr'' converted to [[integer]].
       *   minIntIdx(['a'] (1, 2, 3))  returns  97
       *  @return the minimum index of the array converted to [[integer]].
       *)
      const func integer: minIntIdx (in arrayType: arr)                       is action "ARR_MINIDX";

      (**
       *  Maximum index of array ''arr'' converted to [[integer]].
       *   maxIntIdx(['a'] (1, 2, 3))  returns  99
       *  @return the maximum index of the array converted to [[integer]].
       *)
      const func integer: maxIntIdx (in arrayType: arr)                       is action "ARR_MAXIDX";

      const func stdArrayType: (attr stdArrayType) conv (in arrayType: arr)   is action "ARR_CONV";
      const func arrayType: (attr arrayType) conv (in stdArrayType: arr)      is action "ARR_CONV";
      const func tupleType: (attr tupleType) conv (in arrayType: arr)         is action "ARR_CONV";
      const varfunc stdArrayType: (attr stdArrayType) conv (inout arrayType: arr) is action "TYP_VARCONV";
      const func arrayType: (attr arrayType) . _GENERATE_EMPTY_ARRAY          is action "ARR_EMPTY";
      const arrayType: (attr arrayType) . value                               is arrayType._GENERATE_EMPTY_ARRAY;

      const func arrayType: [ (attr indexType) ] (in tupleType: arr_tuple) is
        return arrayType conv ([ord(indexType.value)] arr_tuple);

      const func arrayType: [ (attr indexType) ] (in baseType: base_elem) is
        return arrayType conv ([ord(indexType.value)] base_elem);

      const func arrayType: [ (in indexType: startIndex) ] (in tupleType: arr_tuple) is
        return arrayType conv ([ord(startIndex)] arr_tuple);

      const func arrayType: [ (in indexType: startIndex) ] (in baseType: base_elem) is
        return arrayType conv ([ord(startIndex)] base_elem);

      (**
       *  Access one element from the array ''an_array''.
       *  @return the element with the specified ''index'' from ''an_array''.
       *  @exception INDEX_ERROR If ''index'' is less than [[#minIdx(in_arrayType)|minIdx]](an_array) or
       *                         greater than [[#minIdx(in_arrayType)|maxIdx]](an_array)
       *)
      const func baseType: (in arrayType: an_array) [ (in indexType: index) ] is
        baseType return stdArrayType conv an_array[ord(index)];

      const varfunc baseType: (inout arrayType: an_array) [ (in indexType: index) ] is
        baseType return var stdArrayType conv an_array[ord(index)];

      (**
       *  Get a sub array beginning at the position ''start''.
       *  @return the sub array beginning at the start position.
       *  @exception MEMORY_ERROR Not enough memory to represent the result.
       *)
      const func arrayType: (in arrayType: an_array) [ (in indexType: start) .. ] is
        return arrayType conv (stdArrayType conv an_array[ord(start) .. ]);

      (**
       *  Get a sub array ending at the position ''stop''.
       *  @return the sub array ending at the stop position.
       *  @exception MEMORY_ERROR Not enough memory to represent the result.
       *)
      const func arrayType: (in arrayType: an_array) [ .. (in indexType: stop) ] is
        return arrayType conv (stdArrayType conv an_array[ .. ord(stop)]);

      (**
       *  Get a sub array from the position ''start'' to the position ''stop''.
       *  @return the sub array from position ''start'' to ''stop''.
       *  @exception MEMORY_ERROR Not enough memory to represent the result.
       *)
      const func arrayType: (in arrayType: an_array) [ (in indexType: start) .. (in indexType: stop) ] is
        return arrayType conv (stdArrayType conv an_array[ord(start) .. ord(stop)]);

      (**
       *  Get a sub array from the position ''start'' with maximum length ''len''.
       *  @return the sub array from position ''start'' with maximum length ''len''.
       *  @exception MEMORY_ERROR Not enough memory to represent the result.
       *)
      const func arrayType: (in arrayType: an_array) [ (in indexType: start) len (in indexType: stop) ] is
        return arrayType conv (stdArrayType conv an_array[ord(start) len ord(stop)]);

      (**
       *  Minimum index of array ''arr''.
       *   minIdx(['a'] (1, 2, 3))  returns  'a'
       *  @return the minimum index of the array.
       *)
      const func indexType: minIdx (in arrayType: arr) is
        return indexType conv minIntIdx(arr);

      (**
       *  Maximum index of array ''arr''.
       *   maxIdx(['a'] (1, 2, 3))  returns  'c'
       *  @return the maximum index of the array.
       *)
      const func indexType: maxIdx (in arrayType: arr) is
        return indexType conv maxIntIdx(arr);

      (**
       *  For-loop where ''forVar'' loops over the elements of the array ''arr''.
       *)
      const proc: for (inout baseType: forVar) range (in arrayType: arr) do
                    (in proc: statements)
                  end for is func
        local
          var integer: number is 0;
        begin
          for number range minIntIdx(arr) to maxIntIdx(arr) do
            forVar := stdArrayType conv arr[number];
            statements;
          end for;
        end func;

      (**
       *  For-loop where ''keyVar'' loops over the indices of the array ''arr''.
       *)
      const proc: for key (inout indexType: keyVar) range (in arrayType: arr) do
                    (in proc: statements)
                  end for is func
        begin
          for keyVar range minIdx(arr) to maxIdx(arr) do
            statements;
          end for;
        end func;

      (**
       *  For-loop where ''forVar'' and ''keyVar' loop over the array ''arr''.
       *)
      const proc: for (inout baseType: forVar) key (inout indexType: keyVar) range (in arrayType: arr) do
                    (in proc: statements)
                  end for is func
        begin
          for keyVar range minIdx(arr) to maxIdx(arr) do
            forVar := stdArrayType conv arr[ord(keyVar)];
            statements;
          end for;
        end func;

      if getobj((attr indexType) . first) <> NIL and
          getobj((attr indexType) . last) <> NIL then

        const func arrayType: (attr indexType) times (in baseType: base_value) is func
          result
            var arrayType: anArray is arrayType.value;
          begin
            anArray := arrayType conv (succ(ord(indexType.last) - ord(indexType.first)) times base_value);
            anArray := arrayType conv ([ord(indexType.first)] (tupleType conv anArray));
          end func;

      end if;
      end global;
    end if;
  end func;


const func type: array [ (attr integer) ] (in type: baseType) is
  return array baseType;