#include #include #include #include #include #include #include "CdbPython.hh" #ifndef CDBPYTHON_NO_NOTEBOOK #include "DataCell.hh" #endif std::string cadabra::escape_quotes(const std::string& line) { return "''"+line+"''"; // std::string ret=std::replace_all_copy(line, "'", "\\'"); // std::replace_all(ret, "\"", "\\\""); // return ret; } std::string cadabra::cdb2python(const std::string& in_name, bool display) { // Read the file into a string. std::ifstream ifs(in_name); std::stringstream buffer; buffer << ifs.rdbuf(); std::time_t t = std::time(nullptr); std::tm tm = *std::localtime(&t); std::ostringstream ofs; ofs << "# cadabra2 package, auto-compiled " << std::put_time(&tm, "%F %T") << '\n' << "# Do not modify - changing the timestamp of this file may cause import errors\n" << "# Original file location: " << in_name << '\n' << "import cadabra2\n" << "from cadabra2 import *\n" << "from cadabra2_defaults import *\n" << "__cdbkernel__ = cadabra2.__cdbkernel__\n" << "temp__all__ = dir() + ['temp__all__']\n\n" << "def display(ex):\n" << " pass\n\n"; ofs << cdb2python_string(buffer.str(), display); ofs << '\n' << "del locals()['display']\n\n" << "try:\n" << " __all__\n" << "except NameError:\n" << " __all__ = list(set(dir()) - set(temp__all__))\n"; return ofs.str(); } std::string cadabra::cdb2python_string(const std::string& blk, bool display) { std::stringstream str(blk); std::string line; std::string newblk; std::string lhs, rhs, op, indent; while(std::getline(str, line, '\n')) { std::string res=cadabra::convert_line(line, lhs, rhs, op, indent, display); // std::cerr << "preparsed : " + res << std::endl; if(res!="::empty") newblk += res+'\n'; } return newblk; } std::string cadabra::convert_line(const std::string& line, std::string& lhs, std::string& rhs, std::string& op, std::string& indent, bool display) { std::string ret; std::regex imatch("([\\s]*)([^\\s].*[^\\s])([\\s]*)"); std::cmatch mres; std::string indent_line, end_of_line, line_stripped; if(std::regex_match(line.c_str(), mres, imatch)) { indent_line=std::string(mres[1].first, mres[1].second); end_of_line=std::string(mres[3].first, mres[3].second); line_stripped=std::string(mres[2]); } else { indent_line=""; end_of_line="\n"; line_stripped=line; } if(line_stripped.size()==0) { return ""; } // Do not do anything with comment lines. if(line_stripped[0]=='#') return line; // Bare ';' gets replaced with 'display(_)'. if(line_stripped==";") { if(display) return indent_line+"display(_)"; else return indent_line; } // 'lastchar' is either a Cadabra termination character, or empty. // 'line_stripped' will have that character stripped, if present. std::string lastchar = line_stripped.substr(line_stripped.size()-1,1); if(lastchar=="." || lastchar==";" || lastchar==":") { if(lhs!="") { line_stripped=line_stripped.substr(0,line_stripped.size()-1); rhs += line_stripped; ret = indent + lhs + " = Ex(r'" + escape_quotes(rhs) + "')"; if(op==":=") { if(ret[ret.size()-1]!=';') ret+=";"; ret+=" _="+lhs; } if(lastchar!=".") if(display) ret = ret + "; display("+lhs+")"; indent=""; lhs=""; rhs=""; op=""; return ret; } } else { // If we are a Cadabra continuation, add to the rhs without further processing // and return an empty line immediately. if(lhs!="") { rhs += line_stripped+" "; return "::empty"; } } // Add '__cdbkernel__' as first argument of post_process if it doesn't have that already. std::regex postprocmatch(R"(def post_process\(([^_]))"); line_stripped = std::regex_replace(line_stripped, postprocmatch, "def post_process(__cdbkernel__, $1"); // Replace $...$ with Ex(...). std::regex dollarmatch(R"(\$([^\$]*)\$)"); line_stripped = std::regex_replace(line_stripped, dollarmatch, "Ex(r'''$1''', False)", std::regex_constants::match_default | std::regex_constants::format_default ); // Replace 'converge(ex):' with 'server.progress('converge'); ex.reset(); while ex.changed(): server.progress(); server.end_progress();' properly indented. std::regex converge_match(R"(([ ]*)converge\(([^\)]*)\):)"); std::smatch converge_res; if(std::regex_match(line_stripped, converge_res, converge_match)) { ret = indent_line+std::string(converge_res[1])+std::string(converge_res[2])+".reset(); _="+std::string(converge_res[2])+"\n" + indent_line+std::string(converge_res[1])+"while "+std::string(converge_res[2])+".changed():"; return ret; } size_t found = line_stripped.find(":="); if(found!=std::string::npos) { // If the last character is not a Cadabra terminator, start a capture process. if(lastchar!="." && lastchar!=";" && lastchar!=":") { indent=indent_line; lhs=line_stripped.substr(0,found); rhs=line_stripped.substr(found+2); op=":="; return "::empty"; } else { line_stripped=line_stripped.substr(0,line_stripped.size()-1); ret = indent_line + line_stripped.substr(0,found) + " = Ex(r'" + escape_quotes(line_stripped.substr(found+2)) + "')"; std::string objname = line_stripped.substr(0,found); ret = ret + "; _="+objname; if(lastchar==";" && indent_line.size()==0 && display) ret = ret + "; display("+objname+")"; } } else { // {a,b,c}::Indices(real, parent=holo); found = line_stripped.find("::"); if(found!=std::string::npos) { std::regex amatch(R"(([a-zA-Z]+)(\(.*\))?[;\.:]*)"); std::smatch ares; std::string subline=line_stripped.substr(found+2); // need to store the copy, not feed it straight into regex_match! if(std::regex_match(subline, ares, amatch)) { auto propname = std::string(ares[1]); auto argument = std::string(ares[2]); if(argument.size()>0) { // declaration with arguments argument=argument.substr(1,argument.size()-2); ret = indent_line + "__cdbtmp__ = "+propname +"(Ex(r'"+escape_quotes(line_stripped.substr(0,found)) +"'), Ex(r'" +escape_quotes(argument) + "') )"; } else { // no arguments line_stripped=line_stripped.substr(0,line_stripped.size()-1); ret = indent_line + "__cdbtmp__ = " + line_stripped.substr(found+2) + "(Ex(r'"+escape_quotes(line_stripped.substr(0,found))+"'), Ex(r''))"; } if(lastchar==";" && display) ret += "; display(__cdbtmp__)"; } else { // inconsistent, who knows what will happen... ret = line; // inconsistent; you are asking for trouble. } } else { if(lastchar==";" && display) ret = indent_line + "_ = " + line_stripped + " display(_)"; else ret = indent_line + line_stripped; } } return ret+end_of_line; } #ifndef CDBPYTHON_NO_NOTEBOOK std::string cadabra::cnb2python(const std::string& in_name, bool for_standalone) { // Read the file into a Json object and get the cells. We go through // a proper notebook read because that way we can benefit from automatic // Jupyter -> Cadabra conversion. std::ifstream ifs(in_name); std::string content, line; while(std::getline(ifs, line)) content+=line; cadabra::DTree doc; cadabra::JSON_deserialise(content, doc); std::time_t t = std::time(nullptr); std::tm tm = *std::localtime(&t); // Loop over input cells, compile them and write to python file std::ostringstream ofs; ofs << "# cadabra2 package, auto-compiled " << std::put_time(&tm, "%F %T") << '\n' << "# Do not modify - changing the timestamp of this file may cause import errors\n" << "# Original file location: " << in_name << '\n' << "import cadabra2\n" << "import imp\n" << "from cadabra2 import *\n" << "from cadabra2_defaults import *\n" << "__cdbkernel__ = cadabra2.__cdbkernel__\n" << "temp__all__ = dir() + ['temp__all__']\n\n" << "def display(ex):\n" << " pass\n\n"; // << "with open(imp.find_module('cadabra2_defaults')[1]) as f:\n" // << " code = compile(f.read(), 'cadabra2_defaults.py', 'exec')\n" // << " exec(code)\n\n"; // FIXME: this only does a single layer of cells below the top-level // 'document' cell; need recursing, in principle. auto docit=doc.begin(); for(auto cell=doc.begin(docit); cell!=doc.end(docit); ++cell) { bool ioi = cell->ignore_on_import; if(for_standalone || !ioi) { if(cell->cell_type==cadabra::DataCell::CellType::python) { std::stringstream s, temp; s << cell->textbuf; // cell["source"].asString(); std::string line, lhs, rhs, op, indent; while (std::getline(s, line)) { auto res = convert_line(line, lhs, rhs, op, indent, for_standalone); if(res!="::empty") ofs << res << '\n'; } } } cell.skip_children(); } // Ensure only symbols defined in this file get exported ofs << '\n' << "del locals()['display']\n\n" << "try:\n" << " __all__\n" << "except NameError:\n" << " __all__ = list(set(dir()) - set(temp__all__))\n"; return ofs.str(); } // std::string cadabra::cnb2python(const std::string& name) // { // // Only compile if the notebook is newer than the compiled package // struct stat f1, f2; // if (stat(std::string(name + ".cnb").c_str(), &f1) == 0 && stat(std::string(name + ".py").c_str(), &f2) == 0) { // if (f1.st_mtime < f2.st_mtime) // return ""; // } // // // Read the file into a Json object and get the cells // std::ifstream ifs(name + ".cnb"); // Json::Value nb; // ifs >> nb; // Json::Value& cells = nb["cells"]; // // // Loop over input cells, compile them and write to python file // // std::ostringstream ofs; // std::time_t t = std::time(nullptr); // std::tm tm = *std::localtime(&t); // ofs << "# cadabra2 package, auto-compiled " << std::put_time(&tm, "%F %T") << '\n' // << "import cadabra2\n" // << "import imp\n" // << "from cadabra2 import *\n" // << "__cdbkernel__ = cadabra2.__cdbkernel__\n" // << "temp__all__ = dir() + ['temp__all__']\n\n" // << "def display(ex):\n" // << " pass\n\n"; // // // << "with open(imp.find_module('cadabra2_defaults')[1]) as f:\n" // // << " code = compile(f.read(), 'cadabra2_defaults.py', 'exec')\n" // // << " exec(code)\n\n"; // // for (auto cell : cells) { // if (cell["cell_type"] == "input") { // std::stringstream s, temp; // s << cell["source"].asString(); // std::string line, lhs, rhs, op, indent; // while (std::getline(s, line)) // ofs << convert_line(line, lhs, rhs, op, indent) << '\n'; // } // } // // Ensure only symbols defined in this file get exported // ofs << '\n' // << "del locals()['display']\n\n" // << "try:\n" // << " __all__\n" // << "except NameError:\n" // << " __all__ = list(set(dir()) - set(temp__all__))\n"; // // return ofs.str(); // } // #endif