From 71672bbd94ded611f49231e1d8d2528d469568fa Mon Sep 17 00:00:00 2001
From: Pablo Winant <pablo.winant@gmail.com>
Date: Thu, 30 Nov 2023 11:54:05 +0100
Subject: [PATCH] First implementation of extension module.

---
 meson.build        |  2 +-
 scripts/wasm32.ini | 25 ++++++++++++++++
 src/DynLib.cc      | 73 ++++++++++++++++++++++++++++++++++++++++++++++
 src/meson.build    | 21 ++++++++++---
 4 files changed, 116 insertions(+), 5 deletions(-)
 create mode 100644 scripts/wasm32.ini
 create mode 100644 src/DynLib.cc

diff --git a/meson.build b/meson.build
index 4ee79d0e..30e77a9d 100644
--- a/meson.build
+++ b/meson.build
@@ -4,7 +4,7 @@
 project('dynare-preprocessor', 'cpp',
         version : '6-unstable',
         # NB: update C++ standard in .clang-format whenever the following is modified
-        default_options : [ 'cpp_std=gnu++20', 'warning_level=2' ],
+        default_options : [ 'cpp_std=gnu++20', 'warning_level=0' ],
         meson_version : '>=0.64.0')
 
 add_global_arguments('-DPACKAGE_VERSION="' + meson.project_version() + '"', language : 'cpp')
diff --git a/scripts/wasm32.ini b/scripts/wasm32.ini
new file mode 100644
index 00000000..923b5708
--- /dev/null
+++ b/scripts/wasm32.ini
@@ -0,0 +1,25 @@
+# Cross-compile file for creating a WebAssembly version of the preprocessor
+# Requires emscripten to be installed
+# Creates a .wasm and .js wrapper under <builddir>/src/
+# Can be run locally with node.js using:
+#  node --no-experimental-fetch dynare-preprocessor.js
+
+[binaries]
+cpp = 'em++'
+
+[host_machine]
+system = 'emscripten'
+cpu_family = 'wasm32'
+cpu = 'wasm32'
+endian = 'little'
+
+[properties]
+# It’s necessary to use a different copy of Boost than the one under
+# /usr/include, because otherwise GCC headers confuse Clang
+#boost_root = '/path/to/boost'
+# For accessing the local filesystem
+; cpp_args = ['-fwasm-exceptions']
+; cpp_link_args = ['--embed-file', 'example1.mod', '-o', 'dynare.html', '-fwasm-exceptions']
+; cpp_args = ['-fexceptions']
+cpp_args = ['-fexceptions']
+cpp_link_args = ['-fexceptions']
diff --git a/src/DynLib.cc b/src/DynLib.cc
new file mode 100644
index 00000000..1ec2e8c3
--- /dev/null
+++ b/src/DynLib.cc
@@ -0,0 +1,73 @@
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <vector>
+#include <string>
+#include <regex>
+#include <thread>
+#include <algorithm>
+#include <filesystem>
+
+#include <cstdlib>
+
+#include <unistd.h>
+
+#include "ParsingDriver.hh"
+#include "ExtendedPreprocessorTypes.hh"
+#include "ConfigFile.hh"
+#include "ModFile.hh"
+
+
+std::string preprocess(const std::string &modfile_string) {
+
+//   dup2(STDOUT_FILENO, STDERR_FILENO);
+
+
+    // # we capture output completely
+    std::stringstream buffer;
+    std::streambuf * old = std::cout.rdbuf(buffer.rdbuf());
+    // std::streambuf * old = std::cerr.rdbuf(buffer.rdbuf());
+
+
+    const string basename = "model";
+
+    stringstream modfile;
+    modfile << modfile_string;
+
+
+    bool debug = false;
+    bool no_warn = true;
+    bool nostrict = true;
+    
+    WarningConsolidation warnings(no_warn);
+    ParsingDriver p(warnings, nostrict);
+
+    unique_ptr<ModFile> mod_file = p.parse(modfile, debug);
+
+    JsonOutputPointType json{JsonOutputPointType::nojson};
+    JsonFileOutputType json_output_mode{JsonFileOutputType::file};
+  
+    json = JsonOutputPointType::parsing;
+    json_output_mode = JsonFileOutputType::standardout;
+  
+    bool onlyjson = false; // remark: why would writeJsonOutput ever decide to exit ?
+
+    mod_file->writeJsonOutput(basename, json, json_output_mode, false);
+
+    std::string output = buffer.str(); // text will now contain "Bla\n"
+
+    return output;
+
+}
+
+
+#include <pybind11/pybind11.h>
+
+namespace py = pybind11;
+
+PYBIND11_MODULE(dynare_preprocessor, m) {
+
+    m.doc() = "Dynare Preprocessor"; // optional module docstring
+    m.def("preprocess", &preprocess, "Another one");
+
+}
diff --git a/src/meson.build b/src/meson.build
index f3829776..cf1cf499 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,3 +1,13 @@
+py3_inst = import('python').find_installation('python3')
+
+pybind11_config = find_program('pybind11-config')
+pybind11_config_ret = run_command(pybind11_config, ['--includes'])
+pybind11 = declare_dependency(
+    include_directories: [pybind11_config_ret.stdout().split('-I')[-1].strip()],
+)
+
+python3 = dependency('python3') 
+
 boost_dep = dependency('boost')
 
 ## Flex stuff
@@ -69,7 +79,10 @@ else
   preprocessor_link_args = []
 endif
 
-executable('dynare-preprocessor', preprocessor_src, flex_src, flexlexer_h, bison_src,
-           include_directories : preprocessor_incdir, dependencies : boost_dep,
-           link_args : preprocessor_link_args,
-           install : true)
+# executable('dynare-preprocessor', preprocessor_src, flex_src, flexlexer_h, bison_src,
+#           include_directories : preprocessor_incdir, dependencies : boost_dep,
+#           link_args : preprocessor_link_args,
+#           install : true)
+
+py3_inst.extension_module('dynare_preprocessor', preprocessor_src, flex_src, flexlexer_h, bison_src, 'DynLib.cc', include_directories : preprocessor_incdir, dependencies : [boost_dep, python3, pybind11],
+           link_args : preprocessor_link_args, install: true)
\ No newline at end of file
-- 
GitLab