diff --git a/analyze.rb b/analyze.rb
index 9c36ac5..87fbc0e 100644
--- a/analyze.rb
+++ b/analyze.rb
@@ -40,11 +40,14 @@ files.each do |file|
name = file
tags = []
group = 1
+ draft = false
value = File.size(file)
url = file.gsub(/^content/, "https://danilafe.com").delete_suffix("/index.md").delete_suffix(".md")
File.readlines(file).each do |l|
if l =~ /^title: (.+)$/
name = $~[1].delete_prefix('"').delete_suffix('"')
+ elsif l =~ /^draft: true$/
+ draft = true
elsif l =~ /^tags: (.+)$/
tags = $~[1].delete_prefix("[").delete_suffix("]").split(/,\s?/).map { |it| it.gsub('"', '') }
if tags.include? "Compilers"
@@ -60,7 +63,8 @@ files.each do |file|
end
end
end
- data[file] = { :id => id, :label => name, :group => group, :tags => tags, :url => url, :value => value }
+ next if draft
+ data[file] = { :id => id, :name => name, :group => group, :tags => tags, :url => url, :value => value }
end
edges = []
@@ -72,6 +76,8 @@ files.each do |file1|
# end
next unless frefs = refs[file1]
frefs.each do |ref|
+ next unless data[file1]
+ next unless data[ref]
edges << { :from => data[file1][:id], :to => data[ref][:id] }
end
end
diff --git a/content/blog/_index.md b/content/blog/_index.md
index 6c432cb..18d45bf 100644
--- a/content/blog/_index.md
+++ b/content/blog/_index.md
@@ -1,3 +1,8 @@
---
title: "Posts"
---
+On this page you can see every article on this blog in reverse
+chronological order. If you like pretty colors and patterns,
+you can also see a visualization of all the articles and links between them
+on the [content graph]({{< relref "graph" >}}) page. If you have
+something specific in mind, you can also try using the [search]({{< relref "search" >}}) page.
diff --git a/content/graph.html b/content/graph.html
new file mode 100644
index 0000000..00cc6f1
--- /dev/null
+++ b/content/graph.html
@@ -0,0 +1,22 @@
+---
+title: "Content Graph"
+---
+
+
+
+
+Here you can see a visualization of the posts on this article using Vis.js.
+In the below graph, each node is an article, and each edge between nodes is a reference from
+one article to another. Each article is sized to be roughly proportional to its word count (file size
+is used as a quick metric for this purpose). You can hover over a node to see the title of the article
+it represents, and double-click to go to that article.
+
+
+
diff --git a/static/graph/graph.gen.js b/static/graph/graph.gen.js
new file mode 100644
index 0000000..bd3f5ab
--- /dev/null
+++ b/static/graph/graph.gen.js
@@ -0,0 +1,826 @@
+export const nodes = [
+ {
+ "id": 1,
+ "name": "Advent of Code in Coq - Day 1",
+ "group": 3,
+ "tags": [
+ "Advent of Code",
+ "Coq"
+ ],
+ "url": "https://danilafe.com/blog/00_aoc_coq",
+ "value": 16279
+ },
+ {
+ "id": 2,
+ "name": "Compiling a Functional Language Using C++, Part 0 - Intro",
+ "group": 2,
+ "tags": [
+ "C and C++",
+ "Functional Languages",
+ "Compilers"
+ ],
+ "url": "https://danilafe.com/blog/00_compiler_intro",
+ "value": 7087
+ },
+ {
+ "id": 3,
+ "name": "Compiling a Functional Language Using C++, Part 1 - Tokenizing",
+ "group": 2,
+ "tags": [
+ "C and C++",
+ "Functional Languages",
+ "Compilers"
+ ],
+ "url": "https://danilafe.com/blog/01_compiler_tokenizing",
+ "value": 10885
+ },
+ {
+ "id": 4,
+ "name": "Compiling a Functional Language Using C++, Part 2 - Parsing",
+ "group": 2,
+ "tags": [
+ "C and C++",
+ "Functional Languages",
+ "Compilers"
+ ],
+ "url": "https://danilafe.com/blog/02_compiler_parsing",
+ "value": 16450
+ },
+ {
+ "id": 5,
+ "name": "Compiling a Functional Language Using C++, Part 3 - Type Checking",
+ "group": 2,
+ "tags": [
+ "C and C++",
+ "Functional Languages",
+ "Compilers"
+ ],
+ "url": "https://danilafe.com/blog/03_compiler_typechecking",
+ "value": 27055
+ },
+ {
+ "id": 6,
+ "name": "Compiling a Functional Language Using C++, Part 4 - Small Improvements",
+ "group": 2,
+ "tags": [
+ "C and C++",
+ "Functional Languages",
+ "Compilers"
+ ],
+ "url": "https://danilafe.com/blog/04_compiler_improvements",
+ "value": 10003
+ },
+ {
+ "id": 7,
+ "name": "Compiling a Functional Language Using C++, Part 5 - Execution",
+ "group": 2,
+ "tags": [
+ "C and C++",
+ "Functional Languages",
+ "Compilers"
+ ],
+ "url": "https://danilafe.com/blog/05_compiler_execution",
+ "value": 28832
+ },
+ {
+ "id": 8,
+ "name": "Compiling a Functional Language Using C++, Part 6 - Compilation",
+ "group": 2,
+ "tags": [
+ "C and C++",
+ "Functional Languages",
+ "Compilers"
+ ],
+ "url": "https://danilafe.com/blog/06_compiler_compilation",
+ "value": 22584
+ },
+ {
+ "id": 9,
+ "name": "Compiling a Functional Language Using C++, Part 7 - Runtime",
+ "group": 2,
+ "tags": [
+ "C and C++",
+ "Functional Languages",
+ "Compilers"
+ ],
+ "url": "https://danilafe.com/blog/07_compiler_runtime",
+ "value": 7410
+ },
+ {
+ "id": 10,
+ "name": "Compiling a Functional Language Using C++, Part 8 - LLVM",
+ "group": 2,
+ "tags": [
+ "C and C++",
+ "Functional Languages",
+ "Compilers"
+ ],
+ "url": "https://danilafe.com/blog/08_compiler_llvm",
+ "value": 23607
+ },
+ {
+ "id": 11,
+ "name": "Compiling a Functional Language Using C++, Part 9 - Garbage Collection",
+ "group": 2,
+ "tags": [
+ "C and C++",
+ "Functional Languages",
+ "Compilers"
+ ],
+ "url": "https://danilafe.com/blog/09_compiler_garbage_collection",
+ "value": 25989
+ },
+ {
+ "id": 12,
+ "name": "Compiling a Functional Language Using C++, Part 10 - Polymorphism",
+ "group": 2,
+ "tags": [
+ "C and C++",
+ "Functional Languages",
+ "Compilers"
+ ],
+ "url": "https://danilafe.com/blog/10_compiler_polymorphism",
+ "value": 42606
+ },
+ {
+ "id": 13,
+ "name": "Compiling a Functional Language Using C++, Part 11 - Polymorphic Data Types",
+ "group": 2,
+ "tags": [
+ "C and C++",
+ "Functional Languages",
+ "Compilers"
+ ],
+ "url": "https://danilafe.com/blog/11_compiler_polymorphic_data_types",
+ "value": 19586
+ },
+ {
+ "id": 14,
+ "name": "Compiling a Functional Language Using C++, Part 12 - Let/In and Lambdas",
+ "group": 2,
+ "tags": [
+ "C and C++",
+ "Functional Languages",
+ "Compilers"
+ ],
+ "url": "https://danilafe.com/blog/12_compiler_let_in_lambda",
+ "value": 49622
+ },
+ {
+ "id": 15,
+ "name": "Compiling a Functional Language Using C++, Part 13 - Cleanup",
+ "group": 2,
+ "tags": [
+ "C and C++",
+ "Functional Languages",
+ "Compilers"
+ ],
+ "url": "https://danilafe.com/blog/13_compiler_cleanup",
+ "value": 44195
+ },
+ {
+ "id": 16,
+ "name": "A Language for an Assignment - Homework 1",
+ "group": 4,
+ "tags": [
+ "Haskell",
+ "Python",
+ "Algorithms",
+ "Programming Languages"
+ ],
+ "url": "https://danilafe.com/blog/00_cs325_languages_hw1",
+ "value": 21326
+ },
+ {
+ "id": 17,
+ "name": "Advent of Code in Coq - Day 8",
+ "group": 3,
+ "tags": [
+ "Advent of Code",
+ "Coq"
+ ],
+ "url": "https://danilafe.com/blog/01_aoc_coq",
+ "value": 46511
+ },
+ {
+ "id": 18,
+ "name": "A Language for an Assignment - Homework 2",
+ "group": 4,
+ "tags": [
+ "Haskell",
+ "Python",
+ "Algorithms",
+ "Programming Languages"
+ ],
+ "url": "https://danilafe.com/blog/01_cs325_languages_hw2",
+ "value": 8949
+ },
+ {
+ "id": 19,
+ "name": "Learning Emulation, Part 1",
+ "group": 1,
+ "tags": [
+ "C and C++",
+ "Emulation"
+ ],
+ "url": "https://danilafe.com/blog/01_learning_emulation",
+ "value": 3387
+ },
+ {
+ "id": 20,
+ "name": "A Language for an Assignment - Homework 3",
+ "group": 4,
+ "tags": [
+ "Haskell",
+ "Python",
+ "Algorithms",
+ "Programming Languages"
+ ],
+ "url": "https://danilafe.com/blog/02_cs325_languages_hw3",
+ "value": 19899
+ },
+ {
+ "id": 21,
+ "name": "Learning Emulation, Part 2",
+ "group": 1,
+ "tags": [
+ "C and C++",
+ "Emulation"
+ ],
+ "url": "https://danilafe.com/blog/02_learning_emulation",
+ "value": 3965
+ },
+ {
+ "id": 22,
+ "name": "Learning Emulation, Part 2.5 - Implementation",
+ "group": 1,
+ "tags": [
+ "C and C++",
+ "Emulation"
+ ],
+ "url": "https://danilafe.com/blog/03_learning_emulation",
+ "value": 4881
+ },
+ {
+ "id": 23,
+ "name": "Posts",
+ "group": 1,
+ "tags": [
+
+ ],
+ "url": "https://danilafe.com/blog/_index",
+ "value": 23
+ },
+ {
+ "id": 24,
+ "name": "Rendering Mathematics On The Back End",
+ "group": 1,
+ "tags": [
+ "Website",
+ "Nix",
+ "Ruby",
+ "KaTeX"
+ ],
+ "url": "https://danilafe.com/blog/backend_math_rendering",
+ "value": 13994
+ },
+ {
+ "id": 25,
+ "name": "Math Rendering is Wrong",
+ "group": 1,
+ "tags": [
+ "Website"
+ ],
+ "url": "https://danilafe.com/blog/math_rendering_is_wrong",
+ "value": 10313
+ },
+ {
+ "id": 26,
+ "name": "Thoughts on Better Explanations",
+ "group": 1,
+ "tags": [
+ "Language Server Protocol"
+ ],
+ "url": "https://danilafe.com/blog/better_explanations",
+ "value": 5815
+ },
+ {
+ "id": 28,
+ "name": "Rendering Mathematics On The Back End",
+ "group": 1,
+ "tags": [
+ "Website",
+ "Nix",
+ "Ruby",
+ "KaTeX"
+ ],
+ "url": "https://danilafe.com/blog/./backend_math_rendering",
+ "value": 13994
+ },
+ {
+ "id": 29,
+ "name": "How Many Values Does a Boolean Have?",
+ "group": 5,
+ "tags": [
+ "Java",
+ "Haskell",
+ "C and C++"
+ ],
+ "url": "https://danilafe.com/blog/boolean_values",
+ "value": 12955
+ },
+ {
+ "id": 30,
+ "name": "Pleasant Code Includes with Hugo",
+ "group": 1,
+ "tags": [
+ "Hugo"
+ ],
+ "url": "https://danilafe.com/blog/codelines",
+ "value": 11846
+ },
+ {
+ "id": 31,
+ "name": "Formalizing Dawn in Coq",
+ "group": 3,
+ "tags": [
+ "Coq",
+ "Dawn",
+ "Programming Languages"
+ ],
+ "url": "https://danilafe.com/blog/coq_dawn",
+ "value": 20615
+ },
+ {
+ "id": 32,
+ "name": "A Verified Evaluator for the Untyped Concatenative Calculus",
+ "group": 3,
+ "tags": [
+ "Dawn",
+ "Coq",
+ "Programming Languages"
+ ],
+ "url": "https://danilafe.com/blog/coq_dawn_eval",
+ "value": 31852
+ },
+ {
+ "id": 33,
+ "name": "Formalizing Dawn in Coq",
+ "group": 3,
+ "tags": [
+ "Coq",
+ "Dawn",
+ "Programming Languages"
+ ],
+ "url": "https://danilafe.com/blog/./coq_dawn",
+ "value": 20615
+ },
+ {
+ "id": 35,
+ "name": "Proof of Inductive Palindrome Definition in Coq",
+ "group": 3,
+ "tags": [
+ "Coq"
+ ],
+ "url": "https://danilafe.com/blog/coq_palindrome",
+ "value": 8177
+ },
+ {
+ "id": 36,
+ "name": "Building a Basic Crystal Project with Nix",
+ "group": 6,
+ "tags": [
+ "Crystal",
+ "Nix"
+ ],
+ "url": "https://danilafe.com/blog/crystal_nix",
+ "value": 5981
+ },
+ {
+ "id": 37,
+ "name": "Building a Crystal Project with Nix, Revisited",
+ "group": 6,
+ "tags": [
+ "Crystal",
+ "Nix"
+ ],
+ "url": "https://danilafe.com/blog/crystal_nix_revisited",
+ "value": 9141
+ },
+ {
+ "id": 38,
+ "name": "Setting Up Crystal on ARM",
+ "group": 6,
+ "tags": [
+ "Crystal",
+ "ARM"
+ ],
+ "url": "https://danilafe.com/blog/crystal_on_arm",
+ "value": 5414
+ },
+ {
+ "id": 39,
+ "name": "DELL Is A Horrible Company And You Should Avoid Them At All Costs",
+ "group": 1,
+ "tags": [
+ "Electronics"
+ ],
+ "url": "https://danilafe.com/blog/dell_is_horrible",
+ "value": 21686
+ },
+ {
+ "id": 40,
+ "name": "Haskell Error Checking and Autocompletion With LSP",
+ "group": 5,
+ "tags": [
+ "Haskell",
+ "Language Server Protocol"
+ ],
+ "url": "https://danilafe.com/blog/haskell_language_server",
+ "value": 10468
+ },
+ {
+ "id": 41,
+ "name": "Using GHC IDE for Haskell Error Checking and Autocompletion",
+ "group": 5,
+ "tags": [
+ "Haskell",
+ "Language Server Protocol"
+ ],
+ "url": "https://danilafe.com/blog/haskell_language_server_again",
+ "value": 4758
+ },
+ {
+ "id": 42,
+ "name": "Time Traveling In Haskell: How It Works And How To Use It",
+ "group": 5,
+ "tags": [
+ "Haskell"
+ ],
+ "url": "https://danilafe.com/blog/haskell_lazy_evaluation",
+ "value": 25280
+ },
+ {
+ "id": 43,
+ "name": "Approximating Custom Functions in Hugo",
+ "group": 1,
+ "tags": [
+ "Hugo"
+ ],
+ "url": "https://danilafe.com/blog/hugo_functions",
+ "value": 4034
+ },
+ {
+ "id": 44,
+ "name": "Introducing Matrix Highlight",
+ "group": 1,
+ "tags": [
+ "Matrix",
+ "Project",
+ "Matrix Highlight"
+ ],
+ "url": "https://danilafe.com/blog/introducing_highlight",
+ "value": 7481
+ },
+ {
+ "id": 45,
+ "name": "Local Development Environment for JOS and CS 444",
+ "group": 1,
+ "tags": [
+ "C and C++",
+ "OS Dev"
+ ],
+ "url": "https://danilafe.com/blog/jos_local",
+ "value": 4839
+ },
+ {
+ "id": 46,
+ "name": "Lambda Calculus and Church Encoded Integers",
+ "group": 1,
+ "tags": [
+ "Lambda Calculus"
+ ],
+ "url": "https://danilafe.com/blog/lambda_calculus_integers",
+ "value": 10351
+ },
+ {
+ "id": 47,
+ "name": "Digit Sum Patterns and Modular Arithmetic",
+ "group": 1,
+ "tags": [
+ "Ruby",
+ "Mathematics"
+ ],
+ "url": "https://danilafe.com/blog/modulo_patterns",
+ "value": 39663
+ },
+ {
+ "id": 48,
+ "name": "New Look, New Features!",
+ "group": 1,
+ "tags": [
+ "Website"
+ ],
+ "url": "https://danilafe.com/blog/new_look",
+ "value": 727
+ },
+ {
+ "id": 50,
+ "name": "JavaScript-Free Sidenotes in Hugo",
+ "group": 1,
+ "tags": [
+ "Website",
+ "Hugo",
+ "CSS"
+ ],
+ "url": "https://danilafe.com/blog/sidenotes",
+ "value": 7762
+ },
+ {
+ "id": 51,
+ "name": "Creating Recursive Functions in a Stack Based Language",
+ "group": 4,
+ "tags": [
+ "Programming Languages"
+ ],
+ "url": "https://danilafe.com/blog/stack_recursion",
+ "value": 15694
+ },
+ {
+ "id": 52,
+ "name": "A Look Into Starbound's File Formats",
+ "group": 1,
+ "tags": [
+ "Starbound"
+ ],
+ "url": "https://danilafe.com/blog/starbound",
+ "value": 11817
+ },
+ {
+ "id": 53,
+ "name": "Switching to a Static Site Generator",
+ "group": 1,
+ "tags": [
+ "Website"
+ ],
+ "url": "https://danilafe.com/blog/static_site",
+ "value": 3402
+ },
+ {
+ "id": 55,
+ "name": "A Typesafe Representation of an Imperative Language",
+ "group": 4,
+ "tags": [
+ "Idris",
+ "Programming Languages"
+ ],
+ "url": "https://danilafe.com/blog/typesafe_imperative_lang",
+ "value": 20619
+ },
+ {
+ "id": 56,
+ "name": "Meaningfully Typechecking a Language in Idris",
+ "group": 4,
+ "tags": [
+ "Haskell",
+ "Idris",
+ "Programming Languages"
+ ],
+ "url": "https://danilafe.com/blog/typesafe_interpreter",
+ "value": 13655
+ },
+ {
+ "id": 57,
+ "name": "Meaningfully Typechecking a Language in Idris, Revisited",
+ "group": 4,
+ "tags": [
+ "Idris",
+ "Programming Languages"
+ ],
+ "url": "https://danilafe.com/blog/typesafe_interpreter_revisited",
+ "value": 16100
+ },
+ {
+ "id": 58,
+ "name": "Meaningfully Typechecking a Language in Idris, With Tuples",
+ "group": 4,
+ "tags": [
+ "Idris",
+ "Programming Languages"
+ ],
+ "url": "https://danilafe.com/blog/typesafe_interpreter_tuples",
+ "value": 10235
+ },
+ {
+ "id": 59,
+ "name": "Type-Safe Event Emitter in TypeScript",
+ "group": 1,
+ "tags": [
+ "TypeScript"
+ ],
+ "url": "https://danilafe.com/blog/typescript_typesafe_events",
+ "value": 5589
+ }
+];
+export const edges = [
+ {
+ "from": 2,
+ "to": 3
+ },
+ {
+ "from": 2,
+ "to": 4
+ },
+ {
+ "from": 2,
+ "to": 5
+ },
+ {
+ "from": 2,
+ "to": 6
+ },
+ {
+ "from": 2,
+ "to": 7
+ },
+ {
+ "from": 2,
+ "to": 8
+ },
+ {
+ "from": 2,
+ "to": 9
+ },
+ {
+ "from": 2,
+ "to": 10
+ },
+ {
+ "from": 2,
+ "to": 11
+ },
+ {
+ "from": 2,
+ "to": 12
+ },
+ {
+ "from": 2,
+ "to": 13
+ },
+ {
+ "from": 2,
+ "to": 14
+ },
+ {
+ "from": 2,
+ "to": 15
+ },
+ {
+ "from": 3,
+ "to": 2
+ },
+ {
+ "from": 3,
+ "to": 4
+ },
+ {
+ "from": 4,
+ "to": 5
+ },
+ {
+ "from": 5,
+ "to": 6
+ },
+ {
+ "from": 6,
+ "to": 7
+ },
+ {
+ "from": 7,
+ "to": 8
+ },
+ {
+ "from": 8,
+ "to": 9
+ },
+ {
+ "from": 9,
+ "to": 10
+ },
+ {
+ "from": 10,
+ "to": 11
+ },
+ {
+ "from": 11,
+ "to": 12
+ },
+ {
+ "from": 12,
+ "to": 10
+ },
+ {
+ "from": 12,
+ "to": 5
+ },
+ {
+ "from": 12,
+ "to": 8
+ },
+ {
+ "from": 12,
+ "to": 13
+ },
+ {
+ "from": 13,
+ "to": 12
+ },
+ {
+ "from": 13,
+ "to": 4
+ },
+ {
+ "from": 13,
+ "to": 14
+ },
+ {
+ "from": 14,
+ "to": 12
+ },
+ {
+ "from": 14,
+ "to": 7
+ },
+ {
+ "from": 14,
+ "to": 8
+ },
+ {
+ "from": 14,
+ "to": 6
+ },
+ {
+ "from": 14,
+ "to": 15
+ },
+ {
+ "from": 15,
+ "to": 14
+ },
+ {
+ "from": 18,
+ "to": 16
+ },
+ {
+ "from": 21,
+ "to": 19
+ },
+ {
+ "from": 22,
+ "to": 19
+ },
+ {
+ "from": 24,
+ "to": 2
+ },
+ {
+ "from": 24,
+ "to": 25
+ },
+ {
+ "from": 30,
+ "to": 2
+ },
+ {
+ "from": 32,
+ "to": 33
+ },
+ {
+ "from": 37,
+ "to": 36
+ },
+ {
+ "from": 41,
+ "to": 40
+ },
+ {
+ "from": 43,
+ "to": 30
+ },
+ {
+ "from": 50,
+ "to": 2
+ },
+ {
+ "from": 57,
+ "to": 56
+ },
+ {
+ "from": 58,
+ "to": 56
+ },
+ {
+ "from": 58,
+ "to": 57
+ }
+];
diff --git a/static/graph/graph.js b/static/graph/graph.js
new file mode 100644
index 0000000..3bc3a1b
--- /dev/null
+++ b/static/graph/graph.js
@@ -0,0 +1,53 @@
+import { nodes, edges } from "./graph.gen.js";
+
+var container = document.getElementById("graph-container");
+var options = {
+ interaction: {
+ hover: true
+ },
+ nodes: {
+ shape: "dot",
+ size: 16,
+ },
+ physics: {
+ forceAtlas2Based: {
+ gravitationalConstant: -10,
+ centralGravity: 0.005,
+ springLength: 230,
+ springConstant: 0.18,
+ },
+ maxVelocity: 146,
+ solver: "forceAtlas2Based",
+ timestep: 0.35,
+ stabilization: { iterations: 150 },
+ },
+};
+
+var nodesDs = new vis.DataSet();
+nodesDs.add(nodes);
+var edgesDs = new vis.DataSet();
+edgesDs.add(edges);
+var network = new vis.Network(container, { nodes: nodesDs, edges: edgesDs }, options);
+
+network.on("doubleClick", function (params) {
+ params.event = "[original event]";
+ if (params.nodes.length !== 1) return;
+ window.open(nodesDs.get(params.nodes[0]).url, "_blank")
+});
+network.on("hoverNode", function (params) {
+ nodesDs.update({ id: params.node, label: nodesDs.get(params.node).name });
+});
+network.on("blurNode", function (params) {
+ nodesDs.update({ id: params.node, label: undefined });
+});
+// network.on("selectNode", function (params) {
+// for (const node of params.nodes) {
+// nodesDs.update({ id: node, label: nodesDs.get(node).name });
+// }
+// });
+// network.on("deselectNode", function (params) {
+// for (const node of params.previousSelection.nodes) {
+// if (params.nodes.some(n => n === node)) continue;
+// nodesDs.update({ id: node, label: undefined });
+// }
+// });