(********************************************************************)
(*                                                                  *)
(*  enu_act.s7i   Generate code for actions of enumeration values.  *)
(*  Copyright (C) 1990 - 1994, 2004 - 2014  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: ENU_CONV    is action "ENU_CONV";
const ACTION: ENU_CPY     is action "ENU_CPY";
const ACTION: ENU_EQ      is action "ENU_EQ";
const ACTION: ENU_ICONV2  is action "ENU_ICONV2";
const ACTION: ENU_LIT     is action "ENU_LIT";
const ACTION: ENU_NE      is action "ENU_NE";
const ACTION: ENU_ORD2    is action "ENU_ORD2";
const ACTION: ENU_VALUE   is action "ENU_VALUE";

var boolean_type_hash: literal_function_of_enum_used is boolean_type_hash.EMPTY_HASH;


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

  begin
    c_expr.expr &:= "(";
    process_expr(params[3], c_expr);
    c_expr.expr &:= ")";
  end func;


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

  local
    var expr_type: statement is expr_type.value;
  begin
    process_expr(params[1], statement);
    statement.expr &:= "=";
    process_expr(params[3], statement);
    statement.expr &:= ";\n";
    doLocalDeclsOfStatement(statement, c_expr);
  end func;


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

  begin
    c_expr.expr &:= "(";
    process_expr(params[1], c_expr);
    c_expr.expr &:= ") == (";
    process_expr(params[3], c_expr);
    c_expr.expr &:= ")";
  end func;


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

  local
    var integer: numberOfEnumLiterals is 0;
    var intRange: number_range is intRange.value;
    var string: number_name is "";
  begin
    numberOfEnumLiterals := length(getValue(evaluate(prog, params[2]), ref_list));
    number_range := getIntRange(params[1]);
    if (number_range.minValue >= numberOfEnumLiterals and
        number_range.maxValue >= numberOfEnumLiterals) or
        (number_range.minValue < 0 and number_range.maxValue < 0) then
      incr(countOptimizations);
      warning(DOES_RAISE, "RANGE_ERROR", c_expr);
      c_expr.expr &:= "(enumType)";
      c_expr.expr &:= intRaiseError("RANGE_ERROR");
    elsif conversion_range_check then
      if number_range.minValue >= 0 and
          number_range.maxValue < numberOfEnumLiterals then
        # This conversion cannot trigger a range error.
        countRangeOptimizations(c_expr);
        c_expr.expr &:= "/*no_range_check_conversion*/(enumType)(";
        process_expr(params[1], c_expr);
        c_expr.expr &:= ")";
      else
        incr(countRangeChecks);
        c_expr.expr &:= "(enumType)(";
        number_name := getParameterAsVariable("intType", "tmp_", params[1], c_expr);
        c_expr.expr &:= "rngChk(";
        if number_range.maxValue < numberOfEnumLiterals then
          c_expr.expr &:= number_name;
          c_expr.expr &:= "<0";
        else
          if ccConf.TWOS_COMPLEMENT_INTTYPE then
            c_expr.expr &:= "(uintType)(";
            c_expr.expr &:= number_name;
            c_expr.expr &:= ")";
          else
            c_expr.expr &:= number_name;
            c_expr.expr &:= "<0||";
            c_expr.expr &:= number_name;
          end if;
          c_expr.expr &:= ">=";
          c_expr.expr &:= str(numberOfEnumLiterals);
        end if;
        c_expr.expr &:= ")?";
        c_expr.expr &:= intRaiseError("RANGE_ERROR");
        c_expr.expr &:= ":";
        c_expr.expr &:= number_name;
        c_expr.expr &:= ")";
      end if;
    else
      incr(countNoRangeChecks);
      c_expr.expr &:= "(enumType)(";
      process_expr(params[1], c_expr);
      c_expr.expr &:= ")";
    end if;
  end func;


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

  local
    var type: enumType is void;
  begin
    enumType := getType(formalParams(function)[1]);
    if enumType not in literal_function_of_enum_used then
      global_c_expr.expr &:= "static striType lit_";
      global_c_expr.expr &:= str(typeNumber(enumType));
      global_c_expr.expr &:= " (";
      global_c_expr.expr &:= type_name(enumType);
      global_c_expr.expr &:= " enumValue);\n\n";
      literal_function_of_enum_used @:= [enumType] TRUE;
    end if;
    c_expr.expr &:= "lit_";
    c_expr.expr &:= str(typeNumber(enumType));
    c_expr.expr &:= "(";
    process_expr(params[1], c_expr);
    c_expr.expr &:= ")";
  end func;


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

  begin
    c_expr.expr &:= "(";
    process_expr(params[1], c_expr);
    c_expr.expr &:= ") != (";
    process_expr(params[3], c_expr);
    c_expr.expr &:= ")";
  end func;


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

  begin
    c_expr.expr &:= "(";
    process_expr(params[1], c_expr);
    c_expr.expr &:= ")";
  end func;


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

  begin
    c_expr.expr &:= "enuValue(";
    process_expr(params[1], c_expr);
    c_expr.expr &:= ")";
  end func;