commit 51171e2b1f3ea041f7acb3b3c22541bec31706fc
Author: St John Karp <contact@stjo.hn>
Date: Tue, 9 Jul 2019 05:58:25 -0500
Initial commit
First pass at a static site generating suite.
Diffstat:
A | entries.pl | | | 38 | ++++++++++++++++++++++++++++++++++++++ |
A | helpers.pl | | | 46 | ++++++++++++++++++++++++++++++++++++++++++++++ |
A | html.pl | | | 230 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | markdown.pl | | | 26 | ++++++++++++++++++++++++++ |
A | rss.pl | | | 143 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | tastic.sh | | | 68 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
6 files changed, 551 insertions(+), 0 deletions(-)
diff --git a/entries.pl b/entries.pl
@@ -0,0 +1,37 @@
+:- include('helpers.pl').
+:- include('html.pl').
+:- include('markdown.pl').
+
+parse_entry:-
+ read_file(user_input, HTML),
+ parse_html(HTML).
+
+parse_entry(Filename):-
+ open(Filename, read, Stream),
+ read_file(Stream, HTML),
+ close(Stream),
+ parse_html(HTML).
+
+parse_html(HTML):-
+ page(EntryCodes, Title, Subtitle, Date, HTML, []),
+ markdown(EntryCodes, Title, Subtitle, Date, MarkdownCodes, []),
+ atom_codes(Markdown, MarkdownCodes),
+ write(Markdown),
+ halt.
+
+generate_entry:-
+ read_file(user_input, Entry),
+ generate_html(Entry).
+
+generate_entry(Filename):-
+ open(Filename, read, Stream),
+ read_file(Stream, Entry),
+ close(Stream),
+ generate_html(Entry).
+
+generate_html(Markdown):-
+ markdown(EntryCodes, Title, Subtitle, Date, Markdown, []),
+ page(EntryCodes, Title, Subtitle, Date, HTMLCodes, []),
+ atom_codes(HTML, HTMLCodes),
+ write(HTML),
+ halt.
+\ No newline at end of file
diff --git a/helpers.pl b/helpers.pl
@@ -0,0 +1,45 @@
+% Helpers
+
+read_file(Stream, []):-
+ at_end_of_stream(Stream).
+
+read_file(Stream, [Code|Rest]):-
+ \+ at_end_of_stream(Stream),
+ get_code(Stream, Code),
+ read_file(Stream, Rest).
+
+
+take_last(_, [], []).
+
+take_last(Max, [First|Rest], Result):-
+ take_last(Max, Rest, ResultSoFar),
+ take_append(Max, First, ResultSoFar, Result).
+
+take_append(Max, _, ResultSoFar, ResultSoFar):-
+ length(ResultSoFar, Max).
+
+take_append(_, Item, ResultSoFar, [Item|ResultSoFar]).
+
+replace(_, _, [], []).
+
+replace(FindCodes, ReplaceCodes, Haystack, Result):-
+ append(FindCodes, HaystackMinusMatch, Haystack),
+ replace(FindCodes, ReplaceCodes, HaystackMinusMatch, ReplacedHaystackMinusMatch),
+ append(ReplaceCodes, ReplacedHaystackMinusMatch, Result).
+
+replace(FindCodes, ReplaceCodes, [Code|Haystack], [Code|Result]):-
+ replace(FindCodes, ReplaceCodes, Haystack, Result).
+
+
+anything([]) --> [].
+
+anything([X|Rest]) --> [X], anything(Rest).
+
+
+whitespace --> [].
+
+whitespace --> "\n", whitespace.
+
+whitespace --> "\t", whitespace.
+
+whitespace --> " ", whitespace.
+\ No newline at end of file
diff --git a/html.pl b/html.pl
@@ -0,0 +1,229 @@
+page(Entry, Title, Subtitle, Date) -->
+ doctype,
+ whitespace,
+ html(Entry, Title, Subtitle, Date).
+
+html(Entry, Title, Subtitle, Date) -->
+ html_open,
+ whitespace,
+ head(Title),
+ whitespace,
+ body(Entry, Title, Subtitle, Date),
+ whitespace,
+ html_close.
+
+head(Title) -->
+ head_open,
+ whitespace,
+ title(Title),
+ whitespace,
+ meta,
+ whitespace,
+ styles,
+ whitespace,
+ rss,
+ whitespace,
+ head_close.
+
+body(Entry, Title, Subtitle, Date) -->
+ body_open,
+ whitespace,
+ header(Title),
+ whitespace,
+ article(Entry, Title, Subtitle, Date),
+ whitespace,
+ entry_utility,
+ whitespace,
+ footer,
+ whitespace,
+ body_close.
+
+header(Title) -->
+ header_open,
+ whitespace,
+ header_title(Title),
+ whitespace,
+ header_subtitle,
+ whitespace,
+ header_close.
+
+article(Entry, Title, Subtitle, Date) -->
+ article_open,
+ whitespace,
+ article_header(Title, Subtitle, Date),
+ whitespace,
+ div_entry_open,
+ whitespace,
+ anything(Entry),
+ whitespace,
+ div_entry_close,
+ whitespace,
+ article_close,
+ { [First|_] = Entry, char_code('<', First) }.
+
+article_header(null, null, null) --> [].
+
+article_header(Title, null, Date) -->
+ article_title(Title),
+ whitespace,
+ article_meta(Date).
+
+article_header(Title, Subtitle, Date) -->
+ article_title(Title),
+ whitespace,
+ article_subtitle(Subtitle),
+ whitespace,
+ article_meta(Date).
+
+footer -->
+ footer_open,
+ whitespace,
+ p_center_open,
+ whitespace,
+ license_link,
+ whitespace,
+ br,
+ whitespace,
+ license_text,
+ whitespace,
+ p_close,
+ whitespace,
+ footer_close.
+
+doctype --> "<!DOCTYPE html>".
+
+html_open --> "<html lang=\"en\">".
+
+head_open --> "<head>".
+
+meta --> "<meta charset=\"utf-8\" />".
+
+title(null) -->
+ "<title>",
+ site_title,
+ " | ",
+ site_subtitle,
+ "</title>".
+
+title(Title) -->
+ "<title>",
+ Title,
+ "</title>".
+
+title(_) -->
+ "<title>",
+ anything(_),
+ "</title>".
+
+styles -->
+ "<link rel=\"stylesheet\" href=\"",
+ site_url,
+ "/theme/css/styles.css\" />".
+
+rss -->
+ "<link rel=\"alternate\" type=\"application/rss+xml\" href=\"",
+ site_url,
+ "/feeds/rss.xml\" title=\"",
+ site_title,
+ " Latest Posts\" />".
+
+head_close --> "</head>".
+
+body_open --> "<body>".
+
+header_open --> "<header>".
+
+header_title(Title) -->
+ "<",
+ header_node(Title),
+ " id=\"blog-title\"><a href=\"",
+ site_url,
+ "\" title=\"",
+ site_title,
+ "\" rel=\"home\">",
+ site_title,
+ "</a></",
+ header_node(Title),
+ ">".
+
+header_node(null) --> "h1".
+
+header_node(_) --> "p".
+
+header_subtitle -->
+ "<p id=\"blog-description\">",
+ site_subtitle,
+ "</p>".
+
+header_close --> "</header>".
+
+article_open --> "<article>".
+
+article_open -->
+ "<article id=\"",
+ anything(_),
+ "\">".
+
+article_title(ArticleTitle) -->
+ "<h1 class=\"entry-title\">",
+ anything(ArticleTitle),
+ "</h1>".
+
+article_subtitle(ArticleSubtitle) -->
+ "<p class=\"entry-subtitle\">",
+ anything(ArticleSubtitle),
+ "</p>".
+
+article_meta(ArticleDate) -->
+ "<div class=\"entry-meta\">",
+ whitespace,
+ "<time datetime=\"",
+ anything(ArticleDate),
+ anything(_),
+ "\">",
+ anything(ArticleDate),
+ "</time>",
+ whitespace,
+ "</div><!-- .entry-meta -->".
+
+div_entry_open --> "<div class=\"entry-content\">".
+
+div_entry_close --> "</div><!-- .entry-content -->".
+
+article_close -->
+ "</article><!-- ",
+ anything(_),
+ " -->".
+
+entry_utility --> [].
+
+entry_utility -->
+ "<div class=\"entry-utility\">",
+ anything(_),
+ "</div><!-- #entry-utility -->".
+
+footer_open --> "<footer>".
+
+p_center_open --> "<p class=\"center\">".
+
+license_link -->
+ "<a rel=\"license\" href=\"http://creativecommons.org/licenses/by-nc-sa/3.0/\"><img alt=\"Creative Commons License\" style=\"border-width:0\" src=\"",
+ site_url,
+ "/theme/images/by-nc-sa_80x15.png\" /></a>".
+
+br --> "<br />".
+
+license_text -->
+ "Unless otherwise noted content on this website by <a href=\"mailto:",
+ email,
+ "\">",
+ name,
+ "</a> is licensed under a<br /><a rel=\"license\" href=\"http://creativecommons.org/licenses/by-nc-sa/3.0/\">Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License</a>.".
+
+p_close --> "</p>".
+
+footer_close --> "</footer>".
+
+body_close --> "</body>".
+
+html_close --> "</html>".
+\ No newline at end of file
diff --git a/markdown.pl b/markdown.pl
@@ -0,0 +1,25 @@
+% Markdown definition
+
+markdown(Entry, Title, null, Date) -->
+ metadata("Title", Title),
+ "\n",
+ metadata("Date", Date),
+ "\n\n",
+ anything(Entry).
+
+markdown(Entry, Title, Subtitle, Date) -->
+ metadata("Title", Title),
+ "\n",
+ metadata("Subtitle", Subtitle),
+ "\n",
+ metadata("Date", Date),
+ "\n\n",
+ anything(Entry).
+
+markdown(Entry, null, null, null) -->
+ anything(Entry).
+
+metadata(Key, Value) -->
+ Key,
+ ": ",
+ anything(Value).
+\ No newline at end of file
diff --git a/rss.pl b/rss.pl
@@ -0,0 +1,142 @@
+:- include('helpers.pl').
+:- include('markdown.pl').
+
+generate_rss(BuildDate, Filenames):-
+ files_to_articles(Filenames, Articles),
+ sort(Articles, SortedArticles),
+ take_last(5, SortedArticles, TakenArticles),
+ rss(BuildDate, TakenArticles, RSSCodes, []),
+ atom_codes(RSS, RSSCodes),
+ write(RSS),
+ halt.
+
+files_to_articles([], []).
+
+files_to_articles([Filename|Filenames], [article(Date, Title, Link, Description)|Articles]):-
+ open(Filename, read, Stream),
+ read_file(Stream, Markdown),
+ close(Stream),
+ % Grab the link.
+ atom_codes(Filename, FilenameCodes),
+ site_url(URL, []),
+ append(_, "/source", StartPath),
+ append(StartPath, Path, FilenameCodes),
+ append(PathWithoutFile, "index.md", Path),
+ append(URL, PathWithoutFile, Link),
+ % Extract the title, entry, etc. from the Markdown.
+ markdown(Entry, Title, _, Date, Markdown, []),
+ % XML escape the description.
+ replace("&", "&", Entry, EntryAmp),
+ replace("<", "<", EntryAmp, EntryLT),
+ replace(">", ">", EntryLT, Description),
+ files_to_articles(Filenames, Articles).
+
+rss(BuildDate, Articles) -->
+ rss_open,
+ "\n",
+ channel_meta(BuildDate),
+ "\n",
+ items(Articles),
+ "\n",
+ rss_close.
+
+rss_open -->
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>",
+ "\n",
+ "<rss version=\"2.0\">",
+ "\n",
+ "<channel>".
+
+channel_meta(BuildDate) -->
+ "<title>",
+ site_title,
+ "</title>",
+ "\n",
+ "<description>",
+ site_subtitle,
+ "</description>",
+ "\n",
+ "<link>",
+ site_url,
+ "</link>",
+ "\n",
+ language,
+ "\n",
+ copyright,
+ "\n",
+ webmaster,
+ "\n",
+ last_build_date(BuildDate).
+
+title(Title) -->
+ "<title>",
+ Title,
+ "</title>".
+
+description(Description) -->
+ "<description>",
+ Description,
+ "</description>".
+
+link(Link) -->
+ "<link>",
+ Link,
+ "</link>".
+
+language -->
+ "<language>",
+ "en-US",
+ "</language>".
+
+copyright -->
+ "<copyright>",
+ "Licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.",
+ "</copyright>".
+
+webmaster -->
+ "<webMaster>",
+ email,
+ "</webMaster>".
+
+last_build_date(BuildDate) -->
+ "<lastBuildDate>",
+ BuildDate,
+ "</lastBuildDate>".
+
+items([]) --> [].
+
+items([First|Rest]) --> item(First), items(Rest).
+
+item(article(Date, Title, Link, Description)) -->
+ item_open,
+ "\n",
+ title(Title),
+ "\n",
+ link(Link),
+ "\n",
+ description(Description),
+ "\n",
+ author,
+ "\n",
+ pubdate(Date),
+ "\n",
+ item_close.
+
+item_open --> "<item>".
+
+author -->
+ "<author>",
+ name,
+ "</author>".
+
+pubdate(Date) -->
+ "<pubDate>",
+ Date,
+ "</pubDate>".
+
+item_close --> "</item>".
+
+rss_close -->
+ "</channel>",
+ "\n",
+ "</rss>".
+\ No newline at end of file
diff --git a/tastic.sh b/tastic.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+
+OUTPUT_DIR=output
+SOURCE_DIR=source
+
+SITE_PATH=$2
+
+if [ "$1" == "ungenerate" ]
+then
+ # Create the directory structure.
+ rm -rf $SITE_PATH/$SOURCE_DIR/*
+ find $SITE_PATH/$OUTPUT_DIR -type d | \
+ sed "s|^$SITE_PATH/$OUTPUT_DIR|$SITE_PATH/$SOURCE_DIR|" | \
+ xargs -0 -d '\n' mkdir -p --
+
+ # Parse and create all the markdown files.
+ for file in `find $SITE_PATH/$OUTPUT_DIR -type f -name "*.html"`; do
+ NEW_PATH=`echo $file | sed "s|^$SITE_PATH/$OUTPUT_DIR|$SITE_PATH/$SOURCE_DIR|" | sed 's|.html$|.md|'`
+ cat $file | \
+ swipl --traditional -q -l entries.pl -g "parse_entry." | \
+ # Unsmarten the punctuation.
+ sed "s| | |g" | \
+ sed "s|‘|'|g" | \
+ sed "s|’|'|g" | \
+ sed "s|“|\"|g" | \
+ sed "s|”|\"|g" \
+ > $NEW_PATH
+ done
+
+ # Copy anything else directly.
+ for file in `find $SITE_PATH/$OUTPUT_DIR -type f -not -name "*.html"`; do
+ NEW_PATH=`echo $file | sed "s|^$SITE_PATH/$OUTPUT_DIR|$SITE_PATH/$SOURCE_DIR|"`
+ cp $file $NEW_PATH
+ done
+elif [ "$1" == "generate" ]
+then
+ # Create the directory structure.
+ rm -rf $SITE_PATH/$OUTPUT_DIR/*
+ find $SITE_PATH/$SOURCE_DIR -type d | \
+ sed "s|^$SITE_PATH/$SOURCE_DIR|$SITE_PATH/$OUTPUT_DIR|" | \
+ xargs -0 -d '\n' mkdir -p --
+
+ # Parse and create all the HTML files.
+ for file in `find $SITE_PATH/$SOURCE_DIR -type f -name "*.md"`; do
+ NEW_PATH=`echo $file | sed "s|^$SITE_PATH/$SOURCE_DIR|$SITE_PATH/$OUTPUT_DIR|" | sed 's|.md$|.html|'`
+ cat $file | \
+ swipl --traditional -q -l entries.pl -g "consult('$SITE_PATH/site.pl'), generate_entry." | \
+ tidy -quiet --indent auto --indent-with-tabs yes --wrap 0 | \
+ ~/.local/bin/smartypants \
+ > $NEW_PATH
+ done
+
+ # Copy anything else directly.
+ for file in `find $SITE_PATH/$SOURCE_DIR -type f -not -name "*.md"`; do
+ NEW_PATH=`echo $file | sed "s|^$SITE_PATH/$SOURCE_DIR|$SITE_PATH/$OUTPUT_DIR|"`
+ cp $file $NEW_PATH
+ done
+
+ # Generate the RSS feed.
+ mkdir -p $SITE_PATH/$OUTPUT_DIR/feeds
+ ARTICLES=`grep -Rl --include=\*.md "^Date: " $SITE_PATH/$SOURCE_DIR | paste -sd ',' - | sed "s|,|','|g"`
+ BUILD_DATE=`date +"%Y-%m-%d %T"`
+ swipl --traditional -q -l rss.pl -g "consult('$SITE_PATH/site.pl'), generate_rss(\"$BUILD_DATE\", ['$ARTICLES'])." \
+ > $SITE_PATH/$OUTPUT_DIR/feeds/rss.xml
+else
+ echo "Invalid argument."
+ exit 1
+fi