Created
March 7, 2025 15:53
-
-
Save fkettelhoit/ec640ffcddc4cb4ec6b1af362d8f8a28 to your computer and use it in GitHub Desktop.
Simple Static Site Generator in a Minimal Lambda Calculus Language + Effects
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| loop map = f => xs => | |
| if xs is Cons(x, xs) | |
| Cons(f(x), map(f, xs)) | |
| else | |
| xs | |
| let reverse = xs => | |
| loop reverse = xs => rest => | |
| if xs is Cons(x, xs) | |
| reverse(xs, Cons(x, rest)) | |
| else | |
| rest | |
| reverse(xs, Nil) | |
| let read-lines = _ => | |
| loop split-lines = curr => lines => | |
| let c = read-char!() | |
| if c is () | |
| Cons(curr, lines) | |
| else if c is "\n" | |
| split-lines(Nil, Cons(curr, lines)) | |
| else | |
| split-lines(Cons(c, curr), lines) | |
| let lines = map(reverse, split-lines(Nil, Nil)) | |
| reverse(lines) | |
| loop append = xs => ys => | |
| if xs is Cons(x, xs) | |
| Cons(x, append(xs, ys)) | |
| else | |
| ys | |
| loop wrap = pre => l => post => | |
| let pre = Cons(pre, Nil) | |
| let post = Cons(post, Nil) | |
| append(pre, append(l, post)) | |
| let is-empty = xs => | |
| if xs is Cons(x, xs) | |
| False | |
| else | |
| True | |
| let is-heading = l => | |
| if l is Cons(x, l') | |
| if l' is Cons(y, l'') | |
| if x is "#" | |
| if y is " " | |
| Heading(l'') | |
| else | |
| Nil | |
| else | |
| Nil | |
| else | |
| Nil | |
| else | |
| Nil | |
| let is-code-block = l => | |
| if l is Cons(a, l') | |
| if l' is Cons(b, l'') | |
| if l'' is Cons(c, l''') | |
| if a is "`" | |
| if b is "`" | |
| if c is "`" | |
| if l''' is Nil | |
| True | |
| else | |
| False | |
| else | |
| False | |
| else | |
| False | |
| else | |
| False | |
| else | |
| False | |
| else | |
| False | |
| else | |
| False | |
| loop inline-to-html = l => style => | |
| if l is Cons(c, l) | |
| if c is "_" | |
| if style is Em | |
| Cons("</em>", inline-to-html(l, None)) | |
| else | |
| Cons("<em>", inline-to-html(l, Em)) | |
| else | |
| Cons(c, inline-to-html(l, style)) | |
| else | |
| l | |
| let line-to-html = l => | |
| if is-empty(l) is True | |
| Nil | |
| else if is-heading(l) is Heading(l) | |
| wrap("<h1>", l, "</h1>\n") | |
| else | |
| wrap("<p>", inline-to-html(l, None), "</p>\n") | |
| loop md-to-html = ls => style => | |
| if ls is Cons(l, ls) | |
| if style is Code | |
| if is-code-block(l) is True | |
| Cons(Cons("\n</code></pre>\n", Nil), md-to-html(ls, None)) | |
| else | |
| Cons(Cons("\n", l), md-to-html(ls, style)) | |
| else if is-code-block(l) is True | |
| Cons(Cons("<pre><code>", Nil), md-to-html(ls, Code)) | |
| else | |
| Cons(line-to-html(l), md-to-html(ls, style)) | |
| else | |
| ls | |
| loop flatten = xs => | |
| if xs is Cons(x, xs) | |
| append(x, flatten(xs)) | |
| else | |
| xs | |
| let html5 = title => body => | |
| let pre = | |
| wrap( | |
| "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\" />\n<title>", | |
| title, | |
| "</title>\n<link rel=\"stylesheet\" href=\"styles/style.css\" />\n</head>\n" | |
| ) | |
| let body = wrap("<body>\n", flatten(body), "</body>\n") | |
| append(pre, append(body, Cons("</html>", Nil))) | |
| let lines = read-lines() | |
| loop title = ls => | |
| if ls is Cons(l, ls) | |
| if is-heading(l) is Heading(l) | |
| l | |
| else | |
| title(ls) | |
| else | |
| Cons((), Nil) | |
| let title = title(lines) | |
| let body = md-to-html(lines, None) | |
| let html = html5(title, body) | |
| write-strs!(html) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment