
(********************************************************************)
(*                                                                  *)
(*  arr_act.s7i   Generate code for actions of the type array.      *)
(*  Copyright (C) 1990 - 1994, 2004 - 2015  Thomas Mertes           *)
(*                                                                  *)
(*  This file is part of the Seed7 compiler.                        *)
(*                                                                  *)
(*  This program 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; either version 2 of  *)
(*  the License, or (at your option) any later version.             *)
(*                                                                  *)
(*  This program 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 this program; if not, write to the           *)
(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
(*                                                                  *)
(********************************************************************)


const ACTION: ARR_APPEND    is action "ARR_APPEND";
const ACTION: ARR_ARRLIT    is action "ARR_ARRLIT";
const ACTION: ARR_ARRLIT2   is action "ARR_ARRLIT2";
const ACTION: ARR_BASELIT   is action "ARR_BASELIT";
const ACTION: ARR_BASELIT2  is action "ARR_BASELIT2";
const ACTION: ARR_CAT       is action "ARR_CAT";
const ACTION: ARR_CONV      is action "ARR_CONV";
const ACTION: ARR_CPY       is action "ARR_CPY";
const ACTION: ARR_CREATE    is action "ARR_CREATE";
const ACTION: ARR_DESTR     is action "ARR_DESTR";
const ACTION: ARR_EXTEND    is action "ARR_EXTEND";
const ACTION: ARR_GEN       is action "ARR_GEN";
const ACTION: ARR_HEAD      is action "ARR_HEAD";
const ACTION: ARR_IDX       is action "ARR_IDX";
const ACTION: ARR_LNG       is action "ARR_LNG";
const ACTION: ARR_MAXIDX    is action "ARR_MAXIDX";
const ACTION: ARR_MINIDX    is action "ARR_MINIDX";
const ACTION: ARR_PUSH      is action "ARR_PUSH";
const ACTION: ARR_RANGE     is action "ARR_RANGE";
const ACTION: ARR_REMOVE    is action "ARR_REMOVE";
const ACTION: ARR_SORT      is action "ARR_SORT";
const ACTION: ARR_SUBARR    is action "ARR_SUBARR";
const ACTION: ARR_TIMES     is action "ARR_TIMES";
const ACTION: ARR_TAIL      is action "ARR_TAIL";


const proc: arr_prototypes (inout file: c_prog) is func

  begin
    declareExtern(c_prog, "void        arrAppend (arrayType *const, const arrayType);");
    declareExtern(c_prog, "arrayType   arrArrlit2 (intType, arrayType);");
    declareExtern(c_prog, "arrayType   arrBaselit (const genericType);");
    declareExtern(c_prog, "arrayType   arrBaselit2 (intType, const genericType);");
    declareExtern(c_prog, "arrayType   arrCat (arrayType, const arrayType);");
    declareExtern(c_prog, "arrayType   arrExtend (arrayType, const genericType);");
    declareExtern(c_prog, "void        arrFree (arrayType);");
    declareExtern(c_prog, "arrayType   arrGen (const genericType, const genericType);");
    declareExtern(c_prog, "arrayType   arrHead (const const_arrayType, intType);");
    declareExtern(c_prog, "arrayType   arrHeadTemp (arrayType *, intType);");
    declareExtern(c_prog, "genericType arrIdxTemp (arrayType *, intType);");
    declareExtern(c_prog, "arrayType   arrMalloc (intType, intType);");
    declareExtern(c_prog, "void        arrPush (arrayType *const, const genericType);");
    declareExtern(c_prog, "arrayType   arrRange (const const_arrayType, intType, intType);");
    declareExtern(c_prog, "arrayType   arrRangeTemp (arrayType *, intType, intType);");
    declareExtern(c_prog, "arrayType   arrRealloc (arrayType, memSizeType, memSizeType);");
    declareExtern(c_prog, "genericType arrRemove (arrayType *, intType);");
    declareExtern(c_prog, "arrayType   arrSort (arrayType, compareType);");
    declareExtern(c_prog, "arrayType   arrSubarr (const const_arrayType, intType, intType);");
    declareExtern(c_prog, "arrayType   arrSubarrTemp (arrayType *, intType, intType);");
    declareExtern(c_prog, "arrayType   arrTail (const const_arrayType, intType);");
    declareExtern(c_prog, "arrayType   arrTailTemp (arrayType *, intType);");
  end func;


const proc: declare_prototype_if_necessary (in reference: function,
    inout expr_type: c_expr) is func

  local
    var ref_list: params is ref_list.EMPTY;
    var type: result_type is void;
    var type: object_type is void;
  begin
    if function not in prototype_declared then
      result_type := resultType(getType(function));
      params := formalParams(function);
      object_type := getType(params[3]);
      process_create_declaration(object_type, c_expr);
      # c_expr.expr &:= type_name(result_type);
      c_expr.expr &:= "static arrayType times_";
      c_expr.expr &:= str(typeNumber(result_type));
      c_expr.expr &:= " (intType, const ";
      if useConstPrefix(object_type) then
        c_expr.expr &:= "const_";
      end if;
      c_expr.expr &:= type_name(object_type);
      c_expr.expr &:= ");\n\n";
      prototype_declared @:= [function] TRUE;
    end if;
  end func;


const proc: process (ARR_APPEND, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var expr_type: c_param1 is expr_type.value;
    var expr_type: c_param3 is expr_type.value;
  begin
    process_expr(params[1], c_param1);
    c_param3.temp_num := c_param1.temp_num;
    getTemporaryToResultExpr(params[3], c_param3);
    incr(c_param3.temp_num);
    if has_temp_values(c_param3) then
      c_expr.expr &:= "{\n";
      appendWithDiagnostic(c_param1.temp_decls, c_expr);
      appendWithDiagnostic(c_param3.temp_decls, c_expr);
      appendWithDiagnostic(c_param1.temp_assigns, c_expr);
      appendWithDiagnostic(c_param3.temp_assigns, c_expr);
    end if;
    setDiagnosticLine(c_expr);
    c_expr.expr &:= "arrAppend(&(";
    c_expr.expr &:= c_param1.expr;
    c_expr.expr &:= "), ";
    c_expr.expr &:= c_param3.result_expr;
    c_expr.expr &:= ");\n";
    if has_temp_values(c_param3) then
      appendWithDiagnostic(c_param1.temp_frees, c_expr);
      appendWithDiagnostic(c_param3.temp_frees, c_expr);
      c_expr.expr &:= "}\n";
    end if;
  end func;


const proc: process (ARR_ARRLIT, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var reference: anArray is NIL;
    var expr_type: c_param is expr_type.value;
  begin
    if isConstant(params[3]) then
      anArray := evaluate(prog, params[3]);
      if category(anArray) = ARRAYOBJECT then
        if anArray not in const_table then
          const_table @:= [anArray] length(const_table);
        end if;
        c_expr.expr &:= "arr[";
        c_expr.expr &:= str(const_table[anArray]);
        c_expr.expr &:= "]";
      end if;
    else
      prepareAnyParamTemporarys(params[3], c_param, c_expr);
      if c_param.result_expr <> "" then
        prepare_typed_result(getExprResultType(params[3]), c_expr);
        c_expr.result_expr := "/*arrArrlit()*/";
        c_expr.result_expr &:= c_param.result_expr;
      else
        c_expr.expr &:= "/*arrArrlit()*/";
        c_expr.expr &:= c_param.expr;
      end if;
    end if;
  end func;


const proc: process (ARR_ARRLIT2, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  begin
    prepare_typed_result(getExprResultType(params[4]), c_expr);
    c_expr.result_expr := "arrArrlit2(";
    getStdParamToResultExpr(params[2], c_expr);
    c_expr.result_expr &:= ", ";
    getTemporaryToResultExpr(params[4], c_expr);
    c_expr.result_expr &:= ")";
  end func;


const proc: process (ARR_BASELIT, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  begin
    if resultType(getType(function)) not in array_element then
      array_element @:= [resultType(getType(function))] getType(params[3]);
    end if;
    prepare_typed_result(resultType(getType(function)), c_expr);
    c_expr.result_expr := "arrBaselit((genericType)(";
    getGenericTemporaryToResultExpr(params[3], c_expr);
    c_expr.result_expr &:= "))";
  end func;


const proc: process (ARR_BASELIT2, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  begin
    if resultType(getType(function)) not in array_element then
      array_element @:= [resultType(getType(function))] getType(params[4]);
    end if;
    prepare_typed_result(resultType(getType(function)), c_expr);
    c_expr.result_expr := "arrBaselit2(";
    getStdParamToResultExpr(params[2], c_expr);
    c_expr.result_expr &:= ", (genericType)(";
    getGenericTemporaryToResultExpr(params[4], c_expr);
    c_expr.result_expr &:= "))";
  end func;


const proc: process (ARR_CAT, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  begin
    prepare_typed_result(getExprResultType(params[1]), c_expr);
    c_expr.result_expr := "arrCat(";
    getTemporaryToResultExpr(params[1], c_expr);
    c_expr.result_expr &:= ", ";
    getTemporaryToResultExpr(params[3], c_expr);
    c_expr.result_expr &:= ")";
  end func;


const proc: process (ARR_CONV, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var expr_type: c_param is expr_type.value;
  begin
    prepareAnyParamTemporarys(params[3], c_param, c_expr);
    if c_param.result_expr <> "" then
      prepare_typed_result(getExprResultType(params[3]), c_expr);
      c_expr.result_expr := "/*arrConv()*/";
      c_expr.result_expr &:= c_param.result_expr;
    else
      c_expr.expr &:= "/*arrConv()*/";
      c_expr.expr &:= c_param.expr;
    end if;
  end func;


const proc: process (ARR_CPY, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var expr_type: c_param1 is expr_type.value;
    var expr_type: c_param3 is expr_type.value;
  begin
    process_expr(params[1], c_param1);
    c_param3.temp_num := c_param1.temp_num;
    process_expr(params[3], c_param3);
    if has_temp_values(c_param3) then
      setDiagnosticLine(c_expr);
      c_expr.expr &:= "{\n";
      appendWithDiagnostic(c_param1.temp_decls, c_expr);
      appendWithDiagnostic(c_param3.temp_decls, c_expr);
      appendWithDiagnostic(c_param1.temp_assigns, c_expr);
      appendWithDiagnostic(c_param3.temp_assigns, c_expr);
    end if;
    if c_param3.result_expr <> "" then
      setDiagnosticLine(c_expr);
      c_expr.expr &:= "{\n";
      setDiagnosticLine(c_expr);
      if isNormalVariable(params[1]) then
        c_expr.expr &:= "arrayType old_array=";
        c_expr.expr &:= c_param1.expr;
        c_expr.expr &:= ";\n";
        setDiagnosticLine(c_expr);
        c_expr.expr &:= c_param1.expr;
        c_expr.expr &:= "=";
      else
        c_expr.expr &:= "arrayType *array_ptr=&(";
        c_expr.expr &:= c_param1.expr;
        c_expr.expr &:= ");\n";
        setDiagnosticLine(c_expr);
        c_expr.expr &:= "arrayType old_array=*array_ptr;\n";
        setDiagnosticLine(c_expr);
        c_expr.expr &:= "*array_ptr=";
      end if;
      c_expr.expr &:= c_param3.result_expr;
      c_expr.expr &:= ";\n";
      process_destr_declaration(getExprResultType(params[1]), global_c_expr);
      setDiagnosticLine(c_expr);
      process_destr_call(getExprResultType(params[1]),
          "old_array", c_expr.expr);
      setDiagnosticLine(c_expr);
      c_expr.expr &:= "}\n";
    else
      process_cpy_declaration(getExprResultType(params[1]), global_c_expr);
      setDiagnosticLine(c_expr);
      process_cpy_call(getExprResultType(params[1]),
          c_param1.expr, c_param3.expr, c_expr.expr);
      c_expr.expr &:= ";\n";
    end if;
    if has_temp_values(c_param3) then
      appendWithDiagnostic(c_param1.temp_frees, c_expr);
      appendWithDiagnostic(c_param3.temp_frees, c_expr);
      setDiagnosticLine(c_expr);
      c_expr.expr &:= "}\n";
    end if;
  end func;


const proc: process (ARR_CREATE, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var type: param_type is void;
  begin
    param_type := getExprResultType(params[3]);
    typeCategory @:= [param_type] ARRAYOBJECT;
    process_create_declaration(param_type, global_c_expr);
    process_expr(params[1], c_expr);
    c_expr.expr &:= "=create_";
    c_expr.expr &:= str(typeNumber(param_type));
    c_expr.expr &:= "(";
    process_expr(params[3], c_expr);
    c_expr.expr &:= ");\n";
  end func;


const proc: process (ARR_DESTR, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var type: param_type is void;
  begin
    param_type := getExprResultType(params[1]);
    process_destr_declaration(param_type, global_c_expr);
    c_expr.expr &:= "destr_";
    c_expr.expr &:= str(typeNumber(param_type));
    c_expr.expr &:= "(";
    process_expr(params[1], c_expr);
    c_expr.expr &:= ");\n";
  end func;


const proc: process (ARR_EXTEND, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  begin
    prepare_typed_result(getExprResultType(params[1]), c_expr);
    c_expr.result_expr := "arrExtend(";
    getTemporaryToResultExpr(params[1], c_expr);
    c_expr.result_expr &:= ", (genericType)(";
    getGenericTemporaryToResultExpr(params[3], c_expr);
    c_expr.result_expr &:= "))";
  end func;


const proc: process (ARR_GEN, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  begin
    prepare_typed_result(resultType(getType(function)), c_expr);
    c_expr.result_expr := "arrGen((genericType)(";
    getGenericTemporaryToResultExpr(params[1], c_expr);
    c_expr.result_expr &:= "), (genericType)(";
    getGenericTemporaryToResultExpr(params[3], c_expr);
    c_expr.result_expr &:= "))";
  end func;


const proc: process (ARR_HEAD, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var type: param_type is void;
    var expr_type: c_param is expr_type.value;
    var string: array_name is "";
  begin
    param_type := getExprResultType(params[1]);
    prepare_typed_result(param_type, c_expr);
    prepareAnyParamTemporarys(params[1], c_param, c_expr);
    if c_param.result_expr <> "" then
      c_expr.temp_decls &:= c_param.result_decl;
      c_expr.temp_frees &:= c_param.result_free;
      c_expr.temp_to_null &:= c_param.result_to_null;
      c_expr.result_expr := "(";
      c_expr.result_expr &:= c_param.result_intro;
      c_expr.result_expr &:= c_param.result_expr;
      c_expr.result_expr &:= c_param.result_finish;
      c_expr.result_expr &:= ", arrHeadTemp(&(";
      c_expr.result_expr &:= c_param.result_name;
      c_expr.result_expr &:= "), ";
      getStdParamToResultExpr(params[4], c_expr);
      c_expr.result_expr &:= "))";
    elsif valueIsAtHeap(array_element[param_type]) then
      incr(c_expr.temp_num);
      array_name := "tmp_" & str(c_expr.temp_num);
      c_expr.temp_decls &:= "arrayType ";
      c_expr.temp_decls &:= array_name;
      c_expr.temp_decls &:= "=NULL;\n";
      c_expr.temp_frees &:= "if (";
      c_expr.temp_frees &:= array_name;
      c_expr.temp_frees &:= " != NULL) {arrFree(";
      c_expr.temp_frees &:= array_name;
      c_expr.temp_frees &:= ");}\n";
      c_expr.temp_to_null &:= array_name;
      c_expr.temp_to_null &:= "=NULL;\n";
      c_expr.result_expr := "(";
      c_expr.result_expr &:= array_name;
      c_expr.result_expr &:= "=arrHead(";
      c_expr.result_expr &:= c_param.expr;
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[4], c_expr);
      c_expr.result_expr &:= "), ";
      typeCategory @:= [param_type] ARRAYOBJECT;
      process_create_declaration(param_type, global_c_expr);
      process_create_call(param_type, array_name, c_expr.result_expr);
      c_expr.result_expr &:= ")";
    else
      c_expr.result_expr := "arrHead(";
      c_expr.result_expr &:= c_param.expr;
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[4], c_expr);
      c_expr.result_expr &:= ")";
    end if;
  end func;


const proc: process_const_arr_idx (in reference: function, in reference: anArray,
    in reference: param3, inout expr_type: c_expr) is func

  local
    var reference: evaluatedParam is NIL;
    var integer: index is 0;
    var string: index_name is "";
  begin
    incr(countOptimizations);
    if anArray not in const_table then
      const_table @:= [anArray] length(const_table);
    end if;
    if getConstant(param3, INTOBJECT, evaluatedParam) then
      index := getValue(evaluatedParam, integer);
      if index < arrayMinIdx(anArray) or index > arrayMaxIdx(anArray) then
        c_expr.expr &:= intRaiseError("RANGE_ERROR");
      else
        c_expr.expr &:= "arr[";
        c_expr.expr &:= str(const_table[anArray]);
        c_expr.expr &:= "]->arr[";
        c_expr.expr &:= str(index - arrayMinIdx(anArray));
        c_expr.expr &:= "]";
        c_expr.expr &:= select_value_from_rtlObjectStruct(resultType(getType(function)));
      end if;
    else
      c_expr.expr &:= "arr[";
      c_expr.expr &:= str(const_table[anArray]);
      c_expr.expr &:= "]->arr[";
      if array_range_check then
        incr(countRangeChecks);
        incr(c_expr.temp_num);
        index_name := "idx_" & str(c_expr.temp_num);
        if ccConf.TWOS_COMPLEMENT_INTTYPE then
          c_expr.temp_decls &:= "uintType ";
        else
          c_expr.temp_decls &:= "intType ";
        end if;
        c_expr.temp_decls &:= index_name;
        c_expr.temp_decls &:= ";\n";
        c_expr.expr &:= "(";
        c_expr.expr &:= index_name;
        c_expr.expr &:= "=(";
        process_expr(param3, c_expr);
        c_expr.expr &:= ")";
        if arrayMinIdx(anArray) <> 0 then
          if ccConf.TWOS_COMPLEMENT_INTTYPE then
            c_expr.expr &:= "- (uintType) ";
          else
            c_expr.expr &:= "- ";
          end if;
          c_expr.expr &:= integerLiteral(arrayMinIdx(anArray));
        end if;
        c_expr.expr &:= ", idxChk(";
        if not ccConf.TWOS_COMPLEMENT_INTTYPE then
          c_expr.expr &:= index_name;
          c_expr.expr &:= "<0 || ";
        end if;
        c_expr.expr &:= index_name;
        c_expr.expr &:= ">=";
        c_expr.expr &:= str(succ(arrayMaxIdx(anArray) - arrayMinIdx(anArray)));
        c_expr.expr &:= ") ? ";
        c_expr.expr &:= intRaiseError("RANGE_ERROR");
        c_expr.expr &:= " : 0, ";
        c_expr.expr &:= index_name;
        c_expr.expr &:= ")";
      else
        incr(countNoRangeChecks);
        c_expr.expr &:= "(";
        process_expr(param3, c_expr);
        c_expr.expr &:= ")";
        if arrayMinIdx(anArray) <> 0 then
          c_expr.expr &:= "- ";
          c_expr.expr &:= integerLiteral(arrayMinIdx(anArray));
        end if;
      end if;
      c_expr.expr &:= "]";
      c_expr.expr &:= select_value_from_rtlObjectStruct(resultType(getType(function)));
    end if;
  end func;


const proc: process_const_arr_idx (in reference: function, in reference: param1,
    in integer: index, inout expr_type: c_expr) is func

  local
    var expr_type: c_param is expr_type.value;
    var string: array_name is "";
    var string: result_name is "";
  begin
    prepareAnyParamTemporarys(param1, c_param, c_expr);
    if c_param.result_expr <> "" then
      incr(c_expr.temp_num);
      result_name := "help_" & str(c_expr.temp_num);
      c_expr.temp_decls &:= "rtlObjectType ";
      c_expr.temp_decls &:= result_name;
      c_expr.temp_decls &:= ";\n";
      c_expr.temp_decls &:= c_param.result_decl;
      c_expr.temp_frees &:= c_param.result_free;
      c_expr.temp_to_null &:= c_param.result_to_null;
      if resultType(getType(function)) in typeCategory and
          typeCategory[resultType(getType(function))] in destrNecessary then
        prepare_typed_result(resultType(getType(function)), c_expr);
        c_expr.result_expr &:= "(";
        c_expr.result_expr &:= c_param.result_intro;
        c_expr.result_expr &:= c_param.result_expr;
        c_expr.result_expr &:= c_param.result_finish;
        c_expr.result_expr &:= ", ";
        c_expr.result_expr &:= result_name;
        c_expr.result_expr &:= ".value.genericValue=arrIdxTemp(&(";
        c_expr.result_expr &:= c_param.result_name;
        c_expr.result_expr &:= "), ";
        c_expr.result_expr &:= str(index);
        c_expr.result_expr &:= "), ";
        c_expr.result_expr &:= result_name;
        c_expr.result_expr &:= select_value_from_rtlObjectStruct(resultType(getType(function)));
        c_expr.result_expr &:= ")";
      else
        c_expr.expr &:= "(";
        c_expr.expr &:= c_param.result_intro;
        c_expr.expr &:= c_param.result_expr;
        c_expr.expr &:= c_param.result_finish;
        c_expr.expr &:= ", ";
        c_expr.expr &:= result_name;
        c_expr.expr &:= ".value.genericValue=arrIdxTemp(&(";
        c_expr.expr &:= c_param.result_name;
        c_expr.expr &:= "), ";
        c_expr.expr &:= str(index);
        c_expr.expr &:= "), ";
        c_expr.expr &:= result_name;
        c_expr.expr &:= select_value_from_rtlObjectStruct(resultType(getType(function)));
        c_expr.expr &:= ")";
      end if;
    else
      incr(countOptimizations);
      if isNormalVariable(param1) then
        array_name := "(" & c_param.expr & ")";
      else
        incr(c_expr.temp_num);
        array_name := "tmp_" & str(c_expr.temp_num);
        if not isVarfunc(getType(function)) then
          c_expr.temp_decls &:= "const_";
        end if;
        c_expr.temp_decls &:= "arrayType ";
        c_expr.temp_decls &:= array_name;
        c_expr.temp_decls &:= ";\n";
        c_expr.expr &:= "(*(";
        c_expr.expr &:= array_name;
        c_expr.expr &:= "=";
        c_expr.expr &:= c_param.expr;
        c_expr.expr &:= ", &";
      end if;
      c_expr.expr &:= array_name;
      c_expr.expr &:= "->arr[";
      if array_range_check then
        incr(countRangeChecks);
        c_expr.expr &:= "(idxChk(";
        c_expr.expr &:= str(index);
        c_expr.expr &:= "<";
        c_expr.expr &:= array_name;
        c_expr.expr &:= "->min_position || ";
        c_expr.expr &:= str(index);
        c_expr.expr &:= ">";
        c_expr.expr &:= array_name;
        c_expr.expr &:= "->max_position) ? ";
        c_expr.expr &:= intRaiseError("RANGE_ERROR");
        c_expr.expr &:= " : 0, ";
        c_expr.expr &:= str(index);
        c_expr.expr &:= "-";
        c_expr.expr &:= array_name;
        c_expr.expr &:= "->min_position)";
      else
        incr(countNoRangeChecks);
        c_expr.expr &:= str(index);
        c_expr.expr &:= "-";
        c_expr.expr &:= array_name;
        c_expr.expr &:= "->min_position";
      end if;
      c_expr.expr &:= "]";
      c_expr.expr &:= select_value_from_rtlObjectStruct(resultType(getType(function)));
      if not isNormalVariable(param1) then
        c_expr.expr &:= "))";
      end if;
    end if;
  end func;


const proc: process (ARR_IDX, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var reference: evaluatedParam is NIL;
    var expr_type: c_param is expr_type.value;
    var string: array_name is "";
    var string: index_name is "";
    var string: result_name is "";
  begin
    if getType(params[1]) not in array_element then
      array_element @:= [getType(params[1])] resultType(getType(function));
    end if;
    if getConstant(params[1], ARRAYOBJECT, evaluatedParam) then
      process_const_arr_idx(function, evaluatedParam, params[3], c_expr);
    elsif getConstant(params[3], INTOBJECT, evaluatedParam) then
      process_const_arr_idx(function, params[1], getValue(evaluatedParam, integer), c_expr);
    else
      prepareAnyParamTemporarys(params[1], c_param, c_expr);
      if c_param.result_expr <> "" then
        incr(c_expr.temp_num);
        result_name := "help_" & str(c_expr.temp_num);
        c_expr.temp_decls &:= "rtlObjectType ";
        c_expr.temp_decls &:= result_name;
        c_expr.temp_decls &:= ";\n";
        c_expr.temp_decls &:= c_param.result_decl;
        c_expr.temp_frees &:= c_param.result_free;
        c_expr.temp_to_null &:= c_param.result_to_null;
        if resultType(getType(function)) in typeCategory and
            typeCategory[resultType(getType(function))] in destrNecessary then
          prepare_typed_result(resultType(getType(function)), c_expr);
          c_expr.result_expr &:= "(";
          c_expr.result_expr &:= c_param.result_intro;
          c_expr.result_expr &:= c_param.result_expr;
          c_expr.result_expr &:= c_param.result_finish;
          c_expr.result_expr &:= ", ";
          c_expr.result_expr &:= result_name;
          c_expr.result_expr &:= ".value.genericValue=arrIdxTemp(&(";
          c_expr.result_expr &:= c_param.result_name;
          c_expr.result_expr &:= "), ";
          getAnyParamToResultExpr(params[3], c_expr);
          c_expr.result_expr &:= "), ";
          c_expr.result_expr &:= result_name;
          c_expr.result_expr &:= select_value_from_rtlObjectStruct(resultType(getType(function)));
          c_expr.result_expr &:= ")";
        else
          c_expr.expr &:= "(";
          c_expr.expr &:= c_param.result_intro;
          c_expr.expr &:= c_param.result_expr;
          c_expr.expr &:= c_param.result_finish;
          c_expr.expr &:= ", ";
          c_expr.expr &:= result_name;
          c_expr.expr &:= ".value.genericValue=arrIdxTemp(&(";
          c_expr.expr &:= c_param.result_name;
          c_expr.expr &:= "), ";
          process_expr(params[3], c_expr);
          c_expr.expr &:= "), ";
          c_expr.expr &:= result_name;
          c_expr.expr &:= select_value_from_rtlObjectStruct(resultType(getType(function)));
          c_expr.expr &:= ")";
        end if;
      else
        if isNormalVariable(params[1]) then
          array_name := "(" & c_param.expr & ")";
        else
          incr(c_expr.temp_num);
          array_name := "tmp_" & str(c_expr.temp_num);
          if not isVarfunc(getType(function)) then
            c_expr.temp_decls &:= "const_";
          end if;
          c_expr.temp_decls &:= "arrayType ";
          c_expr.temp_decls &:= array_name;
          c_expr.temp_decls &:= ";\n";
          c_expr.expr &:= "(*(";
          c_expr.expr &:= array_name;
          c_expr.expr &:= "=";
          c_expr.expr &:= c_param.expr;
          c_expr.expr &:= ", &";
        end if;
        c_expr.expr &:= array_name;
        c_expr.expr &:= "->arr[";
        if array_range_check then
          incr(countRangeChecks);
          index_name := getParameterAsVariable("intType", "idx_", params[3], c_expr);
          c_expr.expr &:= "(idxChk(";
          c_expr.expr &:= index_name;
          c_expr.expr &:= " < ";
          c_expr.expr &:= array_name;
          c_expr.expr &:= "->min_position || ";
          c_expr.expr &:= index_name;
          c_expr.expr &:= " > ";
          c_expr.expr &:= array_name;
          c_expr.expr &:= "->max_position) ? ";
          c_expr.expr &:= intRaiseError("RANGE_ERROR");
          c_expr.expr &:= " : 0, ";
          c_expr.expr &:= index_name;
          c_expr.expr &:= "-";
          c_expr.expr &:= array_name;
          c_expr.expr &:= "->min_position)";
        else
          incr(countNoRangeChecks);
          c_expr.expr &:= "(";
          process_expr(params[3], c_expr);
          c_expr.expr &:= ")-";
          c_expr.expr &:= array_name;
          c_expr.expr &:= "->min_position";
        end if;
        c_expr.expr &:= "]";
        c_expr.expr &:= select_value_from_rtlObjectStruct(resultType(getType(function)));
        if not isNormalVariable(params[1]) then
          c_expr.expr &:= "))";
        end if;
      end if;
    end if;
  end func;


const proc: process (ARR_LNG, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var reference: evaluatedParam is NIL;
    var string: array_name is "";
  begin
    if getConstant(params[1], ARRAYOBJECT, evaluatedParam) then
      incr(countOptimizations);
      c_expr.expr &:= str(arrayMaxIdx(evaluatedParam) - arrayMinIdx(evaluatedParam) + 1);
      if evaluatedParam in const_table then
        c_expr.expr &:= " /* length(arr[";
        c_expr.expr &:= str(const_table[evaluatedParam]);
        c_expr.expr &:= "]) */";
      else
        c_expr.expr &:= " /* length(array) */";
      end if;
    else
      c_expr.expr &:= "(";
      array_name := getParameterAsVariable("const_arrayType", "tmp_", params[1], c_expr);
      c_expr.expr &:= array_name;
      c_expr.expr &:= "->max_position - ";
      c_expr.expr &:= array_name;
      c_expr.expr &:= "->min_position + 1)";
    end if;
  end func;


const proc: process (ARR_MAXIDX, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var reference: evaluatedParam is NIL;
  begin
    if getConstant(params[1], ARRAYOBJECT, evaluatedParam) then
      incr(countOptimizations);
      c_expr.expr &:= str(arrayMaxIdx(evaluatedParam));
      if evaluatedParam in const_table then
        c_expr.expr &:= " /* maxIdx(arr[";
        c_expr.expr &:= str(const_table[evaluatedParam]);
        c_expr.expr &:= "]) */";
      else
        c_expr.expr &:= " /* maxIdx(array) */";
      end if;
    else
      c_expr.expr &:= "(";
      getAnyParamToExpr(params[1], c_expr);
      c_expr.expr &:= ")->max_position";
    end if;
  end func;


const proc: process (ARR_MINIDX, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var reference: evaluatedParam is NIL;
  begin
    if getConstant(params[1], ARRAYOBJECT, evaluatedParam) then
      incr(countOptimizations);
      c_expr.expr &:= str(arrayMinIdx(evaluatedParam));
      if evaluatedParam in const_table then
        c_expr.expr &:= " /* minIdx(arr[";
        c_expr.expr &:= str(const_table[evaluatedParam]);
        c_expr.expr &:= "]) */";
      else
        c_expr.expr &:= " /* minIdx(array) */";
      end if;
    else
      c_expr.expr &:= "(";
      getAnyParamToExpr(params[1], c_expr);
      c_expr.expr &:= ")->min_position";
    end if;
  end func;


const proc: process (ARR_PUSH, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var expr_type: c_param1 is expr_type.value;
    var expr_type: c_param3 is expr_type.value;
  begin
    if getType(params[1]) not in array_element then
      array_element @:= [getType(params[1])] getType(params[3]);
    end if;
    process_expr(params[1], c_param1);
    c_param3.temp_num := c_param1.temp_num;
    getGenericTemporaryToResultExpr(params[3], c_param3);
    incr(c_param3.temp_num);
    if has_temp_values(c_param3) then
      c_expr.expr &:= "{\n";
      appendWithDiagnostic(c_param1.temp_decls, c_expr);
      appendWithDiagnostic(c_param3.temp_decls, c_expr);
      appendWithDiagnostic(c_param1.temp_assigns, c_expr);
      appendWithDiagnostic(c_param3.temp_assigns, c_expr);
    end if;
    setDiagnosticLine(c_expr);
    c_expr.expr &:= "arrPush(&(";
    c_expr.expr &:= c_param1.expr;
    c_expr.expr &:= "), (genericType)(";
    c_expr.expr &:= c_param3.result_expr;
    c_expr.expr &:= "));\n";
    if has_temp_values(c_param3) then
      appendWithDiagnostic(c_param1.temp_frees, c_expr);
      appendWithDiagnostic(c_param3.temp_frees, c_expr);
      c_expr.expr &:= "}\n";
    end if;
  end func;


const proc: process (ARR_RANGE, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var type: param_type is void;
    var expr_type: c_param is expr_type.value;
    var string: array_name is "";
  begin
    param_type := getExprResultType(params[1]);
    prepare_typed_result(param_type, c_expr);
    prepareAnyParamTemporarys(params[1], c_param, c_expr);
    if c_param.result_expr <> "" then
      c_expr.temp_decls &:= c_param.result_decl;
      c_expr.temp_frees &:= c_param.result_free;
      c_expr.temp_to_null &:= c_param.result_to_null;
      c_expr.result_expr := "(";
      c_expr.result_expr &:= c_param.result_intro;
      c_expr.result_expr &:= c_param.result_expr;
      c_expr.result_expr &:= c_param.result_finish;
      c_expr.result_expr &:= ", arrRangeTemp(&(";
      c_expr.result_expr &:= c_param.result_name;
      c_expr.result_expr &:= "), ";
      getStdParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[5], c_expr);
      c_expr.result_expr &:= "))";
    elsif valueIsAtHeap(array_element[param_type]) then
      incr(c_expr.temp_num);
      array_name := "tmp_" & str(c_expr.temp_num);
      c_expr.temp_decls &:= "arrayType ";
      c_expr.temp_decls &:= array_name;
      c_expr.temp_decls &:= "=NULL;\n";
      c_expr.temp_frees &:= "if (";
      c_expr.temp_frees &:= array_name;
      c_expr.temp_frees &:= " != NULL) {arrFree(";
      c_expr.temp_frees &:= array_name;
      c_expr.temp_frees &:= ");}\n";
      c_expr.temp_to_null &:= array_name;
      c_expr.temp_to_null &:= "=NULL;\n";
      c_expr.result_expr := "(";
      c_expr.result_expr &:= array_name;
      c_expr.result_expr &:= "=arrRange(";
      c_expr.result_expr &:= c_param.expr;
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[5], c_expr);
      c_expr.result_expr &:= "), ";
      typeCategory @:= [param_type] ARRAYOBJECT;
      process_create_declaration(param_type, global_c_expr);
      process_create_call(param_type, array_name, c_expr.result_expr);
      c_expr.result_expr &:= ")";
    else
      c_expr.result_expr := "arrRange(";
      c_expr.result_expr &:= c_param.expr;
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[5], c_expr);
      c_expr.result_expr &:= ")";
    end if;
  end func;


const proc: process (ARR_REMOVE, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var type: proc_type is void;
    var type: result_type is void;
    var string: temp_name is "";
  begin
    proc_type := getType(function);
    result_type := resultType(proc_type);
    if valueIsAtHeap(result_type) then
      prepare_typed_result(result_type, c_expr);
      temp_name := beginCastGenericToResultExpr(result_type, c_expr);
      c_expr.result_expr &:= "arrRemove(&(";
      getStdParamToResultExpr(params[1], c_expr);
      c_expr.result_expr &:= "), ";
      getStdParamToResultExpr(params[2], c_expr);
      c_expr.result_expr &:= ")";
      endCastGenericToResultExpr(result_type, temp_name, c_expr);
    else
      temp_name := beginCastGeneric(result_type, c_expr);
      c_expr.expr &:= "arrRemove(&(";
      process_expr(params[1], c_expr);
      c_expr.expr &:= "), ";
      process_expr(params[2], c_expr);
      c_expr.expr &:= ")";
      endCastGeneric(result_type, temp_name, c_expr);
    end if;
  end func;


const proc: process (ARR_SORT, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  begin
    declare_func_pointer_if_necessary(params[2], global_c_expr);
    prepare_typed_result(getExprResultType(params[1]), c_expr);
    c_expr.result_expr := "arrSort(";
    getTemporaryToResultExpr(params[1], c_expr);
    c_expr.result_expr &:= ", (compareType)(";
    getStdParamToResultExpr(params[2], c_expr);
    c_expr.result_expr &:= "))";
  end func;


const proc: process (ARR_SUBARR, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var type: param_type is void;
    var expr_type: c_param is expr_type.value;
    var string: array_name is "";
  begin
    param_type := getExprResultType(params[1]);
    prepare_typed_result(param_type, c_expr);
    prepareAnyParamTemporarys(params[1], c_param, c_expr);
    if c_param.result_expr <> "" then
      c_expr.temp_decls &:= c_param.result_decl;
      c_expr.temp_frees &:= c_param.result_free;
      c_expr.temp_to_null &:= c_param.result_to_null;
      c_expr.result_expr := "(";
      c_expr.result_expr &:= c_param.result_intro;
      c_expr.result_expr &:= c_param.result_expr;
      c_expr.result_expr &:= c_param.result_finish;
      c_expr.result_expr &:= ", arrSubarrTemp(&(";
      c_expr.result_expr &:= c_param.result_name;
      c_expr.result_expr &:= "), ";
      getStdParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[5], c_expr);
      c_expr.result_expr &:= "))";
    elsif valueIsAtHeap(array_element[param_type]) then
      incr(c_expr.temp_num);
      array_name := "tmp_" & str(c_expr.temp_num);
      c_expr.temp_decls &:= "arrayType ";
      c_expr.temp_decls &:= array_name;
      c_expr.temp_decls &:= "=NULL;\n";
      c_expr.temp_frees &:= "if (";
      c_expr.temp_frees &:= array_name;
      c_expr.temp_frees &:= " != NULL) {arrFree(";
      c_expr.temp_frees &:= array_name;
      c_expr.temp_frees &:= ");}\n";
      c_expr.temp_to_null &:= array_name;
      c_expr.temp_to_null &:= "=NULL;\n";
      c_expr.result_expr := "(";
      c_expr.result_expr &:= array_name;
      c_expr.result_expr &:= "=arrSubarr(";
      c_expr.result_expr &:= c_param.expr;
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[5], c_expr);
      c_expr.result_expr &:= "), ";
      typeCategory @:= [param_type] ARRAYOBJECT;
      process_create_declaration(param_type, global_c_expr);
      process_create_call(param_type, array_name, c_expr.result_expr);
      c_expr.result_expr &:= ")";
    else
      c_expr.result_expr := "arrSubarr(";
      c_expr.result_expr &:= c_param.expr;
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[5], c_expr);
      c_expr.result_expr &:= ")";
    end if;
  end func;


const proc: process (ARR_TAIL, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var type: param_type is void;
    var expr_type: c_param is expr_type.value;
    var string: array_name is "";
  begin
    param_type := getExprResultType(params[1]);
    prepare_typed_result(param_type, c_expr);
    prepareAnyParamTemporarys(params[1], c_param, c_expr);
    if c_param.result_expr <> "" then
      c_expr.temp_decls &:= c_param.result_decl;
      c_expr.temp_frees &:= c_param.result_free;
      c_expr.temp_to_null &:= c_param.result_to_null;
      c_expr.result_expr := "(";
      c_expr.result_expr &:= c_param.result_intro;
      c_expr.result_expr &:= c_param.result_expr;
      c_expr.result_expr &:= c_param.result_finish;
      c_expr.result_expr &:= ", arrTailTemp(&(";
      c_expr.result_expr &:= c_param.result_name;
      c_expr.result_expr &:= "), ";
      getStdParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= "))";
    elsif valueIsAtHeap(array_element[param_type]) then
      incr(c_expr.temp_num);
      array_name := "tmp_" & str(c_expr.temp_num);
      c_expr.temp_decls &:= "arrayType ";
      c_expr.temp_decls &:= array_name;
      c_expr.temp_decls &:= "=NULL;\n";
      c_expr.temp_frees &:= "if (";
      c_expr.temp_frees &:= array_name;
      c_expr.temp_frees &:= " != NULL) {arrFree(";
      c_expr.temp_frees &:= array_name;
      c_expr.temp_frees &:= ");}\n";
      c_expr.temp_to_null &:= array_name;
      c_expr.temp_to_null &:= "=NULL;\n";
      c_expr.result_expr := "(";
      c_expr.result_expr &:= array_name;
      c_expr.result_expr &:= "=arrTail(";
      c_expr.result_expr &:= c_param.expr;
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= "), ";
      typeCategory @:= [param_type] ARRAYOBJECT;
      process_create_declaration(param_type, global_c_expr);
      process_create_call(param_type, array_name, c_expr.result_expr);
      c_expr.result_expr &:= ")";
    else
      c_expr.result_expr := "arrTail(";
      c_expr.result_expr &:= c_param.expr;
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= ")";
    end if;
  end func;


const proc: process (ARR_TIMES, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var type: result_type is void;
  begin
    result_type := resultType(getType(function));
    typeCategory @:= [result_type] ARRAYOBJECT;
    declare_prototype_if_necessary(function, global_c_expr);
    prepare_typed_result(result_type, c_expr);
    c_expr.result_expr := "times_";
    c_expr.result_expr &:= str(typeNumber(result_type));
    c_expr.result_expr &:= "(";
    getStdParamToResultExpr(params[1], c_expr);
    c_expr.result_expr &:= ", ";
    getAnyParamToResultExpr(params[3], c_expr);
    c_expr.result_expr &:= ")";
  end func;
