on Lua
This commit is contained in:
parent
ebb4543f8d
commit
b6e803cfee
|
@ -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
|
||||
|
||||
|
|
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"
|
||||
- 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
|
|
@ -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();
|
||||
|
||||
info!("loading config");
|
||||
|
@ -386,13 +386,13 @@ pub fn generate(paths: &Paths<'_>) -> anyhow::Result<Treehouse> {
|
|||
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<Treehouse> {
|
||||
pub fn regenerate_or_report_error(paths: &Paths<'_>) -> anyhow::Result<(Config, Treehouse)> {
|
||||
info!("regenerating site content");
|
||||
|
||||
let result = generate(paths);
|
||||
|
|
|
@ -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<String>, State(state): State<Arc<Server>>)
|
|||
}
|
||||
|
||||
async fn page(Path(path): Path<String>, State(state): State<Arc<Server>>) -> 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
|
||||
|
|
|
@ -17,6 +17,26 @@ pub struct Config {
|
|||
/// Links exported to Markdown for use with reference syntax `[text][def:key]`.
|
||||
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.
|
||||
///
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
#[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 {
|
||||
pub fn load(path: &Path) -> anyhow::Result<Self> {
|
||||
let string = std::fs::read_to_string(path).context("cannot read config file")?;
|
||||
|
|
|
@ -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)?,
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in a new issue