diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml
new file mode 100644
index 0000000..e276411
--- /dev/null
+++ b/.gitea/workflows/build.yaml
@@ -0,0 +1,11 @@
+name: build
+on: [push]
+
+jobs:
+ build:
+ runs-on: nixos
+ steps:
+ - name: Check out repository code
+ uses: actions/checkout@v4
+ - name: build doerg
+ run: nix build -L .#doerg
diff --git a/.gitea/workflows/demo.yaml b/.gitea/workflows/demo.yaml
new file mode 100644
index 0000000..2486fdd
--- /dev/null
+++ b/.gitea/workflows/demo.yaml
@@ -0,0 +1,26 @@
+name: Gitea Actions Demo
+run-name: ${{ gitea.actor }} is testing out Gitea Actions π
+on: [push]
+
+jobs:
+ build:
+ runs-on: nixos
+ steps:
+ - name: Check out repository code
+ uses: actions/checkout@v4
+ - name: build doerg
+ run: nix build -L .#doerg
+ Explore-Gitea-Actions:
+ runs-on: nixos
+ steps:
+ - run: echo "π The job was automatically triggered by a ${{ gitea.event_name }} event."
+ - run: echo "π§ This job is now running on a ${{ runner.os }} server hosted by Gitea!"
+ - run: echo "π The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
+ - name: Check out repository code
+ uses: actions/checkout@v4
+ - run: echo "π‘ The ${{ gitea.repository }} repository has been cloned to the runner."
+ - run: echo "π₯οΈ The workflow is now ready to test your code on the runner."
+ - name: List files in the repository
+ run: |
+ ls ${{ gitea.workspace }}
+ - run: echo "π This job's status is ${{ job.status }}."
diff --git a/deps-lock.json b/deps-lock.json
index 986bedb..8579cce 100644
--- a/deps-lock.json
+++ b/deps-lock.json
@@ -10,6 +10,16 @@
}
],
"mvn-deps": [
+ {
+ "mvn-path": "aero/aero/1.1.6/aero-1.1.6.jar",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-9LCpJy2lDICRxVKcn+NVxHI08e65X+kiluLCP3h/PSI="
+ },
+ {
+ "mvn-path": "aero/aero/1.1.6/aero-1.1.6.pom",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-WUXPjq14D3SX6G8piWPyeGgdUZrpshvpSfrFQg8F01A="
+ },
{
"mvn-path": "babashka/fs/0.5.24/fs-0.5.24.jar",
"mvn-repo": "https://repo.clojars.org/",
@@ -40,6 +50,31 @@
"mvn-repo": "https://repo.clojars.org/",
"hash": "sha256-LVp6g6ymqHa1ZOMHYD0CRaNxZ4osT0xoEH7dC5sLv7s="
},
+ {
+ "mvn-path": "ch/qos/logback/logback-classic/1.1.3/logback-classic-1.1.3.jar",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-mMPxj10NZCzV8yfMckVmzRlklibH2I9wFDvXBMlBV9U="
+ },
+ {
+ "mvn-path": "ch/qos/logback/logback-classic/1.1.3/logback-classic-1.1.3.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-Ond3RjIf1uIuOMzf1PY0vKNysA7mIAPlJ/aqRkkF8+g="
+ },
+ {
+ "mvn-path": "ch/qos/logback/logback-core/1.1.3/logback-core-1.1.3.jar",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-R8D9NCmV0zFbj6zKzDJLKnYUOyfEMNSy1qKeq8MfXBQ="
+ },
+ {
+ "mvn-path": "ch/qos/logback/logback-core/1.1.3/logback-core-1.1.3.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-ziBX5rWqOWLWyNtAxJ6LzhFQqaobNhXtS6b1I9O5pwA="
+ },
+ {
+ "mvn-path": "ch/qos/logback/logback-parent/1.1.3/logback-parent-1.1.3.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-ag4n7GeP9dkLjMZku0UZifXitbMAamW95Lld7BmHOHE="
+ },
{
"mvn-path": "cheshire/cheshire/6.1.0/cheshire-6.1.0.jar",
"mvn-repo": "https://repo.clojars.org/",
@@ -125,6 +160,26 @@
"mvn-repo": "https://repo.clojars.org/",
"hash": "sha256-dXkdZHooK97QEyeXodFSC942EiB9B8MlHUNZlbH8nvI="
},
+ {
+ "mvn-path": "com/nextjournal/beholder/1.0.2/beholder-1.0.2.jar",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-iQOMTbidIDu1sSpfsa2qkpXgWirEsk+u2ZbJNOqJ0Xo="
+ },
+ {
+ "mvn-path": "com/nextjournal/beholder/1.0.2/beholder-1.0.2.pom",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-LRJkSGdQPfLoeGjLwU3deFKoaum2dW/QJ46lCK0d4u4="
+ },
+ {
+ "mvn-path": "com/rpl/specter/1.1.6/specter-1.1.6.jar",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-vqmD19/KmPN99xUGxINR1L2PzgwiUEoa+da1r39BE9U="
+ },
+ {
+ "mvn-path": "com/rpl/specter/1.1.6/specter-1.1.6.pom",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-2Fv7Xf/j/e+vS9pjGOyCkOyQEqA+0rzjZzo3p4T2WmU="
+ },
{
"mvn-path": "com/unboundid/unboundid-ldapsdk/5.1.1/unboundid-ldapsdk-5.1.1.jar",
"mvn-repo": "https://repo1.maven.org/maven2/",
@@ -185,6 +240,36 @@
"mvn-repo": "https://repo.clojars.org/",
"hash": "sha256-2OgLA0KFMl6QX1RkmhWYtoe5pKmaOk9LlO7TWXyyEEg="
},
+ {
+ "mvn-path": "expound/expound/0.9.0/expound-0.9.0.jar",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-1qNyYJkY4DUb+mqL1pPRi8GZ6Lp6r67BHola+uAY+Vw="
+ },
+ {
+ "mvn-path": "expound/expound/0.9.0/expound-0.9.0.pom",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-kJSODD3MvE8aCvaABWue2JizAcvtLd4/9CR5eWmXxdk="
+ },
+ {
+ "mvn-path": "fipp/fipp/0.6.27/fipp-0.6.27.jar",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-qK1dAlan2r+90UOm6QvnZXURhA/is4MMp9tnK20RDPc="
+ },
+ {
+ "mvn-path": "fipp/fipp/0.6.27/fipp-0.6.27.pom",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-ugagpDW5XBNQMTr+1z3s6r5TXwbF/pw6Ffrcc4Tzlvk="
+ },
+ {
+ "mvn-path": "hawk/hawk/0.2.11/hawk-0.2.11.jar",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-6UPy8MSHxsWmsg5wzpHdXzHkBIlXCRe7oT/OpzyaekM="
+ },
+ {
+ "mvn-path": "hawk/hawk/0.2.11/hawk-0.2.11.pom",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-q4PzoWHUY53W2TZWihPpw+qXB4QWWVnS1iW3WlvIxFg="
+ },
{
"mvn-path": "hiccup/hiccup/2.0.0-RC4/hiccup-2.0.0-RC4.jar",
"mvn-repo": "https://repo.clojars.org/",
@@ -215,6 +300,56 @@
"mvn-repo": "https://repo.clojars.org/",
"hash": "sha256-7Vh2tzj7zXYdGd03xiyTExP/beuWSi8Nn1NmRKkw9bQ="
},
+ {
+ "mvn-path": "io/methvin/directory-watcher/0.17.3/directory-watcher-0.17.3.jar",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-BSPdOKOTpjt15DIP9SMDSgrDzqaX9hUaesiok8MiUx4="
+ },
+ {
+ "mvn-path": "io/methvin/directory-watcher/0.17.3/directory-watcher-0.17.3.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-Y/C4zR1No0m2Dfk4kdHgDF0/0sm9M9jlvu0HWijNiFQ="
+ },
+ {
+ "mvn-path": "lambdaisland/clj-diff/1.4.78/clj-diff-1.4.78.jar",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-oovG5LKVI8wppHLlfm6rlYD6DQNorXvliypH6JGhRZw="
+ },
+ {
+ "mvn-path": "lambdaisland/clj-diff/1.4.78/clj-diff-1.4.78.pom",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-2QZ6mFjx7+UGI232PUr3CzsQenqd+xcRsbqEpxs32w0="
+ },
+ {
+ "mvn-path": "lambdaisland/deep-diff2/2.12.219/deep-diff2-2.12.219.jar",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-BkOq5C/7omo09wyTTg7nkBi6GWBmlayVsPa9mrDvCyU="
+ },
+ {
+ "mvn-path": "lambdaisland/deep-diff2/2.12.219/deep-diff2-2.12.219.pom",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-rW/85js2hSWVc36e2BcTRmkahJHc2TjGIEfvForuToY="
+ },
+ {
+ "mvn-path": "lambdaisland/kaocha/1.91.1392/kaocha-1.91.1392.jar",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-W7EV1MqxeIVqyTyt6lV9vpZrLRc+TWQKnmNfiPoieug="
+ },
+ {
+ "mvn-path": "lambdaisland/kaocha/1.91.1392/kaocha-1.91.1392.pom",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-p6+0cuuFYlJU5YlhGIbnuHt1d6yOZXx/FhwAM6wCyzg="
+ },
+ {
+ "mvn-path": "lambdaisland/tools.namespace/0.3.256/tools.namespace-0.3.256.jar",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-jt141ljkjv97etTTE995zZnqiZfoFnyt0g2hwmuOU8M="
+ },
+ {
+ "mvn-path": "lambdaisland/tools.namespace/0.3.256/tools.namespace-0.3.256.pom",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-n+46BXjy57A8898EXgyplEyyqf/S5qocf140ZbglV64="
+ },
{
"mvn-path": "medley/medley/1.4.0/medley-1.4.0.jar",
"mvn-repo": "https://repo.clojars.org/",
@@ -225,6 +360,56 @@
"mvn-repo": "https://repo.clojars.org/",
"hash": "sha256-JhEgB4cMXujVcrvDw4n8a9bMZG1cUAdfbolYQMWGEMA="
},
+ {
+ "mvn-path": "meta-merge/meta-merge/1.0.0/meta-merge-1.0.0.jar",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-1/7i4+PXBuDlRWLnKqNxIQjXAYahLLwJDhBoBYLrAsc="
+ },
+ {
+ "mvn-path": "meta-merge/meta-merge/1.0.0/meta-merge-1.0.0.pom",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-sAogZY/OzCvRNBAx85T1LWjFP7SAxEVBNMyqwgTqWTE="
+ },
+ {
+ "mvn-path": "mvxcvi/arrangement/2.1.0/arrangement-2.1.0.jar",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-83JROF0iDfiVHmjVJVq7UZetvL2PxrPT/KhyojOfOcg="
+ },
+ {
+ "mvn-path": "mvxcvi/arrangement/2.1.0/arrangement-2.1.0.pom",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-fWiMhmYYFAo78Am00FcK7acJA0h7dlH7VNBHf5TT2Is="
+ },
+ {
+ "mvn-path": "mvxcvi/clj-cbor/1.1.1/clj-cbor-1.1.1.jar",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-XqYtfq4Seg2W6Favuh2Ogm2TrZfFCgEFNvsekCgX4I8="
+ },
+ {
+ "mvn-path": "mvxcvi/clj-cbor/1.1.1/clj-cbor-1.1.1.pom",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-SzmvI+F2QOnCPH5H6OKH124pbohIazXgPG3PhqH0VZA="
+ },
+ {
+ "mvn-path": "net/incongru/watchservice/barbary-watchservice/1.0/barbary-watchservice-1.0.jar",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-VMbKvYIJPIUV9uFOQ9lL/n6fx5XRV4nzlDoUGNgmOrU="
+ },
+ {
+ "mvn-path": "net/incongru/watchservice/barbary-watchservice/1.0/barbary-watchservice-1.0.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-k7OxHaltUXiIDfjFBT8Yz8eByv8Nnd9LPGRyRKnRws8="
+ },
+ {
+ "mvn-path": "net/java/dev/jna/jna/5.12.1/jna-5.12.1.jar",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-kagUrE9A1g3ukdhC4aith0xiGXmEQD0OPDDTnlXPU7M="
+ },
+ {
+ "mvn-path": "net/java/dev/jna/jna/5.12.1/jna-5.12.1.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-Zf8lhJuthZVUtQMXeS9Wia20UprkAx6aUkYxnLK4U1Y="
+ },
{
"mvn-path": "org/apache/apache/23/apache-23.pom",
"mvn-repo": "https://repo1.maven.org/maven2/",
@@ -282,62 +467,62 @@
},
{
"mvn-path": "org/clojure/clojure/1.10.3/clojure-1.10.3.jar",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-fxJHLa7Y9rUXSYqqKrE6ViR1w+31FHjkWBzHYemJeaM="
},
{
"mvn-path": "org/clojure/clojure/1.10.3/clojure-1.10.3.pom",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-GJwAxDNAdJai+7DsyzeQjJSVXZHq0b5IFWdE7MGBbZQ="
},
{
"mvn-path": "org/clojure/clojure/1.11.0/clojure-1.11.0.jar",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-PiH6daB+yd278bK1A1bPGAcQ0DmN6qT0TpHNYwRVWUc="
},
{
"mvn-path": "org/clojure/clojure/1.11.0/clojure-1.11.0.pom",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-SQjMS0yeYsmoFJb5PLWsb2lBd8xkXc87jOXkkavOHro="
},
{
"mvn-path": "org/clojure/clojure/1.11.1/clojure-1.11.1.jar",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-I4G26UI6tGUVFFWUSQPROlYkPWAGuRlK/Bv0+HEMtN4="
},
{
"mvn-path": "org/clojure/clojure/1.11.1/clojure-1.11.1.pom",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-IMRaGr7b2L4grvk2BQrjGgjBZ0CzL4dAuIOM3pb/y4o="
},
{
"mvn-path": "org/clojure/clojure/1.11.2/clojure-1.11.2.jar",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-iPqZkT1pIs+39kn1xGdQOHfLb8yMwW02948mSAhLqZc="
},
{
"mvn-path": "org/clojure/clojure/1.11.2/clojure-1.11.2.pom",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-FzbP/xCV4dT+/raogrut9ttB7+MV8pbw/aMtt//EExE="
},
{
"mvn-path": "org/clojure/clojure/1.11.3/clojure-1.11.3.jar",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-nDBUCTKOK5boXdK160t1gQxnt2unCuTQ9t3pvPtVsbc="
},
{
"mvn-path": "org/clojure/clojure/1.11.3/clojure-1.11.3.pom",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-DA2+Ge4NKpxXMQzr3dNWRD8NFlFMQmBHsGLjpXwNuK0="
},
{
"mvn-path": "org/clojure/clojure/1.11.4/clojure-1.11.4.jar",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-/H/xtmENDjSUp1zBHvgYEL2kAqwVcBL+TjuJlYbPQTM="
},
{
"mvn-path": "org/clojure/clojure/1.11.4/clojure-1.11.4.pom",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-a6YADmhI+Cw5y5tJqyqmo6Vi9MJNUrMeUZCuZJXwwwk="
},
{
@@ -350,6 +535,46 @@
"mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-KfRiqonLl2RXWEGKXwjUwagrc1yW569JgX0WqpuQgVA="
},
+ {
+ "mvn-path": "org/clojure/clojure/1.12.1/clojure-1.12.1.jar",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-h+7qnjVdhsBFc4r0lNaD4J6RTLBGeuQNRqZrh6NsctQ="
+ },
+ {
+ "mvn-path": "org/clojure/clojure/1.12.1/clojure-1.12.1.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-JUvpyKuMzDArR9fFaj/KEUl+WcMFvxX6YFTD3/TrkZ0="
+ },
+ {
+ "mvn-path": "org/clojure/clojure/1.12.2/clojure-1.12.2.jar",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-pYv4B+zv7K6iIri4tH4UNo7o4yy0VAs//v/4yglTSA0="
+ },
+ {
+ "mvn-path": "org/clojure/clojure/1.12.2/clojure-1.12.2.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-55suCRfnPnPCX7N5PzFV+PD4jYAvUMJf1Sl3l3rDQiA="
+ },
+ {
+ "mvn-path": "org/clojure/clojure/1.12.3/clojure-1.12.3.jar",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-yyoaPbHCzXbvT6SlRdWmXxCxtIt/dnLwoQn1R28FcWY="
+ },
+ {
+ "mvn-path": "org/clojure/clojure/1.12.3/clojure-1.12.3.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-q2CmyyuMxXyG21ECte4p8hWsg8/20wEblV+fxb5dAZ0="
+ },
+ {
+ "mvn-path": "org/clojure/clojure/1.12.4/clojure-1.12.4.jar",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-S4Hpum2jjEXZzFgCPGdAYrjJ8HFPM/8A3tIuapSdoXc="
+ },
+ {
+ "mvn-path": "org/clojure/clojure/1.12.4/clojure-1.12.4.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-xRh5Bi5B/58hr6DkCbGZ/+nyo8aucizf7F/Z26BeQXI="
+ },
{
"mvn-path": "org/clojure/core.match/1.1.0/core.match-1.1.0.jar",
"mvn-repo": "https://repo1.maven.org/maven2/",
@@ -360,24 +585,34 @@
"mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-NnHYN2UlIwq6Ah8fYmx54g86ELYrXfgXIiWJDsSv4EU="
},
+ {
+ "mvn-path": "org/clojure/core.rrb-vector/0.2.0/core.rrb-vector-0.2.0.jar",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-ftqNXGlaxVNp3NTHN4YUMhdXuBAoG//KMMss02PRZZQ="
+ },
+ {
+ "mvn-path": "org/clojure/core.rrb-vector/0.2.0/core.rrb-vector-0.2.0.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-t+0/8ZfxrdZOHvJS2zRY8wlhPcZXdejY4OkTlx4CRY4="
+ },
{
"mvn-path": "org/clojure/core.specs.alpha/0.2.56/core.specs.alpha-0.2.56.jar",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-/PRCveArBKhj8vzFjuaiowxM8Mlw99q4VjTwq3ERZrY="
},
{
"mvn-path": "org/clojure/core.specs.alpha/0.2.56/core.specs.alpha-0.2.56.pom",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-AarxdIP/HHSCySoHKV1+e8bjszIt9EsptXONAg/wB0A="
},
{
"mvn-path": "org/clojure/core.specs.alpha/0.2.62/core.specs.alpha-0.2.62.jar",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-Bu6owHC75FwVhWfkQ0OWgbyMRukSNBT4G/oyukLWy8g="
},
{
"mvn-path": "org/clojure/core.specs.alpha/0.2.62/core.specs.alpha-0.2.62.pom",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-F3i70Ti9GFkLgFS+nZGdG+toCfhbduXGKFtn1Ad9MA4="
},
{
@@ -390,6 +625,16 @@
"mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-M0EOuKpz1S2Vez3G4KZfOZisBiPL2BPZDDPm5onEJCk="
},
+ {
+ "mvn-path": "org/clojure/java.classpath/1.0.0/java.classpath-1.0.0.jar",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-wU4OEDBKXlz9LMdC+976wfUpPuxgcML/6JA/tcf+fW8="
+ },
+ {
+ "mvn-path": "org/clojure/java.classpath/1.0.0/java.classpath-1.0.0.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-C+AThRRX/CTENM5FU0ZD8iblwQgASGJT/Tc/LglUXig="
+ },
{
"mvn-path": "org/clojure/java.data/1.3.113/java.data-1.3.113.jar",
"mvn-repo": "https://repo1.maven.org/maven2/",
@@ -405,9 +650,14 @@
"mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-RoC9g43MuowXwlgXE0fxb1uq5rXft4Grc4K8Y4X/gAY="
},
+ {
+ "mvn-path": "org/clojure/pom.contrib/0.2.2/pom.contrib-0.2.2.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-4OoifEnFw+MHVM0m/MV75+Telz/kOqXMZmdAHsXBAyM="
+ },
{
"mvn-path": "org/clojure/pom.contrib/0.3.0/pom.contrib-0.3.0.pom",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-fxgrOypUPgV0YL+T/8XpzvasUn3xoTdqfZki6+ee8Rk="
},
{
@@ -427,22 +677,22 @@
},
{
"mvn-path": "org/clojure/spec.alpha/0.2.194/spec.alpha-0.2.194.jar",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-z2iZ+YUpjGSxPqEplGrZAo3uja3w6rmuGORVAn04JJw="
},
{
"mvn-path": "org/clojure/spec.alpha/0.2.194/spec.alpha-0.2.194.pom",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-WhHw4eizwFLmUcSYxpRbRNs1Nb8sGHGf3PZd8fiLE+Y="
},
{
"mvn-path": "org/clojure/spec.alpha/0.3.218/spec.alpha-0.3.218.jar",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-Z+yJjrVcZqlXpVJ53YXRN2u5lL2HZosrDeHrO5foquA="
},
{
"mvn-path": "org/clojure/spec.alpha/0.3.218/spec.alpha-0.3.218.pom",
- "mvn-repo": "https://repo.maven.apache.org/maven2/",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-bY3hTDrIdXYMX/kJVi/5hzB3AxxquTnxyxOeFp/pB1g="
},
{
@@ -465,6 +715,16 @@
"mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-UZ45jnJMYvCsnWsZ15+P8QAdqYWD/eAb1wUrB+Ga1ow="
},
+ {
+ "mvn-path": "org/clojure/tools.cli/1.1.230/tools.cli-1.1.230.jar",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-kWYwtTmkP/RotN0BbGKFfitMtdpmhvEpdYfN1DyhAs0="
+ },
+ {
+ "mvn-path": "org/clojure/tools.cli/1.1.230/tools.cli-1.1.230.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-v7Yh5LAaW4vOEWpgcIQNzdWUnomceEaNgRtuiqqf0cc="
+ },
{
"mvn-path": "org/clojure/tools.logging/1.3.0/tools.logging-1.3.0.jar",
"mvn-repo": "https://repo1.maven.org/maven2/",
@@ -485,6 +745,16 @@
"mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-cGCU9H2ljugXofq5uAwxLs0nZHK85uHVRCOfFAcR2zE="
},
+ {
+ "mvn-path": "org/clojure/tools.reader/1.3.6/tools.reader-1.3.6.jar",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-EdGzHyxlwzVbKSu5tEuPyv2lS0TaY+NKuXt5qKs7uOA="
+ },
+ {
+ "mvn-path": "org/clojure/tools.reader/1.3.6/tools.reader-1.3.6.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-rvXugot8sUocWPRbn4oQ/zQMV2mSXqDvXDXR5J2SC+o="
+ },
{
"mvn-path": "org/junit/junit-bom/5.11.0/junit-bom-5.11.0.pom",
"mvn-repo": "https://repo1.maven.org/maven2/",
@@ -530,11 +800,51 @@
"mvn-repo": "https://repo.clojars.org/",
"hash": "sha256-oLLU3iXdPWNptThQLeay5UJcKxyOM6GQKZgIS4o4EWs="
},
+ {
+ "mvn-path": "org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-0+9XXj5JeWeNwBvx3M5RAhSTtNEft/G+itmCh3wWocA="
+ },
+ {
+ "mvn-path": "org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-+wRqnCKUN5KLsRwtJ8i113PriiXmDL0lPZhSEN7cJoQ="
+ },
+ {
+ "mvn-path": "org/slf4j/slf4j-api/1.7.7/slf4j-api-1.7.7.jar",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-aZgMA4yhsTGSZWFZFhfZwl+r/Hspgor5FZfKhXDPNf4="
+ },
+ {
+ "mvn-path": "org/slf4j/slf4j-api/1.7.7/slf4j-api-1.7.7.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-NTkEp6bCgwTQ3KyA+tMMSM2Jj22wO5PwWtbJDdQtmK0="
+ },
+ {
+ "mvn-path": "org/slf4j/slf4j-parent/1.7.36/slf4j-parent-1.7.36.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-uziNN/vN083mTDzt4hg4aTIY3EUfBAQMXfNgp47X6BI="
+ },
+ {
+ "mvn-path": "org/slf4j/slf4j-parent/1.7.7/slf4j-parent-1.7.7.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-Hf+uPOdo0FR+JhyWiYz12dGUv/1WAPWXyXUcxqc9M9Q="
+ },
{
"mvn-path": "org/sonatype/oss/oss-parent/7/oss-parent-7.pom",
"mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-tR+IZ8kranIkmVV/w6H96ne9+e9XRyL+kM5DailVlFQ="
},
+ {
+ "mvn-path": "org/tcrawley/dynapath/1.1.0/dynapath-1.1.0.jar",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-n8eqO+Y4XaOtIqBlJvFaXTNdnonUDzm/L5oA4Lu9iug="
+ },
+ {
+ "mvn-path": "org/tcrawley/dynapath/1.1.0/dynapath-1.1.0.pom",
+ "mvn-repo": "https://repo1.maven.org/maven2/",
+ "hash": "sha256-5OPOTeIWKm8U9QjB0Nv0s9tsrBk5E+kVeAhcGLesJho="
+ },
{
"mvn-path": "org/xerial/sqlite-jdbc/3.47.1.0/sqlite-jdbc-3.47.1.0.jar",
"mvn-repo": "https://repo1.maven.org/maven2/",
@@ -545,6 +855,26 @@
"mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-Z3XeNvNR9MdFCL/+qtZAIceVhx/ZRIXsdsPhduUwU7g="
},
+ {
+ "mvn-path": "progrock/progrock/0.1.2/progrock-0.1.2.jar",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-Aln+tbAkduswC31k5UPrVM5Kw9yuU5gxDxZCdo/VPyo="
+ },
+ {
+ "mvn-path": "progrock/progrock/0.1.2/progrock-0.1.2.pom",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-6rULcjyeeEkPWDy5n7HUa8KA/xH9X4Ujub7XamTq8CM="
+ },
+ {
+ "mvn-path": "riddley/riddley/0.1.12/riddley-0.1.12.jar",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-OY9h+kgluPhskWrlgMfhM7fEd9C3Kn07KY04EDJ0C64="
+ },
+ {
+ "mvn-path": "riddley/riddley/0.1.12/riddley-0.1.12.pom",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-At+3ryDvgcJTZQVfYCjoscwpBdCyaLuJzEKM2nIwo2U="
+ },
{
"mvn-path": "ring/ring-anti-forgery/1.4.0/ring-anti-forgery-1.4.0.jar",
"mvn-repo": "https://repo.clojars.org/",
@@ -610,6 +940,16 @@
"mvn-repo": "https://repo.clojars.org/",
"hash": "sha256-HHmMD/cPNu7HwRubeajXYOBDYK1y9x9F9KhRytq2AQw="
},
+ {
+ "mvn-path": "slingshot/slingshot/0.12.2/slingshot-0.12.2.jar",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-porCK/LqPNVM4023D9aYRNYx71SfZFDCeMMOb3nfY/M="
+ },
+ {
+ "mvn-path": "slingshot/slingshot/0.12.2/slingshot-0.12.2.pom",
+ "mvn-repo": "https://repo.clojars.org/",
+ "hash": "sha256-SrxOK5ppxzvTc+gy0/AOWQZ4Q/+DUe/V7rsfOCTbnFE="
+ },
{
"mvn-path": "tigris/tigris/0.1.2/tigris-0.1.2.jar",
"mvn-repo": "https://repo.clojars.org/",
diff --git a/doerg/deps.edn b/doerg/deps.edn
index 956b86c..2c1cbff 100644
--- a/doerg/deps.edn
+++ b/doerg/deps.edn
@@ -7,5 +7,10 @@
{:git/sha "531d629b7f05f37232261cf9e8927a4b5915714f"}
hiccup/hiccup {:mvn/version "2.0.0-RC4"}
com.rpl/specter {:mvn/version "1.1.6"}
- lambdaisland/deep-diff2 {:mvn/version "2.12.219"}}
- :paths ["src" "resources" "test"]}
+ lambdaisland/deep-diff2 {:mvn/version "2.12.219"}
+ mvxcvi/clj-cbor {:mvn/version "1.1.1"}
+ ch.qos.logback/logback-classic {:mvn/version "1.1.3"}}
+ :paths ["src" "resources" "test"]
+ :aliases
+ {:test {:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
+ :main-opts ["-m" "kaocha.runner"]}}}
diff --git a/doerg/doerg-parser/package.nix b/doerg/doerg-parser/default.nix
similarity index 100%
rename from doerg/doerg-parser/package.nix
rename to doerg/doerg-parser/default.nix
diff --git a/doerg/doerg-temml-worker/README.org b/doerg/doerg-temml-worker/README.org
new file mode 100644
index 0000000..c2d5bd4
--- /dev/null
+++ b/doerg/doerg-temml-worker/README.org
@@ -0,0 +1 @@
+#+title: doerg-temml-worker
diff --git a/doerg/doerg-temml-worker/default.nix b/doerg/doerg-temml-worker/default.nix
new file mode 100644
index 0000000..94783fe
--- /dev/null
+++ b/doerg/doerg-temml-worker/default.nix
@@ -0,0 +1,15 @@
+{ buildNpmPackage
+, importNpmLock
+, makeWrapper
+, ibm-plex
+, callPackage
+}:
+
+buildNpmPackage {
+ pname = "doerg-temml-worker";
+ version = "0.1.0";
+ src = ./.;
+ npmDeps = importNpmLock { npmRoot = ./.; };
+ npmConfigHook = importNpmLock.npmConfigHook;
+ dontNpmBuild = true;
+}
diff --git a/doerg/doerg-temml-worker/deps.edn b/doerg/doerg-temml-worker/deps.edn
new file mode 100644
index 0000000..65b9cb6
--- /dev/null
+++ b/doerg/doerg-temml-worker/deps.edn
@@ -0,0 +1,6 @@
+{:deps {babashka/fs {:mvn/version "0.5.24"}
+ cheshire/cheshire {:mvn/version "6.1.0"}
+ com.rpl/specter {:mvn/version "1.1.6"}
+ mvxcvi/clj-cbor {:mvn/version "1.1.1"}
+ babashka/process {:mvn/version "0.6.25"}}
+ :paths ["." "classes"]}
diff --git a/doerg/doerg-temml-worker/deserialise.clj b/doerg/doerg-temml-worker/deserialise.clj
new file mode 100644
index 0000000..cb0011c
--- /dev/null
+++ b/doerg/doerg-temml-worker/deserialise.clj
@@ -0,0 +1,5 @@
+(ns deserialise
+ (:require [clj-cbor.core :as cbor]
+ [clojure.string :as str]))
+
+(prn (cbor/decode cbor/default-codec System/in :eof))
diff --git a/doerg/doerg-temml-worker/index.js b/doerg/doerg-temml-worker/index.js
new file mode 100755
index 0000000..d9156c3
--- /dev/null
+++ b/doerg/doerg-temml-worker/index.js
@@ -0,0 +1,55 @@
+#!/usr/bin/env node
+
+const commandLineArgs = require ("command-line-args")
+const temml = require ("temml")
+const fs = require ("node:fs")
+const path = require ("node:path")
+const { DecoderStream, EncoderStream } = require ("cbor-x")
+const { Transform } = require('node:stream')
+
+const cli_spec =
+ [ { name: "preamble" }
+ ]
+
+function load_preambles (preamble) {
+ const data = fs.readFileSync (preamble, "utf8") .toString ()
+ return temml.definePreamble (data)
+}
+
+let macros = {}
+
+function is_render_command (cmd) {
+ return cmd instanceof Array
+ && cmd.length === 1
+ && typeof cmd[0] === "string"
+}
+
+function do_command (cmd) {
+ try {
+ if (typeof cmd === "string") {
+ return temml.renderToString (cmd, {macros})
+ } else if (is_render_command (cmd)) {
+ return temml.renderToString (cmd[0], {displayMode: true,macros})
+ } else {
+ return null
+ }
+ } catch (e) {
+ console.error (e)
+ return {type: "error", error: e}
+ }
+}
+
+function main () {
+ const options = commandLineArgs (cli_spec)
+ macros = load_preambles (options.preamble)
+ const decoder = new DecoderStream ()
+ const encoder = new EncoderStream ()
+ process.stdin.pipe (decoder)
+ const command_responses = decoder.map (do_command)
+ decoder
+ .map (do_command)
+ .pipe (encoder)
+ .pipe (process.stdout)
+}
+
+main ()
diff --git a/doerg/doerg-temml-worker/package-lock.json b/doerg/doerg-temml-worker/package-lock.json
new file mode 100644
index 0000000..e6e6eca
--- /dev/null
+++ b/doerg/doerg-temml-worker/package-lock.json
@@ -0,0 +1,227 @@
+{
+ "name": "doerg-tex",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "doerg-tex",
+ "version": "0.1.0",
+ "dependencies": {
+ "cbor-x": "^1.6.0",
+ "command-line-args": "^6.0.1",
+ "temml": "^0.13.1"
+ },
+ "bin": {
+ "doerg-tex": "index.js"
+ }
+ },
+ "node_modules/@cbor-extract/cbor-extract-darwin-arm64": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-arm64/-/cbor-extract-darwin-arm64-2.2.0.tgz",
+ "integrity": "sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@cbor-extract/cbor-extract-darwin-x64": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-x64/-/cbor-extract-darwin-x64-2.2.0.tgz",
+ "integrity": "sha512-1liF6fgowph0JxBbYnAS7ZlqNYLf000Qnj4KjqPNW4GViKrEql2MgZnAsExhY9LSy8dnvA4C0qHEBgPrll0z0w==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@cbor-extract/cbor-extract-linux-arm": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm/-/cbor-extract-linux-arm-2.2.0.tgz",
+ "integrity": "sha512-QeBcBXk964zOytiedMPQNZr7sg0TNavZeuUCD6ON4vEOU/25+pLhNN6EDIKJ9VLTKaZ7K7EaAriyYQ1NQ05s/Q==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@cbor-extract/cbor-extract-linux-arm64": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm64/-/cbor-extract-linux-arm64-2.2.0.tgz",
+ "integrity": "sha512-rQvhNmDuhjTVXSPFLolmQ47/ydGOFXtbR7+wgkSY0bdOxCFept1hvg59uiLPT2fVDuJFuEy16EImo5tE2x3RsQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@cbor-extract/cbor-extract-linux-x64": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-x64/-/cbor-extract-linux-x64-2.2.0.tgz",
+ "integrity": "sha512-cWLAWtT3kNLHSvP4RKDzSTX9o0wvQEEAj4SKvhWuOVZxiDAeQazr9A+PSiRILK1VYMLeDml89ohxCnUNQNQNCw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@cbor-extract/cbor-extract-win32-x64": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-win32-x64/-/cbor-extract-win32-x64-2.2.0.tgz",
+ "integrity": "sha512-l2M+Z8DO2vbvADOBNLbbh9y5ST1RY5sqkWOg/58GkUPBYou/cuNZ68SGQ644f1CvZ8kcOxyZtw06+dxWHIoN/w==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/array-back": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz",
+ "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.17"
+ }
+ },
+ "node_modules/cbor-extract": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cbor-extract/-/cbor-extract-2.2.0.tgz",
+ "integrity": "sha512-Ig1zM66BjLfTXpNgKpvBePq271BPOvu8MR0Jl080yG7Jsl+wAZunfrwiwA+9ruzm/WEdIV5QF/bjDZTqyAIVHA==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "node-gyp-build-optional-packages": "5.1.1"
+ },
+ "bin": {
+ "download-cbor-prebuilds": "bin/download-prebuilds.js"
+ },
+ "optionalDependencies": {
+ "@cbor-extract/cbor-extract-darwin-arm64": "2.2.0",
+ "@cbor-extract/cbor-extract-darwin-x64": "2.2.0",
+ "@cbor-extract/cbor-extract-linux-arm": "2.2.0",
+ "@cbor-extract/cbor-extract-linux-arm64": "2.2.0",
+ "@cbor-extract/cbor-extract-linux-x64": "2.2.0",
+ "@cbor-extract/cbor-extract-win32-x64": "2.2.0"
+ }
+ },
+ "node_modules/cbor-x": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/cbor-x/-/cbor-x-1.6.0.tgz",
+ "integrity": "sha512-0kareyRwHSkL6ws5VXHEf8uY1liitysCVJjlmhaLG+IXLqhSaOO+t63coaso7yjwEzWZzLy8fJo06gZDVQM9Qg==",
+ "license": "MIT",
+ "optionalDependencies": {
+ "cbor-extract": "^2.2.0"
+ }
+ },
+ "node_modules/command-line-args": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-6.0.1.tgz",
+ "integrity": "sha512-Jr3eByUjqyK0qd8W0SGFW1nZwqCaNCtbXjRo2cRJC1OYxWl3MZ5t1US3jq+cO4sPavqgw4l9BMGX0CBe+trepg==",
+ "license": "MIT",
+ "dependencies": {
+ "array-back": "^6.2.2",
+ "find-replace": "^5.0.2",
+ "lodash.camelcase": "^4.3.0",
+ "typical": "^7.2.0"
+ },
+ "engines": {
+ "node": ">=12.20"
+ },
+ "peerDependencies": {
+ "@75lb/nature": "latest"
+ },
+ "peerDependenciesMeta": {
+ "@75lb/nature": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-replace": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.2.tgz",
+ "integrity": "sha512-Y45BAiE3mz2QsrN2fb5QEtO4qb44NcS7en/0y9PEVsg351HsLeVclP8QPMH79Le9sH3rs5RSwJu99W0WPZO43Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "@75lb/nature": "latest"
+ },
+ "peerDependenciesMeta": {
+ "@75lb/nature": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/lodash.camelcase": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+ "license": "MIT"
+ },
+ "node_modules/node-gyp-build-optional-packages": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz",
+ "integrity": "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "detect-libc": "^2.0.1"
+ },
+ "bin": {
+ "node-gyp-build-optional-packages": "bin.js",
+ "node-gyp-build-optional-packages-optional": "optional.js",
+ "node-gyp-build-optional-packages-test": "build-test.js"
+ }
+ },
+ "node_modules/temml": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/temml/-/temml-0.13.1.tgz",
+ "integrity": "sha512-/fL1utq8QUD9YpcLeZHPRnp9Cbzbexq5hZl5uSBhf8mNYiKkcS4eYbLidDB+/nF8C+RHAcBQbKw2bKoS83mz1Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.13.0"
+ }
+ },
+ "node_modules/typical": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz",
+ "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.17"
+ }
+ }
+ }
+}
diff --git a/doerg/doerg-temml-worker/package.json b/doerg/doerg-temml-worker/package.json
new file mode 100644
index 0000000..f07b9be
--- /dev/null
+++ b/doerg/doerg-temml-worker/package.json
@@ -0,0 +1,6 @@
+{"dependencies":{"temml":"^0.13.1"
+ ,"command-line-args":"^6.0.1"
+ ,"cbor-x":"^1.6.0"}
+,"name":"doerg-temml-worker"
+,"version":"0.1.0"
+,"bin":{"doerg-temml-worker":"index.js"}}
diff --git a/doerg/doerg-temml-worker/serialise.clj b/doerg/doerg-temml-worker/serialise.clj
new file mode 100644
index 0000000..417a648
--- /dev/null
+++ b/doerg/doerg-temml-worker/serialise.clj
@@ -0,0 +1,19 @@
+(ns serialise
+ (:require [clj-cbor.core :as cbor]
+ [clojure.string :as str]))
+
+(defn w [x]
+ (cbor/encode cbor/default-codec System/out x))
+
+(defn c [x]
+ (->> x cbor/encode (map #(format "%02x" %)) (str/join " ")))
+
+#_
+(w "\\naturalto")
+
+(w "\\ifxetex blah \\fi")
+
+#_#_#_
+(w "c = \\sqrt{a^2 + y^2}")
+(w "c = \\sqrt{a^ + y^2")
+(w "\\alpha^\\beta")
diff --git a/doerg/our-tex.nix b/doerg/our-tex.nix
new file mode 100644
index 0000000..fa70de0
--- /dev/null
+++ b/doerg/our-tex.nix
@@ -0,0 +1,43 @@
+{ texlive
+, syd-plex-latex
+}:
+
+texlive.combine {
+ inherit syd-plex-latex;
+ inherit (texlive)
+ scheme-basic
+ xkeyval
+ changepage
+ fancyhdr
+ geometry
+ natbib
+ paralist
+ placeins
+ ragged2e
+ setspace
+ textcase
+ titlesec
+ xcolor
+ hardwrap
+ xifthen
+ catchfile
+ fontspec
+ latexmk
+ xetex
+ dvisvgm dvipng wrapfig # For Org-mode previews/export.
+ amsmath # Essential for mathematics.
+ spath3 # TikZ dependency?
+ ulem hyperref
+ capt-of
+ pgf # Includes TikZ.
+ tikz-cd # Commutative diagrams w/ TikZ.
+ quiver # Commutative diagrams w/ TikZ & q.uiver.app.
+ bookmark
+ metafont
+ preview # For new-gen org-latex-preview.
+ mylatexformat # For new-gen org-latex-preview.
+ collection-fontsrecommended # Essential fonts.
+ etoolbox # For Org-mode exports.
+ caption
+ ;
+}
diff --git a/doerg/package.nix b/doerg/package.nix
index d2a1715..171e5e5 100644
--- a/doerg/package.nix
+++ b/doerg/package.nix
@@ -1,12 +1,18 @@
{ mkCljBin
, callPackage
+, lib
, doerg-parser
+, doerg-temml-worker
, ibm-plex-web
, fake-git
+, our-tex ? callPackage ./our-tex.nix {}
+, makeWrapper
+, breakpointHook
}:
let
- # mkCljBin sans fake-git.
+ # mkCljBin sans fake-git. We don't need it, and I don't want it in
+ # my dev shell.
mkCljBin' = args: (mkCljBin args).overrideAttrs (final: prev: {
nativeBuildInputs =
builtins.filter
@@ -18,6 +24,11 @@ let
plex = ibm-plex-web.override {
families = [ "math" "serif" "sans" "sans-kr" ];
};
+ bin-path = lib.makeBinPath [
+ doerg-parser
+ our-tex
+ doerg-temml-worker
+ ];
in mkCljBin' {
name = "net.deertopia/doerg";
version = "0.1.0";
@@ -26,9 +37,27 @@ in mkCljBin' {
main-ns = "net.deertopia.doerg.main";
nativeBuildInputs = [
plex
+ makeWrapper
+ breakpointHook
];
buildInputs = [
doerg-parser
+ doerg-temml-worker
plex
+ our-tex
];
+ nativeCheckInputs = [
+ doerg-parser
+ doerg-temml-worker
+ plex
+ our-tex
+ ];
+ doCheck = true;
+ checkPhase = ''
+ clojure -M:test
+ '';
+ postInstall = ''
+ wrapProgram $out/bin/doerg \
+ --prefix PATH : ${bin-path}
+ '';
}
diff --git a/doerg/resources/net/deertopia/doerg/Temml-Plex.css b/doerg/resources/net/deertopia/doerg/Temml-Plex.css
new file mode 100644
index 0000000..e4c46c0
--- /dev/null
+++ b/doerg/resources/net/deertopia/doerg/Temml-Plex.css
@@ -0,0 +1,343 @@
+/* Based on Temml-local.css. */
+math {
+ font-family: "IBM Plex Math", "Cambria Math", 'STIXTwoMath-Regular', 'NotoSansMath-Regular', math;
+ font-style: normal;
+ font-weight: normal;
+ line-height: normal;
+ font-size-adjust: none;
+ text-indent: 0;
+ text-transform: none;
+ letter-spacing: normal;
+ word-wrap: normal;
+ direction: ltr;
+ /* Prevent Firefox from omitting the dot on i or j. */
+ font-feature-settings: "dtls" off;
+}
+
+math * {
+ border-color: currentColor;
+}
+
+/* display: block is necessary in Firefox and Safari.
+ * Not in Chromium, which recognizes display: "block math" written inline. */
+ math.tml-display {
+ display: block;
+ width: 100%;
+}
+
+*.mathcal {
+ /* NotoSans */
+ font-feature-settings: 'ss01';
+}
+
+math .mathscr {
+ font-family: "IBM Plex Math";
+}
+
+mo.tml-prime {
+ font-family: "IBM Plex Math";
+}
+
+/* Cramped superscripts in WebKit */
+mfrac > :nth-child(2),
+msqrt,
+mover > :first-child {
+ math-shift: compact
+}
+
+.menclose {
+ display: inline-block;
+ position: relative;
+ padding: 0.5ex 0ex;
+}
+.tml-cancelto {
+ display: inline-block;
+ position: absolute;
+ top: 0;
+ left: 0;
+ padding: 0.5ex 0ex;
+ background-image: url("data:image/svg+xml,");
+}
+
+@supports (-moz-appearance: none) {
+ /* \vec w/o italic correction for Firefox */
+ .tml-vec {
+ transform: scale(0.75)
+ }
+ /* Fix \cancelto in Firefox */
+ .ff-narrow {
+ width: 0em;
+ }
+ .ff-nudge-left {
+ margin-left: -0.2em;
+ }
+}
+
+@supports (not (-moz-appearance: none)) {
+ /* Chromium and WebKit */
+ /* prime vertical alignment */
+ mo.tml-prime {
+ font-family: "IBM Plex Math";
+ }
+ /* Italic correction on superscripts */
+ .tml-sml-pad {
+ padding-left: 0.05em;
+ }
+ .tml-med-pad {
+ padding-left: 0.10em;
+ }
+ .tml-lrg-pad {
+ padding-left: 0.15em;
+ }
+}
+
+@supports (-webkit-backdrop-filter: blur(1px)) {
+ /* WebKit vertical & italic correction on accents */
+ .wbk-acc {
+ /* lower by x-height distance */
+ transform: translate(0em, 0.431em);
+ }
+ .wbk-sml {
+ transform: translate(0.07em, 0);
+ }
+ .wbk-sml-acc {
+ transform: translate(0.07em, 0.431em);
+ }
+ .wbk-sml-vec {
+ transform: scale(0.75) translate(0.07em, 0);
+ }
+ .wbk-med {
+ transform: translate(0.14em, 0);
+ }
+ .wbk-med-acc {
+ transform: translate(0.14em, 0.431em);
+ }
+ .wbk-med-vec {
+ transform: scale(0.75) translate(0.14em, 0);
+ }
+ .wbk-lrg {
+ transform: translate(0.21em, 0);
+ }
+ .wbk-lrg-acc {
+ transform: translate(0.21em, 0.431em);
+ }
+ .wbk-lrg-vec {
+ transform: scale(0.75) translate(0.21em, 0);
+ }
+}
+
+/* \cancel & \phase use background images. Get them to print. */
+menclose {
+ -webkit-print-color-adjust: exact; /* Chrome & Edge */
+ print-color-adjust: exact;
+}
+
+/* Array cell justification in Firefox & WebKit */
+.tml-right {
+ text-align: right;
+}
+.tml-left {
+ text-align: left;
+}
+
+/* For CD labels that grow to the left in Firefox and WebKit */
+.tml-shift-left { margin-left:-200% }
+
+/* Styles for Chromium only */
+@supports (not (-webkit-backdrop-filter: blur(1px))) and (not (-moz-appearance: none)) {
+ /* Italic correction on accents */
+ .chr-sml {
+ transform: translate(0.07em, 0)
+ }
+ .chr-sml-vec {
+ transform: scale(0.75) translate(0.07em, 0)
+ }
+ .chr-med {
+ transform: translate(0.14em, 0)
+ }
+ .chr-med-vec {
+ transform: scale(0.75) translate(0.14em, 0)
+ }
+ .chr-lrg {
+ transform: translate(0.21em, 0)
+ }
+ .chr-lrg-vec {
+ transform: scale(0.75) translate(0.21em, 0)
+ }
+
+ /* For CD labels that grow to the left */
+ .tml-shift-left { margin-left:-100% }
+
+ /* MathML Core & Chromium do not support the MathML 3.0 element attributes. */
+ /* So use styles. */
+ menclose {
+ position: relative;
+ padding: 0.5ex 0ex;
+ }
+
+ .tml-overline {
+ padding: 0.1em 0 0 0;
+ border-top: 0.065em solid;
+ }
+
+ .tml-underline {
+ padding: 0 0 0.1em 0;
+ border-bottom: 0.065em solid;
+ }
+
+ .tml-cancel {
+ display: inline-block;
+ position: absolute;
+ left: 0.5px;
+ bottom: 0;
+ width: 100%;
+ height: 100%;
+ background-color: currentColor;
+ }
+ .upstrike {
+ clip-path: polygon(0.05em 100%, 0em calc(100% - 0.05em), calc(100% - 0.05em) 0em, 100% 0.05em);
+ }
+ .downstrike {
+ clip-path: polygon(0em 0.05em, 0.05em 0em, 100% calc(100% - 0.05em), calc(100% - 0.05em) 100%);
+ }
+ .sout {
+ clip-path: polygon(0em calc(55% + 0.0333em), 0em calc(55% - 0.0333em), 100% calc(55% - 0.0333em), 100% calc(55% + 0.0333em));
+ }
+ .tml-xcancel {
+ background: linear-gradient(to top left,
+ rgba(0,0,0,0) 0%,
+ rgba(0,0,0,0) calc(50% - 0.06em),
+ rgba(0,0,0,1) 50%,
+ rgba(0,0,0,0) calc(50% + 0.06em),
+ rgba(0,0,0,0) 100%),
+ linear-gradient(to top right,
+ rgba(0,0,0,0) 0%,
+ rgba(0,0,0,0) calc(50% - 0.06em),
+ rgba(0,0,0,1) 50%,
+ rgba(0,0,0,0) calc(50% + 0.06em),
+ rgba(0,0,0,0) 100%)
+ }
+
+ .longdiv-top {
+ border-top: 0.067em solid;
+ padding: 0.1em 0.2em 0.2em 0.433em;
+ }
+ .longdiv-arc {
+ position: absolute;
+ top: 0;
+ bottom: 0.1em;
+ left: -0.4em;
+ width: 0.7em;
+ border: 0.067em solid;
+ transform: translateY(-0.067em);
+ border-radius: 70%;
+ clip-path: inset(0 0 0 0.4em);
+ box-sizing: border-box;}
+ .menclose {display: inline-block;
+ text-align: left;
+ position: relative;
+ }
+
+ .phasor-bottom {
+ border-bottom: 0.067em solid;
+ padding: 0.2em 0.2em 0.1em 0.6em;
+ }
+ .phasor-angle {
+ display: inline-block;
+ position: absolute;
+ left: 0.5px;
+ bottom: -0.04em;
+ height: 100%;
+ aspect-ratio: 0.5;
+ background-color: currentColor;
+ clip-path: polygon(0.05em 100%, 0em calc(100% - 0.05em), calc(100% - 0.05em) 0em, 100% 0.05em);
+ }
+
+ .tml-fbox {
+ padding: 3pt;
+ border: 1px solid;
+ }
+
+ .circle-pad {
+ padding: 0.267em;
+ }
+ .textcircle {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ right: 0;
+ left: 0;
+ border: 0.067em solid;
+ border-radius: 50%;
+ }
+
+ .actuarial {
+ padding: 0.03889em 0.03889em 0 0.03889em;
+ border-width: 0.08em 0.08em 0em 0em;
+ border-style: solid;
+ margin-right: 0.03889em;
+ }
+
+ /* Stretch \widetilde */
+ .tml-crooked-2 {
+ transform: scale(2.0, 1.1)
+ }
+ .tml-crooked-3 {
+ transform: scale(3.0, 1.3)
+ }
+ .tml-crooked-4 {
+ transform: scale(4.0, 1.4)
+ }
+ /* set array cell justification */
+ .tml-right {
+ text-align: -webkit-right;
+ }
+ .tml-left {
+ text-align: -webkit-left;
+ }
+}
+
+.special-fraction {
+ font-family: "IBM Plex Math", 'STIX TWO', 'Times New Roman', Times, Tinos, serif;
+}
+
+/* flex-wrap for line-breaking in Chromium */
+math {
+ display: inline-flex;
+ flex-wrap: wrap;
+ align-items: baseline;
+}
+math > mrow {
+ padding: 0.5ex 0ex;
+}
+
+/* Default mtd top padding is 0.5ex per MathML-Core and user-agent CSS */
+/* We adjust for jot and small */
+mtable.tml-jot mtd {
+ padding-top: 0.7ex;
+ padding-bottom: 0.7ex;
+}
+mtable.tml-small mtd {
+ padding-top: 0.35ex;
+ padding-bottom: 0.35ex;
+}
+
+/* Firefox */
+@-moz-document url-prefix() {
+ /* Avoid flex-wrap */
+ math { display: inline; }
+ math > mrow { padding: 0 }
+ /* Adjust Firefox spacing between array rows */
+ mtd, mtable.tml-small mtd { padding-top: 0; padding-bottom: 0; }
+ mtable.tml-jot mtd { padding-top: 0.2ex; padding-bottom: 0.ex; }
+}
+
+/* AMS environment auto-numbering via CSS counter. */
+.tml-eqn::before {
+ counter-increment: tmlEqnNo;
+ content: "(" counter(tmlEqnNo) ")";
+}
+
+body {
+ counter-reset: tmlEqnNo;
+}
diff --git a/doerg/resources/net/deertopia/doerg/prelude.tex b/doerg/resources/net/deertopia/doerg/prelude.tex
new file mode 100644
index 0000000..37a3084
--- /dev/null
+++ b/doerg/resources/net/deertopia/doerg/prelude.tex
@@ -0,0 +1,154 @@
+% Of the highest importance:
+\renewcommand{\phi}{\varphi}
+\renewcommand{\epsilon}{\varepsilon}
+
+% Saner abbreviations for font faces (take an argument rather than
+% applying to the current context).
+\renewcommand{\rm}[1]{\mathrm{#1}}
+\newcommand{\bb}[1]{\mathbb{#1}}
+\renewcommand{\bf}[1]{\mathbf{#1}}
+
+% \cA β¦ \cZ
+\newcommand{\cA}{\mathcal{A}}
+\newcommand{\cB}{\mathcal{B}}
+\newcommand{\cC}{\mathcal{C}}
+\newcommand{\cD}{\mathcal{D}}
+\newcommand{\cE}{\mathcal{E}}
+\newcommand{\cF}{\mathcal{F}}
+\newcommand{\cG}{\mathcal{G}}
+\newcommand{\cH}{\mathcal{H}}
+\newcommand{\cI}{\mathcal{I}}
+\newcommand{\cJ}{\mathcal{J}}
+\newcommand{\cK}{\mathcal{K}}
+\newcommand{\cL}{\mathcal{L}}
+\newcommand{\cM}{\mathcal{M}}
+\newcommand{\cN}{\mathcal{N}}
+\newcommand{\cO}{\mathcal{O}}
+\newcommand{\cP}{\mathcal{P}}
+\newcommand{\cQ}{\mathcal{Q}}
+\newcommand{\cR}{\mathcal{R}}
+\newcommand{\cS}{\mathcal{S}}
+\newcommand{\cT}{\mathcal{T}}
+\newcommand{\cU}{\mathcal{U}}
+\newcommand{\cV}{\mathcal{V}}
+\newcommand{\cW}{\mathcal{W}}
+\newcommand{\cX}{\mathcal{X}}
+\newcommand{\cY}{\mathcal{Y}}
+\newcommand{\cZ}{\mathcal{Z}}
+
+% \bA β¦ \bZ
+\newcommand{\bA}{\mathbb{A}}
+\newcommand{\bB}{\mathbb{B}}
+\newcommand{\bC}{\mathbb{C}}
+\newcommand{\bD}{\mathbb{D}}
+\newcommand{\bE}{\mathbb{E}}
+\newcommand{\bF}{\mathbb{F}}
+\newcommand{\bG}{\mathbb{G}}
+\newcommand{\bH}{\mathbb{H}}
+\newcommand{\bI}{\mathbb{I}}
+\newcommand{\bJ}{\mathbb{J}}
+\newcommand{\bK}{\mathbb{K}}
+\newcommand{\bL}{\mathbb{L}}
+\newcommand{\bM}{\mathbb{M}}
+\newcommand{\bN}{\mathbb{N}}
+\newcommand{\bO}{\mathbb{O}}
+\newcommand{\bP}{\mathbb{P}}
+\newcommand{\bQ}{\mathbb{Q}}
+\newcommand{\bR}{\mathbb{R}}
+\newcommand{\bS}{\mathbb{S}}
+\newcommand{\bT}{\mathbb{T}}
+\newcommand{\bU}{\mathbb{U}}
+\newcommand{\bV}{\mathbb{V}}
+\newcommand{\bW}{\mathbb{W}}
+\newcommand{\bX}{\mathbb{X}}
+\newcommand{\bY}{\mathbb{Y}}
+\newcommand{\bZ}{\mathbb{Z}}
+
+% Notation for lambda abstractions and function application. $~$
+% is the spacing that should be used between arguments.
+\newcommand{\lam}[2]{\lambda #1.\ #2}
+
+% Notation for paths, the interval, and its operations.
+\newcommand{\PathP}[3]{\rm{PathP}~ {#1}~ {#2}~ {#3}}
+\newcommand{\Path}[3]{\rm{Path}~ {#1}~ {#2}~ {#3}}
+\newcommand{\Square}[4]{\rm{Square}~ {#1}~ {#2}~ {#3}~ {#4}}
+\newcommand{\ap}[2]{\rm{ap}~ {#1}~ {#2}}
+
+\newcommand{\subst}{\operatorname{subst}}
+\newcommand{\ua}{\operatorname{ua}}
+
+\newcommand{\iZ}{\rm{i0}}
+\newcommand{\iO}{\rm{i1}}
+
+\newcommand{\ineg}{\lnot}
+\newcommand{\imin}{\land}
+\newcommand{\imax}{\lor}
+
+\newcommand{\transport}[2]{\rm{transport}~ #1~ #2}
+\newcommand{\transp}[3]{\rm{transp}~ #1~ #2~ #3}
+\newcommand{\Partial}[2]{\rm{Partial}~ #1~ #2}
+\newcommand{\dcomp}{\mathrel{\cdot\cdot}}
+\newcommand{\Extn}[2]{{#1}[#2]}
+
+\newcommand{\thecat}[1]{\mathbf{#1}} % Names of "concrete" categories
+\newcommand{\ca}[1]{\mathcal{#1}} % Names of variable categories
+
+%% \newcommand{\knowncat}[1]{\newcommand{\csname #1\endcsname}{\thecat{#1}}}
+%% \newcommand{\knownbicat}[1]{\newcommand{\csname #1\endcsname}{\thebicat{#1}}}
+
+\newcommand{\ty}{\rm{Type}}
+%% \newcommand{\set}{\rm{Set}}
+\newcommand{\prop}{\rm{Prop}}
+
+%% \DeclareMathOperator{\id}{id}
+%% \DeclareMathOperator{\Id}{Id}
+
+%% \knowncat{Sets}
+%% \knowncat{Ab}
+%% \knowncat{Graphs}
+%% \knowncat{MarkedGraphs}
+%% \knowncat{Props}
+%% \knowncat{FinSets}
+%% \knowncat{Rings}
+%% \knowncat{Grp}
+%% \knowncat{Rel}
+%% \knowncat{Par}
+%% \knowncat{Pos}
+
+\renewcommand{\Set}{\thecat{Set}}
+
+% "Postfix" operators that have a subscript and are annoying to write
+% out:
+\newcommand{\inv}{^{-1}}
+\renewcommand{\op}{^{\rm{op}}}
+
+\newcommand{\eps}{\varepsilon}
+
+\newcommand{\B}[1]{\mathbf{B} #1}
+\newcommand{\point}[1]{\bullet_{#1}}
+\newcommand{\List}[1]{\operatorname{List}(#1)}
+
+\renewcommand{\hom}{\mathbf{Hom}}
+
+\newcommand{\refl}{\mathrm{refl}}
+
+\DeclareMathOperator{\isiso}{is-iso}
+\DeclareMathOperator{\isequiv}{is-equiv}
+
+% Relations
+\newcommand{\pathto}{\equiv}
+\newcommand{\is}{\pathto}
+\newcommand{\definedto}{β}
+\newcommand{\equivto}{\simeq}
+\newcommand{\homotopicto}{\sim}
+\newcommand{\homotopyto}{\sim}
+\newcommand{\naturalto}{\Rightarrow}
+\newcommand{\isoto}{\cong}
+\newcommand{\monicto}{\rightarrowtail}
+\newcommand{\epicto}{\twoheadrightarrow}
+
+% Category names
+\newcommand{\C}[1]{\mathbf{#1}}
+
+\newcommand{\homset}[3]{{{#1} \left[ {#2} \to {#3} \right]}}
+\newcommand{\horizontalcompose}{\ast}
diff --git a/doerg/resources/net/deertopia/doerg/preview-template.tex b/doerg/resources/net/deertopia/doerg/preview-template.tex
new file mode 100644
index 0000000..9ecc15a
--- /dev/null
+++ b/doerg/resources/net/deertopia/doerg/preview-template.tex
@@ -0,0 +1,11 @@
+\documentclass{article}
+\usepackage{amsmath}
+\usepackage[active,tightpage,auctex,dvips]{preview}
+\usepackage{fontspec}
+\usepackage{ifxetex}
+\usepackage{syd-plex}
+
+\begin{document}
+\setlength\abovedisplayskip{0pt}
+% {{contents}}
+\end{document}
diff --git a/doerg/resources/net/deertopia/doerg/tuftesque.css b/doerg/resources/net/deertopia/doerg/tuftesque.css
index f1c27de..d71d1ac 100644
--- a/doerg/resources/net/deertopia/doerg/tuftesque.css
+++ b/doerg/resources/net/deertopia/doerg/tuftesque.css
@@ -105,8 +105,8 @@ p,
dl,
ol,
ul {
- font-size: 1.4rem;
- line-height: 2rem;
+ font-size: 1.2rem;
+ line-height: 1.5rem;
}
p {
@@ -542,3 +542,17 @@ figure.fullwidth figcaption {
; max-width: 55%
; font-size: 1.5rem
}
+
+.latex-fragment
+{ fill: currentColor
+}
+
+.latex-fragment.display-math
+{ display: block
+; width: 55%
+/* Center it β do we want to do that? */
+; align-items: center
+; justify-content: center
+; display: flex
+; max-width: 55%
+}
diff --git a/doerg/src/net/deertopia/doerg/common.clj b/doerg/src/net/deertopia/doerg/common.clj
new file mode 100644
index 0000000..8cbdc1c
--- /dev/null
+++ b/doerg/src/net/deertopia/doerg/common.clj
@@ -0,0 +1,125 @@
+(ns net.deertopia.doerg.common
+ (:require [babashka.process :as p]
+ [clojure.string :as str]
+ [clojure.tools.logging :as l]
+ [clojure.java.io :as io])
+ (:import (java.io FilterInputStream StringWriter InputStream
+ OutputStream PrintStream ByteArrayOutputStream
+ ByteArrayInputStream FilterOutputStream)
+ (java.nio.charset StandardCharsets)))
+
+(defn deref-with-timeout [process ms]
+ (let [p (promise)
+ process-future (future (deliver p @process))
+ timeout-future (future (Thread/sleep ms)
+ (future-cancel process-future)
+ (p/destroy-tree process)
+ (deliver p ::timed-out))]
+ (if (= @p ::timed-out)
+ (throw (ex-info (format "external command `%s' timed out after %.2fs."
+ (str/join " " (:cmd process))
+ (/ (double ms) 1000))
+ {:process process
+ :timed-out-after-milliseconds ms}))
+ @p)))
+
+(defn tee-input-stream
+ "Return a wrapped `InputStream` that writes all bytes read from
+ input-stream to sink, Γ la the UNIX command tee(1)."
+ [input-stream sink]
+ (proxy [FilterInputStream] [input-stream]
+ (read
+ ([]
+ (let [c (proxy-super read)]
+ (when (not= c -1)
+ (.write sink c))
+ c))
+ ([^bytes bs]
+ (let [n (proxy-super read bs)]
+ (when (not= n -1)
+ (.write sink bs 0 n))
+ n))
+ ([^bytes bs off len]
+ (let [n (proxy-super read bs off len)]
+ (when (not= n -1)
+ (.write sink bs off n))
+ n)))
+ (close []
+ (try (proxy-super close)
+ (finally (.close sink))))))
+
+(defn tee-output-stream
+ "Return a wrapped `OutputStream` that writes all bytes written to
+ output-stream to sink, Γ la the UNIX command tee(1)."
+ [output-stream sink]
+ (proxy [FilterOutputStream] [output-stream]
+ (write
+ ([bs-or-b]
+ (proxy-super write bs-or-b)
+ (.write sink bs-or-b))
+ ([^bytes bs off len]
+ (proxy-super write bs off len)
+ (.write sink bs off len)))
+ (close []
+ (try (proxy-super close)
+ (finally (.close sink))))))
+
+#_
+(defn hook-input-stream [input-stream hook]
+ (proxy [FilterInputStream] [input-stream]
+ (read
+ ([]
+ (let [c (proxy-super read)]
+ (when (not= c -1)
+ (hook (byte-array [c])))
+ c))
+ ([^bytes bs]
+ (let [n (proxy-super read bs)]
+ (when (not= n -1)
+ (let [bs* (byte-array n 0)]
+ (System/arraycopy bs 0 bs* 0 n)
+ (hook bs*)))
+ n))
+ ([^bytes bs off len]
+ (let [n (proxy-super read bs off len)]
+ (when (not= n -1)
+ (.write sink bs off n))
+ n)))
+ (close []
+ (try (proxy-super close)
+ (finally (.close sink))))))
+
+(comment
+ (with-open [sink (ByteArrayOutputStream.)
+ out (ByteArrayOutputStream.)
+ in (ByteArrayInputStream. (.getBytes "hello worms"))]
+ (io/copy (tee-input-stream in sink) out)
+ (def the-out out)
+ (def the-sink sink)
+ {:out out
+ :sink sink})
+ (with-open [sink (l/log-stream :info "blah")
+ out (ByteArrayOutputStream.)
+ in (ByteArrayInputStream. (.getBytes "hello worms"))]
+ (io/copy (tee-input-stream in sink) out)
+ (def the-out out)
+ (def the-sink sink)
+ {:out out
+ :sink sink}))
+
+(comment
+ (let [out (ByteArrayOutputStream.)]
+ (p/shell {:out (tee-output-stream
+ out (l/log-stream :info "blah"))}
+ "echo" "hello\n" "worms")
+ (.toString out)))
+
+(defn invoke [opts & cmd]
+ (l/info (str/join " " (cons "$" cmd)))
+ (let [r (apply p/shell
+ (merge {:continue true
+ :in nil :out :string :err :string}
+ opts)
+ cmd)
+ bin (first cmd)]
+ r))
diff --git a/doerg/src/net/deertopia/doerg/config.clj b/doerg/src/net/deertopia/doerg/config.clj
index 54e753f..628c1a7 100644
--- a/doerg/src/net/deertopia/doerg/config.clj
+++ b/doerg/src/net/deertopia/doerg/config.clj
@@ -4,16 +4,32 @@
[spec-dict.main :refer [dict]]))
(s/def ::config
- (s/keys :req [::ibm-plex-web]))
+ (s/keys :req [::ibm-plex-web
+ ::latex
+ ::dvisvgm]))
+
+(s/def ::file
+ #(or (instance? java.io.File %)
+ (string? %)))
(def default
{::ibm-plex-web
- (fs/file
- (or (System/getenv "IBM_PLEX_WEB")
- (some #(let [x (fs/path % "ibm-plex-web")]
- (and (fs/exists? x) x))
- (fs/split-paths (System/getenv "XDG_DATA_DIRS")))))})
+ (or (System/getenv "DOERG_IBM_PLEX_WEB")
+ (fs/file (some #(let [x (fs/path % "ibm-plex-web")]
+ (and (fs/exists? x) x))
+ (fs/split-paths (System/getenv "XDG_DATA_DIRS")))))
+ ::latex "xelatex"
+ ::dvisvgm "dvisvgm"
+ ;; TODO: Can we automatically set this to "./doerg-temml-worker/index.js" in
+ ;; a development environment?
+ ::doerg-temml-worker "doerg-temml-worker"})
(def ^:dynamic *cfg* default)
-(s/def ::ibm-plex-web string?)
+(s/def ::ibm-plex-web ::file)
+
+(s/def ::latex ::file)
+
+(s/def ::dvisvgm ::file)
+
+(s/def ::doerg-temml-worker ::file)
diff --git a/doerg/src/net/deertopia/doerg/element.clj b/doerg/src/net/deertopia/doerg/element.clj
index 65c1781..f3a0041 100644
--- a/doerg/src/net/deertopia/doerg/element.clj
+++ b/doerg/src/net/deertopia/doerg/element.clj
@@ -1,5 +1,6 @@
(ns net.deertopia.doerg.element
(:require [babashka.process :as p]
+ [net.deertopia.doerg.common :as common]
[clojure.string :as str]
[clojure.zip]
[babashka.fs :as fs]
@@ -14,26 +15,11 @@
(:refer-clojure :exclude [read-string]))
-(defonce ^:private uniorg-script-path-atom (atom nil))
-
-(def ^:dynamic *uniorg-timeout-after-milliseconds*
+(def ^:dynamic *uniorg-timeout-duration*
+ "Number of milliseconds to wait before killing the external Uniorg
+ process."
(* 10 1000))
-(defn deref-with-timeout [process ms]
- (let [p (promise)
- process-future (future (deliver p @process))
- timeout-future (future (Thread/sleep ms)
- (future-cancel process-future)
- (p/destroy-tree process)
- (deliver p ::timed-out))]
- (if (= @p ::timed-out)
- (throw (ex-info (format "external command `%s' timed out after %.2fs."
- (str/join " " (:cmd process))
- (/ (double ms) 1000))
- {:process process
- :timed-out-after-milliseconds ms}))
- @p)))
-
(defn- camel->kebab [s]
(->> (str/split s #"(?<=[a-z])(?=[A-Z])")
(map str/lower-case)
@@ -44,7 +30,7 @@
(let [r (-> (p/process
{:in in :out :string}
"doerg-parser")
- (deref-with-timeout *uniorg-timeout-after-milliseconds*))]
+ (common/deref-with-timeout *uniorg-timeout-duration*))]
(if (zero? (:exit r))
(-> r :out (json/parse-string (comp keyword camel->kebab))))))
@@ -93,6 +79,13 @@
(when-some [footnotes-headline (first (:children element))]
(= "Footnotes" (:raw-value footnotes-headline)))))
+(defn display-math?
+ "Return truthy if `element` should be considered display math."
+ [element]
+ (or (of-type? element "latex-environment")
+ (and (of-type? element "latex-fragment")
+ (-> element :contents (str/starts-with? "\\[")))))
+
;;; Spec
@@ -192,8 +185,8 @@
parser, return a map with the following keys
β’ :top-level-nodes The nodes that /should/ be at the top-level.
β’ :first-section-nodes The nodes that should be wrapped in a new
- section node
- β’ :rest Everything else!"
+ section node.
+ β’ :rest Everything else."
[nodes]
(let [[of-top-level remaining-nodes]
(->> nodes (split-with #(of-type? % "property-drawer" "keyword")))
diff --git a/doerg/src/net/deertopia/doerg/html.clj b/doerg/src/net/deertopia/doerg/html.clj
index 76cf513..0a17511 100644
--- a/doerg/src/net/deertopia/doerg/html.clj
+++ b/doerg/src/net/deertopia/doerg/html.clj
@@ -31,28 +31,17 @@
[:link {:rel "stylesheet" :type "text/css" :href href}])
(def ibm-plex
- (for [family ["serif" "sans-kr" "math"]]
- #_
- [:style (-> cfg/*cfg* ::cfg/ibm-plex-web
- (fs/file (format "css/ibm-plex-%s-default.min.css" family))
- slurp)]
- (external-stylesheet
- (format "ibm-plex-web/css/ibm-plex-%s-all.min.css" family))))
+ (concat
+ (for [family ["serif" "sans-kr" "math"]]
+ (external-stylesheet
+ (format "ibm-plex-web/css/ibm-plex-%s-all.min.css" family)))
+ [(external-stylesheet "Temml-Plex.css")]))
(def deerstar
- (external-stylesheet "deerstar.css")
- #_
- [:style (slurp (io/resource "net/deertopia/doerg/deerstar.css"))])
+ (external-stylesheet "deerstar.css"))
(def tuftesque
- (external-stylesheet "tuftesque.css")
- #_
- [:link {:rel "stylesheet"
- :type "text/css"
- :href "/resources/tuftesque.css"}]
- #_
- [:style
- (slurp (io/resource "net/deertopia/doerg/tuftesque.css"))])
+ (external-stylesheet "tuftesque.css"))
(def head
(list viewport charset ibm-plex deerstar tuftesque))
diff --git a/doerg/src/net/deertopia/doerg/render.clj b/doerg/src/net/deertopia/doerg/render.clj
index 89b33b0..0619019 100644
--- a/doerg/src/net/deertopia/doerg/render.clj
+++ b/doerg/src/net/deertopia/doerg/render.clj
@@ -7,8 +7,14 @@
[clojure.tools.logging.readable :as lr]
[com.rpl.specter :as sp]
[net.deertopia.doerg.html :as doerg-html]
+ [hiccup2.core :as hiccup]
[clojure.pprint]
- [clojure.zip :as z]))
+ #_
+ [net.deertopia.doerg.tex :as tex]
+ [net.deertopia.doerg.tex.native :as tex-native]
+ [net.deertopia.doerg.tex.temml :as tex-temml]
+ [clojure.zip :as z]
+ [babashka.fs :as fs]))
;;; Top-level API
@@ -38,32 +44,39 @@
(def ^:dynamic ^:private *document-info*)
(declare ^:private gather-footnotes render-renderer-error
- view-children-as-seq)
+ view-children-as-seq render-tex-snippets)
(defn org-element-recursive
"Recursively render an Org-mode element to Hiccup."
[e]
(->> e
- ;; gather-footnotes
(sp/transform
- [element/postorder-walker view-children-as-seq]
- (fn [node]
- (try (org-element node)
- (catch Throwable e
- (lr/error e "Error in renderer" {:node node})
- (render-renderer-error e)))))))
+ [element/postorder-walker view-children-as-seq]
+ (fn [node]
+ (try (org-element node)
+ (catch Throwable e
+ (lr/error e "Error in renderer" {:node node})
+ (render-renderer-error e)))))))
(defn org-document
"Recursively render an Org-mode document to Hiccup."
[doc]
- (let [rendered (org-element-recursive (gather-footnotes doc))]
- [:html
- [:head
- [:title "org document"]
- doerg-html/head]
- [:body
- [:article
- rendered]]]))
+ (tex-temml/binding-worker
+ (let [rendered (-> doc gather-footnotes render-tex-snippets
+ org-element-recursive)]
+ [:html
+ [:head
+ [:title "org document"]
+ doerg-html/head]
+ [:body
+ [:article
+ rendered]]])))
+
+(defn to-html
+ "Read `f` with `slurp` as an Org document and return a string of
+ rendered HTML."
+ [f]
+ (str (hiccup/html {} (-> f slurp element/read-string org-document))))
;;; Further dispatching on `org-element`
@@ -87,17 +100,6 @@
(sp/view #(update % :children seq))
sp/STAY))
-#_
-(defn- gather-footnotes [doc]
- (->> doc
- (sp/select
- [element/children-walker element/footnotes-section?
- element/children-walker
- #(element/of-type? % "footnote-definition")
- (sp/view (fn [d]
- {(:label d) d}))])
- (apply merge)))
-
(defn- contains-footnote-refs? [node]
(some #(element/of-type? % "footnote-reference")
(:children node)))
@@ -134,6 +136,85 @@
element/footnotes-section?]
sp/NONE))))
+(defn- collect-latex-headers [doc]
+ (->> doc
+ (sp/select
+ [element/postorder-walker
+ #(element/of-keyword-type? % "LATEX_HEADER")
+ (sp/view :value)])))
+
+(defn- read-and-patch-generated-svg [{:keys [file height depth]}]
+ ;; dvisvgm writes standalone SVG files, to which we need to make a
+ ;; few changes to use them inline within our HTML.
+ ;; β’ XML header: Bad syntax when embedded in an HTML doc. Remove
+ ;; it.
+ ;; β’ Width and height: We override these with our own values
+ ;; computed by `net.deertopia.doerg.tex` to ensure correct
+ ;; positioning relative to the surrounding text. More
+ ;; accurately, we remove the height and width attributes from
+ ;; the SVG tag, and set the new values for height and
+ ;; vertical-align in the style attribute
+ ;; β’ Viewbox: Must be removed entirely for correct positioning.
+ (-> (slurp file)
+ (str/replace-first #"<\?xml version='1.0' encoding='UTF-8'\?>\n?" "")
+ (str/replace-first #" height=['\"][^\"']+[\"']" "")
+ (str/replace-first #" width=['\"][^\"']+[\"']" "")
+ (str/replace-first
+ #"viewBox=['\"][^\"']+[\"']"
+ (fn [s]
+ (format "%s style=\"%s\""
+ s
+ (format "height:%.4fem;vertical-align:%.4fem;display:inline-block"
+ height (- depth)))))))
+
+(defn render-tex-snippets
+ "Traverse doc, adorning each LaTeX node with a promise resolving to,
+ optimistically, Hiccup-rendered SVG or MathML code."
+ [doc]
+ (let [promises (atom [])
+ r (->> doc (sp/transform
+ [element/postorder-walker
+ #(element/of-type?
+ % "latex-fragment" "latex-environment")]
+ (fn [node]
+ (let [p (promise)]
+ (swap! promises #(conj % {:promise p :node node}))
+ (assoc node ::rendered p)))))
+ f (fn []
+ (fs/with-temp-dir [svg-dir {:prefix "doerg-svg"}]
+ (let [rendered-snippets
+ (delay (->> @promises
+ (map #(-> % :node :value))
+ (apply tex-native/render svg-dir)))]
+ (doseq [{:keys [promise node]} @promises]
+ (try (let [{:keys [value]} node
+ temml (tex-temml/render value)]
+ (if (tex-temml/erroneous-output? temml)
+ (let [tex (get @rendered-snippets value)]
+ (if (:errors tex)
+ (deliver promise (hiccup/raw temml))
+ (->> tex
+ read-and-patch-generated-svg
+ hiccup/raw
+ (deliver promise))))
+ (deliver promise (hiccup/raw temml))))
+ (catch Exception e
+ (lr/error e)
+ (throw e)))))))
+ fut (future-call (bound-fn* f))]
+ ;; Time out after eight seconds. With all the LaTeX and IPC, there
+ ;; are so many opportunities for things to go wrong 3.
+ (let [fut-res (deref fut (* 10 1000) ::timed-out)]
+ (if (= fut-res ::timed-out)
+ (do (future-cancel fut)
+ (doseq [{:keys [promise]} @promises]
+ (deliver promise ::timed-out)))
+ fut-res))
+ r))
+
+(comment
+ (render-tex-snippets doc))
+
(defn- render-pprint
@@ -276,11 +357,15 @@
(defmethod org-element "citation-reference" [{:keys [key]}]
(str "@" key))
-(defmethod org-element "latex-fragment" [{:keys [value]}]
- [:span.latex-fragment value])
+(defmethod org-element "latex-fragment" [{:keys [contents value] :as e}]
+ [:span {:class (if (element/display-math? e)
+ "latex-fragment display-math"
+ "latex-fragment")}
+ (-> e ::rendered deref)])
-(defmethod org-element "latex-environment" [{:keys [value]}]
- [:pre [:code value]])
+(defmethod org-element "latex-environment" [{:keys [value] :as e}]
+ [:span.latex-fragment.display-math
+ (-> e ::rendered deref)])
(defmethod org-element "example-block" [{:keys [value]}]
[:pre value])
@@ -299,7 +384,6 @@
;; Completely ignore the LATEX_COMPILER keyword.
(defmethod org-keyword "LATEX_COMPILER" [_] nil)
-;; TODO: Real LatEx support.
(defmethod org-keyword "LATEX_HEADER" [_] nil)
;; Not sure how to deal with this one yet.
@@ -322,4 +406,3 @@
[:span.org-link.external
[:a {:href raw-link}
(or (seq children) raw-link)]])
-
diff --git a/doerg/src/net/deertopia/doerg/repl.clj b/doerg/src/net/deertopia/doerg/repl.clj
index 56dfe43..f2313cf 100644
--- a/doerg/src/net/deertopia/doerg/repl.clj
+++ b/doerg/src/net/deertopia/doerg/repl.clj
@@ -14,7 +14,11 @@
"/home/msyds/org/20250919114912-homepage.org"
#_
"/home/msyds/org/20251111182118-path_induction.org"
- "/home/msyds/org/20250512144715-natural_transformation_category_theory.org")
+ #_
+ "/home/msyds/org/20250512144715-natural_transformation_category_theory.org"
+ #_
+ "/home/msyds/org/20251021155921-path_action.org"
+ "test/net/deertopia/doerg/render_test/fallbacks.org")
(defn- force-create-sym-link [path target]
(fs/delete-if-exists path)
@@ -30,9 +34,10 @@
(io/resource "net/deertopia/doerg/deerstar.css"))
(force-create-sym-link (fs/file dest "tuftesque.css")
(io/resource "net/deertopia/doerg/tuftesque.css"))
+ (force-create-sym-link (fs/file dest "Temml-Plex.css")
+ (io/resource "net/deertopia/doerg/Temml-Plex.css"))
(fs/delete-if-exists (fs/file dest "index.html"))
- (->> (h/html (-> src slurp element/read-string render/org-document))
- str (spit (fs/file dest "index.html"))))
+ (->> src render/to-html str (spit (fs/file dest "index.html"))))
(defn render-edn [& {:keys [src dest]
:or {src some-org-file
diff --git a/doerg/src/net/deertopia/doerg/tex.clj b/doerg/src/net/deertopia/doerg/tex.clj
new file mode 100644
index 0000000..59c9e0c
--- /dev/null
+++ b/doerg/src/net/deertopia/doerg/tex.clj
@@ -0,0 +1,4 @@
+(ns net.deertopia.doerg.tex
+ (:require [net.deertopia.doerg.tex.native :as native]
+ [net.deertopia.doerg.tex.temml :as temml]
+ [babashka.fs :as fs]))
diff --git a/doerg/src/net/deertopia/doerg/tex/native.clj b/doerg/src/net/deertopia/doerg/tex/native.clj
new file mode 100644
index 0000000..cddbb11
--- /dev/null
+++ b/doerg/src/net/deertopia/doerg/tex/native.clj
@@ -0,0 +1,156 @@
+(ns net.deertopia.doerg.tex.native
+ "Shelling out to (Xe)LaTeX and dvisvgm. Much magic borrowed from
+ the org-latex-preview package for Emacs."
+ (:require [babashka.process :as p]
+ [net.deertopia.doerg.common :as common]
+ [clojure.java.io :as io]
+ [clojure.string :as str]
+ [clojure.tools.logging :as l]
+ [babashka.fs :as fs]
+ [net.deertopia.doerg.config :as cfg])
+ (:import (java.io ByteArrayOutputStream)))
+
+(def ^:private scale-divisor 66873.46948423679)
+
+(def ^:private font-size 10)
+
+(def ^:private tightpage-regexp
+ #"Preview: Tightpage (-?\d+) *(-?\d+) *(-?\d+) *(-?\d+)")
+
+(def ^:private preview-start-regexp
+ #"! Preview: Snippet (\d+) started.")
+
+(def ^:private preview-end-regexp
+ #"(?:^Preview: Tightpage.*$)?\n! Preview: Snippet (\d+) ended.\((\d+)\+(\d+)x(\d+)\)")
+
+(defn- invoke [extra-opts & args]
+ (let [namespace (or (::ns extra-opts) (first args))
+ out-bytes (ByteArrayOutputStream.)
+ out-stream (common/tee-output-stream
+ out-bytes
+ (l/log-stream :info (str namespace "/out")))
+ err-stream (l/log-stream :info (str namespace "/err"))
+ opts (merge extra-opts
+ {:out out-stream :err err-stream :continue true
+ :shutdown p/destroy-tree
+ :pre-start-fn (fn [{:keys [cmd]}]
+ (l/infof "$ %s"
+ (str/join " " cmd)))
+ :exit-fn (fn [{:keys [cmd exit]}]
+ (l/infof "%s exited w/ status %d"
+ (first cmd) exit))})
+ r (apply p/shell opts args)
+ out (.toString out-bytes)]
+ (-> r
+ (assoc ::out out))))
+
+(defn- parse-tightpage [latex-out]
+ (->> (re-find tightpage-regexp latex-out)
+ (drop 1)
+ (map parse-long)))
+
+(defn- compute-geometry [[tp1 tp2 tp3 tp4] [d1 d2 d3]]
+ (let [depth (/ (- d2 tp2) scale-divisor font-size)]
+ {:depth depth
+ :height (+ depth
+ (/ (+ d1 tp4)
+ scale-divisor
+ font-size))
+ :width (/ (+ d3 tp3 (- tp2))
+ scale-divisor
+ font-size)}))
+
+(defn- parse-latex-output [out]
+ (let [tightpage-info (parse-tightpage out)
+ m-start (re-matcher preview-start-regexp out)
+ m-end (re-matcher preview-end-regexp out)]
+ (loop [acc []]
+ (if-some [[_ snippet-ix] (re-find m-start)]
+ (let [r (re-find m-end)
+ [_ snippet-ix* _ _ _] r
+ dimensional-info (->> r (drop 2) (map parse-long))
+ errors (-> out
+ (subs (.end m-start) (.start m-end))
+ (str/replace-first #"[^!]*" "")
+ str/trim)]
+ (assert (= snippet-ix snippet-ix*))
+ (recur (conj acc (-> (compute-geometry
+ tightpage-info dimensional-info)
+ (assoc :errors (if (empty? errors)
+ nil
+ errors))))))
+ acc))))
+
+(defn- invoke-latex [& {:keys [file output-dir]}]
+ (let [latex (::cfg/latex cfg/*cfg*)]
+ (invoke
+ {:dir output-dir}
+ latex "-no-pdf" "-interaction" "nonstopmode"
+ "-output-directory" output-dir file)))
+
+(defn- invoke-dvisvgm [& {:keys [file output-dir]}]
+ (let [dvisvgm (::cfg/dvisvgm cfg/*cfg*)]
+ (invoke
+ {:dir output-dir}
+ dvisvgm "--page=1-" "--optimize" "--clipjoin"
+ "--relative" "--no-fonts" "-v3"
+ "--message=processing page {?pageno}: output written to {?svgpath}"
+ "--bbox=preview" "-o" "%9p.svg" file)))
+
+(defn- snippet-file-names
+ "Return a map of TeX snippets (as strings, including the math
+ delimiters) to file names as would be output by
+ `invoke-dvisvgm`. The returned file names are relative to dvisvgm's
+ output directory."
+ [snippets]
+ (let [svgs (for [i (range)]
+ (format "%09d.svg" i))]
+ (zipmap (reverse snippets) svgs)))
+
+(defn- instantiate-preview-template [snippets]
+ (let [contents (->> (for [s snippets]
+ (format "\\begin{preview}\n%s\n\\end{preview}" s))
+ (str/join "\n"))]
+ (-> (io/resource "net/deertopia/doerg/preview-template.tex")
+ slurp
+ (str/replace-first "% {{contents}}" contents))))
+
+(defn render
+ "Render a collection of `snippets` to SVGs in `output-dir` using a
+ LaTeX engine (XeLaTeX at the moment) and dvisvgm. Returns a map
+ whose keys are `snippets` and whose values are maps containing
+ geometry info, a string of errors output by LaTeX, and the path to
+ the generated SVG file. Math delimiters are *not* implicitly added
+ to each snippet."
+ [output-dir & snippets]
+ (fs/with-temp-dir [dir {:prefix "doerg-latex"}]
+ (let [preview-tex (fs/file dir "preview.tex")
+ preview-xdv (fs/file dir "preview.xdv")
+ distinct-snippets (distinct snippets)]
+ (fs/create-dirs output-dir)
+ (->> (instantiate-preview-template distinct-snippets)
+ (spit preview-tex))
+ (let [dimensions (-> (invoke-latex :output-dir dir :file preview-tex)
+ ::out parse-latex-output)
+ _ (invoke-dvisvgm :output-dir output-dir :file preview-xdv)]
+ ;; Adorn each snippet with dimensions and errors parsed from
+ ;; LaTeX's output, and the paths to SVG files generated by
+ ;; dvisvgm.
+ (assert (= (count distinct-snippets) (count dimensions)))
+ (->> (map (fn [ix snippet dimensions]
+ {snippet
+ (-> dimensions
+ (assoc
+ :file (fs/file output-dir
+ (format "%09d.svg" (inc ix)))))})
+ (range)
+ distinct-snippets
+ dimensions)
+ (into {}))))))
+
+(comment
+ (render "/tmp/doerg-tex-svgs"
+ "\\(c = \\sqrt{x^2 + y^2}\\)"
+ "\\(x\\)" "\\(y\\)" "\\(x\\)"
+ "\\(\\undefinedcommandlol\\)"))
+
diff --git a/doerg/src/net/deertopia/doerg/tex/temml.clj b/doerg/src/net/deertopia/doerg/tex/temml.clj
new file mode 100644
index 0000000..18aacdf
--- /dev/null
+++ b/doerg/src/net/deertopia/doerg/tex/temml.clj
@@ -0,0 +1,68 @@
+(ns net.deertopia.doerg.tex.temml
+ (:require [babashka.process :as p]
+ [net.deertopia.doerg.common :as common]
+ [net.deertopia.doerg.config :as cfg]
+ [clj-cbor.core :as cbor]
+ [clojure.java.io :as io]
+ [clojure.string :as str]
+ [clojure.tools.logging :as l]
+ [babashka.fs :as fs])
+ (:import (java.io ByteArrayOutputStream)))
+
+(def ^:dynamic *worker-timeout-duration*
+ "Number of milliseconds to wait before killing the external Uniorg
+ process."
+ (* 10 1000))
+
+(def ^:dynamic *worker*)
+
+(defn worker [& {:keys [preamble]}]
+ (let [doerg-temml-worker (::cfg/doerg-temml-worker cfg/*cfg*)]
+ (p/process
+ {:shutdown p/destroy-tree
+ :err (l/log-stream :info "temml/err")}
+ doerg-temml-worker
+ "--preamble"
+ "resources/net/deertopia/doerg/prelude.tex")))
+
+(defn close-worker [tw]
+ (.close (:in tw)))
+
+(defmacro with-worker [tw & body]
+ `(let [~tw (worker)]
+ (try
+ (do ~@body)
+ (finally
+ (close-worker ~tw)
+ (p/destroy-tree ~tw)))))
+
+(defmacro binding-worker [& body]
+ `(binding [*worker* (worker)]
+ (try
+ ~@body
+ (finally
+ (close-worker *worker*)))))
+
+(defn command-worker [x]
+ (cbor/encode cbor/default-codec (:in *worker*) x)
+ (.flush (:in *worker*))
+ (cbor/decode cbor/default-codec (:out *worker*)))
+
+(defn render-inline [s]
+ (command-worker s))
+
+(defn render-display [s]
+ (command-worker [s]))
+
+(defn render [s]
+ (if-let [[_ inner] (re-matches #"(?s)\\[(.*)\\]" s)]
+ (render-display inner)
+ (if (re-matches #"(?s)\\begin\{.+?}(.*?)\\end\{.+?}" s)
+ (render-display s)
+ (if-let [[_ inner] (re-matches #"(?s)\\\((.*)\\\)" s)]
+ (render-inline inner)
+ (throw (ex-info "weird" {:snippet s}))))))
+
+;; hackky....
+(defn erroneous-output? [s]
+ (re-find #"(#b22222|temml-error)" s))
diff --git a/doerg/test/net/deertopia/doerg/common_test.clj b/doerg/test/net/deertopia/doerg/common_test.clj
new file mode 100644
index 0000000..9305aec
--- /dev/null
+++ b/doerg/test/net/deertopia/doerg/common_test.clj
@@ -0,0 +1,32 @@
+(ns net.deertopia.doerg.common-test
+ (:require [net.deertopia.doerg.common :as sut]
+ [babashka.process :as p]
+ [clojure.test :as t]))
+
+(defn sleep-vs-timeout [& {:keys [sleep timeout]}]
+ (sut/deref-with-timeout
+ (p/process "sleep" (format "%ds" sleep))
+ (* timeout 1000)))
+
+;; Ideally we would test the following property:
+;;
+;; For natural numbers n and m, evaluating the form
+;; (sut/deref-with-timeout
+;; (p/process "sleep" (format "%ds" n))
+;; (* m 1000))
+;; will throw an exception iff n < m (probably with some margin of
+;; error lol).
+;;
+;; But, this is not something that we want to run dozens-to-hundreds
+;; of times. }:p
+
+(t/deftest long-sleep-vs-short-timeout
+ (t/testing "long sleep vs. short timeout"
+ (t/is (thrown-with-msg?
+ Exception #".*timed out.*"
+ (sleep-vs-timeout :sleep 5 :timeout 1)))))
+
+(t/deftest short-sleep-vs-long-timeout
+ (t/testing "short sleep vs. long timeout"
+ (t/is (instance? babashka.process.Process
+ (sleep-vs-timeout :sleep 1 :timeout 5)))))
diff --git a/doerg/test/net/deertopia/doerg/element_test.clj b/doerg/test/net/deertopia/doerg/element_test.clj
index 981c30b..3406bb1 100644
--- a/doerg/test/net/deertopia/doerg/element_test.clj
+++ b/doerg/test/net/deertopia/doerg/element_test.clj
@@ -6,34 +6,6 @@
[clojure.java.io :as io]
[com.rpl.specter :as sp]))
-(defn sleep-vs-timeout [& {:keys [sleep timeout]}]
- (sut/deref-with-timeout
- (p/process "sleep" (format "%ds" sleep))
- (* timeout 1000)))
-
-;; Ideally we would test the following property:
-;;
-;; For natural numbers n and m, evaluating the form
-;; (sut/deref-with-timeout
-;; (p/process "sleep" (format "%ds" n))
-;; (* m 1000))
-;; will throw an exception iff n < m (probably with some margin of
-;; error lol).
-;;
-;; But, this is not something that we want to run dozens-to-hundreds
-;; of times. }:p
-
-(t/deftest long-sleep-vs-short-timeout
- (t/testing "long sleep vs. short timeout"
- (t/is (thrown-with-msg?
- Exception #".*timed out.*"
- (sleep-vs-timeout :sleep 5 :timeout 1)))))
-
-(t/deftest short-sleep-vs-long-timeout
- (t/testing "short sleep vs. long timeout"
- (t/is (instance? babashka.process.Process
- (sleep-vs-timeout :sleep 1 :timeout 5)))))
-
(defn- first-child-of-type [parent type]
(some #(and (sut/of-type? % type) %) (:children parent)))
diff --git a/doerg/test/net/deertopia/doerg/render_test.clj b/doerg/test/net/deertopia/doerg/render_test.clj
new file mode 100644
index 0000000..976c82b
--- /dev/null
+++ b/doerg/test/net/deertopia/doerg/render_test.clj
@@ -0,0 +1,60 @@
+(ns net.deertopia.doerg.render-test
+ (:require [net.deertopia.doerg.render :as sut]
+ [net.deertopia.doerg.element :as element]
+ [net.deertopia.doerg.tex.temml :as temml]
+ [net.deertopia.doerg.tex.native :as native]
+ [com.rpl.specter :as sp]
+ [clojure.edn :as edn]
+ [clojure.test :as t]
+ [clojure.java.io :as io]
+ [clojure.string :as str]))
+
+;; Stupid and hacky.
+(defn mathml? [s]
+ (str/starts-with? s "