From c9f5cef99b27c7b3a728f8d200b3dee1bc482356 Mon Sep 17 00:00:00 2001
From: Houtan Bastani <houtan@dynare.org>
Date: Tue, 18 Sep 2018 14:50:31 +0200
Subject: [PATCH] write AST in JSON

---
 src/DynamicModel.cc |  42 ++++++
 src/DynamicModel.hh |   3 +
 src/ExprNode.cc     | 307 ++++++++++++++++++++++++++++++++++++++++++++
 src/ExprNode.hh     |  15 +++
 4 files changed, 367 insertions(+)

diff --git a/src/DynamicModel.cc b/src/DynamicModel.cc
index 41892256..127ab721 100644
--- a/src/DynamicModel.cc
+++ b/src/DynamicModel.cc
@@ -6057,6 +6057,46 @@ DynamicModel::writeJsonOutput(ostream &output) const
   writeJsonModelEquations(output, false);
   output << ", ";
   writeJsonXrefs(output);
+  output << ", ";
+  writeJsonAST(output);
+}
+
+void
+DynamicModel::writeJsonAST(ostream &output) const
+{
+  vector<pair<string, string>> eqtags;
+  output << "\"Abstract Syntax Tree\":[" << endl;
+  for (int eq = 0; eq < (int) equations.size(); eq++)
+    {
+      if (eq != 0)
+        output << ", ";
+
+      output << "{ \"number\":" << eq
+             << ", \"line\":" << equations_lineno[eq];
+
+      for (const auto & equation_tag : equation_tags)
+        if (equation_tag.first == eq)
+          eqtags.push_back(equation_tag.second);
+
+      if (!eqtags.empty())
+        {
+          output << ", \"tags\": {";
+          int i = 0;
+          for (vector<pair<string, string>>::const_iterator it = eqtags.begin(); it != eqtags.end(); it++, i++)
+            {
+              if (i != 0)
+                output << ", ";
+              output << "\"" << it->first << "\": \"" << it->second << "\"";
+            }
+          output << "}";
+          eqtags.clear();
+        }
+
+      output << ", \"AST\": ";
+      equations[eq]->writeJsonAST(output);
+      output << "}";
+    }
+  output << "]";
 }
 
 void
@@ -6103,6 +6143,8 @@ void
 DynamicModel::writeJsonOriginalModelOutput(ostream &output) const
 {
   writeJsonModelEquations(output, false);
+  output << ", ";
+  writeJsonAST(output);
 }
 
 void
diff --git a/src/DynamicModel.hh b/src/DynamicModel.hh
index d6e4bc01..2ec940ab 100644
--- a/src/DynamicModel.hh
+++ b/src/DynamicModel.hh
@@ -284,6 +284,9 @@ public:
   //! Writes model initialization and lead/lag incidence matrix to output
   void writeOutput(ostream &output, const string &basename, bool block, bool byte_code, bool use_dll, int order, bool estimation_present, bool compute_xrefs, bool julia) const;
 
+  //! Write JSON AST
+  void writeJsonAST(ostream &output) const;
+
   //! Write JSON Output
   void writeJsonOutput(ostream &output) const;
 
diff --git a/src/ExprNode.cc b/src/ExprNode.cc
index f9e8a10e..d26f2ff1 100644
--- a/src/ExprNode.cc
+++ b/src/ExprNode.cc
@@ -359,6 +359,12 @@ NumConstNode::writeOutput(ostream &output, ExprNodeOutputType output_type,
     output << datatree.num_constants.get(id);
 }
 
+void
+NumConstNode::writeJsonAST(ostream &output) const
+{
+  output << "{\"node_type\" : \"NumConstNode\", \"value\" : " << datatree.num_constants.get(id) << "}";
+}
+
 void
 NumConstNode::writeJsonOutput(ostream &output,
                               const temporary_terms_t &temporary_terms,
@@ -810,6 +816,62 @@ VariableNode::containsExternalFunction() const
   return false;
 }
 
+void
+VariableNode::writeJsonAST(ostream &output) const
+{
+  output << "{\"node_type\" : \"VariableNode\", "
+         << "\"name\" : \"" << datatree.symbol_table.getName(symb_id) << "\", \"type\" : \"";
+  switch (type)
+    {
+    case SymbolType::endogenous:
+      output << "endogenous";
+      break;
+    case SymbolType::exogenous:
+      output << "exogenous";
+      break;
+    case SymbolType::exogenousDet:
+      output << "exogenousDet";
+      break;
+    case SymbolType::parameter:
+      output << "parameter";
+      break;
+    case SymbolType::modelLocalVariable:
+      output << "modelLocalVariable";
+      break;
+    case SymbolType::modFileLocalVariable:
+      output << "modFileLocalVariable";
+      break;
+    case SymbolType::externalFunction:
+      output << "externalFunction";
+      break;
+    case SymbolType::trend:
+      output << "trend";
+      break;
+    case SymbolType::statementDeclaredVariable:
+      output << "statementDeclaredVariable";
+      break;
+    case SymbolType::logTrend:
+      output << "logTrend:";
+      break;
+    case SymbolType::unusedEndogenous:
+      output << "unusedEndogenous";
+      break;
+    case SymbolType::endogenousVAR:
+      output << "endogenousVAR";
+      break;
+    case SymbolType::endogenousEpilogue:
+      output << "endogenousEpilogue";
+      break;
+    case SymbolType::exogenousEpilogue:
+      output << "exogenousEpilogue";
+      break;
+    case SymbolType::parameterEpilogue:
+      output << "parameterEpilogue";
+      break;
+    }
+  output << "\", \"lag\" : " << lag << "}";
+}
+
 void
 VariableNode::writeJsonOutput(ostream &output,
                               const temporary_terms_t &temporary_terms,
@@ -2311,6 +2373,111 @@ UnaryOpNode::containsExternalFunction() const
   return arg->containsExternalFunction();
 }
 
+void
+UnaryOpNode::writeJsonAST(ostream &output) const
+{
+  output << "{\"node_type\" : \"UnaryOpNode\", \"op\" : \"";
+  switch (op_code)
+    {
+    case UnaryOpcode::uminus:
+      output << "uminus";
+      break;
+    case UnaryOpcode::exp:
+      output << "exp";
+      break;
+    case UnaryOpcode::log:
+      output << "log";
+      break;
+    case UnaryOpcode::log10:
+      output << "log10";
+      break;
+    case UnaryOpcode::cos:
+      output << "cos";
+      break;
+    case UnaryOpcode::sin:
+      output << "sin";
+      break;
+    case UnaryOpcode::tan:
+      output << "tan";
+      break;
+    case UnaryOpcode::acos:
+      output << "acos";
+      break;
+    case UnaryOpcode::asin:
+      output << "asin";
+      break;
+    case UnaryOpcode::atan:
+      output << "atan";
+      break;
+    case UnaryOpcode::cosh:
+      output << "cosh";
+      break;
+    case UnaryOpcode::sinh:
+      output << "sinh";
+      break;
+    case UnaryOpcode::tanh:
+      output << "tanh";
+      break;
+    case UnaryOpcode::acosh:
+      output << "acosh";
+      break;
+    case UnaryOpcode::asinh:
+      output << "asinh";
+      break;
+    case UnaryOpcode::atanh:
+      output << "atanh";
+      break;
+    case UnaryOpcode::sqrt:
+      output << "sqrt";
+      break;
+    case UnaryOpcode::abs:
+      output << "abs";
+      break;
+    case UnaryOpcode::sign:
+      output << "sign";
+      break;
+    case UnaryOpcode::diff:
+      output << "diff";
+      break;
+    case UnaryOpcode::adl:
+      output << "adl";
+      break;
+    case UnaryOpcode::steadyState:
+      output << "steady_state";
+    case UnaryOpcode::steadyStateParamDeriv:
+      output << "steady_state_param_deriv";
+      break;
+    case UnaryOpcode::steadyStateParam2ndDeriv:
+      output << "steady_state_param_second_deriv";
+      break;
+    case UnaryOpcode::expectation:
+      output << "expectation";
+      break;
+    case UnaryOpcode::erf:
+      output << "erf";
+      break;
+    }
+  output << "\", \"arg\" : ";
+  arg->writeJsonAST(output);
+  switch (op_code)
+    {
+    case UnaryOpcode::adl:
+      output << ", \"adl_param_name\" : \"" << adl_param_name << "\""
+             << ", \"lags\" : [";
+      for (auto it = adl_lags.begin(); it != adl_lags.end(); it++)
+        {
+          if (it != adl_lags.begin())
+            output << ", ";
+          output << *it;
+        }
+      output << "]";
+      break;
+    default:
+      break;
+    }
+  output << "}";
+}
+
 void
 UnaryOpNode::writeJsonOutput(ostream &output,
                              const temporary_terms_t &temporary_terms,
@@ -4104,6 +4271,66 @@ BinaryOpNode::containsExternalFunction() const
     || arg2->containsExternalFunction();
 }
 
+void
+BinaryOpNode::writeJsonAST(ostream &output) const
+{
+  output << "{\"node_type\" : \"BinaryOpNode\","
+         << " \"op\" : \"";
+  switch (op_code)
+    {
+    case BinaryOpcode::plus:
+      output << "+";
+      break;
+    case BinaryOpcode::minus:
+      output << "-";
+      break;
+    case BinaryOpcode::times:
+      output << "*";
+      break;
+    case BinaryOpcode::divide:
+      output << "/";
+      break;
+    case BinaryOpcode::power:
+      output << "^";
+      break;
+    case BinaryOpcode::less:
+      output << "<";
+      break;
+    case BinaryOpcode::greater:
+      output << ">";
+      break;
+    case BinaryOpcode::lessEqual:
+      output << "<=";
+      break;
+    case BinaryOpcode::greaterEqual:
+      output << ">=";
+      break;
+    case BinaryOpcode::equalEqual:
+      output << "==";
+      break;
+    case BinaryOpcode::different:
+      output << "!=";
+      break;
+    case BinaryOpcode::equal:
+      output << "=";
+      break;
+    case BinaryOpcode::max:
+      output << "max";
+      break;
+    case BinaryOpcode::min:
+      output << "min";
+      break;
+    case BinaryOpcode::powerDeriv:
+      output << "power_deriv";
+      break;
+    }
+  output << "\", \"arg1\" : ";
+  arg1->writeJsonAST(output);
+  output << ", \"arg2\" : ";
+  arg2->writeJsonAST(output);
+  output << "}";
+}
+
 void
 BinaryOpNode::writeJsonOutput(ostream &output,
                               const temporary_terms_t &temporary_terms,
@@ -5951,6 +6178,29 @@ TrinaryOpNode::containsExternalFunction() const
     || arg3->containsExternalFunction();
 }
 
+void
+TrinaryOpNode::writeJsonAST(ostream &output) const
+{
+  output << "{\"node_type\" : \"TrinaryOpNode\", "
+         << "\"op\" : \"";
+  switch (op_code)
+    {
+    case TrinaryOpcode::normcdf:
+      output << "normcdf";
+      break;
+    case TrinaryOpcode::normpdf:
+      output << "normpdf";
+      break;
+    }
+  output << "\", \"arg1\" : ";
+  arg1->writeJsonAST(output);
+  output << ", \"arg2\" : ";
+  arg2->writeJsonAST(output);
+  output << ", \"arg2\" : ";
+  arg3->writeJsonAST(output);
+  output << "}";
+}
+
 void
 TrinaryOpNode::writeJsonOutput(ostream &output,
                                const temporary_terms_t &temporary_terms,
@@ -7109,6 +7359,22 @@ AbstractExternalFunctionNode::writeExternalFunctionArguments(ostream &output, Ex
     }
 }
 
+void
+AbstractExternalFunctionNode::writeJsonASTExternalFunctionArguments(ostream &output) const
+{
+  int i = 0;
+  output << "{";
+  for (auto it = arguments.begin(); it != arguments.end(); it++, i++)
+    {
+      if (it != arguments.begin())
+        output << ",";
+
+      output << "\"arg" << i << "\" : ";
+      (*it)->writeJsonAST(output);
+    }
+    output << "}";
+}
+
 void
 AbstractExternalFunctionNode::writeJsonExternalFunctionArguments(ostream &output,
                                                                  const temporary_terms_t &temporary_terms,
@@ -7292,6 +7558,15 @@ ExternalFunctionNode::compileExternalFunctionOutput(ostream &CompileCode, unsign
     }
 }
 
+void
+ExternalFunctionNode::writeJsonAST(ostream &output) const
+{
+  output << "{\"node_type\" : \"ExternalFunctionNode\", "
+         << "\"name\" : \"" << datatree.symbol_table.getName(symb_id) << "\", \"args\" : [";
+  writeJsonASTExternalFunctionArguments(output);
+  output << "]}";
+}
+
 void
 ExternalFunctionNode::writeJsonOutput(ostream &output,
                                       const temporary_terms_t &temporary_terms,
@@ -7527,6 +7802,15 @@ FirstDerivExternalFunctionNode::composeDerivatives(const vector<expr_t> &dargs)
   return theDeriv;
 }
 
+void
+FirstDerivExternalFunctionNode::writeJsonAST(ostream &output) const
+{
+  output << "{\"node_type\" : \"FirstDerivExternalFunctionNode\", "
+         << "\"name\" : \"" << datatree.symbol_table.getName(symb_id) << "\", \"args\" : [";
+  writeJsonASTExternalFunctionArguments(output);
+  output << "]}";
+}
+
 void
 FirstDerivExternalFunctionNode::writeJsonOutput(ostream &output,
                                                 const temporary_terms_t &temporary_terms,
@@ -7907,6 +8191,15 @@ SecondDerivExternalFunctionNode::composeDerivatives(const vector<expr_t> &dargs)
   exit(EXIT_FAILURE);
 }
 
+void
+SecondDerivExternalFunctionNode::writeJsonAST(ostream &output) const
+{
+  output << "{\"node_type\" : \"SecondDerivExternalFunctionNode\", "
+         << "\"name\" : \"" << datatree.symbol_table.getName(symb_id) << "\", \"args\" : [";
+  writeJsonASTExternalFunctionArguments(output);
+  output << "]}";
+}
+
 void
 SecondDerivExternalFunctionNode::writeJsonOutput(ostream &output,
                                                  const temporary_terms_t &temporary_terms,
@@ -8657,6 +8950,13 @@ VarExpectationNode::fillErrorCorrectionRow(int eqn, const vector<int> &nontrend_
   exit(EXIT_FAILURE);
 }
 
+void
+VarExpectationNode::writeJsonAST(ostream &output) const
+{
+  output << "{\"node_type\" : \"VarExpectationNode\", "
+         << "\"name\" : \"" << model_name << "\"}";
+}
+
 void
 VarExpectationNode::writeJsonOutput(ostream &output,
                                     const temporary_terms_t &temporary_terms,
@@ -9151,6 +9451,13 @@ PacExpectationNode::fillErrorCorrectionRow(int eqn, const vector<int> &nontrend_
   exit(EXIT_FAILURE);
 }
 
+void
+PacExpectationNode::writeJsonAST(ostream &output) const
+{
+  output << "{\"node_type\" : \"PacExpectationNode\", "
+         << "\"name\" : \"" << model_name << "\"}";
+}
+
 void
 PacExpectationNode::writeJsonOutput(ostream &output,
                                     const temporary_terms_t &temporary_terms,
diff --git a/src/ExprNode.hh b/src/ExprNode.hh
index 77106f23..1bd85c81 100644
--- a/src/ExprNode.hh
+++ b/src/ExprNode.hh
@@ -262,6 +262,9 @@ class ExprNode
       //! Writes output of node in JSON syntax
       virtual void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, const bool isdynamic = true) const = 0;
 
+      //! Writes the Abstract Syntax Tree in JSON
+      virtual void writeJsonAST(ostream &output) const = 0;
+
       virtual int precedenceJson(const temporary_terms_t &temporary_terms) const;
 
       //! Writes the output for an external function, ensuring that the external function is called as few times as possible using temporary terms
@@ -612,6 +615,7 @@ public:
   };
   void prepareForDerivation() override;
   void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const override;
+  void writeJsonAST(ostream &output) const override;
   void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const override;
   bool containsExternalFunction() const override;
   void collectVARLHSVariable(set<expr_t> &result) const override;
@@ -692,6 +696,7 @@ public:
   VariableNode(DataTree &datatree_arg, int idx_arg, int symb_id_arg, int lag_arg);
   void prepareForDerivation() override;
   void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const override;
+  void writeJsonAST(ostream &output) const override;
   void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const override;
   bool containsExternalFunction() const override;
   void collectVARLHSVariable(set<expr_t> &result) const override;
@@ -804,6 +809,7 @@ public:
                                      map<NodeTreeReference, temporary_terms_t> &temp_terms_map,
                                      bool is_matlab, NodeTreeReference tr) const override;
   void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const override;
+  void writeJsonAST(ostream &output) const override;
   void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const override;
   bool containsExternalFunction() const override;
   void writeExternalFunctionOutput(ostream &output, ExprNodeOutputType output_type,
@@ -928,6 +934,7 @@ public:
                                      map<NodeTreeReference, temporary_terms_t> &temp_terms_map,
                                      bool is_matlab, NodeTreeReference tr) const override;
   void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const override;
+  void writeJsonAST(ostream &output) const override;
   void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const override;
   bool containsExternalFunction() const override;
   void writeExternalFunctionOutput(ostream &output, ExprNodeOutputType output_type,
@@ -1084,6 +1091,7 @@ public:
                                      map<NodeTreeReference, temporary_terms_t> &temp_terms_map,
                                      bool is_matlab, NodeTreeReference tr) const override;
   void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const override;
+  void writeJsonAST(ostream &output) const override;
   void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const override;
   bool containsExternalFunction() const override;
   void writeExternalFunctionOutput(ostream &output, ExprNodeOutputType output_type,
@@ -1190,6 +1198,7 @@ protected:
   int getIndxInTefTerms(int the_symb_id, const deriv_node_temp_terms_t &tef_terms) const noexcept(false);
   //! Helper function to write output arguments of any given external function
   void writeExternalFunctionArguments(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const;
+  void writeJsonASTExternalFunctionArguments(ostream &output) const;
   void writeJsonExternalFunctionArguments(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const;
   /*! Returns a predicate that tests whether an other ExprNode is an external
     function which is computed by the same external function call (i.e. it has
@@ -1203,6 +1212,7 @@ public:
                                      map<NodeTreeReference, temporary_terms_t> &temp_terms_map,
                                      bool is_matlab, NodeTreeReference tr) const override;
   void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const override = 0;
+  void writeJsonAST(ostream &output) const override = 0;
   void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, const bool isdynamic = true) const override = 0;
   bool containsExternalFunction() const override;
   void writeExternalFunctionOutput(ostream &output, ExprNodeOutputType output_type,
@@ -1306,6 +1316,7 @@ public:
   ExternalFunctionNode(DataTree &datatree_arg, int idx_arg, int symb_id_arg,
                        const vector<expr_t> &arguments_arg);
   void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const override;
+  void writeJsonAST(ostream &output) const override;
   void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const override;
   void writeExternalFunctionOutput(ostream &output, ExprNodeOutputType output_type,
                                            const temporary_terms_t &temporary_terms,
@@ -1351,6 +1362,7 @@ public:
                                      vector< vector<temporary_terms_t>> &v_temporary_terms,
                                      int equation) const override;
   void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const override;
+  void writeJsonAST(ostream &output) const override;
   void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const override;
   void compile(ostream &CompileCode, unsigned int &instruction_number,
                        bool lhs_rhs, const temporary_terms_t &temporary_terms,
@@ -1395,6 +1407,7 @@ public:
                                      vector< vector<temporary_terms_t>> &v_temporary_terms,
                                      int equation) const override;
   void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const override;
+  void writeJsonAST(ostream &output) const override;
   void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const override;
   void compile(ostream &CompileCode, unsigned int &instruction_number,
                        bool lhs_rhs, const temporary_terms_t &temporary_terms,
@@ -1501,6 +1514,7 @@ public:
   bool isVarModelReferenced(const string &model_info_name) const override;
   void getEndosAndMaxLags(map<string, int> &model_endos_and_lags) const override;
   expr_t substituteStaticAuxiliaryVariable() const override;
+  void writeJsonAST(ostream &output) const override;
   void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const override;
 };
 
@@ -1599,6 +1613,7 @@ public:
   bool isVarModelReferenced(const string &model_info_name) const override;
   void getEndosAndMaxLags(map<string, int> &model_endos_and_lags) const override;
   expr_t substituteStaticAuxiliaryVariable() const override;
+  void writeJsonAST(ostream &output) const override;
   void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const override;
 };
 
-- 
GitLab