diff --git a/site_test/css/main.scss b/site_test/css/main.scss index 11ddc1f..8b0a87a 100644 --- a/site_test/css/main.scss +++ b/site_test/css/main.scss @@ -71,6 +71,10 @@ blockquote { left: -25px; top: -10px; } + + em { + font-style: normal; + } } img { @@ -119,6 +123,10 @@ hr { border: 1px solid var(--secondary-text-color); } +.italic { + font-style: italic; +} + html { font-family: "Valkyrie A", Charter, serif; font-size: 16px; @@ -176,6 +184,7 @@ header { .body-content { font-size: 1.25rem; + line-height: 1.4; // Chrome only, but minimizes orphan words text-wrap: pretty; } @@ -223,7 +232,6 @@ aside:not(.inline) { margin-right: -50%; width: 40%; font-size: 1rem; - line-height: 1.25; color: var(--secondary-text-color); transition: color 0.2s ease-in-out; display: block; @@ -318,7 +326,6 @@ aside.inline { float: none; margin-right: 0; width: auto; - line-height: initial; transform: none; background-color: lighten($link-color, 43%); padding: 1rem; diff --git a/site_test/layout/article.html b/site_test/layout/article.html index 23a8c67..b807789 100644 --- a/site_test/layout/article.html +++ b/site_test/layout/article.html @@ -52,6 +52,9 @@

+ {% if metadata.preamble %} + {{ metadata.preamble }} + {% endif %} {{ content }}
diff --git a/site_test/posts/2020/2020-06-05-algorithmic-bias.md b/site_test/posts/2020/2020-06-05-algorithmic-bias.md index 326b2a1..6b0f313 100644 --- a/site_test/posts/2020/2020-06-05-algorithmic-bias.md +++ b/site_test/posts/2020/2020-06-05-algorithmic-bias.md @@ -1,6 +1,6 @@ ``` title = "Algorithmic Bias" -tags = ["misc", "social media"] +tags = ["politics", "social media"] date = "2020-06-05 09:55:42 -0400" slug = "algorithmic-bias" ``` @@ -15,4 +15,3 @@ This is what algorithmic bias looks like. **Algorithms are not neutral.**[^1] [^1]: "Algorithm" is a word here used not in the purely computer science sense, but to mean a element of software which operates in a black box, often with a machine learning component, with little or no human supervision, input, or control. - diff --git a/site_test/posts/2020/2020-12-07-parsing-id3-tags.md b/site_test/posts/2020/2020-12-07-parsing-id3-tags.md index fd0c4cf..d37d0d0 100644 --- a/site_test/posts/2020/2020-12-07-parsing-id3-tags.md +++ b/site_test/posts/2020/2020-12-07-parsing-id3-tags.md @@ -10,9 +10,9 @@ On and off for the past year and a half or so, I've been working on a small side I knew that MP3 files had some embedded metadata, only for the reason that looking at most tracks in Finder shows album artwork and information about the track. Cursory googling led me to the [ID3 spec](https://id3.org/). -[^1]: Actual, DRM-free files because music streaming services by and large don't pay artists fairly[^2]. MP3s specifically because they Just Work everywhere, and I cannot for the life of me hear the difference between a 320kbps MP3 and an \ file. - -[^2]: Spotify pays artists 0.38¢ per play and Apple Music pays 0.783¢ per play ([source](https://help.songtrust.com/knowledge/what-is-the-pay-rate-for-spotify-streams)). For an album of 12 songs that costs $10 (assuming wherever you buy it from takes a 30% cut), you would have to listen all the way through it between 75 and 150 times for the artist to receive as much money as if you had just purchased the album outright. That's hardly fair and is not sustainable for all but the largest of musicians. +[^1]: Actual, DRM-free files because music streaming services by and large don't pay artists fairly. MP3s specifically because they Just Work everywhere, and I cannot for the life of me hear the difference between a 320kbps MP3 and an \ file. +

+Spotify pays artists 0.38¢ per play and Apple Music pays 0.783¢ per play ([source](https://help.songtrust.com/knowledge/what-is-the-pay-rate-for-spotify-streams)). For an album of 12 songs that costs $10 (assuming wherever you buy it from takes a 30% cut), you would have to listen all the way through it between 75 and 150 times for the artist to receive as much money as if you had just purchased the album outright. That's hardly fair and is not sustainable for all but the largest of musicians. @@ -136,7 +136,7 @@ reversed: 00011010 01010010 00000001 You may have noticed the `unsynchronized` flag in the tag header. The ID3 unschronization scheme is another way of preventing false syncs in longer blocks of data within the tag (such as image data in the frame used for album artwork). I elected not to handle this flag for now, since none of the tracks in my library have the flag set. The ID3v2.4 spec says the unsynchronization scheme is primarily intended to prevent old software which isn't aware of ID3 tags from incorrectly trying to sync onto data in the ID3 tag. Since the ID3v2 spec is over 20 years old, pieces of software which aren't aware of it are few and far between, so I guess the unsynchronization scheme has fallen out of favor. -So, since we've gotten the 4-byte binary that contains the tag size out of the header, we can use the `decode_synchsafe_integer` function to decode it. +So, since we've gotten the 4-byte binary that contains the tag size out of the header, we can use the `decode_synchsafe_integer` function to decode it. ```elixir def parse_tag(...) do @@ -207,7 +207,7 @@ def parse_frames(major_version, data, tag_length_remaining, frames \\ []) The first case of the function is for if it's reached the total length of the tag, in which case it will just convert the accumulated tags into a map, and return the data that's left (we want to return whatever data's left after the end of the ID3 tag so that it can be used by other parts of the code, say, an MP3 parser...). We can just directly convert the list of frames into a map because, as you'll see shortly, each frame is a tuple of the name of the frame and its data, in whatever form that may be. ```elixir -def parse_frames(_, data, tag_length_remaining, frames) +def parse_frames(_, data, tag_length_remaining, frames) when tag_length_remaining <= 0 do {Map.new(frames), data} end @@ -489,4 +489,3 @@ iex> ID3.parse_tag(data) ``` One of the pieces of information I was hoping I could get from the ID3 tags was the durations of the MP3s in my library. But alas, none of the tracks I have use the TLEN frame, so it looks like I'll have to try and pull that data out of the MP3 myself. But that's a post for another time... - diff --git a/site_test/posts/2021/2021-04-13-lexing.md b/site_test/posts/2021/2021-04-13-lexing.md index ea0bf25..66a9a3e 100644 --- a/site_test/posts/2021/2021-04-13-lexing.md +++ b/site_test/posts/2021/2021-04-13-lexing.md @@ -4,7 +4,7 @@ tags = ["build a programming language", "rust"] date = "2021-04-13 17:00:42 -0400" short_desc = "Turning a string into a sequence of tokens." slug = "lexing" -preamble = '

This post is part of a series about learning Rust and building a small programming language.


' +preamble = '

This post is part of a series about learning Rust and building a small programming language.


' ``` The first part of the language I've built is the lexer. It takes the program text as input and produces a vector of tokens. Tokens are the individual units that the parser will work with, rather than it having to work directly with characters. A token could be a bunch of different things. It could be a literal value (like a number or string), or it could be an identifier, or a specific symbol (like a plus sign). @@ -96,5 +96,3 @@ fn main() { $ cargo run tokens: [Integer(12), Plus, Integer(34)] ``` - - diff --git a/site_test/posts/2021/2021-04-14-parsing.md b/site_test/posts/2021/2021-04-14-parsing.md index 43339bc..79479d1 100644 --- a/site_test/posts/2021/2021-04-14-parsing.md +++ b/site_test/posts/2021/2021-04-14-parsing.md @@ -4,7 +4,7 @@ tags = ["build a programming language", "rust"] date = "2021-04-14 17:00:42 -0400" short_desc = "Building a small AST from the stream of tokens." slug = "parsing" -preamble = '

This post is part of a series about learning Rust and building a small programming language.


' +preamble = '

This post is part of a series about learning Rust and building a small programming language.


' ``` Now that the lexer is actually lexing, we can start parsing. This is where the Tree in Abstract Syntax Tree really comes in. What the parser is going to do is take a flat sequence of tokens and transform it into a shape that represents the actual structure of the code. @@ -97,4 +97,3 @@ node: Some( ``` The eagle-eyed may notice that while we have parsed the expression, we have not parsed it correctly. What's missing is operator precedence and associativity, but that will have to wait for next time. - diff --git a/site_test/posts/2021/2021-04-15-evaluation.md b/site_test/posts/2021/2021-04-15-evaluation.md index 7d4f9e3..2f96984 100644 --- a/site_test/posts/2021/2021-04-15-evaluation.md +++ b/site_test/posts/2021/2021-04-15-evaluation.md @@ -4,7 +4,7 @@ tags = ["build a programming language", "rust"] date = "2021-04-15 17:00:42 -0400" short_desc = "A bad calculator." slug = "evaluation" -preamble = '

This post is part of a series about learning Rust and building a small programming language.


' +preamble = '

This post is part of a series about learning Rust and building a small programming language.


' ``` Last time I said operator precedence was going to be next. Well, if you've read the title, you know that's not the case. I decided I really wanted to see this actually run[^1] some code[^2], so let's do that. @@ -61,7 +61,7 @@ fn eval_binary_op(left: &Node, right: &Node) -> Value { } ``` -And with that surpisingly small amount of code, I've got a very dumb calculator that can perform arbitrary additions: +And with that surpisingly small amount of code, I've got a very dumb calculator that can perform arbitrary additions: ```rust fn main() { @@ -78,4 +78,3 @@ result: Integer(6) ``` Next time, I'll add some more operators and actually get around to operator precedence. - diff --git a/site_test/posts/2021/2021-04-16-operator-precedence.md b/site_test/posts/2021/2021-04-16-operator-precedence.md index 2893e1e..c9dc5de 100644 --- a/site_test/posts/2021/2021-04-16-operator-precedence.md +++ b/site_test/posts/2021/2021-04-16-operator-precedence.md @@ -3,7 +3,7 @@ title = "Part 4: Operator Precedence" tags = ["build a programming language", "rust"] date = "2021-04-16 17:00:42 -0400" slug = "operator-precedence" -preamble = '

This post is part of a series about learning Rust and building a small programming language.


' +preamble = '

This post is part of a series about learning Rust and building a small programming language.


' ``` I've gone through the lexer, parser, and evaluator and added subtraction, multiplication, and division in addition to, uh... addition. And they kind of work, but there's one glaring issue that I mentioned back in part 2. It's that the parser has no understanding of operator precedence. That is to say, it doesn't know which operators have a higher priority in the order of operations when implicit grouping is taking place. @@ -189,4 +189,3 @@ fn main() { $ cargo run result: Integer(10) ``` - diff --git a/site_test/posts/2021/2021-04-17-fixing-floats.md b/site_test/posts/2021/2021-04-17-fixing-floats.md index 0cb3780..20ece74 100644 --- a/site_test/posts/2021/2021-04-17-fixing-floats.md +++ b/site_test/posts/2021/2021-04-17-fixing-floats.md @@ -4,7 +4,7 @@ tags = ["build a programming language", "rust"] date = "2021-04-17 17:00:42 -0400" short_desc = "A small gotcha in Rust's TakeWhile iterator." slug = "fixing-floats" -preamble = '

This post is part of a series about learning Rust and building a small programming language.


' +preamble = '

This post is part of a series about learning Rust and building a small programming language.


' ``` In the process of adding floating point numbers, I ran into something a little bit unexpected. The issue turned out to be pretty simple, but I thought it was worth mentioning. @@ -56,7 +56,7 @@ I inquired about this behavior on the fediverse, and learned that I missed a key I would have expected it to use the `peek()` method on peekable iterators to avoid this, but I guess not. No matter, a peeking version is easy to implement: ```rust -fn take_while_peek(peekable: &mut Peekable, mut predicate: P) -> Vec +fn take_while_peek(peekable: &mut Peekable, mut predicate: P) -> Vec where I: Iterator, P: FnMut(&I::Item) -> bool, @@ -84,5 +84,3 @@ fn parse_number>(it: &mut T) -> Option { // ... } ``` - - diff --git a/site_test/posts/2021/2021-04-18-grouping.md b/site_test/posts/2021/2021-04-18-grouping.md index 93a3604..51f3c43 100644 --- a/site_test/posts/2021/2021-04-18-grouping.md +++ b/site_test/posts/2021/2021-04-18-grouping.md @@ -3,7 +3,7 @@ title = "Part 6: Grouping" tags = ["build a programming language", "rust"] date = "2021-04-18 14:42:42 -0400" slug = "grouping" -preamble = '

This post is part of a series about learning Rust and building a small programming language.


' +preamble = '

This post is part of a series about learning Rust and building a small programming language.


' ``` Parsing groups is pretty straightforward, with only one minor pain point to keep in mind. I'll gloss over adding left and right parentheses because it's super easy—just another single character token. @@ -97,4 +97,3 @@ node: Group { ``` (I won't bother discussing evaluating groups because it's trivial.) - diff --git a/site_test/posts/2021/2021-04-19-cleaning-up-binary-operators.md b/site_test/posts/2021/2021-04-19-cleaning-up-binary-operators.md index b612d28..b8c8296 100644 --- a/site_test/posts/2021/2021-04-19-cleaning-up-binary-operators.md +++ b/site_test/posts/2021/2021-04-19-cleaning-up-binary-operators.md @@ -4,7 +4,7 @@ tags = ["build a programming language", "rust"] date = "2021-04-19 17:00:42 -0400" short_desc = "A minor fight with the Rust borrow checker." slug = "cleaning-up-binary-operators" -preamble = '

This post is part of a series about learning Rust and building a small programming language.


' +preamble = '

This post is part of a series about learning Rust and building a small programming language.


' ``` The code from [part 4](/2021/operator-precedence/) that checks whether a pair of binary operators should be grouped to the left or right works, but I'm not particularly happy with it. The issue is that it needs to pattern match on the right node twice: first in the `should_group_left` function, and then again in `combine_with_binary_operator` if `should_group_left` returned true. @@ -140,4 +140,3 @@ fn combine_with_binary_operator(left: Node, token: &Token, right: Node) -> Node } } ``` - diff --git a/site_test/posts/2021/2021-04-25-variable-lookups-and-function-calls.md b/site_test/posts/2021/2021-04-25-variable-lookups-and-function-calls.md index 2353bc1..15c64db 100644 --- a/site_test/posts/2021/2021-04-25-variable-lookups-and-function-calls.md +++ b/site_test/posts/2021/2021-04-25-variable-lookups-and-function-calls.md @@ -3,7 +3,7 @@ title = "Part 8: Variable Lookups and Function Calls" tags = ["build a programming language", "rust"] date = "2021-04-25 11:15:42 -0400" slug = "variable-lookups-and-function-calls" -preamble = '

This post is part of a series about learning Rust and building a small programming language.


' +preamble = '

This post is part of a series about learning Rust and building a small programming language.


' ``` Arithmetic expressions are all well and good, but they don't really feel much like a programming language. To fix that, let's start working on variables and function calls. @@ -127,4 +127,3 @@ Call { ], } ``` - diff --git a/site_test/posts/2021/2021-05-03-statements.md b/site_test/posts/2021/2021-05-03-statements.md index 95731df..4c1abd3 100644 --- a/site_test/posts/2021/2021-05-03-statements.md +++ b/site_test/posts/2021/2021-05-03-statements.md @@ -3,7 +3,7 @@ title = "Part 9: Statements" tags = ["build a programming language", "rust"] date = "2021-05-03 17:46:42 -0400" slug = "statements" -preamble = '

This post is part of a series about learning Rust and building a small programming language.


' +preamble = '

This post is part of a series about learning Rust and building a small programming language.


' ``` So the parser can handle a single expression, but since we're not building a Lisp, that's not enough. It needs to handle multiple statements. For context, an expression is a piece of code that represents a value whereas a statement is a piece of code that can be executed but does not result in a value. @@ -61,7 +61,7 @@ fn parse_statement<'a, I: Iterator>(it: &mut Peekable<'a, I>) } None => (), } - + node } ``` @@ -93,5 +93,3 @@ statements: [ ), ] ``` - - diff --git a/site_test/posts/2021/2021-05-09-variable-declarations.md b/site_test/posts/2021/2021-05-09-variable-declarations.md index 595c137..e1310d3 100644 --- a/site_test/posts/2021/2021-05-09-variable-declarations.md +++ b/site_test/posts/2021/2021-05-09-variable-declarations.md @@ -3,7 +3,7 @@ title = "Part 10: Variable Declarations" tags = ["build a programming language", "rust"] date = "2021-05-09 19:14:42 -0400" slug = "variable-declarations" -preamble = '

This post is part of a series about learning Rust and building a small programming language.


' +preamble = '

This post is part of a series about learning Rust and building a small programming language.


' ``` Now that the parser can handle multiple statements and the usage of variables, let's add the ability to actually declare variables. @@ -64,7 +64,7 @@ There are also a few methods for `Context`, one to construct a new context and o ```rust impl Context { - fn new() -> Self { + fn new() -> Self { Self { variables: HashMap::new(), } @@ -110,4 +110,3 @@ Integer(1) ``` [^2]: The `dbg` function is a builtin I added that prints out the Rust version of the `Value` it's passed. - diff --git a/site_test/posts/2021/2021-06-29-lexical-scope.md b/site_test/posts/2021/2021-06-29-lexical-scope.md index 107eeed..733c837 100644 --- a/site_test/posts/2021/2021-06-29-lexical-scope.md +++ b/site_test/posts/2021/2021-06-29-lexical-scope.md @@ -4,7 +4,7 @@ tags = ["build a programming language", "rust"] date = "2021-06-29 19:14:42 -0400" short_desc = "Evaluating if statements and dealing with nested scopes." slug = "lexical-scope" -preamble = '

This post is part of a series about learning Rust and building a small programming language.


' +preamble = '

This post is part of a series about learning Rust and building a small programming language.


' ``` After adding variables, I added boolean values and comparison operators, because why not. With that in place, I figured it would be a good time to add if statements. Parsing them is straightforward—you just look for the `if` keyword, followed by a bunch of stuff—so I won't go into the details. But actually evaluating them was a bit more complicated. @@ -98,4 +98,3 @@ fn main() { $ cargo run Integer(1) ``` - diff --git a/site_test/posts/2021/2021-07-07-gemini-client-debugging.md b/site_test/posts/2021/2021-07-07-gemini-client-debugging.md index 06c1426..cd1e076 100644 --- a/site_test/posts/2021/2021-07-07-gemini-client-debugging.md +++ b/site_test/posts/2021/2021-07-07-gemini-client-debugging.md @@ -81,11 +81,10 @@ There's a note in the Network.framework header comments[^2] for `nw_framer_parse The possibility of a copy being needed to form a contiguous buffer implies that there could be discontiguous data, which lines up with my "chunks" hypothesis and would explain the behavior I observed. -