<commit_before>/*! \file cooling_cpu.cpp
 *  \brief Functions to calculate cooling rate for a given rho, P, dt. */

#ifdef COOLING_CPU

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "global.h"
#include "grid3D.h"
#include "error_handling.h"
#include <gsl/gsl_errno.h>
#include <gsl/gsl_spline.h>
#include <gsl/gsl_spline2d.h>

gsl_interp_accel *xacc;
gsl_interp_accel *yacc;
gsl_spline *highT_C_spline;
gsl_spline2d *lowT_C_spline;
gsl_spline2d *lowT_H_spline;

Real test_cool(const Real n, const Real T);
Real Cloudy_cool(const Real n, const Real T);

void Grid3D::Cool_CPU(void)
{
  int i, j, k, id;
  int istart, jstart, kstart, iend, jend, kend;
  Real d, vx, vy, vz, E, P, n;
  Real T, T_init, T_init_P, del_T, dt_left, dt_sub, cool, E_old;
  #ifdef DE
  Real ge, T_init_ge;
  #endif

  istart = H.n_ghost;
  iend   = H.nx-H.n_ghost;
  if (H.ny > 1) {
    jstart = H.n_ghost;
    jend   = H.ny-H.n_ghost;
  }
  else {
    jstart = 0;
    jend   = H.ny;
  }
  if (H.nz > 1) {
    kstart = H.n_ghost;
    kend   = H.nz-H.n_ghost;
  }
  else {
    kstart = 0;
    kend   = H.nz;
  }

  // set initial values of conserved variables
  for(k=kstart; k<kend; k++) {
    for(j=jstart; j<jend; j++) {
      for(i=istart; i<iend; i++) {

        //get cell index
        id = i + j*H.nx + k*H.nx*H.ny;

        // load values of density and pressure
        d  =  C.density[id];
        vx =  C.momentum_x[id] / d;
        vy =  C.momentum_y[id] / d;
        vz =  C.momentum_z[id] / d;
        E  =  C.Energy[id];
        P  = (E - 0.5*d*(vx*vx + vy*vy + vz*vz)) * (gama - 1.0);
        #ifdef DE
        ge = C.GasEnergy[id] / d;
        P  = d * ge * (gama - 1.0);
        #endif
        P  = fmax(P, (Real) TINY_NUMBER);

        // calculate the number density of the gas (in cgs)
        n = d*DENSITY_UNIT / MP;

        // calculate the temperature of the gas
        T_init_P = P*PRESSURE_UNIT/ (n*KB);
        T_init = T_init_P;
        #ifdef DE
        T_init_ge = ge*(gama-1.0)*SP_ENERGY_UNIT*MP/KB;
        T_init = T_init_ge;
        #endif

        dt_left = H.dt;

        // only allow cooling between 10^1 K and 10^9 K
        //if (T_init > 1e1 && T_init < 1e9) {

          // calculate cooling rate per volume
          T = T_init;
          cool = Cloudy_cool(n, T);

          // calculate change in temperature given dt
          del_T = cool*dt_left*TIME_UNIT*(gama-1.0)/(n*KB);

          // limit change in temperature to 5%
          while (del_T/T > 0.05) {

            // what dt gives del_T = 0.1*T?
            dt_sub = 0.05*T*n*KB/(cool*TIME_UNIT*(gama-1.0));
            // apply that dt
            T -= cool*dt_sub*TIME_UNIT*(gama-1.0)/(n*KB);
            // how much time is left from the original timestep?
            dt_left -= dt_sub;
            // calculate cooling again
            cool = Cloudy_cool(n, T);
            // calculate new change in temperature
            del_T = cool*dt_left*TIME_UNIT*(gama-1.0)/(n*KB);

          }

          // calculate final temperature
          T -= del_T;

          // adjust value of energy based on total change in temperature
          del_T = T_init - T; // total change in T
          E_old = E;
          E -= n*KB*del_T / ((gama-1.0)*ENERGY_UNIT);
          if (E < 0.0) printf("%3d %3d %3d Negative E after cooling. %f %f %f %f %f\n", i, j, k, del_T, T_init, E_old, n, E);
          #ifdef DE
          ge -= KB*del_T / (MP*(gama-1.0)*SP_ENERGY_UNIT);
          #endif

          // update grid
          C.Energy[id] = E;
          #ifdef DE
          C.GasEnergy[id] = d*ge;
          #endif
        //}

      }
    }
  }

}

/* \fn Real test_cool(Real n, Real T)
 * \brief Cooling function from Creasey 2011. */
Real test_cool(const Real n, const Real T)
{
  Real T0, T1, lambda, cool;
  T0 = 1000.0;
  T1 = 20*T0;
  cool = 0.0;
  lambda = 5.0e-24; //cooling coefficient, erg cm^3 s^-1

  // constant cooling rate
  //cool = n*n*lambda;

  // Creasey cooling function
  if (T >= T0 && T <= 0.5*(T1+T0)) {
    cool = n*n*lambda*(T - T0) / T0;
  }
  if (T >= 0.5*(T1+T0) && T <= T1) {
    cool = n*n*lambda*(T1 - T) / T0;
  }

  return cool;

}

Real Cloudy_cool(const Real n, const Real T)
{
  Real log_n, log_T, lambda, H, cool;
  log_n = log10(n);
  log_T = log10(T);
  cool = 0.0;

  // Use 2d interpolation to calculate the cooling & heating rates for
  // for low temperature gas
  if (T > 1e1 && T <= 1e5) {
    if (log_n > -3.9 && log_n < 3.9) {
      lambda = gsl_spline2d_eval(lowT_C_spline, log_n, log_T, xacc, yacc);
      cool = n*n*pow(10, lambda);
      H = gsl_spline2d_eval(lowT_H_spline, log_n, log_T, xacc, yacc);
      H = n*n*pow(10, H);
      cool -= H;
    }
    else if (log_n <= -3.9) {
      lambda = gsl_spline2d_eval(lowT_C_spline, -3.9, log_T, xacc, yacc);
      cool = n*n*pow(10, lambda);
      H = gsl_spline2d_eval(lowT_H_spline, -3.9, log_T, xacc, yacc);
      H = n*n*pow(10, H);
      cool -= H;
    }
    else {
      lambda = gsl_spline2d_eval(lowT_C_spline, 3.9, log_T, xacc, yacc);
      cool = n*n*pow(10, lambda);
      H = gsl_spline2d_eval(lowT_H_spline, 3.9, log_T, xacc, yacc);
      H = n*n*pow(10, H);
      cool -= H;
    }
  }
  // At high temps, 1D interpolation is fine
  else if (T > 1e5 && T < 1e9) {
    lambda = gsl_spline_eval(highT_C_spline, log_T, xacc);
    cool = n*n*pow(10, lambda);
  }

  return cool;
}

void Grid3D::Load_Cooling_Tables()
{
  Real *n_arr;
  Real *T_arr;
  Real *L_arr;
  Real *H_arr;
  Real *za;

  int i, xi, yi;
  int nx = 81;
  int ny = 41;
  double x[nx], y[ny];
  za = (Real *) malloc(nx*ny*sizeof(Real));

  FILE *infile;
  char buffer[0x1000];
  char * pch;

  // Allocate arrays for high temperature data
  n_arr = (Real *) malloc(81*sizeof(Real));
  T_arr = (Real *) malloc(81*sizeof(Real));
  L_arr = (Real *) malloc(81*sizeof(Real));
  H_arr = (Real *) malloc(81*sizeof(Real));

  // Read in high T cooling curve (single density)
  i=0;
  infile = fopen("./cloudy_coolingcurve.txt", "r");
  if (infile == NULL) {
    printf("Unable to open Cloudy file.\n");
    chexit(1);
  }
  while (fgets(buffer, sizeof(buffer), infile) != NULL)
  {
    if (buffer[0] == '#') {
      continue;
    }
    else {
      pch = strtok(buffer, "\t");
      n_arr[i] = atof(pch);
      while (pch != NULL)
      {
        pch = strtok(NULL, "\t");
        if (pch != NULL)
          T_arr[i] = atof(pch);
        pch = strtok(NULL, "\t");
        if (pch != NULL)
          L_arr[i] = atof(pch);
        pch = strtok(NULL, "\t");
        if (pch != NULL)
          H_arr[i] = atof(pch);
      }
      i++;
    }
  }
  fclose(infile);

  xacc = gsl_interp_accel_alloc();
  yacc = gsl_interp_accel_alloc();
  // Initialize the spline for the high T cooling curve
  highT_C_spline = gsl_spline_alloc(gsl_interp_cspline, 81);
  gsl_spline_init(highT_C_spline, T_arr, L_arr, 81);

  free(n_arr);
  free(T_arr);
  free(L_arr);
  free(H_arr);

  // Reallocate arrays for low temperature data
  n_arr = (Real *) malloc(nx*ny*sizeof(Real));
  T_arr = (Real *) malloc(nx*ny*sizeof(Real));
  L_arr = (Real *) malloc(nx*ny*sizeof(Real));
  H_arr = (Real *) malloc(nx*ny*sizeof(Real));

  // Read in low T cooling curve (function of density and temperature)
  i=0;
  infile = fopen("./cloudy_coolingcurve_lowT.txt", "r");
  if (infile == NULL) {
    printf("Unable to open Cloudy file.\n");
    chexit(1);
  }
  while (fgets(buffer, sizeof(buffer), infile) != NULL)
  {
    if (buffer[0] == '#') {
      continue;
    }
    else {
      pch = strtok(buffer, "\t");
      n_arr[i] = atof(pch);
      while (pch != NULL)
      {
        pch = strtok(NULL, "\t");
        if (pch != NULL)
          T_arr[i] = atof(pch);
        pch = strtok(NULL, "\t");
        if (pch != NULL)
          L_arr[i] = atof(pch);
        pch = strtok(NULL, "\t");
        if (pch != NULL)
          H_arr[i] = atof(pch);
      }
      i++;
    }
  }
  fclose(infile);

  // Allocate memory for 2d interpolation
  lowT_C_spline = gsl_spline2d_alloc(gsl_interp2d_bilinear, nx, ny);
  lowT_H_spline = gsl_spline2d_alloc(gsl_interp2d_bilinear, nx, ny);

  // Set x and y values
  for (xi=0; xi<nx; xi++) {
    x[xi] = -4.0 + 0.1*xi;
  }
  for (yi=0; yi<ny; yi++) {
    y[yi] = 1.0 + 0.1*yi;
  }

  // Set z grid values for cooling interpolation
  for (xi=0; xi<nx; xi++) {
    for (yi=0; yi<ny; yi++) {
      gsl_spline2d_set(lowT_C_spline, za, xi, yi, L_arr[yi + ny*xi]);
    }
  }

  // Initialize the interpolation for the low T cooling curve
  gsl_spline2d_init(lowT_C_spline, x, y, za, nx, ny);

  // Set z grid values for heating interpolation
  for (xi=0; xi<nx; xi++) {
    for (yi=0; yi<ny; yi++) {
      gsl_spline2d_set(lowT_H_spline, za, xi, yi, H_arr[yi + ny*xi]);
    }
  }

  // Initialize the interpolation for the low T heating curve
  gsl_spline2d_init(lowT_H_spline, x, y, za, nx, ny);

  free(n_arr);
  free(T_arr);
  free(L_arr);
  free(H_arr);
  free(za);

}

void Grid3D::Free_Cooling_Tables()
{
  gsl_interp_accel_free(xacc);
  gsl_interp_accel_free(yacc);
  gsl_spline_free(highT_C_spline);
  gsl_spline2d_free(lowT_C_spline);
  gsl_spline2d_free(lowT_H_spline);
}

#endif //COOLING_CPU
<commit_msg>new density limits -6.0 & 6.0<commit_after>/*! \file cooling_cpu.cpp
 *  \brief Functions to calculate cooling rate for a given rho, P, dt. */

#ifdef COOLING_CPU

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "global.h"
#include "grid3D.h"
#include "error_handling.h"
#include <gsl/gsl_errno.h>
#include <gsl/gsl_spline.h>
#include <gsl/gsl_spline2d.h>

gsl_interp_accel *xacc;
gsl_interp_accel *yacc;
gsl_spline *highT_C_spline;
gsl_spline2d *lowT_C_spline;
gsl_spline2d *lowT_H_spline;

Real test_cool(const Real n, const Real T);
Real Cloudy_cool(const Real n, const Real T);

void Grid3D::Cool_CPU(void)
{
  int i, j, k, id;
  int istart, jstart, kstart, iend, jend, kend;
  Real d, vx, vy, vz, E, P, n;
  Real T, T_init, T_init_P, del_T, dt_left, dt_sub, cool, E_old;
  #ifdef DE
  Real ge, T_init_ge;
  #endif

  istart = H.n_ghost;
  iend   = H.nx-H.n_ghost;
  if (H.ny > 1) {
    jstart = H.n_ghost;
    jend   = H.ny-H.n_ghost;
  }
  else {
    jstart = 0;
    jend   = H.ny;
  }
  if (H.nz > 1) {
    kstart = H.n_ghost;
    kend   = H.nz-H.n_ghost;
  }
  else {
    kstart = 0;
    kend   = H.nz;
  }

  // set initial values of conserved variables
  for(k=kstart; k<kend; k++) {
    for(j=jstart; j<jend; j++) {
      for(i=istart; i<iend; i++) {

        //get cell index
        id = i + j*H.nx + k*H.nx*H.ny;

        // load values of density and pressure
        d  =  C.density[id];
        vx =  C.momentum_x[id] / d;
        vy =  C.momentum_y[id] / d;
        vz =  C.momentum_z[id] / d;
        E  =  C.Energy[id];
        P  = (E - 0.5*d*(vx*vx + vy*vy + vz*vz)) * (gama - 1.0);
        #ifdef DE
        ge = C.GasEnergy[id] / d;
        P  = d * ge * (gama - 1.0);
        #endif
        P  = fmax(P, (Real) TINY_NUMBER);

        // calculate the number density of the gas (in cgs)
        n = d*DENSITY_UNIT / MP;

        // calculate the temperature of the gas
        T_init_P = P*PRESSURE_UNIT/ (n*KB);
        T_init = T_init_P;
        #ifdef DE
        T_init_ge = ge*(gama-1.0)*SP_ENERGY_UNIT*MP/KB;
        T_init = T_init_ge;
        #endif

        dt_left = H.dt;

        // only allow cooling between 10^1 K and 10^9 K
        //if (T_init > 1e1 && T_init < 1e9) {

          // calculate cooling rate per volume
          T = T_init;
          cool = Cloudy_cool(n, T);

          // calculate change in temperature given dt
          del_T = cool*dt_left*TIME_UNIT*(gama-1.0)/(n*KB);

          // limit change in temperature to 5%
          while (del_T/T > 0.05) {

            // what dt gives del_T = 0.1*T?
            dt_sub = 0.05*T*n*KB/(cool*TIME_UNIT*(gama-1.0));
            // apply that dt
            T -= cool*dt_sub*TIME_UNIT*(gama-1.0)/(n*KB);
            // how much time is left from the original timestep?
            dt_left -= dt_sub;
            // calculate cooling again
            cool = Cloudy_cool(n, T);
            // calculate new change in temperature
            del_T = cool*dt_left*TIME_UNIT*(gama-1.0)/(n*KB);

          }

          // calculate final temperature
          T -= del_T;

          // adjust value of energy based on total change in temperature
          del_T = T_init - T; // total change in T
          E_old = E;
          E -= n*KB*del_T / ((gama-1.0)*ENERGY_UNIT);
          if (E < 0.0) printf("%3d %3d %3d Negative E after cooling. %f %f %f %f %f\n", i, j, k, del_T, T_init, E_old, n, E);
          #ifdef DE
          ge -= KB*del_T / (MP*(gama-1.0)*SP_ENERGY_UNIT);
          #endif

          // update grid
          C.Energy[id] = E;
          #ifdef DE
          C.GasEnergy[id] = d*ge;
          #endif
        //}

      }
    }
  }

}

/* \fn Real test_cool(Real n, Real T)
 * \brief Cooling function from Creasey 2011. */
Real test_cool(const Real n, const Real T)
{
  Real T0, T1, lambda, cool;
  T0 = 1000.0;
  T1 = 20*T0;
  cool = 0.0;
  lambda = 5.0e-24; //cooling coefficient, erg cm^3 s^-1

  // constant cooling rate
  //cool = n*n*lambda;

  // Creasey cooling function
  if (T >= T0 && T <= 0.5*(T1+T0)) {
    cool = n*n*lambda*(T - T0) / T0;
  }
  if (T >= 0.5*(T1+T0) && T <= T1) {
    cool = n*n*lambda*(T1 - T) / T0;
  }

  return cool;

}

Real Cloudy_cool(const Real n, const Real T)
{
  Real log_n, log_T, lambda, H, cool;
  log_n = log10(n);
  log_T = log10(T);
  cool = 0.0;

  // Use 2d interpolation to calculate the cooling & heating rates for
  // for low temperature gas
  if (T > 1e1 && T <= 1e5) {
    if (log_n > -5.9 && log_n < 5.9) {
      lambda = gsl_spline2d_eval(lowT_C_spline, log_n, log_T, xacc, yacc);
      cool = n*n*pow(10, lambda);
      H = gsl_spline2d_eval(lowT_H_spline, log_n, log_T, xacc, yacc);
      H = n*n*pow(10, H);
      cool -= H;
    }
    else if (log_n <= -5.9) {
      lambda = gsl_spline2d_eval(lowT_C_spline, -5.9, log_T, xacc, yacc);
      cool = n*n*pow(10, lambda);
      H = gsl_spline2d_eval(lowT_H_spline, -5.9, log_T, xacc, yacc);
      H = n*n*pow(10, H);
      cool -= H;
    }
    else {
      lambda = gsl_spline2d_eval(lowT_C_spline, 5.9, log_T, xacc, yacc);
      cool = n*n*pow(10, lambda);
      H = gsl_spline2d_eval(lowT_H_spline, 5.9, log_T, xacc, yacc);
      H = n*n*pow(10, H);
      cool -= H;
    }
  }
  // At high temps, 1D interpolation is fine
  else if (T > 1e5 && T < 1e9) {
    lambda = gsl_spline_eval(highT_C_spline, log_T, xacc);
    cool = n*n*pow(10, lambda);
  }

  return cool;
}

void Grid3D::Load_Cooling_Tables()
{
  Real *n_arr;
  Real *T_arr;
  Real *L_arr;
  Real *H_arr;
  Real *za;

  int i, xi, yi;
  int nx = 121;
  int ny = 41;
  double x[nx], y[ny];
  za = (Real *) malloc(nx*ny*sizeof(Real));

  FILE *infile;
  char buffer[0x1000];
  char * pch;

  // Allocate arrays for high temperature data
  n_arr = (Real *) malloc(81*sizeof(Real));
  T_arr = (Real *) malloc(81*sizeof(Real));
  L_arr = (Real *) malloc(81*sizeof(Real));
  H_arr = (Real *) malloc(81*sizeof(Real));

  // Read in high T cooling curve (single density)
  i=0;
  infile = fopen("./cloudy_coolingcurve.txt", "r");
  if (infile == NULL) {
    printf("Unable to open Cloudy file.\n");
    chexit(1);
  }
  while (fgets(buffer, sizeof(buffer), infile) != NULL)
  {
    if (buffer[0] == '#') {
      continue;
    }
    else {
      pch = strtok(buffer, "\t");
      n_arr[i] = atof(pch);
      while (pch != NULL)
      {
        pch = strtok(NULL, "\t");
        if (pch != NULL)
          T_arr[i] = atof(pch);
        pch = strtok(NULL, "\t");
        if (pch != NULL)
          L_arr[i] = atof(pch);
        pch = strtok(NULL, "\t");
        if (pch != NULL)
          H_arr[i] = atof(pch);
      }
      i++;
    }
  }
  fclose(infile);

  xacc = gsl_interp_accel_alloc();
  yacc = gsl_interp_accel_alloc();
  // Initialize the spline for the high T cooling curve
  highT_C_spline = gsl_spline_alloc(gsl_interp_cspline, 81);
  gsl_spline_init(highT_C_spline, T_arr, L_arr, 81);

  free(n_arr);
  free(T_arr);
  free(L_arr);
  free(H_arr);

  // Reallocate arrays for low temperature data
  n_arr = (Real *) malloc(nx*ny*sizeof(Real));
  T_arr = (Real *) malloc(nx*ny*sizeof(Real));
  L_arr = (Real *) malloc(nx*ny*sizeof(Real));
  H_arr = (Real *) malloc(nx*ny*sizeof(Real));

  // Read in low T cooling curve (function of density and temperature)
  i=0;
  infile = fopen("./cloudy_coolingcurve_lowT.txt", "r");
  if (infile == NULL) {
    printf("Unable to open Cloudy file.\n");
    chexit(1);
  }
  while (fgets(buffer, sizeof(buffer), infile) != NULL)
  {
    if (buffer[0] == '#') {
      continue;
    }
    else {
      pch = strtok(buffer, "\t");
      n_arr[i] = atof(pch);
      while (pch != NULL)
      {
        pch = strtok(NULL, "\t");
        if (pch != NULL)
          T_arr[i] = atof(pch);
        pch = strtok(NULL, "\t");
        if (pch != NULL)
          L_arr[i] = atof(pch);
        pch = strtok(NULL, "\t");
        if (pch != NULL)
          H_arr[i] = atof(pch);
      }
      i++;
    }
  }
  fclose(infile);

  // Allocate memory for 2d interpolation
  lowT_C_spline = gsl_spline2d_alloc(gsl_interp2d_bilinear, nx, ny);
  lowT_H_spline = gsl_spline2d_alloc(gsl_interp2d_bilinear, nx, ny);

  // Set x and y values
  for (xi=0; xi<nx; xi++) {
    x[xi] = -6.0 + 0.1*xi;
  }
  for (yi=0; yi<ny; yi++) {
    y[yi] = 1.0 + 0.1*yi;
  }

  // Set z grid values for cooling interpolation
  for (xi=0; xi<nx; xi++) {
    for (yi=0; yi<ny; yi++) {
      gsl_spline2d_set(lowT_C_spline, za, xi, yi, L_arr[yi + ny*xi]);
    }
  }

  // Initialize the interpolation for the low T cooling curve
  gsl_spline2d_init(lowT_C_spline, x, y, za, nx, ny);

  // Set z grid values for heating interpolation
  for (xi=0; xi<nx; xi++) {
    for (yi=0; yi<ny; yi++) {
      gsl_spline2d_set(lowT_H_spline, za, xi, yi, H_arr[yi + ny*xi]);
    }
  }

  // Initialize the interpolation for the low T heating curve
  gsl_spline2d_init(lowT_H_spline, x, y, za, nx, ny);

  free(n_arr);
  free(T_arr);
  free(L_arr);
  free(H_arr);
  free(za);

}

void Grid3D::Free_Cooling_Tables()
{
  gsl_interp_accel_free(xacc);
  gsl_interp_accel_free(yacc);
  gsl_spline_free(highT_C_spline);
  gsl_spline2d_free(lowT_C_spline);
  gsl_spline2d_free(lowT_H_spline);
}

#endif //COOLING_CPU
<|endoftext|>


<commit_before>#ifndef STAN_MODEL_INDEXING_ACCESS_HELPERS_HPP
#define STAN_MODEL_INDEXING_ACCESS_HELPERS_HPP

#include <stan/math/prim.hpp>

namespace stan {

namespace model {

namespace internal {
// Internal helpers so we can reuse min_max index assign for Eigen/var<Eigen>
template <typename T, require_var_matrix_t<T>* = nullptr>
auto rowwise_reverse(T&& x) {
  return std::forward<T>(x).rowwise_reverse();
}

template <typename T, require_eigen_t<T>* = nullptr>
auto rowwise_reverse(T&& x) {
  return std::forward<T>(x).rowwise().reverse();
}

template <typename T, require_var_matrix_t<T>* = nullptr>
auto colwise_reverse(T&& x) {
  return std::forward<T>(x).colwise_reverse();
}

template <typename T, require_eigen_t<T>* = nullptr>
auto colwise_reverse(T&& x) {
  return std::forward<T>(x).colwise().reverse();
}

bool check_duplicate(const arena_t<std::vector<std::array<int, 2>>>& x_idx,
                     int i, int j) {
  for (size_t k = 0; k < x_idx.size(); ++k) {
    if (x_idx[k][0] == i && x_idx[k][1] == j) {
      return true;
    }
  }
  return false;
}

bool check_duplicate(const arena_t<std::vector<int>>& x_idx, int i) {
  for (size_t k = 0; k < x_idx.size(); ++k) {
    if (x_idx[k] == i) {
      return true;
    }
  }
  return false;
}

}  // namespace internal
}  // namespace model
}  // namespace stan
#endif
<commit_msg>move check_duplicates to the access_helpers<commit_after>#ifndef STAN_MODEL_INDEXING_ACCESS_HELPERS_HPP
#define STAN_MODEL_INDEXING_ACCESS_HELPERS_HPP

#include <stan/math/prim.hpp>

namespace stan {

namespace model {

namespace internal {
// Internal helpers so we can reuse min_max index assign for Eigen/var<Eigen>
template <typename T, require_var_matrix_t<T>* = nullptr>
inline auto rowwise_reverse(T&& x) {
  return std::forward<T>(x).rowwise_reverse();
}

template <typename T, require_eigen_t<T>* = nullptr>
inline auto rowwise_reverse(T&& x) {
  return std::forward<T>(x).rowwise().reverse();
}

template <typename T, require_var_matrix_t<T>* = nullptr>
inline auto colwise_reverse(T&& x) {
  return std::forward<T>(x).colwise_reverse();
}

template <typename T, require_eigen_t<T>* = nullptr>
inline auto colwise_reverse(T&& x) {
  return std::forward<T>(x).colwise().reverse();
}

inline bool check_duplicate(const arena_t<std::vector<std::array<int, 2>>>& x_idx,
                     int i, int j) {
  for (size_t k = 0; k < x_idx.size(); ++k) {
    if (x_idx[k][0] == i && x_idx[k][1] == j) {
      return true;
    }
  }
  return false;
}

inline bool check_duplicate(const arena_t<std::vector<int>>& x_idx, int i) {
  for (size_t k = 0; k < x_idx.size(); ++k) {
    if (x_idx[k] == i) {
      return true;
    }
  }
  return false;
}

}  // namespace internal
}
}
#endif
<|endoftext|>


<commit_before>#pragma once

#include <cstdint>

template <class T, typename = void>
class sref {
private:
  constexpr explicit sref(T* p) noexcept : ptr_(p) { }

public:
  constexpr sref() noexcept : ptr_(nullptr) { }

  sref(const sref &o) : ptr_(o.ptr_) {
    if (ptr_)
      ptr_->inc();
  }

  sref(sref &&o) noexcept : ptr_(o.ptr_)
  {
    o.ptr_ = nullptr;
  }

  ~sref() {
    if (ptr_)
      ptr_->dec();
  }

  sref& operator=(const sref& o) {
    T *optr = o.ptr_;
    if (optr != ptr_) {
      if (optr)
        optr->inc();
      if (ptr_)
        ptr_->dec();
      ptr_ = optr;
    }
    return *this;
  }

  sref& operator=(sref&& o) {
    if (ptr_)
      ptr_->dec();
    ptr_ = o.ptr_;
    o.ptr_ = nullptr;
    return *this;
  }

  // Transfer ownership of a pointer to this sref.  This *does not*
  // increment the reference count and is primarily meant for getting
  // srefs for freshly allocated objects that have come with an
  // implicit reference count of 1.
  static constexpr sref transfer(T* p) {
    return sref(p);
  }

  // Create a new reference to a pointer.  This *does* increment the
  // reference count and is primarily meant for transferring between
  // manual pointer-based reference counting and automatic counting.
  static sref newref(T* p) {
    if (p)
      p->inc();
    return sref(p);
  }

  void reset()
  {
    if (ptr_) {
      ptr_->dec();
      ptr_ = nullptr;
    }
  }

  bool init(T* p) {
    if (ptr_ || !p->tryinc())
      return false;
    ptr_ = p;
    return true;
  }

  // init_nonzero(p) can be called only if it is guaranteed that
  // there is an existing reference to p.
  bool init_nonzero(T* p) {
    if (ptr_)
      return false;
    p->inc();
    ptr_ = p;
    return true;
  }

  bool operator==(const sref<T>& pr) const { return ptr_ == pr.ptr_; }
  bool operator!=(const sref<T>& pr) const { return ptr_ != pr.ptr_; }
  bool operator==(T* p) const { return ptr_ == p; }
  bool operator!=(T* p) const { return ptr_ != p; }

  explicit operator bool() const noexcept { return !!ptr_; }

  T * operator->() const noexcept { return ptr_; }
  T & operator*() const noexcept { return *ptr_; }
  T * get() const noexcept { return ptr_; }

private:
  T *ptr_;
};

class referenced {
public:
  // Start with 1 reference
  referenced() { ref_.v = 0; }

  // The number of valid references is:
  //   ref_.invalid ? 0 : ref_.count+1;

  inline bool valid() const {
    return ref_.invalid == 0;
  }

  inline void inc() {
    // If references is 0 (i.e. ref_.count is 0xffffffff) a 32-bit
    // increment will increases ref_.count to 0, but ref_.invalid
    // will remain unchanged.
    asm volatile("lock; incl %0" : "+m" (ref_.count) :: "memory", "cc");
  }

  inline bool tryinc() {
    inc();
    return valid();
  }

  inline void dec() {
    unsigned char c;
    // If references is 1 (i.e. ref_.v is 0), a 64-bit decrement will
    // underflow ref_.invalid to 0xffffffff (and ref_.count to 0xffffffff).
    asm volatile("lock; decq %0; sets %1" : "+m" (ref_.v), "=qm" (c)
                 :: "memory", "cc");
    if (c)
      onzero();
  }

protected:
  virtual ~referenced() { }
  virtual void onzero() { delete this; }

private:
  mutable union {
    volatile uint64_t v;
    struct {
      volatile uint32_t count;
      volatile uint32_t invalid;
    };
  } ref_;
};
<commit_msg>libutil: Allow ::referenced to reach zero<commit_after>#pragma once

#include <cstdint>

template <class T, typename = void>
class sref {
private:
  constexpr explicit sref(T* p) noexcept : ptr_(p) { }

public:
  constexpr sref() noexcept : ptr_(nullptr) { }

  sref(const sref &o) : ptr_(o.ptr_) {
    if (ptr_)
      ptr_->inc();
  }

  sref(sref &&o) noexcept : ptr_(o.ptr_)
  {
    o.ptr_ = nullptr;
  }

  ~sref() {
    if (ptr_)
      ptr_->dec();
  }

  sref& operator=(const sref& o) {
    T *optr = o.ptr_;
    if (optr != ptr_) {
      if (optr)
        optr->inc();
      if (ptr_)
        ptr_->dec();
      ptr_ = optr;
    }
    return *this;
  }

  sref& operator=(sref&& o) {
    if (ptr_)
      ptr_->dec();
    ptr_ = o.ptr_;
    o.ptr_ = nullptr;
    return *this;
  }

  // Transfer ownership of a pointer to this sref.  This *does not*
  // increment the reference count and is primarily meant for getting
  // srefs for freshly allocated objects that have come with an
  // implicit reference count of 1.
  static constexpr sref transfer(T* p) {
    return sref(p);
  }

  // Create a new reference to a pointer.  This *does* increment the
  // reference count and is primarily meant for transferring between
  // manual pointer-based reference counting and automatic counting.
  static sref newref(T* p) {
    if (p)
      p->inc();
    return sref(p);
  }

  void reset()
  {
    if (ptr_) {
      ptr_->dec();
      ptr_ = nullptr;
    }
  }

  bool init(T* p) {
    if (ptr_ || !p->tryinc())
      return false;
    ptr_ = p;
    return true;
  }

  // init_nonzero(p) can be called only if it is guaranteed that
  // there is an existing reference to p.
  bool init_nonzero(T* p) {
    if (ptr_)
      return false;
    p->inc();
    ptr_ = p;
    return true;
  }

  bool operator==(const sref<T>& pr) const { return ptr_ == pr.ptr_; }
  bool operator!=(const sref<T>& pr) const { return ptr_ != pr.ptr_; }
  bool operator==(T* p) const { return ptr_ == p; }
  bool operator!=(T* p) const { return ptr_ != p; }

  explicit operator bool() const noexcept { return !!ptr_; }

  T * operator->() const noexcept { return ptr_; }
  T & operator*() const noexcept { return *ptr_; }
  T * get() const noexcept { return ptr_; }

private:
  T *ptr_;
};

class referenced {
public:
  // Start with 1 reference by default
  referenced(uint64_t refcount = 1) { ref_.v = refcount - 1; }

  // The number of valid references is:
  //   ref_.invalid ? 0 : ref_.count+1;

  inline void inc() {
    asm volatile("lock; incq %0" : "+m" (ref_.v) :: "memory", "cc");
  }

  // Attempt to increment the reference count, failing if the count is
  // currently zero.  If there is ever a tryinc from zero, the count
  // will remain zero forever after.
  inline bool tryinc() {
    // If references is 0 (i.e. ref_.count is 0xffffffff) a 32-bit
    // increment will increases ref_.count to 0, but ref_.invalid
    // will remain unchanged.
    asm volatile("lock; incl %0" : "+m" (ref_.count) :: "memory", "cc");
    return ref_.invalid == 0;
  }

  inline void dec() {
    unsigned char c;
    // If references is 1 (i.e. ref_.v is 0), a 64-bit decrement will
    // underflow ref_.invalid to 0xffffffff (and ref_.count to 0xffffffff).
    asm volatile("lock; decq %0; sets %1" : "+m" (ref_.v), "=qm" (c)
                 :: "memory", "cc");
    if (c)
      onzero();
  }

protected:
  virtual ~referenced() { }
  virtual void onzero() { delete this; }

private:
  mutable union {
    volatile uint64_t v;
    struct {
      volatile uint32_t count;
      volatile uint32_t invalid;
    };
  } ref_;
};
<|endoftext|>


<commit_before>/* AFE Firmware for smart home devices
  LICENSE: https://github.com/tschaban/AFE-Firmware/blob/master/LICENSE
  DOC: http://smart-house.adrian.czabanowski.com/afe-firmware-pl/ */

#include "AFE-Web-Server.h"

AFEWebServer::AFEWebServer() {}

void AFEWebServer::begin() {
  httpUpdater.setup(&server);
  server.begin();
}

void AFEWebServer::listener() { server.handleClient(); }

boolean AFEWebServer::httpAPIlistener() { return receivedHTTPCommand; }

void AFEWebServer::publishHTML(String page) {
  server.send(200, "text/html", page);
}

void AFEWebServer::sendJSON(String json) {
  server.send(200, "application/json", json);
}

void AFEWebServer::handle(const char *uri,
                          ESP8266WebServer::THandlerFunction handler) {
  // Serial << endl << "INFO: Added url : " << uri << " for listening";
  server.on(uri, handler);
}

HTTPCOMMAND AFEWebServer::getHTTPCommand() {
  receivedHTTPCommand = false;
  return httpCommand;
}

void AFEWebServer::generate() {

  if (_refreshConfiguration) {
    _refreshConfiguration = false;
    Device.begin();
  }

  /* @TODO this method is not writen well */
  if (getOptionName() == "language") {
    uint8_t data;
    if (getCommand() == SERVER_CMD_SAVE) {
      data = getLanguageData();
    }
    publishHTML(ConfigurationPanel.getLanguageConfigurationSite(
        getOptionName(), getCommand(), data));

    if (getCommand() == SERVER_CMD_SAVE) {
      Device.reboot(Device.getMode());
    }
  } else if (getOptionName() == "device") {
    DEVICE data;
    if (getCommand() == SERVER_CMD_SAVE) {
      data = getDeviceData();
    }
    publishHTML(ConfigurationPanel.getDeviceConfigurationSite(
        getOptionName(), getCommand(), data));
  } else if (getOptionName() == "network") {
    NETWORK data;
    if (getCommand() == SERVER_CMD_SAVE) {
      data = getNetworkData();
    }
    publishHTML(ConfigurationPanel.getNetworkConfigurationSite(
        getOptionName(), getCommand(), data));

  } else if (getOptionName() == "mqtt") {
    MQTT data;
    if (getCommand() == SERVER_CMD_SAVE) {
      data = getMQTTData();
    }
    publishHTML(ConfigurationPanel.getMQTTConfigurationSite(
        getOptionName(), getCommand(), data));
  } else if (getOptionName() == "led") {
    LED data[5] = {};
    uint8_t dataLedID;
    if (getCommand() == SERVER_CMD_SAVE) {
      for (uint8_t i = 0; i < sizeof(Device.configuration.isLED); i++) {
        data[i] = getLEDData(i);
      }
      dataLedID = getSystemLEDData();
    }
    publishHTML(ConfigurationPanel.getLEDConfigurationSite(
        getOptionName(), getCommand(), data, dataLedID));
  } else if (getOptionName() == "exit") {
    publishHTML(
        ConfigurationPanel.getSite(getOptionName(), getCommand(), true));
    Device.reboot(MODE_NORMAL);
  } else if (getOptionName() == "reset") {
    publishHTML(
        ConfigurationPanel.getSite(getOptionName(), getCommand(), false));
    if (getCommand() == 1) {
      Device.setDevice();
      Device.reboot(MODE_ACCESS_POINT);
    }
  } else if (getOptionName() == "help") {
    publishHTML(ConfigurationPanel.getSite(getOptionName(), getCommand(),
                                           getCommand() == 0 ? false : true));
    if (getCommand() == 1) {
      Device.reboot(MODE_CONFIGURATION);
    } else if (getCommand() == 2) {
      Device.reboot(MODE_ACCESS_POINT);
    }
  } else {

    for (uint8_t i = 0; i < sizeof(Device.configuration.isRelay); i++) {
      if (Device.configuration.isRelay[i]) {
        if (getOptionName() == "relay" + String(i)) {
          RELAY data = {};
          if (getCommand() == SERVER_CMD_SAVE) {
            data = getRelayData(i);
          }
          publishHTML(ConfigurationPanel.getRelayConfigurationSite(
              getOptionName(), getCommand(), data, i));
        }
      } else {
        break;
      }
    }

    for (uint8_t i = 0; i < sizeof(Device.configuration.isSwitch); i++) {
      if (Device.configuration.isSwitch[i]) {
        if (getOptionName() == "switch" + String(i)) {
          SWITCH data = {};
          if (getCommand() == SERVER_CMD_SAVE) {
            data = getSwitchData(i);
          }
          publishHTML(ConfigurationPanel.getSwitchConfigurationSite(
              getOptionName(), getCommand(), data, i));
        }
      } else {
        break;
      }
    }
  }
}

String AFEWebServer::getOptionName() {

  if (Device.getMode() == MODE_NORMAL) {
    /* Recived HTTP API Command */
    if (server.hasArg("command")) {
      /* Constructing command */
      server.arg("command").toCharArray(httpCommand.command,
                                        sizeof(httpCommand.command));
      if (server.arg("device")) {
        server.arg("device").toCharArray(httpCommand.device,
                                         sizeof(httpCommand.device));
      } else {
        memset(httpCommand.device, 0, sizeof httpCommand.device);
      }
      if (server.arg("name")) {
        server.arg("name").toCharArray(httpCommand.name,
                                       sizeof(httpCommand.name));
      } else {
        memset(httpCommand.name, 0, sizeof httpCommand.name);
      }

      receivedHTTPCommand = true;
      return server.arg("command");

    } else {
      return "help";
    }
  } else {
    if (server.hasArg("option")) {
      return server.arg("option");
    } else {
      return "device";
    }
  }
}

uint8_t AFEWebServer::getCommand() {
  if (server.hasArg("cmd")) {
    return server.arg("cmd").toInt();
  }
}

DEVICE AFEWebServer::getDeviceData() {
  DEVICE data;

  _refreshConfiguration =
      true; // it will cause that device configuration will be refeshed

  if (server.arg("n").length() > 0) {
    server.arg("n").toCharArray(data.name, sizeof(data.name));
  }

  server.arg("h").length() > 0 ? data.httpAPI = true : data.httpAPI = false;

  server.arg("m").length() > 0 ? data.mqttAPI = true : data.mqttAPI = false;

  for (uint8_t i = 0; i < sizeof(Device.configuration.isLED); i++) {
    server.arg("hl").toInt() > i ? data.isLED[i] = true : data.isLED[i] = false;
  }

  for (uint8_t i = 0; i < sizeof(Device.configuration.isRelay); i++) {
    server.arg("hr").toInt() > i ? data.isRelay[i] = true
                                 : data.isRelay[i] = false;
  }

  for (uint8_t i = 0; i < sizeof(Device.configuration.isSwitch); i++) {
    server.arg("hs").toInt() > i ? data.isSwitch[i] = true
                                 : data.isSwitch[i] = false;
  }

  return data;
}

NETWORK AFEWebServer::getNetworkData() {
  NETWORK data;
  if (server.arg("n").length() > 0) {
    server.arg("n").toCharArray(data.ssid, sizeof(data.ssid));
  }

  if (server.arg("p").length() > 0) {
    server.arg("p").toCharArray(data.password, sizeof(data.password));
  }

  if (server.arg("d").length() > 0) {
    data.isDHCP = true;
  } else {
    data.isDHCP = false;
  }

  if (server.arg("i1").length() > 0 && server.arg("i2").length() > 0 &&
      server.arg("i3").length() > 0 && server.arg("i4").length() > 0) {

    data.ip = IPAddress(server.arg("i1").toInt(), server.arg("i2").toInt(),
                        server.arg("i3").toInt(), server.arg("i4").toInt());
  }
  if (server.arg("g1").length() > 0 && server.arg("g2").length() > 0 &&
      server.arg("g3").length() > 0 && server.arg("g4").length() > 0) {

    data.gateway =
        IPAddress(server.arg("g1").toInt(), server.arg("g2").toInt(),
                  server.arg("g3").toInt(), server.arg("g4").toInt());
  }
  if (server.arg("s1").length() > 0 && server.arg("s2").length() > 0 &&
      server.arg("s3").length() > 0 && server.arg("s4").length() > 0) {

    data.subnet = IPAddress(server.arg("s1").toInt(), server.arg("s2").toInt(),
                            server.arg("s3").toInt(), server.arg("s4").toInt());
  }
  if (server.arg("na").length() > 0) {
    data.noConnectionAttempts = server.arg("na").toInt();
  }
  if (server.arg("wc").length() > 0) {
    data.waitTimeConnections = server.arg("wc").toInt();
  }
  if (server.arg("ws").length() > 0) {
    data.waitTimeSeries = server.arg("ws").toInt();
  }

  return data;
}

MQTT AFEWebServer::getMQTTData() {
  MQTT data;
  if (server.arg("h").length() > 0) {
    server.arg("h").toCharArray(data.host, sizeof(data.host));
  }

  if (server.arg("m1").length() > 0 && server.arg("m2").length() > 0 &&
      server.arg("m3").length() > 0 && server.arg("m4").length() > 0) {

    data.ip = IPAddress(server.arg("m1").toInt(), server.arg("m2").toInt(),
                        server.arg("m3").toInt(), server.arg("m4").toInt());
  }

  if (server.arg("p").length() > 0) {
    data.port = server.arg("p").toInt();
  }

  if (server.arg("u").length() > 0) {
    server.arg("u").toCharArray(data.user, sizeof(data.user));
  }

  if (server.arg("s").length() > 0) {
    server.arg("s").toCharArray(data.password, sizeof(data.password));
  }

  if (server.arg("t").length() > 0) {
    server.arg("t").toCharArray(data.topic, sizeof(data.topic));
  }

  return data;
}

LED AFEWebServer::getLEDData(uint8_t id) {
  LED data;
  if (server.arg("g" + String(id)).length() > 0) {
    data.gpio = server.arg("g" + String(id)).toInt();
  }

  server.arg("o" + String(id)).length() > 0
      ? data.changeToOppositeValue = true
      : data.changeToOppositeValue = false;

  return data;
}

uint8_t AFEWebServer::getSystemLEDData() {
  uint8_t data;

  if (server.arg("i").length() > 0) {
    data = server.arg("i").toInt();
  }

  return data;
}

RELAY AFEWebServer::getRelayData(uint8_t id) {
  RELAY data;

  if (server.arg("g" + String(id)).length() > 0) {
    data.gpio = server.arg("g" + String(id)).toInt();
  }

  if (server.arg("r" + String(id)).length() > 0) {
    data.statePowerOn = server.arg("r" + String(id)).toInt();
  }

  if (server.arg("n" + String(id)).length() > 0) {
    server.arg("n" + String(id)).toCharArray(data.name, sizeof(data.name));
  }

  if (server.arg("c" + String(id)).length() > 0) {
    data.stateMQTTConnected = server.arg("c" + String(id)).toInt();
  }

  if (server.arg("t" + String(id)).length() > 0) {
    data.timeToOff = server.arg("t" + String(id)).toFloat();
  }

  if (server.arg("l" + String(id)).length() > 0) {
    data.ledID = server.arg("l" + String(id)).toInt();
  }

  return data;
}

SWITCH AFEWebServer::getSwitchData(uint8_t id) {
  SWITCH data;

  if (server.arg("t" + String(id)).length() > 0) {
    data.type = server.arg("t" + String(id)).toInt();
  }

  if (server.arg("s" + String(id)).length() > 0) {
    data.sensitiveness = server.arg("s" + String(id)).toInt();
  }

  if (server.arg("f" + String(id)).length() > 0) {
    data.functionality = server.arg("f" + String(id)).toInt();
  }

  if (server.arg("g" + String(id)).length() > 0) {
    data.gpio = server.arg("g" + String(id)).toInt();
  }

  if (server.arg("r" + String(id)).length() > 0) {
    data.relayID = server.arg("r" + String(id)).toInt();
  }

  return data;
}

uint8_t AFEWebServer::getLanguageData() {
  return server.arg("l").length() > 0 ? server.arg("l").toInt() : 1;
}
<commit_msg>change hardcoded number of LEDs<commit_after>/* AFE Firmware for smart home devices
  LICENSE: https://github.com/tschaban/AFE-Firmware/blob/master/LICENSE
  DOC: http://smart-house.adrian.czabanowski.com/afe-firmware-pl/ */

#include "AFE-Web-Server.h"

AFEWebServer::AFEWebServer() {}

void AFEWebServer::begin() {
  httpUpdater.setup(&server);
  server.begin();
}

void AFEWebServer::listener() { server.handleClient(); }

boolean AFEWebServer::httpAPIlistener() { return receivedHTTPCommand; }

void AFEWebServer::publishHTML(String page) {
  server.send(200, "text/html", page);
}

void AFEWebServer::sendJSON(String json) {
  server.send(200, "application/json", json);
}

void AFEWebServer::handle(const char *uri,
                          ESP8266WebServer::THandlerFunction handler) {
  // Serial << endl << "INFO: Added url : " << uri << " for listening";
  server.on(uri, handler);
}

HTTPCOMMAND AFEWebServer::getHTTPCommand() {
  receivedHTTPCommand = false;
  return httpCommand;
}

void AFEWebServer::generate() {

  if (_refreshConfiguration) {
    _refreshConfiguration = false;
    Device.begin();
  }

  /* @TODO this method is not writen well */
  if (getOptionName() == "language") {
    uint8_t data;
    if (getCommand() == SERVER_CMD_SAVE) {
      data = getLanguageData();
    }
    publishHTML(ConfigurationPanel.getLanguageConfigurationSite(
        getOptionName(), getCommand(), data));

    if (getCommand() == SERVER_CMD_SAVE) {
      Device.reboot(Device.getMode());
    }
  } else if (getOptionName() == "device") {
    DEVICE data;
    if (getCommand() == SERVER_CMD_SAVE) {
      data = getDeviceData();
    }
    publishHTML(ConfigurationPanel.getDeviceConfigurationSite(
        getOptionName(), getCommand(), data));
  } else if (getOptionName() == "network") {
    NETWORK data;
    if (getCommand() == SERVER_CMD_SAVE) {
      data = getNetworkData();
    }
    publishHTML(ConfigurationPanel.getNetworkConfigurationSite(
        getOptionName(), getCommand(), data));

  } else if (getOptionName() == "mqtt") {
    MQTT data;
    if (getCommand() == SERVER_CMD_SAVE) {
      data = getMQTTData();
    }
    publishHTML(ConfigurationPanel.getMQTTConfigurationSite(
        getOptionName(), getCommand(), data));
  } else if (getOptionName() == "led") {
    LED data[sizeof(Device.configuration.isLED)] = {};
    uint8_t dataLedID;
    if (getCommand() == SERVER_CMD_SAVE) {
      for (uint8_t i = 0; i < sizeof(Device.configuration.isLED); i++) {
        data[i] = getLEDData(i);
      }
      dataLedID = getSystemLEDData();
    }
    publishHTML(ConfigurationPanel.getLEDConfigurationSite(
        getOptionName(), getCommand(), data, dataLedID));
  } else if (getOptionName() == "exit") {
    publishHTML(
        ConfigurationPanel.getSite(getOptionName(), getCommand(), true));
    Device.reboot(MODE_NORMAL);
  } else if (getOptionName() == "reset") {
    publishHTML(
        ConfigurationPanel.getSite(getOptionName(), getCommand(), false));
    if (getCommand() == 1) {
      Device.setDevice();
      Device.reboot(MODE_ACCESS_POINT);
    }
  } else if (getOptionName() == "help") {
    publishHTML(ConfigurationPanel.getSite(getOptionName(), getCommand(),
                                           getCommand() == 0 ? false : true));
    if (getCommand() == 1) {
      Device.reboot(MODE_CONFIGURATION);
    } else if (getCommand() == 2) {
      Device.reboot(MODE_ACCESS_POINT);
    }
  } else {

    for (uint8_t i = 0; i < sizeof(Device.configuration.isRelay); i++) {
      if (Device.configuration.isRelay[i]) {
        if (getOptionName() == "relay" + String(i)) {
          RELAY data = {};
          if (getCommand() == SERVER_CMD_SAVE) {
            data = getRelayData(i);
          }
          publishHTML(ConfigurationPanel.getRelayConfigurationSite(
              getOptionName(), getCommand(), data, i));
        }
      } else {
        break;
      }
    }

    for (uint8_t i = 0; i < sizeof(Device.configuration.isSwitch); i++) {
      if (Device.configuration.isSwitch[i]) {
        if (getOptionName() == "switch" + String(i)) {
          SWITCH data = {};
          if (getCommand() == SERVER_CMD_SAVE) {
            data = getSwitchData(i);
          }
          publishHTML(ConfigurationPanel.getSwitchConfigurationSite(
              getOptionName(), getCommand(), data, i));
        }
      } else {
        break;
      }
    }
  }
}

String AFEWebServer::getOptionName() {

  if (Device.getMode() == MODE_NORMAL) {
    /* Recived HTTP API Command */
    if (server.hasArg("command")) {
      /* Constructing command */
      server.arg("command").toCharArray(httpCommand.command,
                                        sizeof(httpCommand.command));
      if (server.arg("device")) {
        server.arg("device").toCharArray(httpCommand.device,
                                         sizeof(httpCommand.device));
      } else {
        memset(httpCommand.device, 0, sizeof httpCommand.device);
      }
      if (server.arg("name")) {
        server.arg("name").toCharArray(httpCommand.name,
                                       sizeof(httpCommand.name));
      } else {
        memset(httpCommand.name, 0, sizeof httpCommand.name);
      }

      receivedHTTPCommand = true;
      return server.arg("command");

    } else {
      return "help";
    }
  } else {
    if (server.hasArg("option")) {
      return server.arg("option");
    } else {
      return "device";
    }
  }
}

uint8_t AFEWebServer::getCommand() {
  if (server.hasArg("cmd")) {
    return server.arg("cmd").toInt();
  }
}

DEVICE AFEWebServer::getDeviceData() {
  DEVICE data;

  _refreshConfiguration =
      true; // it will cause that device configuration will be refeshed

  if (server.arg("n").length() > 0) {
    server.arg("n").toCharArray(data.name, sizeof(data.name));
  }

  server.arg("h").length() > 0 ? data.httpAPI = true : data.httpAPI = false;

  server.arg("m").length() > 0 ? data.mqttAPI = true : data.mqttAPI = false;

  for (uint8_t i = 0; i < sizeof(Device.configuration.isLED); i++) {
    server.arg("hl").toInt() > i ? data.isLED[i] = true : data.isLED[i] = false;
  }

  for (uint8_t i = 0; i < sizeof(Device.configuration.isRelay); i++) {
    server.arg("hr").toInt() > i ? data.isRelay[i] = true
                                 : data.isRelay[i] = false;
  }

  for (uint8_t i = 0; i < sizeof(Device.configuration.isSwitch); i++) {
    server.arg("hs").toInt() > i ? data.isSwitch[i] = true
                                 : data.isSwitch[i] = false;
  }

  return data;
}

NETWORK AFEWebServer::getNetworkData() {
  NETWORK data;
  if (server.arg("n").length() > 0) {
    server.arg("n").toCharArray(data.ssid, sizeof(data.ssid));
  }

  if (server.arg("p").length() > 0) {
    server.arg("p").toCharArray(data.password, sizeof(data.password));
  }

  if (server.arg("d").length() > 0) {
    data.isDHCP = true;
  } else {
    data.isDHCP = false;
  }

  if (server.arg("i1").length() > 0 && server.arg("i2").length() > 0 &&
      server.arg("i3").length() > 0 && server.arg("i4").length() > 0) {

    data.ip = IPAddress(server.arg("i1").toInt(), server.arg("i2").toInt(),
                        server.arg("i3").toInt(), server.arg("i4").toInt());
  }
  if (server.arg("g1").length() > 0 && server.arg("g2").length() > 0 &&
      server.arg("g3").length() > 0 && server.arg("g4").length() > 0) {

    data.gateway =
        IPAddress(server.arg("g1").toInt(), server.arg("g2").toInt(),
                  server.arg("g3").toInt(), server.arg("g4").toInt());
  }
  if (server.arg("s1").length() > 0 && server.arg("s2").length() > 0 &&
      server.arg("s3").length() > 0 && server.arg("s4").length() > 0) {

    data.subnet = IPAddress(server.arg("s1").toInt(), server.arg("s2").toInt(),
                            server.arg("s3").toInt(), server.arg("s4").toInt());
  }
  if (server.arg("na").length() > 0) {
    data.noConnectionAttempts = server.arg("na").toInt();
  }
  if (server.arg("wc").length() > 0) {
    data.waitTimeConnections = server.arg("wc").toInt();
  }
  if (server.arg("ws").length() > 0) {
    data.waitTimeSeries = server.arg("ws").toInt();
  }

  return data;
}

MQTT AFEWebServer::getMQTTData() {
  MQTT data;
  if (server.arg("h").length() > 0) {
    server.arg("h").toCharArray(data.host, sizeof(data.host));
  }

  if (server.arg("m1").length() > 0 && server.arg("m2").length() > 0 &&
      server.arg("m3").length() > 0 && server.arg("m4").length() > 0) {

    data.ip = IPAddress(server.arg("m1").toInt(), server.arg("m2").toInt(),
                        server.arg("m3").toInt(), server.arg("m4").toInt());
  }

  if (server.arg("p").length() > 0) {
    data.port = server.arg("p").toInt();
  }

  if (server.arg("u").length() > 0) {
    server.arg("u").toCharArray(data.user, sizeof(data.user));
  }

  if (server.arg("s").length() > 0) {
    server.arg("s").toCharArray(data.password, sizeof(data.password));
  }

  if (server.arg("t").length() > 0) {
    server.arg("t").toCharArray(data.topic, sizeof(data.topic));
  }

  return data;
}

LED AFEWebServer::getLEDData(uint8_t id) {
  LED data;
  if (server.arg("g" + String(id)).length() > 0) {
    data.gpio = server.arg("g" + String(id)).toInt();
  }

  server.arg("o" + String(id)).length() > 0
      ? data.changeToOppositeValue = true
      : data.changeToOppositeValue = false;

  return data;
}

uint8_t AFEWebServer::getSystemLEDData() {
  uint8_t data;

  if (server.arg("i").length() > 0) {
    data = server.arg("i").toInt();
  }

  return data;
}

RELAY AFEWebServer::getRelayData(uint8_t id) {
  RELAY data;

  if (server.arg("g" + String(id)).length() > 0) {
    data.gpio = server.arg("g" + String(id)).toInt();
  }

  if (server.arg("r" + String(id)).length() > 0) {
    data.statePowerOn = server.arg("r" + String(id)).toInt();
  }

  if (server.arg("n" + String(id)).length() > 0) {
    server.arg("n" + String(id)).toCharArray(data.name, sizeof(data.name));
  }

  if (server.arg("c" + String(id)).length() > 0) {
    data.stateMQTTConnected = server.arg("c" + String(id)).toInt();
  }

  if (server.arg("t" + String(id)).length() > 0) {
    data.timeToOff = server.arg("t" + String(id)).toFloat();
  }

  if (server.arg("l" + String(id)).length() > 0) {
    data.ledID = server.arg("l" + String(id)).toInt();
  }

  return data;
}

SWITCH AFEWebServer::getSwitchData(uint8_t id) {
  SWITCH data;

  if (server.arg("t" + String(id)).length() > 0) {
    data.type = server.arg("t" + String(id)).toInt();
  }

  if (server.arg("s" + String(id)).length() > 0) {
    data.sensitiveness = server.arg("s" + String(id)).toInt();
  }

  if (server.arg("f" + String(id)).length() > 0) {
    data.functionality = server.arg("f" + String(id)).toInt();
  }

  if (server.arg("g" + String(id)).length() > 0) {
    data.gpio = server.arg("g" + String(id)).toInt();
  }

  if (server.arg("r" + String(id)).length() > 0) {
    data.relayID = server.arg("r" + String(id)).toInt();
  }

  return data;
}

uint8_t AFEWebServer::getLanguageData() {
  return server.arg("l").length() > 0 ? server.arg("l").toInt() : 1;
}
<|endoftext|>


<commit_before>/*
    Copyright 2011 Joe Hermaszewski. All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:

    1. Redistributions of source code must retain the above copyright notice,
    this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.

    THIS SOFTWARE IS PROVIDED BY JOE HERMASZEWSKI "AS IS" AND ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
    EVENT SHALL JOE HERMASZEWSKI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    The views and conclusions contained in the software and documentation are
    those of the authors and should not be interpreted as representing official
    policies, either expressed or implied, of Joe Hermaszewski.
*/

#include "declaration.hpp"

#include <algorithm>
#include <cassert>
#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include <compiler/casting.hpp>
#include <compiler/parser.hpp>
#include <compiler/sema_analyzer.hpp>
#include <compiler/terminal_types.hpp>
#include <compiler/tokens/compound_statement.hpp>
#include <compiler/tokens/declaration_specifier.hpp>
#include <compiler/tokens/declarator.hpp>
#include <compiler/tokens/definition.hpp>
#include <compiler/tokens/state_assignment_statement.hpp>
#include <compiler/tokens/token.hpp>
#include <engine/internal/type_properties.hpp>
#include <engine/state_assignment.hpp>
#include <engine/technique.hpp>

namespace JoeLang
{
namespace Compiler
{

//------------------------------------------------------------------------------
// DeclarationBase
//------------------------------------------------------------------------------

DeclarationBase::DeclarationBase( TokenTy sub_class_id )
    :Token( sub_class_id )
{
}

DeclarationBase::~DeclarationBase()
{
}

bool DeclarationBase::Parse( Parser& parser,
                             std::unique_ptr<DeclarationBase>& token )
{
    // Try and parse any of the top level declarations
    std::unique_ptr<Token> t;
    if( !parser.ExpectAnyOf< TechniqueDeclaration,
                             PassDeclaration,
                             VariableListOrFunctionDefinition,
                             EmptyDeclaration>( t ) )
        return false;

    //TODO set up casting for every token
    //assert( isa<DeclarationBase>( t ) &&
            //"DeclarationBase parsed a non-declaration" );
    // Cast it back to a DeclarationBase because ExpectAnyOf only returns Token*
    token.reset( static_cast<DeclarationBase*>( t.release() ) );
    return true;
}

bool DeclarationBase::classof( const Token* d )
{
    return d->GetSubClassID() >= TokenTy::Declaration_Start &&
           d->GetSubClassID() <= TokenTy::Declaration_End;
}

bool DeclarationBase::classof( const DeclarationBase* d )
{
    // A DeclarationBase is always a DeclarationBase
    return true;
}

//------------------------------------------------------------------------------
// EmptyDeclaration
//------------------------------------------------------------------------------

EmptyDeclaration::EmptyDeclaration()
    :DeclarationBase( TokenTy::EmptyDeclaration )
{
}

EmptyDeclaration::~EmptyDeclaration()
{
}

void EmptyDeclaration::PerformSema( SemaAnalyzer& sema )
{
}

void EmptyDeclaration::Print(int depth) const
{
    for( int i = 0; i < depth * 4; ++i )
        std::cout << " ";
    std::cout << "Empty Declaration\n";
}

bool EmptyDeclaration::Parse( Parser& parser,
                              std::unique_ptr<EmptyDeclaration>& token )
{
    // Try to parse just a semicolon
    if( !parser.ExpectTerminal( TerminalType::SEMICOLON ) )
        return false;

    token.reset( new EmptyDeclaration );
    return true;
}

bool EmptyDeclaration::classof( const DeclarationBase* d )
{
    return d->GetSubClassID() == TokenTy::EmptyDeclaration;
}

bool EmptyDeclaration::classof( const EmptyDeclaration* d )
{
    return true;
}

//------------------------------------------------------------------------------
// PassDeclaration
//------------------------------------------------------------------------------

PassDeclaration::PassDeclaration( std::string name,
                                  std::unique_ptr<PassDefinition> definition )
    :DeclarationBase( TokenTy::PassDeclaration )
    ,m_Name         ( std::move(name) )
    ,m_Definition   ( std::move(definition) )
{
}

PassDeclaration::~PassDeclaration()
{
}

void PassDeclaration::PerformSema( SemaAnalyzer& sema )
{
    // Only declare it if it's not an anonymous pass
    if( !m_Name.empty() )
        sema.DeclarePass( m_Name, std::move(m_Definition) );
    else
    {
        if( sema.InGlobalScope() )
            sema.Error( "Declaring an anonymous pass in global scope" );
        // We haven't given the pass to sema, so it must have sema performed on
        // it now
        m_Definition->PerformSema( sema );
    }
}

const std::string& PassDeclaration::GetName() const
{
    return m_Name;
}

bool PassDeclaration::HasDefinition() const
{
    return bool(m_Definition);
}

const std::unique_ptr<PassDefinition>& PassDeclaration::GetDefinition() const
{
    return m_Definition;
}

std::unique_ptr<PassDefinition> PassDeclaration::TakeDefinition()
{
    return std::move(m_Definition);
}

void PassDeclaration::Print( int depth ) const
{
    for( int i = 0; i < depth * 4; ++i )
        std::cout << " ";

    std::cout << "Pass: " << ( m_Name.size() ? m_Name : "Unnamed" ) << "\n";
    if( m_Definition )
    {
        m_Definition->Print( depth + 1);
    }
    else
    {
        for( int i = 0; i < depth * 4; ++i )
            std::cout << " ";
        std::cout << "No definition\n";
    }
}

bool PassDeclaration::Parse( Parser& parser,
                             std::unique_ptr<PassDeclaration>& token )
{
    // Needs to start with 'pass'
    if( !parser.ExpectTerminal( TerminalType::PASS ) )
        return false;

    // Allow anonymous passes
    std::string name;
    parser.ExpectTerminal( TerminalType::IDENTIFIER, name );

    // If we have a semicolon here, this pass doesn't have a definition
    if( parser.ExpectTerminal( TerminalType::SEMICOLON ) )
    {
        token.reset( new PassDeclaration( name, nullptr ) );
        return true;
    }
    // We may have moved the lexer on trying to parse the semicolon
    CHECK_PARSER;

    // This declaration must have a definition
    std::unique_ptr< PassDefinition > definition;
    if( !parser.Expect<PassDefinition>( definition ) )
        return false;

    token.reset( new PassDeclaration( std::move(name),
                                      std::move(definition) ) );
    return true;
}

bool PassDeclaration::classof( const DeclarationBase* d )
{
    return d->GetSubClassID() == TokenTy::PassDeclaration;
}

bool PassDeclaration::classof( const PassDeclaration* d )
{
    return true;
}

//------------------------------------------------------------------------------
// TechniqueDeclaration
//------------------------------------------------------------------------------

TechniqueDeclaration::TechniqueDeclaration( std::string name,
                                            PassDeclarationVector passes )

    :DeclarationBase( TokenTy::TechniqueDeclaration )
    ,m_Name( std::move(name) )
    ,m_Passes( std::move(passes) )
{
#ifndef NDEBUG
    for( const auto& p : m_Passes )
        assert( p && "TechniqueDeclaration given a null pass" );
#endif
}

TechniqueDeclaration::~TechniqueDeclaration()
{
}

void TechniqueDeclaration::PerformSema( SemaAnalyzer& sema )
{
    sema.DeclareTechnique( m_Name );
    SemaAnalyzer::ScopeHolder scope( sema );
    scope.Enter();
    for( auto& p : m_Passes )
        p->PerformSema( sema );
    scope.Leave();
}

const std::string& TechniqueDeclaration::GetName() const
{
    return m_Name;
}

Technique TechniqueDeclaration::GenerateTechnique(
                                                 CodeGenerator& code_gen ) const
{
    std::vector<Pass> passes;
    for( const auto& p : m_Passes )
        passes.push_back( p->GeneratePass( code_gen ) );
    return Technique( m_Name, std::move(passes) );
}

void TechniqueDeclaration::Print( int depth ) const
{
    for( int i = 0; i < depth * 4; ++i )
        std::cout << " ";

    std::cout << "Technique: " << (m_Name.size() ? m_Name : "Unnamed") << "\n";
    for( const auto& p : m_Passes )
        p->Print( depth + 1 );
}

bool TechniqueDeclaration::Parse( Parser& parser,
                                  std::unique_ptr<TechniqueDeclaration>& token )
{
    // Has to start with 'technique'
    if( !parser.ExpectTerminal( TerminalType::TECHNIQUE ) )
        return false;

    // Parse the technique's name into name
    // Don't worry if there isn't a name, techniques can be anonymous
    std::string name;
    parser.ExpectTerminal( TerminalType::IDENTIFIER, name );

    // Technique Declarations always have a definition

    // Start with an open brace
    if( !parser.ExpectTerminal( TerminalType::OPEN_BRACE ) )
        return false;

    // Try and parse the pass declarations
    PassDeclarationVector pass_declarations;
    parser.ExpectSequenceOf<PassDeclarationOrIdentifier>( pass_declarations );
    CHECK_PARSER;

    // End with a close brace
    if( !parser.ExpectTerminal( TerminalType::CLOSE_BRACE ) )
        return false;

    token.reset( new TechniqueDeclaration( std::move(name),
                                           std::move(pass_declarations) ) );
    return true;
}

bool TechniqueDeclaration::classof( const DeclarationBase* d )
{
    return d->GetSubClassID() == TokenTy::TechniqueDeclaration;
}

bool TechniqueDeclaration::classof( const TechniqueDeclaration* d )
{
    return true;
}

//------------------------------------------------------------------------------
// VariableListOrFunctionDefinition
//------------------------------------------------------------------------------

VariableListOrFunctionDefinition::VariableListOrFunctionDefinition(
                                                    TokenTy sub_class_id )
    :DeclarationBase( sub_class_id )
{
}

VariableListOrFunctionDefinition::~VariableListOrFunctionDefinition()
{
}

bool VariableListOrFunctionDefinition::Parse(
                      Parser& parser,
                      std::unique_ptr<VariableListOrFunctionDefinition>& token )
{
    DeclSpecsVector decl_specs;

    // We need at least one declaration specifier
    if( !parser.ExpectSequenceOf<DeclarationSpecifier>( decl_specs ) )
        return false;

    // Try and parse some declarators
    DeclaratorVector declarators;
    if( !parser.ExpectListOf<InitDeclarator, TerminalType::COMMA>(
                                                                 declarators ) )
        return false;

    if( declarators.empty() )
    {
        parser.Error( "Declaration without a declarator" );
        return false;
    }

    CompoundStatement_up function_body;

    if( declarators.size() == 1 &&
        declarators[0]->IsFunctionDeclarator() &&
        parser.Expect<CompoundStatement>( function_body ) )
    {
        // If there was only one declarator and it's a function declarator and
        // there's a compound statement then we have a function definiton;
        token.reset( new FunctionDefinition( std::move(decl_specs),
                                             declarators[0]->TakeDeclarator(),
                                             std::move(function_body) ) );
        return true;
    }
    CHECK_PARSER;

    // variable declarations must end in a semicolon
    if( !parser.ExpectTerminal( TerminalType::SEMICOLON ) )
        return false;

    token.reset( new VariableDeclarationList( std::move(decl_specs),
                                              std::move(declarators) ) );
    return true;
}

//------------------------------------------------------------------------------
// VariableDeclarationList
//------------------------------------------------------------------------------

VariableDeclarationList::VariableDeclarationList( DeclSpecsVector decl_specs,
                                                  DeclaratorVector declarators)
    :VariableListOrFunctionDefinition( TokenTy::VariableDeclarationList )
    ,m_DeclSpecs( std::move(decl_specs) )
    ,m_Declarators( std::move(declarators) )
{
    assert( !m_DeclSpecs.empty() &&
            "VariableDeclarationList given no declaration specifiers" );
}

VariableDeclarationList::~VariableDeclarationList()
{
}

void VariableDeclarationList::Print( int depth ) const
{
}

void VariableDeclarationList::PerformSema( SemaAnalyzer& sema )
{
    DeclSpecs decl_specs;
    decl_specs.AnalyzeDeclSpecs( m_DeclSpecs, sema );

    for( const auto& declarator : m_Declarators )
        declarator->PerformSema( sema, decl_specs );
}

void VariableDeclarationList::CodeGen( CodeGenerator& code_gen ) const
{
}

//------------------------------------------------------------------------------
// FunctionDefinition
//------------------------------------------------------------------------------

FunctionDefinition::FunctionDefinition( DeclSpecsVector decl_specs,
                                        Declarator_up declarator,
                                        CompoundStatement_up body )
    :VariableListOrFunctionDefinition( TokenTy::FunctionDefinition )
    ,m_DeclarationSpecifiers( std::move(decl_specs) )
    ,m_Declarator( std::move(declarator) )
    ,m_Body( std::move(body) )
{
#if !defined(NDEBUG)
    assert( !m_DeclarationSpecifiers.empty() &&
            "FunctionDefinition given no decl specs" );
    for( const auto& d : m_DeclarationSpecifiers )
        assert( d && "FunctionDefinition given a null declaration specifier" );
    assert( m_Declarator && "FunctionDefinition given a null declarator" );
    assert( m_Declarator->IsFunctionDeclarator() &&
            "FunctionDefinition given a non-function declarator" );
    assert( m_Body && "FunctionDefinition given a null body" );
#endif
}

FunctionDefinition::~FunctionDefinition()
{
}

void FunctionDefinition::PerformSema( SemaAnalyzer& sema )
{
    DeclSpecs decl_specs;
    decl_specs.AnalyzeDeclSpecs( m_DeclarationSpecifiers, sema );

    // This will register the function with sema and verify that it's all ok
    m_Declarator->PerformSema( sema, decl_specs );

    SemaAnalyzer::ScopeHolder scope( sema );
    scope.Enter();

    // Register the variables with sema
    m_Declarator->DeclareFunctionParameters( sema );

    // Pass the return type to sema for generating the return statements
    m_Body->PerformSema( sema,
                         decl_specs.GetType(),
                         m_Declarator->GetArrayExtents() );
    scope.Leave();
}

} // namespace Compiler
} // namespace JoeLang
<commit_msg>[+] comment<commit_after>/*
    Copyright 2011 Joe Hermaszewski. All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:

    1. Redistributions of source code must retain the above copyright notice,
    this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.

    THIS SOFTWARE IS PROVIDED BY JOE HERMASZEWSKI "AS IS" AND ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
    EVENT SHALL JOE HERMASZEWSKI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    The views and conclusions contained in the software and documentation are
    those of the authors and should not be interpreted as representing official
    policies, either expressed or implied, of Joe Hermaszewski.
*/

#include "declaration.hpp"

#include <algorithm>
#include <cassert>
#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include <compiler/casting.hpp>
#include <compiler/parser.hpp>
#include <compiler/sema_analyzer.hpp>
#include <compiler/terminal_types.hpp>
#include <compiler/tokens/compound_statement.hpp>
#include <compiler/tokens/declaration_specifier.hpp>
#include <compiler/tokens/declarator.hpp>
#include <compiler/tokens/definition.hpp>
#include <compiler/tokens/state_assignment_statement.hpp>
#include <compiler/tokens/token.hpp>
#include <engine/internal/type_properties.hpp>
#include <engine/state_assignment.hpp>
#include <engine/technique.hpp>

namespace JoeLang
{
namespace Compiler
{

//------------------------------------------------------------------------------
// DeclarationBase
//------------------------------------------------------------------------------

DeclarationBase::DeclarationBase( TokenTy sub_class_id )
    :Token( sub_class_id )
{
}

DeclarationBase::~DeclarationBase()
{
}

bool DeclarationBase::Parse( Parser& parser,
                             std::unique_ptr<DeclarationBase>& token )
{
    // Try and parse any of the top level declarations
    std::unique_ptr<Token> t;
    if( !parser.ExpectAnyOf< TechniqueDeclaration,
                             PassDeclaration,
                             VariableListOrFunctionDefinition,
                             EmptyDeclaration>( t ) )
        return false;

    //TODO set up casting for every token
    //assert( isa<DeclarationBase>( t ) &&
            //"DeclarationBase parsed a non-declaration" );
    // Cast it back to a DeclarationBase because ExpectAnyOf only returns Token*
    token.reset( static_cast<DeclarationBase*>( t.release() ) );
    return true;
}

bool DeclarationBase::classof( const Token* d )
{
    return d->GetSubClassID() >= TokenTy::Declaration_Start &&
           d->GetSubClassID() <= TokenTy::Declaration_End;
}

bool DeclarationBase::classof( const DeclarationBase* d )
{
    // A DeclarationBase is always a DeclarationBase
    return true;
}

//------------------------------------------------------------------------------
// EmptyDeclaration
//------------------------------------------------------------------------------

EmptyDeclaration::EmptyDeclaration()
    :DeclarationBase( TokenTy::EmptyDeclaration )
{
}

EmptyDeclaration::~EmptyDeclaration()
{
}

void EmptyDeclaration::PerformSema( SemaAnalyzer& sema )
{
}

void EmptyDeclaration::Print(int depth) const
{
    for( int i = 0; i < depth * 4; ++i )
        std::cout << " ";
    std::cout << "Empty Declaration\n";
}

bool EmptyDeclaration::Parse( Parser& parser,
                              std::unique_ptr<EmptyDeclaration>& token )
{
    // Try to parse just a semicolon
    if( !parser.ExpectTerminal( TerminalType::SEMICOLON ) )
        return false;

    token.reset( new EmptyDeclaration );
    return true;
}

bool EmptyDeclaration::classof( const DeclarationBase* d )
{
    return d->GetSubClassID() == TokenTy::EmptyDeclaration;
}

bool EmptyDeclaration::classof( const EmptyDeclaration* d )
{
    return true;
}

//------------------------------------------------------------------------------
// PassDeclaration
//------------------------------------------------------------------------------

PassDeclaration::PassDeclaration( std::string name,
                                  std::unique_ptr<PassDefinition> definition )
    :DeclarationBase( TokenTy::PassDeclaration )
    ,m_Name         ( std::move(name) )
    ,m_Definition   ( std::move(definition) )
{
}

PassDeclaration::~PassDeclaration()
{
}

void PassDeclaration::PerformSema( SemaAnalyzer& sema )
{
    // Only declare it if it's not an anonymous pass
    if( !m_Name.empty() )
        sema.DeclarePass( m_Name, std::move(m_Definition) );
    else
    {
        if( sema.InGlobalScope() )
            sema.Error( "Declaring an anonymous pass in global scope" );
        // We haven't given the pass to sema, so it must have sema performed on
        // it now
        m_Definition->PerformSema( sema );
    }
}

const std::string& PassDeclaration::GetName() const
{
    return m_Name;
}

bool PassDeclaration::HasDefinition() const
{
    return bool(m_Definition);
}

const std::unique_ptr<PassDefinition>& PassDeclaration::GetDefinition() const
{
    return m_Definition;
}

std::unique_ptr<PassDefinition> PassDeclaration::TakeDefinition()
{
    return std::move(m_Definition);
}

void PassDeclaration::Print( int depth ) const
{
    for( int i = 0; i < depth * 4; ++i )
        std::cout << " ";

    std::cout << "Pass: " << ( m_Name.size() ? m_Name : "Unnamed" ) << "\n";
    if( m_Definition )
    {
        m_Definition->Print( depth + 1);
    }
    else
    {
        for( int i = 0; i < depth * 4; ++i )
            std::cout << " ";
        std::cout << "No definition\n";
    }
}

bool PassDeclaration::Parse( Parser& parser,
                             std::unique_ptr<PassDeclaration>& token )
{
    // Needs to start with 'pass'
    if( !parser.ExpectTerminal( TerminalType::PASS ) )
        return false;

    // Allow anonymous passes
    std::string name;
    parser.ExpectTerminal( TerminalType::IDENTIFIER, name );

    // If we have a semicolon here, this pass doesn't have a definition
    if( parser.ExpectTerminal( TerminalType::SEMICOLON ) )
    {
        token.reset( new PassDeclaration( name, nullptr ) );
        return true;
    }
    // We may have moved the lexer on trying to parse the semicolon
    CHECK_PARSER;

    // This declaration must have a definition
    std::unique_ptr< PassDefinition > definition;
    if( !parser.Expect<PassDefinition>( definition ) )
        return false;

    token.reset( new PassDeclaration( std::move(name),
                                      std::move(definition) ) );
    return true;
}

bool PassDeclaration::classof( const DeclarationBase* d )
{
    return d->GetSubClassID() == TokenTy::PassDeclaration;
}

bool PassDeclaration::classof( const PassDeclaration* d )
{
    return true;
}

//------------------------------------------------------------------------------
// TechniqueDeclaration
//------------------------------------------------------------------------------

TechniqueDeclaration::TechniqueDeclaration( std::string name,
                                            PassDeclarationVector passes )

    :DeclarationBase( TokenTy::TechniqueDeclaration )
    ,m_Name( std::move(name) )
    ,m_Passes( std::move(passes) )
{
#ifndef NDEBUG
    for( const auto& p : m_Passes )
        assert( p && "TechniqueDeclaration given a null pass" );
#endif
}

TechniqueDeclaration::~TechniqueDeclaration()
{
}

void TechniqueDeclaration::PerformSema( SemaAnalyzer& sema )
{
    sema.DeclareTechnique( m_Name );
    SemaAnalyzer::ScopeHolder scope( sema );
    scope.Enter();
    for( auto& p : m_Passes )
        p->PerformSema( sema );
    scope.Leave();
}

const std::string& TechniqueDeclaration::GetName() const
{
    return m_Name;
}

Technique TechniqueDeclaration::GenerateTechnique(
                                                 CodeGenerator& code_gen ) const
{
    std::vector<Pass> passes;
    for( const auto& p : m_Passes )
        passes.push_back( p->GeneratePass( code_gen ) );
    return Technique( m_Name, std::move(passes) );
}

void TechniqueDeclaration::Print( int depth ) const
{
    for( int i = 0; i < depth * 4; ++i )
        std::cout << " ";

    std::cout << "Technique: " << (m_Name.size() ? m_Name : "Unnamed") << "\n";
    for( const auto& p : m_Passes )
        p->Print( depth + 1 );
}

bool TechniqueDeclaration::Parse( Parser& parser,
                                  std::unique_ptr<TechniqueDeclaration>& token )
{
    // Has to start with 'technique'
    if( !parser.ExpectTerminal( TerminalType::TECHNIQUE ) )
        return false;

    // Parse the technique's name into name
    // Don't worry if there isn't a name, techniques can be anonymous
    std::string name;
    parser.ExpectTerminal( TerminalType::IDENTIFIER, name );

    // Technique Declarations always have a definition

    // Start with an open brace
    if( !parser.ExpectTerminal( TerminalType::OPEN_BRACE ) )
        return false;

    // Try and parse the pass declarations
    PassDeclarationVector pass_declarations;
    parser.ExpectSequenceOf<PassDeclarationOrIdentifier>( pass_declarations );
    CHECK_PARSER;

    // End with a close brace
    if( !parser.ExpectTerminal( TerminalType::CLOSE_BRACE ) )
        return false;

    token.reset( new TechniqueDeclaration( std::move(name),
                                           std::move(pass_declarations) ) );
    return true;
}

bool TechniqueDeclaration::classof( const DeclarationBase* d )
{
    return d->GetSubClassID() == TokenTy::TechniqueDeclaration;
}

bool TechniqueDeclaration::classof( const TechniqueDeclaration* d )
{
    return true;
}

//------------------------------------------------------------------------------
// VariableListOrFunctionDefinition
//------------------------------------------------------------------------------

VariableListOrFunctionDefinition::VariableListOrFunctionDefinition(
                                                    TokenTy sub_class_id )
    :DeclarationBase( sub_class_id )
{
}

VariableListOrFunctionDefinition::~VariableListOrFunctionDefinition()
{
}

bool VariableListOrFunctionDefinition::Parse(
                      Parser& parser,
                      std::unique_ptr<VariableListOrFunctionDefinition>& token )
{
    DeclSpecsVector decl_specs;

    // We need at least one declaration specifier
    if( !parser.ExpectSequenceOf<DeclarationSpecifier>( decl_specs ) )
        return false;

    // Try and parse some declarators
    DeclaratorVector declarators;
    if( !parser.ExpectListOf<InitDeclarator, TerminalType::COMMA>(
                                                                 declarators ) )
        return false;

    if( declarators.empty() )
    {
        parser.Error( "Declaration without a declarator" );
        return false;
    }

    CompoundStatement_up function_body;

    if( declarators.size() == 1 &&
        declarators[0]->IsFunctionDeclarator() &&
        parser.Expect<CompoundStatement>( function_body ) )
    {
        // If there was only one declarator and it's a function declarator and
        // there's a compound statement then we have a function definiton;
        token.reset( new FunctionDefinition( std::move(decl_specs),
                                             declarators[0]->TakeDeclarator(),
                                             std::move(function_body) ) );
        return true;
    }
    CHECK_PARSER;

    // variable declarations must end in a semicolon
    if( !parser.ExpectTerminal( TerminalType::SEMICOLON ) )
        return false;

    token.reset( new VariableDeclarationList( std::move(decl_specs),
                                              std::move(declarators) ) );
    return true;
}

//------------------------------------------------------------------------------
// VariableDeclarationList
//------------------------------------------------------------------------------

VariableDeclarationList::VariableDeclarationList( DeclSpecsVector decl_specs,
                                                  DeclaratorVector declarators)
    :VariableListOrFunctionDefinition( TokenTy::VariableDeclarationList )
    ,m_DeclSpecs( std::move(decl_specs) )
    ,m_Declarators( std::move(declarators) )
{
    assert( !m_DeclSpecs.empty() &&
            "VariableDeclarationList given no declaration specifiers" );
}

VariableDeclarationList::~VariableDeclarationList()
{
}

void VariableDeclarationList::Print( int depth ) const
{
}

void VariableDeclarationList::PerformSema( SemaAnalyzer& sema )
{
    DeclSpecs decl_specs;
    decl_specs.AnalyzeDeclSpecs( m_DeclSpecs, sema );

    for( const auto& declarator : m_Declarators )
        declarator->PerformSema( sema, decl_specs );
}

void VariableDeclarationList::CodeGen( CodeGenerator& code_gen ) const
{
    //The variables have already been codegened in performsema
}

//------------------------------------------------------------------------------
// FunctionDefinition
//------------------------------------------------------------------------------

FunctionDefinition::FunctionDefinition( DeclSpecsVector decl_specs,
                                        Declarator_up declarator,
                                        CompoundStatement_up body )
    :VariableListOrFunctionDefinition( TokenTy::FunctionDefinition )
    ,m_DeclarationSpecifiers( std::move(decl_specs) )
    ,m_Declarator( std::move(declarator) )
    ,m_Body( std::move(body) )
{
#if !defined(NDEBUG)
    assert( !m_DeclarationSpecifiers.empty() &&
            "FunctionDefinition given no decl specs" );
    for( const auto& d : m_DeclarationSpecifiers )
        assert( d && "FunctionDefinition given a null declaration specifier" );
    assert( m_Declarator && "FunctionDefinition given a null declarator" );
    assert( m_Declarator->IsFunctionDeclarator() &&
            "FunctionDefinition given a non-function declarator" );
    assert( m_Body && "FunctionDefinition given a null body" );
#endif
}

FunctionDefinition::~FunctionDefinition()
{
}

void FunctionDefinition::PerformSema( SemaAnalyzer& sema )
{
    DeclSpecs decl_specs;
    decl_specs.AnalyzeDeclSpecs( m_DeclarationSpecifiers, sema );

    // This will register the function with sema and verify that it's all ok
    m_Declarator->PerformSema( sema, decl_specs );

    SemaAnalyzer::ScopeHolder scope( sema );
    scope.Enter();

    // Register the variables with sema
    m_Declarator->DeclareFunctionParameters( sema );

    // Pass the return type to sema for generating the return statements
    m_Body->PerformSema( sema,
                         decl_specs.GetType(),
                         m_Declarator->GetArrayExtents() );
    scope.Leave();
}

} // namespace Compiler
} // namespace JoeLang
<|endoftext|>