From b6e803cfee57d7375d7a2b0e70c8d37fecf1868d Mon Sep 17 00:00:00 2001 From: liquidev Date: Fri, 8 Mar 2024 17:06:47 +0100 Subject: [PATCH] on Lua --- content/programming.tree | 33 ++++++--- content/programming/{ => languages}/cxx.tree | 0 content/programming/languages/lua.tree | 74 +++++++++++++++++++ .../{ => technologies}/unreal-engine.tree | 6 +- .../unreal-engine/blueprint.tree | 0 .../unreal-engine/fixes.tree | 0 crates/treehouse/src/cli/generate.rs | 6 +- crates/treehouse/src/cli/serve.rs | 37 +++++++--- crates/treehouse/src/config.rs | 27 +++++++ crates/treehouse/src/main.rs | 4 +- treehouse.toml | 5 ++ 11 files changed, 162 insertions(+), 30 deletions(-) rename content/programming/{ => languages}/cxx.tree (100%) create mode 100644 content/programming/languages/lua.tree rename content/programming/{ => technologies}/unreal-engine.tree (71%) rename content/programming/{ => technologies}/unreal-engine/blueprint.tree (100%) rename content/programming/{ => technologies}/unreal-engine/fixes.tree (100%) diff --git a/content/programming.tree b/content/programming.tree index 3592eca..6bdaf17 100644 --- a/content/programming.tree +++ b/content/programming.tree @@ -8,21 +8,30 @@ content.link = "programming/projects" + ### projects -% content.link = "programming/cxx" - id = "programming/cxx" -+ ### C++ - -% content.link = "programming/unreal-engine" - id = "programming/unreal-engine" -+ ### Unreal Engine - -% content.link = "programming/opinions" - id = "programming/opinions" -+ ### opinions - % id = "01HPD4XQQ5GPQ20C6BPA8G670F" - ### blog % content.link = "programming/blog/tairu" id = "01HPD4XQQ5WM0APCAX014HM43V" + tairu - an interactive exploration of 2D autotiling techniques + +- ### languages + + % content.link = "programming/languages/cxx" + id = "programming/languages/cxx" + + ### C++ + + % content.link = "programming/languages/lua" + id = "programming/languages/lua" + + ### Lua + +- ### technologies + + % content.link = "programming/technologies/unreal-engine" + id = "programming/technologies/unreal-engine" + + ### Unreal Engine + +% content.link = "programming/opinions" + id = "programming/opinions" ++ ### opinions + diff --git a/content/programming/cxx.tree b/content/programming/languages/cxx.tree similarity index 100% rename from content/programming/cxx.tree rename to content/programming/languages/cxx.tree diff --git a/content/programming/languages/lua.tree b/content/programming/languages/lua.tree new file mode 100644 index 0000000..d63bcb7 --- /dev/null +++ b/content/programming/languages/lua.tree @@ -0,0 +1,74 @@ +- TODO: this page could really use an interactive Lua interpreter. can we have that? + +- Lua is a really cool language! did you know that? + ++ lots of people complain about it being really weird for various reasons, but these are generally superficial + + - usually it's cosmetic stuff, so these aren't any arguments of technical merit, but... + + - stuff like indexing from 1 instead of 0, which is _just a design choice_ and does not impact your programming that much + + - in fact, one could argue that regular programmers are weird for counting from zero :thinking: + + - or using `~=` instead of `!=`, which is _just a syntax choice_, and you only have to get used to it once + + - or using `do`..`end` style blocks instead of `{`..`}`, which again is _just a syntax choice_ and does not impact programming that much + + - it's a tad bit more line noise, but not that terrible. I [did design a language using `do`..`end` blocks][def:mica/repo] and it really doesn't look that bad + +- but I think Lua is a pretty damn genius programming language. + + - the use of tables as The One Data Structure for Literally Everything strikes me as a 200 IQ choice I could never come up with myself + + - partly because it's so fucking bold I can literally not imagine myself designing a language with a strong distinction between hash tables and arrays, and even tuples and records! + but the authors of had the restraint to just have One. and that One is **tables.** + + - tables are extremely powerful in what they can do, because they're more than just a way of structuring data - they also allow for interfacing with the language *syntax* through operator overloading + + + in fact object oriented programming in Lua is typically done by overloading the `[]` indexing operator. + + - the way it works is that `a.b` is just syntax sugar for `a["b"]`, which means you overload `[]` to _fall back to another table_ - and that way you can achieve [prototype-based inheritance](https://en.wikipedia.org/wiki/Prototype-based_programming)! + + ```lua + local fallback = { b = 2 } + local base = { a = 1 } + + -- The __index field can be both a function *and* a table. + -- { __index = the_table } is a shorthand for { __index = function (t, k) return the_table[k] end } + setmetatable(base, { __index = fallback }) + assert(base.b == 2) + ``` + + - TODO: even more on restraint (standard library) + + - TODO: restrained sugar: function calls `require "abc"`, `do_stuff { with_a_table = 1 }` + +- I really wish Lua had at least *a* form of static typing though, since knowing about errors you make early is _really_ helpful during development. + + - it regularly happened to me that a type error I made only occured at *some point* later during runtime; and then you have to track down a reproduction case and make a fix at the source. not fun. + +- and it's really a bummer that Lua is not that strict! + + - global variables by default are a pretty bad design choice in my opinion. having any form of uncontrolled globals hurts local reasoning and makes it harder to tell whatever your code is going to do. + + - but fortunately it is possible to freeze your global variables by setting a metatable on `_G`, which is a table that represents the global scope. + + - TODO: example + +- there are also some bits of syntax that haven't aged very well. + + - as much as people complain about cosmetics [TODO: link], I think there's a particular design choice that has aged very poorly in the face of modern, functional programming - function literals. + + these tend to be quite verbose in Lua which hurts readability in functional code: + + ```lua + local u = map(t, function (v) return v + 2 end) + ``` + + compare that to JavaScript's arrow functions `=>`, which I think are a prime example of good syntax sugar that encourages more function-oriented programming: + + ```javascript + let u = t.map(v => v + 2) + ``` + + - the lack of a pipelining operator `|>` is also an annoyance, albeit most modern imperative languages don't have it either. diff --git a/content/programming/unreal-engine.tree b/content/programming/technologies/unreal-engine.tree similarity index 71% rename from content/programming/unreal-engine.tree rename to content/programming/technologies/unreal-engine.tree index 9340adb..49f445a 100644 --- a/content/programming/unreal-engine.tree +++ b/content/programming/technologies/unreal-engine.tree @@ -7,13 +7,13 @@ % id = "01H8YGXP0ZWG6X3PB6GWSGKAT0" - both the fun and the good, and that which ruins my mood -% id = "programming/unreal-engine/blueprint" - content.link = "programming/unreal-engine/blueprint" +% id = "programming/technologies/unreal-engine/blueprint" + content.link = "programming/technologies/unreal-engine/blueprint" + ### thoughts on Blueprint % id = "01HP1FESY5WVJG4X80AZ4ZBX5D" - ### random but cool things - % content.link = "programming/unreal-engine/fixes" + % content.link = "programming/technologies/unreal-engine/fixes" id = "01HP1FESY5ZS6YTZXA8QTT5V1Z" + data validation quick fixes diff --git a/content/programming/unreal-engine/blueprint.tree b/content/programming/technologies/unreal-engine/blueprint.tree similarity index 100% rename from content/programming/unreal-engine/blueprint.tree rename to content/programming/technologies/unreal-engine/blueprint.tree diff --git a/content/programming/unreal-engine/fixes.tree b/content/programming/technologies/unreal-engine/fixes.tree similarity index 100% rename from content/programming/unreal-engine/fixes.tree rename to content/programming/technologies/unreal-engine/fixes.tree diff --git a/crates/treehouse/src/cli/generate.rs b/crates/treehouse/src/cli/generate.rs index eee907f..583c9d1 100644 --- a/crates/treehouse/src/cli/generate.rs +++ b/crates/treehouse/src/cli/generate.rs @@ -343,7 +343,7 @@ impl Generator { } } -pub fn generate(paths: &Paths<'_>) -> anyhow::Result { +pub fn generate(paths: &Paths<'_>) -> anyhow::Result<(Config, Treehouse)> { let start = Instant::now(); info!("loading config"); @@ -386,13 +386,13 @@ pub fn generate(paths: &Paths<'_>) -> anyhow::Result { info!("generation done in {duration:?}"); if !treehouse.has_errors() { - Ok(treehouse) + Ok((config, treehouse)) } else { bail!("generation errors occurred; diagnostics were emitted with detailed descriptions"); } } -pub fn regenerate_or_report_error(paths: &Paths<'_>) -> anyhow::Result { +pub fn regenerate_or_report_error(paths: &Paths<'_>) -> anyhow::Result<(Config, Treehouse)> { info!("regenerating site content"); let result = generate(paths); diff --git a/crates/treehouse/src/cli/serve.rs b/crates/treehouse/src/cli/serve.rs index 3ee911d..94647a0 100644 --- a/crates/treehouse/src/cli/serve.rs +++ b/crates/treehouse/src/cli/serve.rs @@ -1,12 +1,15 @@ #[cfg(debug_assertions)] mod live_reload; -use std::{borrow::Cow, net::Ipv4Addr, path::PathBuf, sync::Arc}; +use std::{net::Ipv4Addr, path::PathBuf, sync::Arc}; use anyhow::Context; use axum::{ extract::{Path, RawQuery, State}, - http::{header::CONTENT_TYPE, HeaderValue, StatusCode}, + http::{ + header::{CONTENT_TYPE, LOCATION}, + HeaderValue, StatusCode, + }, response::{Html, IntoResponse, Response}, routing::get, Router, @@ -15,7 +18,10 @@ use log::{error, info}; use pulldown_cmark::escape::escape_html; use tokio::net::TcpListener; -use crate::state::{Source, Treehouse}; +use crate::{ + config::Config, + state::{Source, Treehouse}, +}; use super::Paths; @@ -29,12 +35,18 @@ struct SystemPages { } struct Server { + config: Config, treehouse: Treehouse, target_dir: PathBuf, system_pages: SystemPages, } -pub async fn serve(treehouse: Treehouse, paths: &Paths<'_>, port: u16) -> anyhow::Result<()> { +pub async fn serve( + config: Config, + treehouse: Treehouse, + paths: &Paths<'_>, + port: u16, +) -> anyhow::Result<()> { let app = Router::new() .route("/", get(index)) .route("/*page", get(page)) @@ -44,6 +56,7 @@ pub async fn serve(treehouse: Treehouse, paths: &Paths<'_>, port: u16) -> anyhow .route("/static/*file", get(static_file)) .fallback(get(four_oh_four)) .with_state(Arc::new(Server { + config, treehouse, target_dir: paths.target_dir.to_owned(), system_pages: SystemPages { @@ -114,13 +127,17 @@ async fn static_file(Path(path): Path, State(state): State>) } async fn page(Path(path): Path, State(state): State>) -> Response { - let path = if !path.ends_with(".html") { - Cow::Owned(path + ".html") - } else { - Cow::Borrowed(&path) - }; + let bare_path = path.strip_suffix(".html").unwrap_or(&path); + if let Some(redirected_path) = state.config.redirects.page.get(bare_path) { + return ( + StatusCode::MOVED_PERMANENTLY, + [(LOCATION, format!("{}/{redirected_path}", state.config.site))], + ) + .into_response(); + } - if let Ok(file) = tokio::fs::read(state.target_dir.join(&*path)).await { + let html_path = format!("{bare_path}.html"); + if let Ok(file) = tokio::fs::read(state.target_dir.join(&*html_path)).await { ([(CONTENT_TYPE, "text/html")], file).into_response() } else { four_oh_four(State(state)).await diff --git a/crates/treehouse/src/config.rs b/crates/treehouse/src/config.rs index 6b109a9..6dbe936 100644 --- a/crates/treehouse/src/config.rs +++ b/crates/treehouse/src/config.rs @@ -17,6 +17,26 @@ pub struct Config { /// Links exported to Markdown for use with reference syntax `[text][def:key]`. pub defs: HashMap, + /// Redirects for moving pages around. These are used solely by the treehouse server. + /// + /// Note that redirects are only resolved _non-recursively_ by the server. For a configuration + /// like: + /// + /// ```toml + /// page.redirects.foo = "bar" + /// page.redirects.bar = "baz" + /// ``` + /// + /// the user will be redirected from `foo` to `bar`, then from `bar` to `baz`. This isn't + /// optimal for UX and causes unnecessary latency. Therefore you should always make redirects + /// point to the newest version of the page. + /// + /// ```toml + /// page.redirects.foo = "baz" + /// page.redirects.bar = "baz" + /// ``` + pub redirects: Redirects, + /// Overrides for emoji filenames. Useful for setting up aliases. /// /// On top of this, emojis are autodiscovered by walking the `static/emoji` directory. @@ -30,6 +50,13 @@ pub struct Config { pub pics: HashMap, } +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Redirects { + /// Page redirects. When a user navigates to a page, if they navigate to `url`, they will + /// be redirected to `page[url]`. + pub page: HashMap, +} + impl Config { pub fn load(path: &Path) -> anyhow::Result { let string = std::fs::read_to_string(path).context("cannot read config file")?; diff --git a/crates/treehouse/src/main.rs b/crates/treehouse/src/main.rs index e223200..7d92ace 100644 --- a/crates/treehouse/src/main.rs +++ b/crates/treehouse/src/main.rs @@ -42,8 +42,8 @@ async fn fallible_main() -> anyhow::Result<()> { generate: _, serve: serve_args, } => { - let treehouse = regenerate_or_report_error(&paths)?; - serve(treehouse, &paths, serve_args.port).await?; + let (config, treehouse) = regenerate_or_report_error(&paths)?; + serve(config, treehouse, &paths, serve_args.port).await?; } Command::Fix(fix_args) => fix_file_cli(fix_args)?, diff --git a/treehouse.toml b/treehouse.toml index e1bf528..c1a8f8e 100644 --- a/treehouse.toml +++ b/treehouse.toml @@ -23,6 +23,7 @@ description = "a place on the Internet I like to call home" "dispatchers/repo" = "https://github.com/liquidev/dispatchers" "abit/repo" = "https://github.com/abyteintime/abit" "rokugo/repo" = "https://github.com/rokugo-lang/rokugo" +"mica/repo" = "https://github.com/mica-lang/mica" # Blog posts I like to reference "article/function_coloring" = "https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/" @@ -36,6 +37,10 @@ description = "a place on the Internet I like to call home" "person/firstbober" = "https://firstbober.com" "person/vixenka" = "https://github.com/Vixenka" +[redirects.page] +"programming/cxx" = "programming/languages/cxx" +"programming/unreal-engine" = "programming/technologies/unreal-engine" + [emoji] [pics]