🧮

実行 & ジャッジ環境同梱のHTMLを出力できるMarkdownパーサを作ってみた

2023/08/10に公開

春の風物詩: 新入部員の環境構築、大爆発!

はじめまして。abap34と言います。普段は東京工業大学デジタル創作同好会traPというサークルで活動していて、主に機械学習/データ分析関連のことをしています。

https://trap.jp/

traPでは幅広いデジタル創作に関する活動が行われていて、私は競技プログラミング・Kaggleなどに取り組む人たちのグループである「アルゴリズム班」という組織に所属しています。

さて、traPは単に個人で活動するだけではなく、新入生に既存の部員が色々な技術や知識を教えるという文化があります。今やtraPは毎年250人以上新入部員が入ってくるようになり、新入生むけにアルゴリズム・機械学習を教える機会がたくさん提供されているのですが...





大体の人は想像がついたのではないかと思うのですが、環境構築、本当に大爆発します。

既存部員総出でかかなければいけないですし、さらには、ほとんどが一年生なのでパソコンの扱いに慣れていない人も多く、新入部員にとっても環境構築はかなり負担が大きいです。もちろんいずれ環境構築とは向き合う必要はありますが、せっかく入部しても初っ端からよくわからない黒い画面の操作で挫折して、そのまま幽霊部員に...というのはあまりにも勿体無い話です。

オンラインジャッジ作る?

解決策の一つは、ひとまずpaizaなど既存のオンラインでの実行環境を使ってもらうことかと思います。ですがデジタル創作同好会を名乗っているのに完全に既存のサービスに乗っかるのはなんとなく嫌です。

もう一つの選択肢はtraPがオンラインの実行環境を提供することですが、学生のサークルなので金銭面が少し心配になります。たとえばみんなで集まってアルゴリズムの勉強をする会などをした場合かなりのパワーが要求されそうです。
それに加えてオンラインジャッジはセキュリティなどをきちんと考えるのが大変です。
悪意を持った人に大量のコードを投げられたりしてしまったら間違いなく破産です。

そこで、自分たちでこれらを解決するソフトウェアを作ることにしました。

ALMOの紹介

そこで開発したのが、タイトルにもある
「実行 & ジャッジ環境同梱のHTMLを出力できる拡張Markdownパーサ」である
ALMOというソフトウェアです。

ALMOの既存のMarkdownパーサと最も異なるポイントは、
「実行 & ジャッジ可能なコードブロックを作成できる」ところです。

百聞は一見にしかずということでデモを見せたいと思います。

こんな感じのMarkdownを用意してみました。

https://gist.github.com/abap34/37d8d5fc450164c55d4605a6576b51df

ほとんどは普通のMarkdownですが、
21行目付近に見慣れない記法があるかと思います。

:::code
title=Hello ALMO!
sample_in=example/helloalmo/in/sample.txt
sample_out=example/helloalmo/out/sample.txt
in=example/helloalmo/in/*.txt
out=example/helloalmo/out/*.txt
judge=equal
:::

この謎の記法を含むMarkdownファイルから出来上がるページがこちらです。

https://www.abap34.com/almo.html

該当部分にエディタが用意されており、実行・ジャッジを行うことができます!

さらに、実行やジャッジがかなりサクサクなことにお気づきでしょうか。
このコードブロックの実行・ジャッジは、サーバで行われておらず、ブラウザだけで完結しています!

(試しにWifiをオフにしてみてください。問題なく実行とジャッジが行えるはずです)

これを実現しているのがWebAseemblyです。

WebAssemblyはブラウザで実行可能なバイナリフォーマットで、C++やRustなど既存のプログラミング言語をWebAssemblyバイナリにコンパイルしてブラウザ上で動作させることができます。

したがってこの辺りの言語で書かれた処理系は原理的にはブラウザ上で動かすことが可能です。
具体的には CPythonをWebAssemblyにコンパイルしたブラウザ上で動くPython処理系PyOdideなどがあります。

ALMOではPyOdideを使うことでブラウザ上で実行を完結させています。

これで無事に金銭的な問題やセキュリティ問題が解決されました。

ALMOの開発記・仕組み

そもそもオンラインジャッジが作りたいという話は僕が二ヶ月ほど前からずっと騒いでいたネタなのですが、WebAssemblyを使えばいけるということに気づき、簡単に手元で実装してみたところ行けそうなことがわかったのでサークルのメンバーに声をかけて、ガッと一日で作ることにしました。(試しに作ったもの: https://github.com/abap34/ALMO.jl)

開発は、わたし(@abap34)と先輩(@ebi_fly_fly)と同級生(@noya2ruler)の三人で行い、結局二日かかりました。
実装はC++です。

実行のインターフェースであるalmo.cppはこんな感じになっています。

#include "makejson.hpp"
#include "parse.hpp"
#include "render.hpp"
#include <iostream>
#include <fstream>

int main(int argc, char *argv[]){
    auto [meta_data, asts] = almo::parse_md_file(argv[1]);
    nlohmann::json json_meta_data = almo::make_meta_data_json(meta_data);
    nlohmann::json json_ir = almo::make_json(asts);
    almo::render(json_ir, json_meta_data);
}

まず、入力のMarkdownファイルは
parse.cppparse_md_fileに渡され、メタデータの抽出とASTが構築が行われます。
この部分は競技プログラミングで構文解析ばかりしているebi_fly先輩が担当してくれました。

次にASTはmakejson.hppでJSON形式に変換されます。
この部分はnoya2rulerがやってくれました。

最後にrender.cpprender関数で実際のHTMLに変換されます。
画像や入出力データの埋め込み、ジャッジコードの埋め込みなどもrenderの担当です。
この部分は僕が担当しました。

こんな感じの流れでALMOはHTMLファイルを生成しています。
ソースコードは全てGitHubで公開しているので、ぜひチェックしてください。

https://github.com/abap34/ALMO/

導入・使用方法

m1 MacユーザーはHomeBrewで導入できます。

brew tap abap34/homebrew-almo
brew install almo

almo example.mdのようにして使えます。

それ以外の方はsrc/almo.cppを適宜コンパイルして使ってください。
(C++20とcurl.hが必要です)

さいごに

ガッと作ったものなのでまだまだ対応していない記法があったり、スタイルの切り替えを実装していなかったり、Pythonしか実行できなかったりと、割とすぐに手が動く改善も残っているのですが、キリがなさそうなのでひとまず1チームハッカソン(?)の成果として公開することにしました。

そのため開発中のもののpreview版程度に捉えてもらえるとありがたいです。

今後も開発を続けますので、ぜひプログラミング教材作成の一つの手段として検討してみてください!

以上です

Discussion