on Lua
This commit is contained in:
parent
ebb4543f8d
commit
b6e803cfee
|
@ -8,21 +8,30 @@
|
||||||
content.link = "programming/projects"
|
content.link = "programming/projects"
|
||||||
+ ### 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"
|
% id = "01HPD4XQQ5GPQ20C6BPA8G670F"
|
||||||
- ### blog
|
- ### blog
|
||||||
|
|
||||||
% content.link = "programming/blog/tairu"
|
% content.link = "programming/blog/tairu"
|
||||||
id = "01HPD4XQQ5WM0APCAX014HM43V"
|
id = "01HPD4XQQ5WM0APCAX014HM43V"
|
||||||
+ tairu - an interactive exploration of 2D autotiling techniques
|
+ 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
|
||||||
|
|
||||||
|
|
74
content/programming/languages/lua.tree
Normal file
74
content/programming/languages/lua.tree
Normal file
|
@ -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.
|
|
@ -7,13 +7,13 @@
|
||||||
% id = "01H8YGXP0ZWG6X3PB6GWSGKAT0"
|
% id = "01H8YGXP0ZWG6X3PB6GWSGKAT0"
|
||||||
- both the fun and the good, and that which ruins my mood
|
- both the fun and the good, and that which ruins my mood
|
||||||
|
|
||||||
% id = "programming/unreal-engine/blueprint"
|
% id = "programming/technologies/unreal-engine/blueprint"
|
||||||
content.link = "programming/unreal-engine/blueprint"
|
content.link = "programming/technologies/unreal-engine/blueprint"
|
||||||
+ ### thoughts on Blueprint
|
+ ### thoughts on Blueprint
|
||||||
|
|
||||||
% id = "01HP1FESY5WVJG4X80AZ4ZBX5D"
|
% id = "01HP1FESY5WVJG4X80AZ4ZBX5D"
|
||||||
- ### random but cool things
|
- ### random but cool things
|
||||||
|
|
||||||
% content.link = "programming/unreal-engine/fixes"
|
% content.link = "programming/technologies/unreal-engine/fixes"
|
||||||
id = "01HP1FESY5ZS6YTZXA8QTT5V1Z"
|
id = "01HP1FESY5ZS6YTZXA8QTT5V1Z"
|
||||||
+ data validation quick fixes
|
+ data validation quick fixes
|
|
@ -343,7 +343,7 @@ impl Generator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate(paths: &Paths<'_>) -> anyhow::Result<Treehouse> {
|
pub fn generate(paths: &Paths<'_>) -> anyhow::Result<(Config, Treehouse)> {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
info!("loading config");
|
info!("loading config");
|
||||||
|
@ -386,13 +386,13 @@ pub fn generate(paths: &Paths<'_>) -> anyhow::Result<Treehouse> {
|
||||||
info!("generation done in {duration:?}");
|
info!("generation done in {duration:?}");
|
||||||
|
|
||||||
if !treehouse.has_errors() {
|
if !treehouse.has_errors() {
|
||||||
Ok(treehouse)
|
Ok((config, treehouse))
|
||||||
} else {
|
} else {
|
||||||
bail!("generation errors occurred; diagnostics were emitted with detailed descriptions");
|
bail!("generation errors occurred; diagnostics were emitted with detailed descriptions");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn regenerate_or_report_error(paths: &Paths<'_>) -> anyhow::Result<Treehouse> {
|
pub fn regenerate_or_report_error(paths: &Paths<'_>) -> anyhow::Result<(Config, Treehouse)> {
|
||||||
info!("regenerating site content");
|
info!("regenerating site content");
|
||||||
|
|
||||||
let result = generate(paths);
|
let result = generate(paths);
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
mod live_reload;
|
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 anyhow::Context;
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{Path, RawQuery, State},
|
extract::{Path, RawQuery, State},
|
||||||
http::{header::CONTENT_TYPE, HeaderValue, StatusCode},
|
http::{
|
||||||
|
header::{CONTENT_TYPE, LOCATION},
|
||||||
|
HeaderValue, StatusCode,
|
||||||
|
},
|
||||||
response::{Html, IntoResponse, Response},
|
response::{Html, IntoResponse, Response},
|
||||||
routing::get,
|
routing::get,
|
||||||
Router,
|
Router,
|
||||||
|
@ -15,7 +18,10 @@ use log::{error, info};
|
||||||
use pulldown_cmark::escape::escape_html;
|
use pulldown_cmark::escape::escape_html;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
use crate::state::{Source, Treehouse};
|
use crate::{
|
||||||
|
config::Config,
|
||||||
|
state::{Source, Treehouse},
|
||||||
|
};
|
||||||
|
|
||||||
use super::Paths;
|
use super::Paths;
|
||||||
|
|
||||||
|
@ -29,12 +35,18 @@ struct SystemPages {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Server {
|
struct Server {
|
||||||
|
config: Config,
|
||||||
treehouse: Treehouse,
|
treehouse: Treehouse,
|
||||||
target_dir: PathBuf,
|
target_dir: PathBuf,
|
||||||
system_pages: SystemPages,
|
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()
|
let app = Router::new()
|
||||||
.route("/", get(index))
|
.route("/", get(index))
|
||||||
.route("/*page", get(page))
|
.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))
|
.route("/static/*file", get(static_file))
|
||||||
.fallback(get(four_oh_four))
|
.fallback(get(four_oh_four))
|
||||||
.with_state(Arc::new(Server {
|
.with_state(Arc::new(Server {
|
||||||
|
config,
|
||||||
treehouse,
|
treehouse,
|
||||||
target_dir: paths.target_dir.to_owned(),
|
target_dir: paths.target_dir.to_owned(),
|
||||||
system_pages: SystemPages {
|
system_pages: SystemPages {
|
||||||
|
@ -114,13 +127,17 @@ async fn static_file(Path(path): Path<String>, State(state): State<Arc<Server>>)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn page(Path(path): Path<String>, State(state): State<Arc<Server>>) -> Response {
|
async fn page(Path(path): Path<String>, State(state): State<Arc<Server>>) -> Response {
|
||||||
let path = if !path.ends_with(".html") {
|
let bare_path = path.strip_suffix(".html").unwrap_or(&path);
|
||||||
Cow::Owned(path + ".html")
|
if let Some(redirected_path) = state.config.redirects.page.get(bare_path) {
|
||||||
} else {
|
return (
|
||||||
Cow::Borrowed(&path)
|
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()
|
([(CONTENT_TYPE, "text/html")], file).into_response()
|
||||||
} else {
|
} else {
|
||||||
four_oh_four(State(state)).await
|
four_oh_four(State(state)).await
|
||||||
|
|
|
@ -17,6 +17,26 @@ pub struct Config {
|
||||||
/// Links exported to Markdown for use with reference syntax `[text][def:key]`.
|
/// Links exported to Markdown for use with reference syntax `[text][def:key]`.
|
||||||
pub defs: HashMap<String, String>,
|
pub defs: HashMap<String, String>,
|
||||||
|
|
||||||
|
/// 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.
|
/// Overrides for emoji filenames. Useful for setting up aliases.
|
||||||
///
|
///
|
||||||
/// On top of this, emojis are autodiscovered by walking the `static/emoji` directory.
|
/// On top of this, emojis are autodiscovered by walking the `static/emoji` directory.
|
||||||
|
@ -30,6 +50,13 @@ pub struct Config {
|
||||||
pub pics: HashMap<String, String>,
|
pub pics: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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<String, String>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn load(path: &Path) -> anyhow::Result<Self> {
|
pub fn load(path: &Path) -> anyhow::Result<Self> {
|
||||||
let string = std::fs::read_to_string(path).context("cannot read config file")?;
|
let string = std::fs::read_to_string(path).context("cannot read config file")?;
|
||||||
|
|
|
@ -42,8 +42,8 @@ async fn fallible_main() -> anyhow::Result<()> {
|
||||||
generate: _,
|
generate: _,
|
||||||
serve: serve_args,
|
serve: serve_args,
|
||||||
} => {
|
} => {
|
||||||
let treehouse = regenerate_or_report_error(&paths)?;
|
let (config, treehouse) = regenerate_or_report_error(&paths)?;
|
||||||
serve(treehouse, &paths, serve_args.port).await?;
|
serve(config, treehouse, &paths, serve_args.port).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::Fix(fix_args) => fix_file_cli(fix_args)?,
|
Command::Fix(fix_args) => fix_file_cli(fix_args)?,
|
||||||
|
|
|
@ -23,6 +23,7 @@ description = "a place on the Internet I like to call home"
|
||||||
"dispatchers/repo" = "https://github.com/liquidev/dispatchers"
|
"dispatchers/repo" = "https://github.com/liquidev/dispatchers"
|
||||||
"abit/repo" = "https://github.com/abyteintime/abit"
|
"abit/repo" = "https://github.com/abyteintime/abit"
|
||||||
"rokugo/repo" = "https://github.com/rokugo-lang/rokugo"
|
"rokugo/repo" = "https://github.com/rokugo-lang/rokugo"
|
||||||
|
"mica/repo" = "https://github.com/mica-lang/mica"
|
||||||
|
|
||||||
# Blog posts I like to reference
|
# Blog posts I like to reference
|
||||||
"article/function_coloring" = "https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/"
|
"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/firstbober" = "https://firstbober.com"
|
||||||
"person/vixenka" = "https://github.com/Vixenka"
|
"person/vixenka" = "https://github.com/Vixenka"
|
||||||
|
|
||||||
|
[redirects.page]
|
||||||
|
"programming/cxx" = "programming/languages/cxx"
|
||||||
|
"programming/unreal-engine" = "programming/technologies/unreal-engine"
|
||||||
|
|
||||||
[emoji]
|
[emoji]
|
||||||
|
|
||||||
[pics]
|
[pics]
|
||||||
|
|
Loading…
Reference in a new issue