Skip to content

Commit

Permalink
Merge pull request #150 from abap34/dev
Browse files Browse the repository at this point in the history
Pythonインターフェース の utils の追加
  • Loading branch information
abap34 authored Aug 7, 2024
2 parents f5a9769 + 2d9e715 commit 6a90220
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 159 deletions.
Binary file added almo.so
Binary file not shown.
1 change: 0 additions & 1 deletion src/almo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,6 @@ int main(int argc, char* argv[]) {
meta_data["css_setting"] = config.css_setting;
meta_data["editor_theme"] = config.editor_theme;
meta_data["syntax_theme"] = config.syntax_theme;
meta_data["required_pyodide"] = "false";

almo::ParseSummary summary = almo::md_to_summary(md_content, meta_data);

Expand Down
4 changes: 4 additions & 0 deletions src/pyalmo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ PYBIND11_MODULE(almo, m) {
m.doc() = "almo interface for python.";

m.def("parse", &parse_md, "Parse markdown to AST.");
m.def("move_footnote_to_end", &almo::move_footnote_to_end,
"Move footnote definition to the end of the document.");
m.def("required_pyodide", &almo::required_pyodide,
"Check if pyodide is required.");

py::class_<almo::ASTNode, std::shared_ptr<almo::ASTNode>>(
m, "ASTNode", py::dynamic_attr())
Expand Down
340 changes: 187 additions & 153 deletions src/render.hpp
Original file line number Diff line number Diff line change
@@ -1,180 +1,214 @@
#include <iostream>
#include <glob.h>

#include <fstream>
#include <string>
#include <vector>
#include <map>
#include <functional>
#include <sstream>
#include <glob.h>
#include <iostream>
#include <map>
#include <regex>
#include "utils.hpp"
#include <sstream>
#include <string>
#include <vector>

#include "parse.hpp"
#include "utils.hpp"

namespace almo {
std::string LIGHT_THEME = {
#include "light.css"
};
std::string DARK_THEME = {
#include "dark.css"
};

std::string RUNNER = {
#include "runner.js"
};

std::string TEMPLATE = {
#include "template.html"
};



std::string load_html_template(std::string html_path, std::string css_setting, bool required_pyodide) {
const std::string pyodide_loader = "<script src=\"https://cdn.jsdelivr.net/pyodide/v0.24.0/full/pyodide.js\"></script>";

std::string html;

if (html_path == "__default__") {
html = TEMPLATE;
}
else {
html = join(read_file(html_path), "\n");
}

std::string result;
if (css_setting == "light") {
std::string css = "<style>" + LIGHT_THEME + "</style>";
result = std::regex_replace(html, std::regex("\\{\\{style\\}\\}"), css);
}
else if (css_setting == "dark") {
std::string css = "<style>" + DARK_THEME + "</style>";
result = std::regex_replace(html, std::regex("\\{\\{style\\}\\}"), css);
}
else if (css_setting.ends_with(".css")) {
std::string css = "<style>" + join(read_file(css_setting), "\n") + "</style>";

result = std::regex_replace(html, std::regex("\\{\\{style\\}\\}"), css);
}
else {
throw InvalidCommandLineArgumentsError("不正なCSSの設定です。 `light`, `dark` もしくは `.css` で終了するファイル名を指定してください。");
}

std::string runner = "<script>" + RUNNER + "</script>";

if (required_pyodide) {
// runnner の先頭に  pyodide を挿入
runner = pyodide_loader + runner;
}
else {
runner = "<!-- Runner is not required. Skip this. -->";
}
// runner を挿入
result = std::regex_replace(result, std::regex("\\{\\{runner\\}\\}"), runner);

return result;
std::string LIGHT_THEME = {
#include "light.css"
};
std::string DARK_THEME = {
#include "dark.css"
};

std::string RUNNER = {
#include "runner.js"
};

std::string TEMPLATE = {
#include "template.html"
};

std::string load_html_template(std::string html_path, std::string css_setting,
bool required_pyodide) {
const std::string pyodide_loader =
"<script "
"src=\"https://cdn.jsdelivr.net/pyodide/v0.24.0/full/pyodide.js\"></"
"script>";

std::string html;

if (html_path == "__default__") {
html = TEMPLATE;
} else {
html = join(read_file(html_path), "\n");
}

std::string replace_template(std::string html_template, std::map<std::string, std::string> meta_data, std::string content) {
std::string output_html = html_template;

for (auto [key, value] : meta_data) {
std::string replace_key = "\\{\\{" + key + "\\}\\}";
output_html = std::regex_replace(output_html, std::regex(replace_key), value);
}

std::string syntax_theme = meta_data["syntax_theme"];

output_html = std::regex_replace(output_html, std::regex("\\{\\{syntax_theme\\}\\}"), syntax_theme);
output_html = std::regex_replace(output_html, std::regex("\\{\\{contents\\}\\}"), content);

return output_html;
std::string result;
if (css_setting == "light") {
std::string css = "<style>" + LIGHT_THEME + "</style>";
result = std::regex_replace(html, std::regex("\\{\\{style\\}\\}"), css);
} else if (css_setting == "dark") {
std::string css = "<style>" + DARK_THEME + "</style>";
result = std::regex_replace(html, std::regex("\\{\\{style\\}\\}"), css);
} else if (css_setting.ends_with(".css")) {
std::string css =
"<style>" + join(read_file(css_setting), "\n") + "</style>";

result = std::regex_replace(html, std::regex("\\{\\{style\\}\\}"), css);
} else {
throw InvalidCommandLineArgumentsError(
"不正なCSSの設定です。 `light`, `dark` もしくは `.css` "
"で終了するファイル名を指定してください。");
}

void move_footnote_definition(Markdown& ast) {
std::vector<std::shared_ptr<ASTNode>> footnote_defs = ast.nodes_byclass("FootnoteDefinition");
std::string runner = "<script>" + RUNNER + "</script>";

std::shared_ptr<DivBlock> footnote_div = std::make_shared<DivBlock>("footnote");

ast.pushback_child(footnote_div);

for (auto node : footnote_defs) {
ast.move_node(node, footnote_div);
}
if (required_pyodide) {
// runnner の先頭に  pyodide を挿入
runner = pyodide_loader + runner;
} else {
runner = "<!-- Runner is not required. Skip this. -->";
}
// runner を挿入
result =
std::regex_replace(result, std::regex("\\{\\{runner\\}\\}"), runner);

return result;
}

std::string replace_template(std::string html_template,
std::map<std::string, std::string> meta_data,
std::string content) {
std::string output_html = html_template;

for (auto [key, value] : meta_data) {
std::string replace_key = "\\{\\{" + key + "\\}\\}";
output_html =
std::regex_replace(output_html, std::regex(replace_key), value);
}

std::string render(Markdown ast, std::map<std::string, std::string> meta_data) {
std::vector<std::shared_ptr<ASTNode>> footnote_defs = ast.nodes_byclass("FootnoteDefinition");

std::shared_ptr<DivBlock> footnote_div = std::make_shared<DivBlock>("footnote");
std::string syntax_theme = meta_data["syntax_theme"];

std::string content = ast.to_html();
output_html = std::regex_replace(
output_html, std::regex("\\{\\{syntax_theme\\}\\}"), syntax_theme);
output_html = std::regex_replace(
output_html, std::regex("\\{\\{contents\\}\\}"), content);

std::string html_template = load_html_template(meta_data["template_file"], meta_data["css_setting"], meta_data["required_pyodide"] == "true");
return output_html;
}

std::string output_html = replace_template(html_template, meta_data, content);
void move_footnote_to_end(Markdown& ast) {
std::vector<std::shared_ptr<ASTNode>> footnote_defs =
ast.nodes_byclass("FootnoteDefinition");

return output_html;
}
std::shared_ptr<DivBlock> footnote_div =
std::make_shared<DivBlock>("footnote");

// Call from python to get html from lines without metadata `md_content` and metadata `meta_data`.
//
// - md_to_html
// - md_to_json
// - md_to_dot
// - md_to_ast
// - md_to_summary
//
// note : meta_data may change

std::string md_to_html(const std::vector<std::string>& md_content, std::map<std::string, std::string>& meta_data) {
Markdown ast;
MarkdownParser parser(md_content);
parser.process(ast);
return render(ast, meta_data);
}
ast.pushback_child(footnote_div);

std::string md_to_json(const std::vector<std::string>& md_content, std::map<std::string, std::string>& meta_data) {
Markdown ast;
MarkdownParser parser(md_content);
parser.process(ast);
return ast.to_json();
for (auto node : footnote_defs) {
ast.move_node(node, footnote_div);
}
}

std::string md_to_dot(const std::vector<std::string>& md_content, std::map<std::string, std::string>& meta_data) {
Markdown ast;
MarkdownParser parser(md_content);
parser.process(ast);
std::string dot = ast.to_dot();
dot = "digraph G {\n graph [labelloc=\"t\"; \n ]\n" + dot + "}";
return dot;
bool required_pyodide(Markdown& ast) {
if (ast.nodes_byclass("ExecutableCodeBlock").size() > 0) {
return true;
}

Markdown md_to_ast(const std::vector<std::string>& md_content, std::map<std::string, std::string>& meta_data){
Markdown ast;
MarkdownParser parser(md_content);
parser.process(ast);
return ast;
if (ast.nodes_byclass("Judge").size() > 0) {
return true;
}

struct ParseSummary {
Markdown ast;
std::string html, json, dot;
return false;
}

std::string render(Markdown ast, std::map<std::string, std::string> meta_data) {
std::vector<std::shared_ptr<ASTNode>> footnote_defs =
ast.nodes_byclass("FootnoteDefinition");

std::shared_ptr<DivBlock> footnote_div =
std::make_shared<DivBlock>("footnote");

std::string content = ast.to_html();

std::string html_template =
load_html_template(meta_data["template_file"], meta_data["css_setting"],
required_pyodide(ast));

std::string output_html =
replace_template(html_template, meta_data, content);

return output_html;
}

// Call from python to get html from lines without metadata `md_content` and
// metadata `meta_data`.
//
// - md_to_html
// - md_to_json
// - md_to_dot
// - md_to_ast
// - md_to_summary
//
// note : meta_data may change

std::string md_to_html(const std::vector<std::string>& md_content,
std::map<std::string, std::string>& meta_data) {
Markdown ast;
MarkdownParser parser(md_content);
parser.process(ast);
return render(ast, meta_data);
}

std::string md_to_json(const std::vector<std::string>& md_content,
std::map<std::string, std::string>& meta_data) {
Markdown ast;
MarkdownParser parser(md_content);
parser.process(ast);
return ast.to_json();
}

std::string md_to_dot(const std::vector<std::string>& md_content,
std::map<std::string, std::string>& meta_data) {
Markdown ast;
MarkdownParser parser(md_content);
parser.process(ast);
std::string dot = ast.to_dot();
dot = "digraph G {\n graph [labelloc=\"t\"; \n ]\n" + dot + "}";
return dot;
}

Markdown md_to_ast(const std::vector<std::string>& md_content,
std::map<std::string, std::string>& meta_data) {
Markdown ast;
MarkdownParser parser(md_content);
parser.process(ast);
return ast;
}

struct ParseSummary {
Markdown ast;
std::string html, json, dot;
};

ParseSummary md_to_summary(const std::vector<std::string>& md_content,
std::map<std::string, std::string>& meta_data) {
Markdown ast;
MarkdownParser parser(md_content);
parser.process(ast);

move_footnote_to_end(ast);

render(ast, meta_data);
ParseSummary summary = {
.ast = ast,
.html = render(ast, meta_data),
.json = ast.to_json(),
.dot =
"digraph G {\n graph [labelloc=\"t\"; \n ]\n" + ast.to_dot() + "}",
};
return summary;
}

ParseSummary md_to_summary(const std::vector<std::string>& md_content, std::map<std::string, std::string>& meta_data){
Markdown ast;
MarkdownParser parser(md_content);
parser.process(ast);

move_footnote_definition(ast);

render(ast, meta_data);
ParseSummary summary = {
.ast = ast,
.html = render(ast, meta_data),
.json = ast.to_json(),
.dot = "digraph G {\n graph [labelloc=\"t\"; \n ]\n" + ast.to_dot() + "}",
};
return summary;
}

} // namespace almo
} // namespace almo
Loading

0 comments on commit 6a90220

Please sign in to comment.