/*
 * Copyright © 2003-2026 Dynare Team
 *
 * This file is part of Dynare.
 *
 * Dynare 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 3 of the License, or
 * (at your option) any later version.
 *
 * Dynare 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 Dynare.  If not, see <https://www.gnu.org/licenses/>.
 */

#ifndef PARSING_DRIVER_HH
#define PARSING_DRIVER_HH

#ifdef MACRO_DRIVER_HH
# error Impossible to include both ParsingDriver.hh and macro/Driver.hh
#endif

#include <array>
#include <istream>
#include <optional>
#include <stack>
#include <string>
#include <string_view>
#include <variant>
#include <vector>

#include "ModFile.hh"
#include "SymbolList.hh"

#include "DynareBison.hh"
#include "ExprNode.hh"

#include "ComputingTasks.hh"
#include "DynamicModel.hh"
#include "NumericalInitialization.hh"
#include "Shocks.hh"

using namespace std;

// Declare DynareFlexLexer class
#ifndef __FLEX_LEXER_H
# define yyFlexLexer DynareFlexLexer
# include <FlexLexer.h>
# undef yyFlexLexer
#endif

//! The lexer class
/*! Actually it was necessary to subclass the DynareFlexLexer class generated by Flex,
  since the prototype for DynareFlexLexer::yylex() was not convenient.
*/
class DynareFlex : public DynareFlexLexer
{
public:
  DynareFlex(istream* in = nullptr, ostream* out = nullptr);

  DynareFlex(const DynareFlex&) = delete;
  DynareFlex& operator=(const DynareFlex&) = delete;

  //! The main lexing function
  Dynare::parser::token_type lex(Dynare::parser::semantic_type* yylval,
                                 Dynare::parser::location_type* yylloc, ParsingDriver& driver);

  //! The filename being parsed
  /*! The bison parser locations (begin and end) contain a pointer to that string */
  string filename;

  //! Increment the location counter given a token
  static void location_increment(Dynare::parser::location_type* yylloc, const char* yytext);
};

//! Drives the scanning and parsing of the .mod file, and constructs its abstract representation
/*! It is built along the guidelines given in Bison 2.3 manual. */
class ParsingDriver
{
private:
  //! Checks that a given symbol exists, and stops with an error message if it doesn't
  void check_symbol_existence(const string& name);

  //! Checks that a given symbol exists and is a parameter, and stops with an error message if it
  //! isn't
  void check_symbol_is_parameter(const string& name);

  //! Checks that a given symbol was assigned within a Statement
  void check_symbol_is_statement_variable(const string& name);

  //! Checks that a given symbol exists and is a endogenous or exogenous, and stops with an error
  //! message if it isn't
  void check_symbol_is_endogenous_or_exogenous(const string& name, bool allow_exo_det);

public:
  //! Checks that a given symbol exists and is a endogenous, and stops with an error message if it
  //! isn't
  void check_symbol_is_endogenous(const string& name);

  //! Checks that a given symbol exists and is a exogenous, and stops with an error message if it
  //! isn't
  void check_symbol_is_exogenous(const string& name, bool allow_exo_det);

  //! Known sub-fields for the use_pct estimation option
  static constexpr array use_pct_estimation_known_fields {"dime", "sampler", "smc_initialization"};

private:
  //! Checks for symbol existence in model block. If it doesn't exist, an error message is stored to
  //! be printed at the end of the model block
  void check_symbol_existence_in_model_block(const string& name);

  //! Helper to add a symbol declaration (returns its symbol ID)
  int declare_symbol(const string& name, SymbolType type, const string& tex_name,
                     const vector<pair<string, string>>& partition_value,
                     const optional<int>& heterogeneity_dimension);

  //! Temporary store for the planner objective
  unique_ptr<PlannerObjective> planner_objective;

  //! Temporary store for the occbin_constraints statement
  unique_ptr<DataTree> occbin_constraints_tree;

  //! The data tree in which to add expressions currently parsed
  /*! The object pointed to is not owned by the parsing driver. It is essentially a
      reference. */
  DataTree* data_tree;

  //! The model tree in which to add expressions currently parsed
  /*! It is only a dynamic cast of data_tree pointer, and is therefore null if data_tree is not a
   * ModelTree instance */
  ModelTree* model_tree;

  //! The dynamic model tree in which to add expressions currently parsed
  /*! It is only a dynamic cast of data_tree pointer, and is therefore null if data_tree is not a
   * DynamicModel instance */
  DynamicModel* dynamic_model;

  //! The heterogeneous model tree in which to add expressions currently parsed
  /*! It is only a dynamic cast of data_tree pointer, and is therefore null if data_tree is not a
   * HeterogeneousModel instance */
  HeterogeneousModel* heterogeneous_model;

  //! Sets data_tree and model_tree pointers
  void set_current_data_tree(DataTree* data_tree_arg);

  //! Stores options lists
  OptionsList options_list;
  //! Temporary storage for trend elements
  ObservationTrendsStatement::trend_elements_t trend_elements;
  //! Temporary storage for filter_initial_state elements
  FilterInitialStateStatement::filter_initial_state_elements_t filter_initial_state_elements;
  //! Temporary storage for filename list of ModelComparison (contains weights)
  ModelComparisonStatement::filename_list_t filename_list;
  //! Temporary storage for list of EstimationParams (from estimated_params* statements)
  vector<EstimationParams> estim_params_list;
  //! Temporary storage for list of OsrParams (from osr_params_block statements)
  vector<OsrParams> osr_params_list;
  //! Temporary storage of variances from optim_weights
  OptimWeightsStatement::var_weights_t var_weights;
  //! Temporary storage of covariances from optim_weights
  OptimWeightsStatement::covar_weights_t covar_weights;
  /* Temporary storage for deterministic shocks. Also used for
     conditional_forecast paths, for mshocks, shocks(surprise) and shocks(learnt_in=…)
     (for the latter, only used for shocks declared in level through “values”). */
  ShocksStatement::det_shocks_t det_shocks;
  // Temporary storage for shocks declared with “add” and “multiply” in shocks(learnt_in=…)
  ShocksStatement::det_shocks_t learnt_shocks_add, learnt_shocks_multiply;
  //! Temporary storage for variances of shocks
  ShocksStatement::var_and_std_shocks_t var_shocks;
  //! Temporary storage for standard errors of shocks
  ShocksStatement::var_and_std_shocks_t std_shocks;
  //! Temporary storage for covariances of shocks
  ShocksStatement::covar_and_corr_shocks_t covar_shocks;
  //! Temporary storage for correlations of shocks
  ShocksStatement::covar_and_corr_shocks_t corr_shocks;
  //! Temporary storage for skewness of shocks
  ShocksStatement::skew_shocks_t skew_shocks;
  // Temporary storage for shock_paths block
  variant<int, string> shock_paths_learnt_in_period;
  ShockPathsStatement::exo_paths_t shock_paths_exo;
  PerfectForesightControlledPathsStatement::paths_t shock_paths_controlled;
  bool is_parsing_shock_paths_controlled {false};
  //! Temporary storage for values and scales of heteroskedastic_shocks (exogenous)
  HeteroskedasticShocksStatement::heteroskedastic_shocks_t heteroskedastic_shocks_exo_values,
      heteroskedastic_shocks_exo_scales;
  //! Temporary storage for values and scales of heteroskedastic_shocks (endogenous)
  HeteroskedasticShocksStatement::heteroskedastic_shocks_t heteroskedastic_shocks_endo_values,
      heteroskedastic_shocks_endo_scales;
  //! Temporary storage for initval blocks
  InitOrEndValStatement::init_values_t init_values;
  /* Temporary storage for endval blocks. Uses a type that encompasses both
     regular “endval” blocks and “endval(learnt_in=…)” blocks. */
  EndValLearntInStatement::learnt_end_values_t end_values;
  //! Temporary storage for histval blocks
  HistValStatement::hist_values_t hist_values;
  //! Temporary storage for homotopy_setup blocks
  HomotopySetupStatement::homotopy_values_t homotopy_values;
  //! Temporary storage for moment_calibration
  MomentCalibration::constraints_t moment_calibration_constraints;
  //! Temporary storage for irf_calibration
  IrfCalibration::constraints_t irf_calibration_constraints;
  //! Temporary storage for svar_identification blocks
  SvarIdentificationStatement::svar_identification_restrictions_t svar_ident_restrictions;
  //! Temporary storage for mapping the equation number to the restrictions within an
  //! svar_identification block
  map<int, vector<int>> svar_equation_restrictions;
  //! Temporary storage for constants exculsion within an svar_identification
  bool svar_constants_exclusion;
  //! Temporary storage for upper cholesky within an svar_identification block
  bool svar_upper_cholesky;
  //! Temporary storage for lower cholesky within an svar_identification block
  bool svar_lower_cholesky;
  //! Temporary storage for equation number for a restriction within an svar_identification block
  int svar_equation_nbr;
  //! Temporary storage for left/right handside of a restriction equation within an
  //! svar_identificaton block
  bool svar_left_handside;
  //! Temporary storage for current restriction number in svar_identification block
  map<int, int> svar_Qi_restriction_nbr;
  map<int, int> svar_Ri_restriction_nbr;
  //! Stores undeclared model variables
  set<string> undeclared_model_vars;
  //! Temporary storage for restriction type
  enum class SvarRestrictionType
  {
    NOT_SET,
    Qi_TYPE,
    Ri_TYPE
  };
  SvarRestrictionType svar_restriction_type;
  //! Temporary storage for generate_irfs
  vector<string> generate_irf_names;
  vector<map<string, double>> generate_irf_elements;
  map<string, double> generate_irf_exos;
  //! Temporary storage for parameters in joint prior statement
  vector<string> joint_parameters;
  //! Temporary storage for a variance declared in the prior statement
  expr_t prior_variance;
  SubsamplesStatement::subsample_declaration_map_t subsample_declaration_map;
  //! Temporary storage for subsample statement: map<pair<var_name1, var_name2>>,
  //! subsample_declaration_map >
  using subsample_declarations_t
      = map<pair<string, string>, SubsamplesStatement::subsample_declaration_map_t>;
  subsample_declarations_t subsample_declarations;
  //! Temporary storage for shock_groups
  vector<string> shock_group;
  vector<ShockGroupsStatement::Group> shock_groups;
  //! Temporary storage for init2shocks
  vector<pair<int, int>> init2shocks;
  /* Temporary storage for planner_discount and planner_discount_latex_name
     options of ramsey_model, ramsey_policy, and discretionary_policy */
  expr_t planner_discount {nullptr};
  string planner_discount_latex_name;
  //! Adds a model lagged variable to ModelTree and VariableTable
  expr_t add_model_variable(int symb_id, int lag);
  //! For parsing the graph_format option
  vector<string> graph_formats;
  // Temporary storages for pac_target_info
  string pac_target_info_name;
  PacModelTable::target_component_t pac_target_info_component;
  // Temporary storage for filter_tunes
  FilterTunesStatement::tunes_t tunes;

  //! The mod file representation constructed by this ParsingDriver
  unique_ptr<ModFile> mod_file;

  WarningConsolidation& warnings;

  //! Temporary storage for several options of pac_model
  expr_t pac_growth;
  string pac_auxname;
  PacTargetKind pac_kind;

  bool nostrict;

  vector<pair<string, string>> undeclared_model_variable_errors;

  //! True if a ramsey_model statement has already been seen
  bool ramsey_model_seen {false};
  //! True if a ramsey_policy statement has already been seen
  bool ramsey_policy_seen {false};
  // True if a “varobs” statement has already been seen
  bool varobs_seen {false};

  [[nodiscard]] bool
  is_parsing_epilogue() const
  {
    return data_tree == &mod_file->epilogue;
  }

  [[nodiscard]] bool
  is_parsing_planner_objective() const
  {
    return data_tree == planner_objective.get();
  }

  [[nodiscard]] bool
  is_parsing_occbin_constraints() const
  {
    return data_tree == occbin_constraints_tree.get();
  }

public:
  [[nodiscard]] bool
  is_parsing_shock_paths() const
  {
    return data_tree == &mod_file->shock_paths_tree;
  }

  ParsingDriver(WarningConsolidation& warnings_arg, bool nostrict_arg) :
      warnings {warnings_arg}, nostrict {nostrict_arg}
  {
  }

  ParsingDriver(const ParsingDriver&) = delete;
  ParsingDriver& operator=(const ParsingDriver&) = delete;

  //! Starts parsing, and constructs the MOD file representation
  unique_ptr<ModFile> parse(istream& in, bool debug);

  //! Reference to the lexer
  unique_ptr<DynareFlex> lexer;

  //! Copy of parsing location, maintained by YYLLOC_DEFAULT macro in DynareBison.yy
  Dynare::parser::location_type location;

  //! Estimation parameters
  EstimationParams estim_params;

  //! OSR parameters
  OsrParams osr_params;

  //! Temporary storage for the prior shape
  PriorDistributions prior_shape;

  //! Temporary storage for "expression" option of VAR_EXPECTATION_MODEL
  expr_t var_expectation_model_expression {nullptr};
  //! Temporary storage for discount option of VAR_EXPECTATION_MODEL
  expr_t var_expectation_model_discount {nullptr};

  //! Error handler with explicit location
  [[noreturn]] void error(const Dynare::parser::location_type& l, const string& m);
  //! Error handler using saved location
  [[noreturn]] void error(const string& m);
  //! Warning handler using saved location
  void warning(const string& m);

  //! Error handler with explicit location (used in model block, accumulating error messages to be
  //! printed later)
  void undeclared_model_variable_error(const string& m, const string& var);

  //! Check if a given symbol exists in the parsing context, and is not a mod file local variable
  [[nodiscard]] bool
  symbol_exists_and_is_not_modfile_local_or_external_function(const string& s) const;
  //! Sets mode of ModelTree class to use C output
  void use_dll();
  //! the modelis block decomposed
  void block();

  //! the model is stored in a binary file
  void bytecode();
  //! the static model is not computed
  void no_static();
  //! the differentiate_forward_vars option is enabled (for all vars)
  void differentiate_forward_vars_all();
  //! the differentiate_forward_vars option is enabled (for a subset of vars)
  void differentiate_forward_vars_some(vector<string> symbol_list);
  //! cutoff option of model block
  void cutoff(const string& value);
  //! mfs option of model block
  void mfs(const string& value);
  //! static_mfs option of model block
  void static_mfs(const string& value);
  //! the flags to substitute for the default compiler flags used by `use_dll`
  void compilation_setup_substitute_flags(const string& flags);
  //! the flags to add to the default compiler flags used by `use_dll`
  void compilation_setup_add_flags(const string& flags);
  //! the libs to substitute for the default compiler libs used by `use_dll`
  void compilation_setup_substitute_libs(const string& libs);
  //! the libs to add to the default compiler libs used by `use_dll`
  void compilation_setup_add_libs(const string& libs);
  //! the compiler to replace the default compiler used by `use_dll`
  void compilation_setup_compiler(const string& path);
  //! balanced_growth_test_tol option of model block
  void balanced_growth_test_tol(const string& value);
  //! Sets the FILENAME for the initial value in initval
  void initval_file();
  //! Declares an endogenous variable (and returns its symbol ID)
  int declare_endogenous(const string& name, const string& tex_name = "",
                         const vector<pair<string, string>>& partition_value = {});
  // Handles a “var” or “var(log)” statement (without “deflator” or “log_deflator” options)
  void var(const vector<tuple<string, string, vector<pair<string, string>>>>& symbol_list,
           const optional<string>& heterogeneity_dimension, bool log_option);
  //! Declares an exogenous variable (and returns its symbol ID)
  int declare_exogenous(const string& name, const string& tex_name = "",
                        const vector<pair<string, string>>& partition_value = {});
  // Handles a “varexo” statement
  void varexo(const vector<tuple<string, string, vector<pair<string, string>>>>& symbol_list,
              const optional<string>& heterogeneity_dimension);
  // Handles a “varexo_det” statement
  void varexo_det(const vector<tuple<string, string, vector<pair<string, string>>>>& symbol_list);
  //! Declares a parameter (and returns its symbol ID)
  int declare_parameter(const string& name, const string& tex_name = "",
                        const vector<pair<string, string>>& partition_value = {});
  // Handles a “parameters” statement
  void parameters(const vector<tuple<string, string, vector<pair<string, string>>>>& symbol_list,
                  const optional<string>& heterogeneity_dimension);
  // Handles a “model_local_variable” statement
  void model_local_variable(const vector<pair<string, string>>& symbol_list);
  //! Declares a statement local variable
  void declare_statement_local_variable(const string& name);
  //! Completes a subsample statement
  void set_subsamples(string name1, string name2);
  //! Declares a subsample, assigning the value to name
  void set_subsample_name_equal_to_date_range(string name, string date1, string date2);
  //! Checks that a subsample statement (and given name) were provided for the pair name1 & name2
  void check_subsample_declaration_exists(const string& name1, const string& subsample_name);
  void check_subsample_declaration_exists(const string& name1, const string& name2,
                                          const string& subsample_name);
  //! Copies the set of subsamples from_name to_name
  void copy_subsamples(string to_name1, string to_name2, string from_name1, string from_name2);
  //! Sets the value of the planner_discount option of ramsey_{model,policy}, discretionary_policy
  void set_planner_discount(expr_t value);
  //! Sets the value of the planner_discount_latex_name option of ramsey_model and
  //! discretionary_policy
  void set_planner_discount_latex_name(string tex_name);
  //! Handles a “predetermined_variables” statement
  void predetermined_variables(const vector<string>& symbol_list);
  //! Declares and initializes a local parameter
  void declare_and_init_model_local_variable(const string& name, expr_t rhs);
  //! Changes type of a symbol
  void change_type(SymbolType new_type, const vector<string>& symbol_list);
  //! Adds a non-negative constant to DataTree
  expr_t add_non_negative_constant(const string& constant);
  //! Adds a NaN constant to DataTree
  expr_t add_nan_constant();
  //! Adds an Inf constant to DataTree
  expr_t add_inf_constant();
  //! Adds a model variable to ModelTree and VariableTable
  expr_t add_model_variable(const string& name);
  //! Declares a variable of type new_type OR changes a variable in the equations to type new_type
  //! and removes any error messages that may have been issued in model_errors
  expr_t declare_or_change_type(SymbolType new_type, const string& name);
  //! Adds an Expression's variable
  expr_t add_expression_variable(const string& name);
  // Adds a variable in the “self” namespace
  expr_t add_self_variable(const string& name, expr_t lag);
  // Adds a variable in the “initval” or “init” namespace
  expr_t add_initval_variable(const string& name);
  // Adds a variable in the “prev” namespace
  expr_t add_prev_variable(const string& name, expr_t lag = nullptr);
  // Adds a variable in a database namespace
  expr_t add_database_variable(const string& database_name, const string& symbol_name,
                               expr_t lag = nullptr);
  // Adds a variable in the “learnt_in” namespace
  expr_t add_learnt_in_variable(const variant<int, string>& learnt_in_period, const string& name,
                                expr_t lag = nullptr);
  //! Adds a "dsample" statement
  void dsample(const string& arg1);
  //! Adds a "dsample" statement
  void dsample(const string& arg1, const string& arg2);
  //! Writes parameter intitialisation expression
  void init_param(const string& name, expr_t rhs);
  //! Add a line inside an initval block
  void init_val(const string& name, expr_t rhs);
  //! Add a line inside an endval block
  void end_val(EndValLearntInStatement::LearntEndValType type, const string& name, expr_t rhs);
  //! Add a line inside a histval block
  void hist_val(const string& name, const string& lag, expr_t rhs);
  //! Adds an entry in a homotopy_setup block
  /*! Second argument "val1" can be NULL if no initial value provided */
  void homotopy_val(const string& name, expr_t val1, expr_t val2);
  //! Writes end of an initval block
  void end_initval(bool all_values_required);
  //! Writes end of an endval block
  void end_endval(bool all_values_required);
  //! Writes end of an endval(learnt_in=…) block
  void end_endval_learnt_in(variant<int, string> learnt_in_period);
  //! Writes end of an histval block
  void end_histval(bool all_values_required);
  //! Writes end of an homotopy_setup block
  void end_homotopy(bool from_initval_to_endval);
  //! Begin epilogue block
  void begin_epilogue();
  //! End epilogue block
  void end_epilogue();
  //! Add epilogue variable
  void add_epilogue_variable(const string& varname);
  //! Add equation in epilogue block
  void add_epilogue_equal(const string& varname, expr_t expr);
  /* Begin a model block (without heterogeneity option), or an expression as an option value of some
     statement. Must be followed by a call to reset_data_tree(). */
  void begin_model();
  // Begin a model(heterogeneity=…) block
  void begin_heterogeneous_model(const string& heterogeneity_dimension);
  //! End a model or model_replace block, printing errors that were encountered in parsing
  void end_model();
  //! Writes a shocks statement
  void end_shocks(bool overwrite);
  //! Writes a mshocks statement
  void end_mshocks(bool overwrite, bool relative_to_initval);
  //! Writes a shocks(surprise) statement
  void end_shocks_surprise(bool overwrite);
  //! Writes a shocks(learnt_in=…) block
  void end_shocks_learnt_in(variant<int, string> learnt_in_period, bool overwrite);
  // For a shocks(heterogeneity=…) block
  void end_heterogeneous_shocks(const string& heterogeneity_dimension, bool overwrite);
  //! Writes a mshocks(learnt_in=…) block
  void end_mshocks_learnt_in(variant<int, string> learnt_in_period, bool overwrite,
                             bool relative_to_initval);
  //! Writes a heteroskedastic_shocks statement
  void end_heteroskedastic_shocks(bool overwrite);
  /* Adds a deterministic shock, a path element inside a
     conditional_forecast_paths block, or a surprise shock */
  enum class DetShockType
  {
    standard,
    add,      // for “add” in “shocks(learnt_in)”
    multiply, // for “multiply” in “shocks(learnt_in)”
    conditional_forecast
  };
  void add_det_shock(const string& var,
                     const vector<AbstractShocksStatement::period_range_t>& periods,
                     const vector<expr_t>& values, DetShockType type);
  // Adds an exogenous path inside a shock_paths block
  void add_shock_paths_exo_elem(const string& var,
                                const vector<ShockPathsStatement::period_range_t>& periods,
                                const vector<expr_t>& values);
  // Adds a controlled path inside a shock_paths block
  void begin_shock_paths_controlled_elem();
  void
  end_shock_paths_controlled_elem(const string& exogenize,
                                  const vector<AbstractShocksStatement::period_range_t>& periods,
                                  const vector<expr_t>& values, const string& endogenize);
  //! Adds a heteroskedastic shock (either values or scales)
  void add_heteroskedastic_shock(const string& var,
                                 const vector<AbstractShocksStatement::period_range_t>& periods,
                                 const vector<expr_t>& values, bool scales);
  //! Adds a std error shock
  void add_stderr_shock(const string& var, expr_t value);
  //! Adds a variance shock
  void add_var_shock(const string& var, expr_t value);
  //! Adds a covariance shock
  void add_covar_shock(const string& var1, const string& var2, expr_t value);
  //! Adds a correlated shock
  void add_correl_shock(const string& var1, const string& var2, expr_t value);
  //! Adds a skewness shock (single variable)
  void add_skew_single_shock(const string& var, expr_t value);
  //! Adds a co-skewness shock (three variables)
  void add_skew_triple_shock(const string& var1, const string& var2, const string& var3,
                             expr_t value);
  //! Adds a shock period range
  void add_period(const string& p1, const string& p2);
  //! Adds a shock period
  void add_period(const string& p1);
  //! Adds a deterministic shock value
  void add_value(expr_t value);
  //! Adds a deterministic shock value
  /*! \param v a string containing a (possibly negative) numeric constant */
  void add_value(const string& v);
  //! Write a steady command
  void steady();
  //! Sets an option to a numerical value
  void option_num(string name_option, string opt);
  //! Sets an option to a numerical value
  void option_num(string name_option, string opt1, string opt2);
  //! Sets an option to a string value
  void option_str(string name_option, string opt);
  //! Sets an option to a date value
  void option_date(string name_option, string opt);
  //! Sets an option to a list of symbols
  void option_symbol_list(string name_option, vector<string> symbol_list);
  //! Sets an option to a vector of integers
  void option_vec_int(string name_option, vector<int> opt);
  //! Sets an option to a vector of strings
  void option_vec_str(string name_option, vector<string> opt);
  //! Sets an option to a celll array of strings
  void option_vec_cellstr(string name_option, vector<string> opt);
  //! Sets an option to a mixed string/integer list (outputs as MATLAB cell array)
  void option_str_or_int_list(string name_option, vector<string> opt);
  //! Sets an option to a vector of (numerical) values
  void option_vec_value(string name_option, vector<string> opt);
  //! Sets an option to a vector of vectors of (numerical) values
  void option_vec_of_vec_value(string name_option, vector<vector<string>> opt);
  //! Indicates that the model is linear
  void linear();
  //! Writes a rplot() command
  void rplot(vector<string> symbol_list);
  //! Writes a stock_simul command
  void stoch_simul(SymbolList symbol_list);
  //! Writes a trend component command
  void trend_component_model();
  //! Writes a var (vector autoregression) command
  void var_model();
  //! Writes a simul command
  void simul();
  //! Writes check command
  void check();
  //! Writes model_info command
  void model_info();
  //! Writes estimated params command
  void estimated_params(bool overwrite);
  //! Writes estimated params init command
  void estimated_params_init(bool use_calibration = false);
  //! Writes estimated params bound command
  void estimated_params_bounds();
  //! Add an estimated_params_remove block
  void estimated_params_remove();
  //! Adds a declaration for a user-defined external function
  void external_function(const map<string, string>& options);
  //! Add a line in an estimated params block
  void add_estimated_params_element();
  //! Writes osr params bounds command
  void osr_params_bounds();
  //! Add a line in an osr params block
  void add_osr_params_element();
  // Sets the initial period for estimation
  void set_time(string period);
  //! Estimation Data
  void estimation_data();
  //! Sets the prior for a parameter
  void set_prior(string name, string subsample_name);
  //! Sets the joint prior for a set of parameters
  void set_joint_prior(const vector<string>& symbol_vec);
  //! Adds a parameters to the list of joint parameters
  void add_joint_parameter(string name);
  //! Adds the variance option to its temporary holding place
  void set_prior_variance(expr_t variance = nullptr);
  //! Copies the prior from_name to_name
  void copy_prior(string to_declaration_type, string to_name1, string to_name2,
                  string to_subsample_name, string from_declaration_type, string from_name1,
                  string from_name2, string from_subsample_name);
  //! Sets the options for a parameter
  void set_options(string name, string subsample_name);
  //! Copies the options from_name to_name
  void copy_options(string to_declaration_type, string to_name1, string to_name2,
                    string to_subsample_name, string from_declaration_type, string from_name1,
                    string from_name2, string from_subsample_name);
  //! Sets the prior for estimated std dev
  void set_std_prior(string name, string subsample_name);
  //! Sets the options for estimated std dev
  void set_std_options(string name, string subsample_name);
  //! Sets the prior for estimated correlation
  void set_corr_prior(string name1, string name2, string subsample_name);
  //! Sets the options for estimated correlation
  void set_corr_options(string name1, string name2, string subsample_name);
  //! Runs estimation process
  void run_estimation(vector<string> symbol_list);
  //! Runs sensitivity
  void sensitivity();
  //! Check that no observed variable has yet be defined
  void check_varobs();
  //! Add a new observed variable
  void add_varobs(const string& name);
  //! Svar_Identification Statement
  void begin_svar_identification();
  void end_svar_identification();
  //! Svar_Identification Statement: match list of restrictions and equation number with lag
  void combine_lag_and_restriction(const string& lag);
  //! Svar_Identification Statement: match list of restrictions with equation number
  void add_restriction_in_equation(const string& equation, const vector<string>& symbol_list);
  //! Svar_Identification Statement: add exclusions of constants
  void add_constants_exclusion();
  //! Svar_Identification Statement: add equation number for following restriction equations
  void add_restriction_equation_nbr(const string& eq_nbr);
  //! Svar_Identification Statement: record presence of equal sign
  void add_restriction_equal();
  //! Svar_Idenditification Statement: add coefficient of a linear restriction (positive value)
  void add_positive_restriction_element(expr_t value, const string& variable, const string& lag);
  //! Svar_Idenditification Statement: add unit coefficient of a linear restriction
  void add_positive_restriction_element(const string& variable, const string& lag);
  //! Svar_Idenditification Statement: add coefficient of a linear restriction (negative value)
  void add_negative_restriction_element(expr_t value, const string& variable, const string& lag);
  //! Svar_Idenditification Statement: add negative unit coefficient of a linear restriction
  void add_negative_restriction_element(const string& variable, const string& lag);
  //! Svar_Idenditification Statement: add restriction element
  void add_restriction_element(expr_t value, const string& variable, const string& lag);
  //! Svar_Identification Statement: check that restriction is homogenous
  void check_restriction_expression_constant(expr_t value);
  //! Svar_Identification Statement: restriction of form upper cholesky
  void add_upper_cholesky();
  //! Svar_Identification Statement: restriction of form lower cholesky
  void add_lower_cholesky();
  //! Svar_Global_Identification_Check Statement
  void add_svar_global_identification_check();
  //! generate_irfs Block
  void end_generate_irfs();
  void add_generate_irfs_element(string name);
  void add_generate_irfs_exog_element(string exo, const string& value);
  //! Forecast Statement
  void forecast(vector<string> symbol_list);
  void set_trends();
  void set_deterministic_trends();
  void set_trend_element(const string& arg1, expr_t arg2);
  //! filter_initial_state block
  void set_filter_initial_state();
  //! element for filter_initial_state block
  void set_filter_initial_state_element(const string& name, const string& lag, expr_t rhs);
  void optim_weights();
  void set_optim_weights(string name, expr_t value);
  void set_optim_weights(const string& name1, const string& name2, expr_t value);
  void set_osr_params(vector<string> symbol_list);
  void run_osr(vector<string> symbol_list);
  void run_dynasave(string filename, vector<string> symbol_list);
  void run_dynatype(string filename, vector<string> symbol_list);
  void run_load_params_and_steady_state(const string& filename);
  void run_save_params_and_steady_state(string filename);
  void run_identification();
  void heterogeneity_load_steady_state();
  void heterogeneity_compute_steady_state();
  void heterogeneity_solve();
  void heterogeneity_simulate(vector<string> symbol_list = {});
  void add_mc_filename(string filename, string prior = "1");
  void run_model_comparison();
  //! Begin a planner_objective statement
  void begin_planner_objective();
  //! End a planner objective statement
  void end_planner_objective(expr_t expr);
  //! Ramsey model statement
  void ramsey_model();
  // Ramsey constraints statement
  void begin_ramsey_constraints();
  void end_ramsey_constraints(const vector<expr_t>& constraints);
  //! Ramsey policy statement
  void ramsey_policy(vector<string> symbol_list);
  //! Evaluate Planner Objective
  void evaluate_planner_objective();
  //! Set up Occbin
  void occbin_setup();
  //! Run Occbin solver
  void occbin_solver();
  //! Run Occbin write XLS
  void occbin_write_regimes();
  //! Run plotting of Occbin results
  void occbin_graph(vector<string> symbol_list);
  //! Discretionary policy statement
  void discretionary_policy(vector<string> symbol_list);
  //! Adds a write_latex_dynamic_model statement
  void write_latex_dynamic_model(bool write_equation_tags);
  //! Adds a write_latex_static_model statement
  void write_latex_static_model(bool write_equation_tags);
  //! Adds a write_latex_original_model statement
  void write_latex_original_model(bool write_equation_tags);
  //! Adds a write_latex_steady_state_model statement
  void write_latex_steady_state_model();
  //! BVAR marginal density
  void bvar_density(const string& maxnlags);
  //! BVAR forecast
  void bvar_forecast(const string& nlags);
  //! BVAR IRF
  void bvar_irf(const string& nirf, string identificationname);
  //! SBVAR statement
  void sbvar();
  //! Markov Switching Statement: Estimation
  void ms_estimation();
  //! Markov Switching Statement: Simulation
  void ms_simulation();
  //! Markov Switching Statement: MDD
  void ms_compute_mdd();
  //! Markov Switching Statement: Probabilities
  void ms_compute_probabilities();
  //! Markov Switching Statement: IRF
  void ms_irf(vector<string> symbol_list);
  //! Markov Switching Statement: Forecast
  void ms_forecast();
  //! Markov Switching Statement: Variance Decomposition
  void ms_variance_decomposition();
  //! Svar statement
  void svar();
  //! MarkovSwitching statement
  void markov_switching();
  //! Shock decomposition
  void shock_decomposition(vector<string> symbol_list);
  //! Realtime Shock decomposition
  void realtime_shock_decomposition(vector<string> symbol_list);
  //! Plot Shock decomposition
  void plot_shock_decomposition(vector<string> symbol_list);
  //! Initial Condition decomposition
  void initial_condition_decomposition(vector<string> symbol_list);
  //! squeeze_shock_decomposition statement
  void squeeze_shock_decomposition(vector<string> symbol_list);
  //! Conditional forecast statement
  void conditional_forecast();
  //! Conditional forecast paths block
  void conditional_forecast_paths();
  //! Plot conditional forecast statement
  void plot_conditional_forecast(const optional<string>& periods, vector<string> symbol_list);
  //! Smoother on calibrated models
  void calib_smoother(vector<string> symbol_list);
  //! Extended path
  void extended_path();
  //! Writes token "arg1=arg2" to model tree
  expr_t add_model_equal(expr_t arg1, expr_t arg2, map<string, string> eq_tags,
                         expr_t complementarity_condition = nullptr);
  //! Writes token "arg=0" to model tree
  expr_t add_model_equal_with_zero_rhs(expr_t arg, map<string, string> eq_tags,
                                       expr_t complementarity_condition = nullptr);
  //! Writes token "arg1+arg2" to model tree
  expr_t add_plus(expr_t arg1, expr_t arg2);
  //! Writes token "arg1-arg2" to model tree
  expr_t add_minus(expr_t arg1, expr_t arg2);
  //! Writes token "-arg1" to model tree
  expr_t add_uminus(expr_t arg1);
  //! Writes token "arg1*arg2" to model tree
  expr_t add_times(expr_t arg1, expr_t arg2);
  //! Writes token "arg1/arg2" to model tree
  expr_t add_divide(expr_t arg1, expr_t arg2);
  //! Writes token "arg1<arg2" to model tree
  expr_t add_less(expr_t arg1, expr_t arg2);
  //! Writes token "arg1>arg2" to model treeexpr_t
  expr_t add_greater(expr_t arg1, expr_t arg2);
  //! Writes token "arg1<=arg2" to model treeexpr_t
  expr_t add_less_equal(expr_t arg1, expr_t arg2);
  //! Writes token "arg1>=arg2" to model treeexpr_t
  expr_t add_greater_equal(expr_t arg1, expr_t arg2);
  //! Writes token "arg1==arg2" to model treeexpr_texpr_t
  expr_t add_equal_equal(expr_t arg1, expr_t arg2);
  //! Writes token "arg1!=arg2" to model treeexpr_texpr_t
  expr_t add_different(expr_t arg1, expr_t arg2);
  //! Writes token "arg1^arg2" to model tree
  expr_t add_power(expr_t arg1, expr_t arg2);
  //! Writes token "E(arg1)(arg2)" to model tree
  expr_t add_expectation(const string& arg1, expr_t arg2);
  //! Writes token "VAR_EXPECTATION(model_name)" to model tree
  expr_t add_var_expectation(const string& model_name);
  //! Writes token "PAC_EXPECTATION(model_name, discount, growth)" to model tree
  expr_t add_pac_expectation(const string& model_name);
  //! Adds a pac_target_nonstationary(model_name, discount, growth) node to model tree
  expr_t add_pac_target_nonstationary(const string& model_name);
  //! Creates pac_model statement
  void begin_pac_model();
  void pac_model();
  //! Adds growth for pac
  void set_pac_growth(expr_t pac_growth_arg);
  //! Sets the value of “auxname” option for the current “pac_model”
  void set_pac_auxname(string auxname);
  //! Sets the value of “kind” option for the current “pac_model”
  void set_pac_kind(PacTargetKind kind);
  //! Writes token "diff(arg1)" to model tree
  expr_t add_diff(expr_t arg1);
  //! Writes token "exp(arg1)" to model tree
  expr_t add_exp(expr_t arg1);
  //! Writes token "log(arg1)" to model tree
  expr_t add_log(expr_t arg1);
  //! Writes token "log10(arg1)" to model tree
  expr_t add_log10(expr_t arg1);
  //! Writes token "cos(arg1)" to model tree
  expr_t add_cos(expr_t arg1);
  //! Writes token "sin(arg1)" to model tree
  expr_t add_sin(expr_t arg1);
  //! Writes token "tan(arg1)" to model tree
  expr_t add_tan(expr_t arg1);
  //! Writes token "acos(arg1)" to model tree
  expr_t add_acos(expr_t arg1);
  //! Writes token "asin(arg1)" to model tree
  expr_t add_asin(expr_t arg1);
  //! Writes token "atan(arg1)" to model tree
  expr_t add_atan(expr_t arg1);
  //! Writes token "cosh(arg1)" to model tree
  expr_t add_cosh(expr_t arg1);
  //! Writes token "sinh(arg1)" to model tree
  expr_t add_sinh(expr_t arg1);
  //! Writes token "tanh(arg1)" to model tree
  expr_t add_tanh(expr_t arg1);
  //! Writes token "acosh(arg1)" to model tree
  expr_t add_acosh(expr_t arg1);
  //! Writes token "asin(arg1)" to model tree
  expr_t add_asinh(expr_t arg1);
  //! Writes token "atanh(arg1)" to model tree
  expr_t add_atanh(expr_t arg1);
  //! Writes token "sqrt(arg1)" to model tree
  expr_t add_sqrt(expr_t arg1);
  //! Writes token "cbrt(arg1)" to model tree
  expr_t add_cbrt(expr_t arg1);
  //! Writes token "abs(arg1)" to model tree
  expr_t add_abs(expr_t arg1);
  //! Writes token "sign(arg1)" to model tree
  expr_t add_sign(expr_t arg1);
  //! Writes token "max(arg1,arg2)" to model tree
  expr_t add_max(expr_t arg1, expr_t arg2);
  //! Writes token "min(arg1,arg2)" to model tree
  expr_t add_min(expr_t arg1, expr_t arg2);
  //! Writes token "normcdf(arg1,arg2,arg3)" to model tree
  expr_t add_normcdf(expr_t arg1, expr_t arg2, expr_t arg3);
  //! Writes token "normcdf(arg,0,1)" to model tree
  expr_t add_normcdf(expr_t arg);
  //! Writes token "normpdf(arg1,arg2,arg3)" to model tree
  expr_t add_normpdf(expr_t arg1, expr_t arg2, expr_t arg3);
  //! Writes token "normpdf(arg,0,1)" to model tree
  expr_t add_normpdf(expr_t arg);
  //! Writes token "erf(arg)" to model tree
  expr_t add_erf(expr_t arg);
  //! Writes token "erfc(arg)" to model tree
  expr_t add_erfc(expr_t arg);
  //! Writes token "steadyState(arg1)" to model tree
  expr_t add_steady_state(expr_t arg1);
  // Add a “sum(arg)” node to model tree
  expr_t add_sum(expr_t arg);
  /* Adds a variable with a lead/lag or an external function call.
     Can be called from both an expression context and a model expression context.
     If this is an external function call, name can be namespace-qualified, i.e. have one
     or several dots separating the namespace(s) from the function name itself */
  expr_t add_lead_lag_var_or_external_function(const string& name, vector<expr_t> arguments,
                                               bool in_model_expression);
  //! Adds a native statement
  void add_native(string s);
  //! Adds a native statement, first removing the set of characters passed in token (and everything
  //! after)
  void add_native_remove_charset(string_view str, string_view token);
  //! Adds a verbatim statement
  void add_verbatim(string s);
  //! Adds a verbatim statement, first removing the set of characters passed in token (and
  //! everything after)
  void add_verbatim_remove_charset(string_view str, string_view token);
  //! Resets data_tree and model_tree pointers to default (i.e. mod_file->expressions_tree)
  void reset_data_tree();
  //! Begin a steady_state_model block
  void begin_steady_state_model();
  //! Add an assignment equation in steady_state_model block
  void add_steady_state_model_equal(const string& varname, expr_t expr);
  //! Add a multiple assignment equation in steady_state_model block
  void add_steady_state_model_equal_multiple(const vector<string>& symbol_list, expr_t expr);
  // Add an implicit definition in steady_state_model block
  void add_steady_state_model_solve_from_equation(const string& symbol, const string& eqname);
  //! Ends declaration of trend variable
  void end_trend_var(bool log_trend, expr_t growth_factor,
                     const vector<pair<string, string>>& symbol_list);
  //! Handles a “var(deflator=…)”, “var(log, deflator=…)” or “var(log_deflator=…)” statement
  void end_nonstationary_var(
      bool log_deflator, expr_t deflator,
      const vector<tuple<string, string, vector<pair<string, string>>>>& symbol_list,
      bool log_option);
  //! Add a graph format to the list of formats requested
  void add_graph_format(string name);
  //! Add the graph_format option to the OptionsList structure
  void process_graph_format_option();
  //! Add the graph_format option to the initial_condition_decomp substructure of the OptionsList
  //! structure
  void initial_condition_decomp_process_graph_format_option();
  //! Add the graph_format option to the plot_shock_decomp substructure of the OptionsList structure
  void plot_shock_decomp_process_graph_format_option();
  //! Model diagnostics
  void model_diagnostics();
  //! Processing the parallel_local_files option
  void add_parallel_local_file(string filename);
  //! Add an item of a moment_calibration statement
  void add_moment_calibration_item(const string& endo1, const string& endo2, string lags,
                                   const pair<expr_t, expr_t>& range);
  //! End a moment_calibration statement
  void end_moment_calibration();
  //! Add an item of an irf_calibration statement
  void add_irf_calibration_item(const string& endo, string periods, const string& exo,
                                const pair<expr_t, expr_t>& range);
  //! End a moment_calibration statement
  void end_irf_calibration();
  //! Add a shock to a group
  void add_shock_group_element(string name);
  //! Add a set of shock groups
  void add_shock_group(string name);
  //! End shock groups declaration
  void end_shock_groups(string name);
  //! Add a set of init2shocks
  void add_init2shocks(const string& endo_name, const string& exo_name);
  //! End init2shocks declaration
  void end_init2shocks(string name);
  void smoother2histval();
  void histval_file();
  void perfect_foresight_setup();
  void perfect_foresight_solver();
  void perfect_foresight_with_expectation_errors_setup();
  void perfect_foresight_with_expectation_errors_solver();
  void perfect_foresight_controlled_paths(
      const vector<tuple<string, vector<AbstractShocksStatement::period_range_t>, vector<expr_t>,
                         string>>& paths,
      variant<int, string> learnt_in_period);
  void prior_posterior_function(bool prior_func);
  //! Method of Moments estimation statement
  void method_of_moments();
  //! Add a var_expectation_model statement
  void var_expectation_model();
  //! Start parsing a matched_moments block
  void begin_matched_moments();
  //! Add a matched_moments block
  void end_matched_moments(const vector<expr_t>& moments);
  //! Start parsing an occbin_constraints block
  void begin_occbin_constraints();
  //! Add an occbin_constraints block
  void end_occbin_constraints(
      vector<tuple<string, BinaryOpNode*, BinaryOpNode*, expr_t, expr_t>> constraints);
  // Process a model_remove statement
  void model_remove(const vector<map<string, string>>& listed_eqs_by_tags);
  // Begin a model_replace statement
  void begin_model_replace(const vector<map<string, string>>& listed_eqs_by_tags);
  // Add a var_remove statement
  void var_remove(const vector<string>& symbol_list);
  void begin_pac_target_info(string name);
  void end_pac_target_info();
  void set_pac_target_info_target(expr_t target);
  void set_pac_target_info_auxname_target_nonstationary(string auxname);
  void add_pac_target_info_component(expr_t component_expr);
  void set_pac_target_info_component_growth(expr_t growth);
  void set_pac_target_info_component_auxname(string auxname);
  void set_pac_target_info_component_kind(PacTargetKind kind);
  // Add a resid statement
  void resid();
  // Add a matched_irfs block
  void matched_irfs(MatchedIrfsStatement::matched_irfs_t values_weights, bool overwrite);
  // Add a matched_irfs_weights block
  void matched_irfs_weights(MatchedIrfsWeightsStatement::matched_irfs_weights_t weights,
                            bool overwrite);
  void heterogeneity_dimension(const vector<string>& dims);
  void database(const vector<string>& names);
  [[nodiscard]] bool database_exists(const string& name) const;
  void begin_shock_paths(const variant<int, string>& learnt_in_period);
  void end_shock_paths(bool overwrite);

  void add_filter_tunes_elem(const string& name,
                             vector<AbstractShocksStatement::period_range_t>&& periods,
                             const vector<expr_t>& values, const vector<expr_t>& stderrs);
  void end_filter_tunes();

  // Returns true iff the string is a legal symbol identifier (see NAME token in lexer)
  static bool isSymbolIdentifier(const string& str);
  // Given an Occbin regime name, returns the corresponding auxiliary parameter
  static string
  buildOccbinBindParamName(const string& regime)
  {
    return "occbin_" + regime + "_bind";
  }
};

#endif
