<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[From Scratch Code RSS Feed]]></title><description><![CDATA[Helping quietly ambitious engineers get unstuck and build real things.]]></description><link>http://github.com/dylang/node-rss</link><generator>GatsbyJS</generator><lastBuildDate>Tue, 05 May 2026 15:11:42 GMT</lastBuildDate><item><title><![CDATA[Build Your First REST API in Rust - LIU Brooklyn Workshop]]></title><link>https://fromscratchcode.com/blog/build-your-first-rest-api-in-rust-liu-brooklyn-workshop</link><guid isPermaLink="true">https://fromscratchcode.com/blog/build-your-first-rest-api-in-rust-liu-brooklyn-workshop</guid><pubDate>Mon, 06 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A few weeks ago, I dusted off my &lt;a href=&quot;https://fromscratchcode.com/blog/build-your-first-rest-api-in-rust-ccny-workshop/&quot;&gt;“Build Your First REST API in Rust”&lt;/a&gt; workshop and took it to another borough. Hello, LIU Brooklyn!&lt;/p&gt;
&lt;p&gt;My workshops begin with a show of hands to see who has worked with Rust and REST APIs before. About 20 students attended this time, and the topics were new to nearly all of them! I’m hopeful they left feeling Rust was even a tiny bit more accessible.&lt;/p&gt;
&lt;p&gt;I’m grateful to my friend and colleague Samir Iabbassen for hosting, and Professor Christopher League for seeding the discussion with some nerdy programming language topics.&lt;/p&gt;
&lt;p&gt;After the primary REST API material, I also demoed &lt;a href=&quot;https://fromscratchcode.com/ozark/&quot;&gt;Ozark&lt;/a&gt;, an example of Rust compiled to WebAssembly for use in the browser. No server required!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/blog/2026-04-06/liu.jpg&quot; alt=&quot;Tyler presenting “Build Your First REST API in Rust” workshop at LIU Brooklyn.&quot;&gt;
If you want to try it yourself, here are the materials:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/blog/2026-04-06/BuildYourFirstRESTAPIinRust.pdf&quot;&gt;Slides (PDF)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/fromscratchcode/rust-api-workshop&quot;&gt;GitHub repo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See you at the next one!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Build Your First REST API in Rust - CCNY Workshop]]></title><link>https://fromscratchcode.com/blog/build-your-first-rest-api-in-rust-ccny-workshop</link><guid isPermaLink="true">https://fromscratchcode.com/blog/build-your-first-rest-api-in-rust-ccny-workshop</guid><pubDate>Mon, 23 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Earlier this month, I ran my first in-person workshop, “Build Your First REST API in Rust,” for the Association for Computing Machinery (ACM) at the City College of New York (CCNY). That’s a lot of acronyms, and it went well!&lt;/p&gt;
&lt;p&gt;It was a great group, very engaged, and I enjoyed chatting with several students afterwards. About 30 people attended, and we built a CRUD API together from scratch using Axum.&lt;/p&gt;
&lt;p&gt;My goal was to make Rust feel more approachable and to introduce a practical topic that’s often missing from a school curriculum: building REST APIs.&lt;/p&gt;
&lt;p&gt;One thing I’m thinking about for next time is easing into the command-line setup a bit more, but overall I was really happy with how it went.&lt;/p&gt;
&lt;p&gt;A few photos from the session:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/blog/2026-03-23/ccny1.jpg&quot; alt=&quot;About 30 students following along during the CCNY Rust workshop.&quot;&gt;
&lt;img src=&quot;/blog/2026-03-23/ccny2.jpg&quot; alt=&quot;Workshop participants looking at a README file on a projected screen during the session.&quot;&gt;
&lt;img src=&quot;/blog/2026-03-23/ccny3.jpg&quot; alt=&quot;Tyler presenting “Build Your First REST API in Rust” workshop at CCNY.&quot;&gt;
&lt;img src=&quot;/blog/2026-03-23/ccny4.jpg&quot; alt=&quot;Group photo of students and instructor after the Rust API workshop at CCNY.&quot;&gt;
If you want to try it yourself, here are the materials:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/blog/2026-03-23/BuildYourFirstRESTAPIinRust.pdf&quot;&gt;Slides (PDF)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/fromscratchcode/rust-api-workshop&quot;&gt;GitHub repo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’m planning to run more workshops like this in the future.&lt;/p&gt;
&lt;p&gt;See you there, maybe!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Write your first library]]></title><link>https://fromscratchcode.com/blog/write-your-first-library</link><guid isPermaLink="true">https://fromscratchcode.com/blog/write-your-first-library</guid><pubDate>Mon, 17 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;At my first real software internship, the highlight of the summer was a 24-hour hackathon. My team and I sat in a conference room for way too many hours, hacking together a prototype. I barely remember the end goal. A chat something?&lt;/p&gt;
&lt;p&gt;What I recall most was the feeling of trying one JavaScript library after another, failing to get basic tabs to work. I felt helpless!&lt;/p&gt;
&lt;p&gt;Clearly I needed more JS experience and should’ve asked for help, but there’s a different point I want to emphasize: the shift from being a library user to being a library writer.&lt;/p&gt;
&lt;h2&gt;What is a library?&lt;/h2&gt;
&lt;p&gt;You’ll see different terms depending on the person, language, and ecosystem: library, package, crate, SDK. These all represent code which is already written in your language which you can pull in freely to your project. I prefer the term library because it’s timeless, and would make Andrew Carnegie proud (I hope!).&lt;/p&gt;
&lt;p&gt;The size can vary anywhere from a small date/time utility to a massive framework like React. Each of these is a library and was written by someone with a keyboard like you and me.&lt;/p&gt;
&lt;p&gt;The code itself typically lives on GitHub, but could be anywhere, and a built/packaged version of that code lives in an online package index. For JavaScript, this is NPM, in Python it’s PyPI, in Rust it’s crates.io.&lt;/p&gt;
&lt;p&gt;It’d be easy to get lost in the licensing details or best practices, but others have covered this better and more deeply than I will.&lt;/p&gt;
&lt;p&gt;I’m going to make the case for why you should write your own, and how it will transform your relationship with your code.&lt;/p&gt;
&lt;h2&gt;You’ll feel more confident in your stack.&lt;/h2&gt;
&lt;p&gt;The vague uncertainty and powerlessness I felt during the hackathon? I had no idea what I was doing.&lt;/p&gt;
&lt;p&gt;Each JavaScript library I found on the internet, each example I stumbled through, all I could think was “I hope this one works.” I was at the whim of what was published, potentially incomplete, years prior.&lt;/p&gt;
&lt;p&gt;After you’ve written even one library, your thinking shifts to: “I understand the general shape of what’s happening here.”&lt;/p&gt;
&lt;p&gt;When you see an import, you’ll know roughly what that maps back to on the file system, and how they exposed that set of symbols (functions, classes, etc) publicly.&lt;/p&gt;
&lt;p&gt;You don’t need to be able to regurgitate the pseudocode behind the scenes. Just knowing you can navigate their repo turns what used to be a black box into just another piece of code in your system, this time written by someone else.&lt;/p&gt;
&lt;p&gt;Once the black box feeling dissolves, something interesting happens: you start forming opinions. You start noticing which libraries feel intuitive and which feel clunky. That’s discernment, and it naturally leads you to the next question: what makes a good interface in the first place?&lt;/p&gt;
&lt;h2&gt;Your discernment around other libraries will increase.&lt;/h2&gt;
&lt;p&gt;The conventional wisdom for picking a library is to check their GitHub stars (a popularity contest for nerds), version number (be wary of anything below 1.0!), and how recent its commits are. This is all valid, but I’d encourage you to go one step further: look at their examples and see if they match your mental model of the problem you are trying to solve. This forces you to think through what you need the library for in the first place. Sometimes you&apos;ll think “uhhhh this looks more complicated than I expected,” other times &quot;ah perfect, they make this super easy.&quot;&lt;/p&gt;
&lt;p&gt;A library is ultimately a contract: when you ask for X, it will do Y or give Z. A good one will do this with minimal friction.&lt;/p&gt;
&lt;p&gt;When I pull up the docs for a new library, my first thought is about their interface. What functions and classes they expose, how they fit together. And to be honest, I often look for a bit of awe, a sense of “how did they do this?!” (&lt;a href=&quot;https://fromscratchcode.com/blog/declarative-macro-magic-from-axum-in-rust/&quot;&gt;I wrote about this in Rust’s Axum&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;I’m not saying every library you use needs to be fancy, elegant, or clever. But there’s a sense of “does this match what I was expecting it to do?” that’s always worth asking yourself.&lt;/p&gt;
&lt;p&gt;The moment you can recognize a clean interface in someone else’s library, you’re ready to design your own.&lt;/p&gt;
&lt;h2&gt;You&apos;ll learn to design interfaces.&lt;/h2&gt;
&lt;p&gt;You may recall when you wrote your first function and called it. Code reuse! Writing a library is exactly the same, just with harder interfaces and more pomp and circumstance.&lt;/p&gt;
&lt;p&gt;Each time you think “this function could really use another parameter,” that’s interface work. Each time you realize a function printed an error rather than throwing or returning an error to the caller, that’s interface work.&lt;/p&gt;
&lt;p&gt;Designing a usable interface is as much art as science. A good one will hide most of the details and expose a small surface area of knobs for the user at the right time. This will feel difficult and awkward at first, but with time you’ll get the hang of it.&lt;/p&gt;
&lt;p&gt;Once you’ve designed your first library interface, you’ll start to notice places &lt;em&gt;within&lt;/em&gt; your projects where a harder interface is more useful. The voice I often hear is “hmm this file shouldn’t need to know about this,” and it’s a sign my interface could be stronger.&lt;/p&gt;
&lt;p&gt;At this point, the difference between “code I wrote and am now reusing” and “code someone else wrote and I’m now reusing” starts to go away.&lt;/p&gt;
&lt;p&gt;It’s all just code, available for your next idea.&lt;/p&gt;
&lt;h2&gt;It’s empowering.&lt;/h2&gt;
&lt;p&gt;This is the real reason. Everything else about saving time and not reinventing the wheel is valid and probably what you should tell your manager.&lt;/p&gt;
&lt;p&gt;There’s a deeper truth here. Early on in your career, you will use libraries to build applications. You may even have users, and hopefully your code is solving a problem for them.&lt;/p&gt;
&lt;p&gt;When you write a library, your users are other engineers like yourself. If your goal is to build an app that changes the world, this isn’t the fastest path. But if you’re driven to feel like a real engineer, someone fully in control of their tools and their craft, this is the way.&lt;/p&gt;
&lt;p&gt;Some people may say writing a library will take 10 pounds off and make people laugh harder at your jokes. I can’t speak to that, but it will give you something equally elusive: agency. The agency I was severely missing sitting in the conference room praying one of the libraries I found would finally work. No more fingers crossed that the next one will magically work. You get to be the person who writes the thing others depend on.&lt;/p&gt;
&lt;p&gt;This post is about libraries, but this is also why I started From Scratch Code. This skill set we share, which is sometimes valued by the job market and sometimes not, is something no one can take away from you. Why not use it to add to the canon of code in the world?&lt;/p&gt;
&lt;p&gt;Writing a library is the best first step to own your craft deeply. If you want to get started and aren’t sure how, I’d love to help.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Memphis goes online: adding socket support]]></title><link>https://fromscratchcode.com/blog/memphis-goes-online-adding-socket-support</link><guid isPermaLink="true">https://fromscratchcode.com/blog/memphis-goes-online-adding-socket-support</guid><pubDate>Mon, 03 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Before we begin, let’s agree that “online” in this context means accepting bytes from &lt;code&gt;localhost&lt;/code&gt;. Deal? Deal.&lt;/p&gt;
&lt;p&gt;You may recall that sometime last century I had the brilliant idea to boot a Flask server from inside Memphis. Trudging toward that goal exposed me to a ton of types and functionality I didn’t yet support. I even ran &lt;a href=&quot;https://fromscratchcode.com/blog/an-interpreter-inside-an-interpreter/&quot;&gt;an interpreter inside an interpreter&lt;/a&gt;! But it was a &lt;em&gt;slog&lt;/em&gt;. Literally line 1 imports something from the stdlib which imports something from the stdlib which imports something else from the stdlib. I never got to line 2. At a certain point, I realized there was no light at the end of the tunnel.&lt;/p&gt;
&lt;p&gt;Skip to a few weeks ago, it hit me I could make my whole life easier by writing my own HTTP library in Python, wire up the necessary system resources from the Rust side, and start running some &lt;code&gt;curl&lt;/code&gt; commands.&lt;/p&gt;
&lt;p&gt;This post documents Memphis’ journey from being air-gapped to responding in kind:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&gt; curl localhost:8080
Hello from Enid (powered by Memphis)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ll approach this like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What we want a &lt;code&gt;net&lt;/code&gt; module for Memphis to look like&lt;/li&gt;
&lt;li&gt;How I implemented this&lt;/li&gt;
&lt;li&gt;Introducing Enid, a micro HTTP framework to wrap this up (and allow us to pretend we got Flask working all along)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;A &lt;code&gt;net&lt;/code&gt; module for Memphis&lt;/h2&gt;
&lt;p&gt;The surface area to respond to an HTTP request is surprisingly small. (Maybe that’s why I keep reimplementing them? Let’s put a pin in that for later.)&lt;/p&gt;
&lt;p&gt;Essentially, we need to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Listen on a port&lt;/li&gt;
&lt;li&gt;Accept new connections on that port&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That’s it!&lt;/p&gt;
&lt;p&gt;Here’s a minimal interface to do this.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from memphis.net import listen

sock = listen((&quot;127.0.0.1&quot;, 8080))
print(&quot;Listening...&quot;)
conn, _ = sock.accept()
data = conn.recv(1024)
print(&quot;Sending...&quot;)
conn.send(b&quot;HTTP/1.1 200 OK\r\n\r\nHello from Memphis!&quot;)
print(&quot;Closing...&quot;)
conn.close()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;If you want to skip ahead, &lt;a href=&quot;https://bsky.app/profile/fromscratchcode.com/post/3m4e6coytc22x&quot;&gt;here’s a short video showing this in action&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This code should block on the &lt;code&gt;sock.accept()&lt;/code&gt;, just like any socket-based server, until it receives a connection on &lt;code&gt;localhost:8080&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In other words, this is the simplest possible Memphis web server, one that says hello over TCP. And we format the bytes we send back as HTTP so we can test this with &lt;code&gt;curl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You’ll notice we’re importing &lt;code&gt;memphis.net.listen&lt;/code&gt; rather than &lt;code&gt;socket&lt;/code&gt; like you would in CPython. That’s what we have to build. &lt;strong&gt;From scratch&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;To do so, we’ll need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a &lt;code&gt;memphis.net&lt;/code&gt; module, which contains&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;listen&lt;/code&gt; function, which, when called, returns&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;Socket&lt;/code&gt; object, which contains an &lt;code&gt;accept&lt;/code&gt; method which, when called, returns&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;Connection&lt;/code&gt; object, which contains &lt;code&gt;recv&lt;/code&gt;, &lt;code&gt;send&lt;/code&gt;, and &lt;code&gt;close&lt;/code&gt; methods.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s a lot to wire up! But we can split this into two parts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the wiring up of that laundry list&lt;/li&gt;
&lt;li&gt;connecting the &lt;code&gt;Socket&lt;/code&gt; and &lt;code&gt;Connection&lt;/code&gt; objects to use Rust utilities to actually do things&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is the paradox of implementing a programming language: you spend 90% of your time on the piping, and then just call your host language to do the &lt;code&gt;real work&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here our tools are Rust’s &lt;code&gt;TcpListener&lt;/code&gt; and &lt;code&gt;TcpStream&lt;/code&gt;. Let’s take a look at how we can connect those to our Python code.&lt;/p&gt;
&lt;h2&gt;Implementing the &lt;code&gt;net&lt;/code&gt; module&lt;/h2&gt;
&lt;h3&gt;1. Building the module&lt;/h3&gt;
&lt;p&gt;Our first step is to create a &lt;code&gt;memphis.net&lt;/code&gt; module, which will contain the symbols &lt;code&gt;listen&lt;/code&gt;, &lt;code&gt;Socket&lt;/code&gt;, and &lt;code&gt;Connection&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;
fn builtins() -&gt; Vec&amp;#x3C;Box&amp;#x3C;dyn CloneableCallable&gt;&gt; {
    vec![Box::new(NetListenBuiltin)]
}

fn init(type_registry: &amp;#x26;TypeRegistry) -&gt; Module {
    let mut net_mod = Module::new(Source::default());
    for builtin in builtins() {
        net_mod.insert(&amp;#x26;builtin.name(), TreewalkValue::BuiltinFunction(builtin));
    }

    register_native_class::&amp;#x3C;Socket&gt;(&amp;#x26;mut net_mod, &quot;Socket&quot;, type_registry);
    register_native_class::&amp;#x3C;Connection&gt;(&amp;#x26;mut net_mod, &quot;Connection&quot;, type_registry);

    net_mod
}

pub fn import(module_store: &amp;#x26;mut ModuleStore, type_registry: &amp;#x26;TypeRegistry) {
    let net_mod = init(type_registry);

    let memphis_mod = module_store.get_or_create_module(&amp;#x26;ImportPath::from(&quot;memphis&quot;));
    memphis_mod.borrow_mut().insert(
        &quot;net&quot;,
        TreewalkValue::Module(Container::new(net_mod.clone())),
    );

    module_store.store_module(&amp;#x26;ImportPath::from(&quot;memphis.net&quot;), Container::new(net_mod));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We store our new module in the &lt;code&gt;ModuleStore&lt;/code&gt;, which will allow us to find our module when a user’s code imports it.&lt;/p&gt;
&lt;p&gt;Next, let’s take a look at the two native structs that back the classes we just registered.&lt;/p&gt;
&lt;h3&gt;2. Defining our native structs&lt;/h3&gt;
&lt;p&gt;First, here is our native &lt;code&gt;Socket&lt;/code&gt;, which is a thin Rust wrapper.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;use std::{
    io,
    net::{SocketAddr, TcpListener, TcpStream},
};

pub struct Socket {
    listener: TcpListener,
}

impl Socket {
    pub fn new(host: String, port: usize) -&gt; io::Result&amp;#x3C;Self&gt; {
        Ok(Self {
            listener: TcpListener::bind(format!(&quot;{}:{}&quot;, host, port))?,
        })
    }

    pub fn accept(&amp;#x26;self) -&gt; io::Result&amp;#x3C;(TcpStream, SocketAddr)&gt; {
        self.listener.accept()
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And our native &lt;code&gt;Connection&lt;/code&gt;, another thin Rust wrapper.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;use std::{
    io::{self, Read, Write},
    net::{Shutdown, TcpStream},
};

pub struct Connection {
    stream: TcpStream,
}

impl Connection {
    pub fn new(stream: TcpStream) -&gt; Self {
        Self { stream }
    }

    pub fn recv(&amp;#x26;mut self, bufsize: usize) -&gt; io::Result&amp;#x3C;Vec&amp;#x3C;u8&gt;&gt; {
        let mut buffer = vec![0; bufsize];
        let n = self.stream.read(&amp;#x26;mut buffer)?;
        Ok(buffer[..n].to_vec())
    }

    pub fn send(&amp;#x26;mut self, data: &amp;#x26;[u8]) -&gt; io::Result&amp;#x3C;()&gt; {
        self.stream.write_all(data)
    }

    pub fn close(&amp;#x26;mut self) -&gt; io::Result&amp;#x3C;()&gt; {
        self.stream.shutdown(Shutdown::Both)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s worth mentioning that these are both synchronous. I could integrate these with &lt;code&gt;tokio&lt;/code&gt; down the road, but that felt like a can of worms best left for future me.&lt;/p&gt;
&lt;p&gt;These structs encapsulate the socket behaviors we need, but we still need to connect those up to their Python bindings. You’ll notice these two structs don’t reference our actual interpreter logic: no &lt;code&gt;Module&lt;/code&gt;, no knowledge they will be used to evaluate Python code. That’s on purpose!&lt;/p&gt;
&lt;p&gt;To make them useful, we’ll bridge our native structs to our Python type system.&lt;/p&gt;
&lt;h3&gt;3. Wiring them into the type system&lt;/h3&gt;
&lt;p&gt;In the snippet below, we register our builtin methods to &lt;code&gt;Socket&lt;/code&gt; and &lt;code&gt;Connection&lt;/code&gt;. &lt;code&gt;impl_method_provider&lt;/code&gt; is a macro to reduce the boilerplate to assign our methods to a type.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;impl_method_provider!(Socket, [AcceptBuiltin]);
impl_method_provider!(Connection, [ConnRecv, ConnSend, ConnClose,]);

fn register_native_class&amp;#x3C;T: MethodProvider&gt;(
    mod_: &amp;#x26;mut Module,
    name: &amp;#x26;str,
    type_registry: &amp;#x26;TypeRegistry,
) {
    let object_class = type_registry.get_type_class(&amp;#x26;Type::Object);
    let type_class = type_registry.get_type_class(&amp;#x26;Type::Type);

    let mut class = Class::new_direct(name, Some(type_class.clone()), vec![object_class.clone()]);

    for builtin in T::get_methods() {
        class.set_on_class(&amp;#x26;builtin.name(), TreewalkValue::BuiltinMethod(builtin));
    }

    mod_.insert(name, TreewalkValue::Class(Container::new(class)));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In &lt;code&gt;register_native_class&lt;/code&gt;, we create a Python &lt;code&gt;Class&lt;/code&gt; for each and add these two new classes to our new &lt;code&gt;memphis.net&lt;/code&gt; module, each with the specified builtin methods.&lt;/p&gt;
&lt;p&gt;With this in place, we can finally begin filling out our builtin methods, starting with &lt;code&gt;NetListenBuiltin&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;4. Adding builtin functions&lt;/h3&gt;
&lt;p&gt;In Memphis, all builtin functions or methods are just structs which implement the &lt;code&gt;Callable&lt;/code&gt; trait. This trait requires the struct provide its name and implement a &lt;code&gt;call&lt;/code&gt; method. The struct itself doesn’t need any fields because those will all be passed in as runtime parameters.&lt;/p&gt;
&lt;p&gt;When a builtin is invoked, it’s provided a reference to the interpreter &lt;code&gt;&amp;#x26;TreewalkInterpreter&lt;/code&gt; and any arguments &lt;code&gt;Args&lt;/code&gt;. The former is needed to access any other runtime resources or raise errors, the latter because that’s how functions work.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;pub struct NetListenBuiltin;

impl Callable for NetListenBuiltin {
    fn call(&amp;#x26;self, interpreter: &amp;#x26;TreewalkInterpreter, args: Args) -&gt; TreewalkResult&amp;#x3C;TreewalkValue&gt; {
        check_args(&amp;#x26;args, |len| len == 1, interpreter)?;

        let host_port = args.get_arg(0).as_tuple().raise(interpreter)?;
        let host = host_port.first().as_str().raise(interpreter)?;
        let port = host_port.second().as_int().raise(interpreter)?;

        let socket = Socket::new(host, port as usize)
            .map_err(|e| interpreter.runtime_error_with(format!(&quot;Failed to bind Socket: {}&quot;, e)))?;

        let socket_class = interpreter
            .state
            .read_class(&amp;#x26;ImportPath::from(&quot;memphis.net.Socket&quot;))
            .ok_or_else(|| interpreter.runtime_error_with(&quot;Socket class not found&quot;))?;

        let obj = Object::with_payload(socket_class.clone(), socket);

        Ok(TreewalkValue::Object(Container::new(obj)))
    }

    fn name(&amp;#x26;self) -&gt; String {
        &quot;listen&quot;.into()
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this builtin, we:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;evaluate and type check the positional arguments&lt;/li&gt;
&lt;li&gt;create a new &lt;code&gt;Socket&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;look up the &lt;code&gt;memphis.net.Socket&lt;/code&gt; class&lt;/li&gt;
&lt;li&gt;create an &lt;code&gt;Object&lt;/code&gt; of this class and provide it a &lt;code&gt;Socket&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For this last step to work, we must add native object support to our Python &lt;code&gt;Object&lt;/code&gt;. Let’s take a look at that now.&lt;/p&gt;
&lt;h3&gt;5. Storing native payloads on objects&lt;/h3&gt;
&lt;p&gt;We start by adding a new field to our &lt;code&gt;Object&lt;/code&gt;, an optional &lt;code&gt;Box&amp;#x3C;dyn Any&gt;&lt;/code&gt;. I considered using an enum here, something like &lt;code&gt;Option&amp;#x3C;NativeResource&gt;&lt;/code&gt;, but I was stubborn and went with dynamic dispatch. I’ll explain why shortly.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;pub struct Object {
    class: Container&amp;#x3C;Class&gt;,
    scope: Scope,
    native_payload: Option&amp;#x3C;Box&amp;#x3C;dyn Any&gt;&gt;, // this is new!!!!!
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are now up to the &lt;code&gt;sock.accept()&lt;/code&gt; call.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;struct AcceptBuiltin;

impl Callable for AcceptBuiltin {
    fn call(&amp;#x26;self, interpreter: &amp;#x26;TreewalkInterpreter, args: Args) -&gt; TreewalkResult&amp;#x3C;TreewalkValue&gt; {
        let self_val = args.get_self().raise(interpreter)?;
        let socket = self_val.as_native_object::&amp;#x3C;Socket&gt;().raise(interpreter)?;

        let (stream, addr) = socket.accept().map_err(|e| {
            interpreter.runtime_error_with(format!(&quot;Socket.accept() failed: {}&quot;, e))
        })?;

        let conn = Connection::new(stream);

        let conn_class = interpreter
            .state
            .read_class(&amp;#x26;ImportPath::from(&quot;memphis.net.Connection&quot;))
            .ok_or_else(|| interpreter.runtime_error_with(&quot;Connection class not found&quot;))?;

        let conn_obj = Object::with_payload(conn_class.clone(), conn);

        Ok(TreewalkValue::Tuple(Tuple::new(vec![
            TreewalkValue::Object(Container::new(conn_obj)),
            TreewalkValue::Str(Str::new(&amp;#x26;addr.to_string())),
        ])))
    }

    fn name(&amp;#x26;self) -&gt; String {
        &quot;accept&quot;.into()
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The key line here is &lt;code&gt;as_native_object::&amp;#x3C;Socket&gt;()&lt;/code&gt;, which reads our new &lt;code&gt;Object.native_payload&lt;/code&gt; field and attempts to downcast it to a &lt;code&gt;Socket&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;If I had gone with an enum, I could have used an &lt;code&gt;as_socket()&lt;/code&gt; pattern or similar, but I just didn’t want to. There are other potential benefits to using &lt;code&gt;Box&amp;#x3C;dyn Any&gt;&lt;/code&gt;, like dynamic registration of native types, but that feels a ways off.&lt;/p&gt;
&lt;p&gt;I won’t show the &lt;code&gt;recv&lt;/code&gt;, &lt;code&gt;send&lt;/code&gt;, and &lt;code&gt;close&lt;/code&gt; methods on &lt;code&gt;Connection&lt;/code&gt;, but they work very similarly to &lt;code&gt;accept&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With that, our &lt;code&gt;memphis.net&lt;/code&gt; module is wired up and implemented. Thanks for sticking with me! I know those code snippets were a slog.&lt;/p&gt;
&lt;p&gt;To recap, we:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;created a new &lt;code&gt;memphis.net&lt;/code&gt; module&lt;/li&gt;
&lt;li&gt;populated the module with a &lt;code&gt;listen&lt;/code&gt; builtin function and &lt;code&gt;Socket&lt;/code&gt; and &lt;code&gt;Connection&lt;/code&gt; classes&lt;/li&gt;
&lt;li&gt;added native object support to our &lt;code&gt;Object&lt;/code&gt;, with the ability to downcast to retrieve the native objects&lt;/li&gt;
&lt;li&gt;created a native &lt;code&gt;Socket&lt;/code&gt; and &lt;code&gt;Connection&lt;/code&gt; using &lt;code&gt;TcpListener&lt;/code&gt; and &lt;code&gt;TcpStream&lt;/code&gt; in Rust&lt;/li&gt;
&lt;li&gt;created builtins for &lt;code&gt;Socket.accept&lt;/code&gt;, &lt;code&gt;Connection.recv&lt;/code&gt;, &lt;code&gt;Connection.send&lt;/code&gt;, and &lt;code&gt;Connection.close&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before we stop, let’s make our HTTP server a bit more fun.&lt;/p&gt;
&lt;h2&gt;Introducing Enid&lt;/h2&gt;
&lt;p&gt;With the &lt;code&gt;net&lt;/code&gt; module hooked up and working, I wanted to make my HTTP calls look more like Flask, with its decorator usage and general clean feel. I chose to call this &lt;a href=&quot;https://github.com/fromscratchcode/enid&quot;&gt;Enid&lt;/a&gt;, because that’s another small town in the middle of the US.&lt;/p&gt;
&lt;p&gt;Our new API looks like this.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from enid import App, Response

app = App()

@app.get(&quot;/&quot;)
def home(req):
    return Response.text(&quot;Hello from Enid (powered by Memphis)&quot;)

if __name__ == &quot;__main__&quot;:
    app.run()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Any calls to the root &lt;code&gt;/&lt;/code&gt; should return a 200 with a text response, everything else should return a 404. Here’s an example &lt;code&gt;curl&lt;/code&gt; session.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&gt; curl localhost:8080
Hello from Enid (powered by Memphis)
&gt; curl localhost:8080/
Hello from Enid (powered by Memphis)
&gt; curl localhost:8080/another_endpoint
404 Not Found
&gt; curl localhost:8080/anything_else
404 Not Found
&gt; curl localhost:8080
Hello from Enid (powered by Memphis)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Implementing socket support also revealed several Python details I’d missed. I didn’t support &lt;code&gt;__name__&lt;/code&gt;, I had a &lt;a href=&quot;https://bsky.app/profile/fromscratchcode.com/post/3m46tbkm5o22u&quot;&gt;ROUGH kwargs bug&lt;/a&gt;, plus I needed to implement &lt;code&gt;str.encode&lt;/code&gt;, &lt;code&gt;str.join&lt;/code&gt;, &lt;code&gt;str.split&lt;/code&gt;, and &lt;code&gt;bytes.decode&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you want to &lt;a href=&quot;https://github.com/fromscratchcode/enid&quot;&gt;try it on GitHub&lt;/a&gt;, you can run an Enid app on either Python or Memphis. With ChatGPT’s assistance, I wrote a shim so that it will try to import from Memphis, then fallback to Python’s stdlib, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;try:
    from memphis.net import listen
except ImportError:
    # Fallback to the pure-Python shim
    from .shim_net import listen
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This whole project took about two weeks, which is massively shorter than if I’d kept on the road to Flask nirvana. The wiring to reach the inside of the &lt;code&gt;listen&lt;/code&gt; function took about a day, getting the rest of the stdlib and pipes working about a week, and the last week was integrating it into the type system.&lt;/p&gt;
&lt;p&gt;To show why the type system work was necessary, this snippet shows two things I wasn’t able to do even when I could technically respond to a &lt;code&gt;curl&lt;/code&gt; command.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from memphis import net

type(net.Connection) # &amp;#x3C;class &apos;type&apos;&gt;
&quot;send&quot; in dir(net.Connection) # True
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This reflection was only possible once we registered the &lt;code&gt;Connection&lt;/code&gt; class into &lt;code&gt;memphis.net&lt;/code&gt; and assigned it methods. This, plus adding the native payload to a Python object, was just as rewarding as seeing the &lt;code&gt;curl&lt;/code&gt; work because it meant Memphis is growing up. And while I can’t say this was easy, I also don’t feel like the code is all going to fall apart tomorrow. So we proceed!&lt;/p&gt;
&lt;p&gt;There’s a ton here I could do next. Maybe I’ll support route pattern matching (&lt;code&gt;/endpoint/{id}&lt;/code&gt;) and middleware in Enid, or get this all working on my bytecode VM. I could even try to wrap my head around what an async version of this would look like. The order I tackle these will probably depend on how I feel when I wake up tomorrow.&lt;/p&gt;
&lt;p&gt;With that, Memphis is officially online! Just don’t count on it to respond to your Slack messages.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Python’s operator chaining is mildly interesting]]></title><link>https://fromscratchcode.com/blog/pythons-operator-chaining-is-mildly-interesting</link><guid isPermaLink="true">https://fromscratchcode.com/blog/pythons-operator-chaining-is-mildly-interesting</guid><pubDate>Mon, 20 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Nary a week ago, I typed a harmless expression into my &lt;a href=&quot;https://fromscratchcode.com/memphis/&quot;&gt;Memphis&lt;/a&gt; REPL: &lt;code&gt;4 == 4 == 4&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;My REPL’s response? &lt;code&gt;False&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I’m glad it can speak its mind, but the gall!&lt;/p&gt;
&lt;p&gt;Apparently, you can’t just treat a chain of comparison operators like a tree of binary expressions. According to my &lt;a href=&quot;https://bsky.app/profile/fromscratchcode.com/post/3m2akhxfezc22&quot;&gt;BlueSky&lt;/a&gt; &lt;a href=&quot;https://bsky.app/profile/fromscratchcode.com/post/3m2al6xlpa22w&quot;&gt;timestamps&lt;/a&gt;, it took me 13 minutes to figure this out. And I’m here today so you don’t make the same misunderstanding I did.&lt;/p&gt;
&lt;h2&gt;Fixing the parser&lt;/h2&gt;
&lt;p&gt;Expressions in Python are parsed in a left-associative way, meaning something like &lt;code&gt;2 + 3 + 4&lt;/code&gt; will be parsed as &lt;code&gt;(2 + 3) + 4&lt;/code&gt;. This works fine for numerical operations like addition, and we can rely on operator precedence and parentheses to control evaluation order.&lt;/p&gt;
&lt;p&gt;If you apply this same approach to comparison operators, like I was doing, you get this: &lt;code&gt;(4 == 4) == 4&lt;/code&gt;. Which reduces to &lt;code&gt;True == 4&lt;/code&gt;. Which is &lt;code&gt;False&lt;/code&gt;. Where I thought my REPL was giving me sass, it was just doing math.&lt;/p&gt;
&lt;p&gt;How can we fix this? We need to move from my tree-based approach to a flat structure, something that records all the operators and operands in order. I ended up introducing a new &lt;code&gt;Expr&lt;/code&gt; variant to represent these chains.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;enum Expr {
    ...
    BinaryOperation {
        left: Box&amp;#x3C;Expr&gt;,
        op: BinOp,
        right: Box&amp;#x3C;Expr&gt;,
    },
    ComparisonChain {
        left: Box&amp;#x3C;Expr&gt;,
        ops: Vec&amp;#x3C;(CompareOp, Expr)&gt;,
    },
    ....
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You may be wondering why we need to store the &lt;code&gt;CompareOp&lt;/code&gt; in between each right operand. My first thought was that we’d only need to store it once! As I looked more into the Python rules (AKA asked ChatGPT for test cases), I learned that Python allows you to write heterogeneous chains. Take these examples.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;a = 1 &amp;#x3C; 2 == 2 &amp;#x3C; 3                    # True
b = 1 == 2 != 3                       # False
c = 2 in [1,2,3] in [[1,2,3],[4,5,6]] # True
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Honestly, I’d be pretty annoyed if I saw any of these in code. But Python allows them, so we must fall in line.&lt;/p&gt;
&lt;p&gt;Here are my new parser tests, along with a Rust macro to make it easier to build these expressions.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;#[test]
fn operator_chaining() {
    let input = &quot;a == b == c&quot;;
    let expected_ast = cmp_chain!(var!(&quot;a&quot;), [(Equals, var!(&quot;b&quot;)), (Equals, var!(&quot;c&quot;)),]);

    assert_ast_eq!(input, expected_ast, Expr);

    let input = &quot;a == b &amp;#x3C; c &gt; d&quot;;
    let expected_ast = cmp_chain!(
        var!(&quot;a&quot;),
        [
            (Equals, var!(&quot;b&quot;)),
            (LessThan, var!(&quot;c&quot;)),
            (GreaterThan, var!(&quot;d&quot;)),
        ]
    );

    assert_ast_eq!(input, expected_ast, Expr);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that the parser knows how to build these chains, we can begin to interpret them correctly.&lt;/p&gt;
&lt;h2&gt;Fixing the treewalk interpreter&lt;/h2&gt;
&lt;p&gt;The treewalk implementation is closest to my mental model, so I began there. The main trick is to reuse the right-hand side of each evaluation in the next comparison. Once we hit the first &lt;code&gt;False&lt;/code&gt; result, we can short-circuit the entire operation. If we got to the end without seeing any &lt;code&gt;False&lt;/code&gt; results, we are free to return &lt;code&gt;True&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;fn evaluate_comparison_chain(
    &amp;#x26;self,
    left: &amp;#x26;Expr,
    ops: &amp;#x26;[(CompareOp, Expr)],
) -&gt; TreewalkResult&amp;#x3C;TreewalkValue&gt; {
    let mut left = self.evaluate_expr(left)?;

    for (op, right) in ops {
        let right = self.evaluate_expr(right)?;

        // ideally, we wouldn&apos;t need to clone `right` here, but this requires ownership for now.
        let result = self.evaluate_compare_op(left, op, right.clone())?;
        if !result.as_boolean() {
            return Ok(TreewalkValue::Bool(false));
        }

        left = right;
    }

    Ok(TreewalkValue::Bool(true))
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The other fun part about the treewalk interpreter is that it’s further along, which means I support operator overloading. Each time a comparison chain evaluates a particular operation, under the hood it is calling a dunder method on an object.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;fn evaluate_compare_op(
    &amp;#x26;self,
    left: TreewalkValue,
    op: &amp;#x26;CompareOp,
    right: TreewalkValue,
) -&gt; TreewalkResult&amp;#x3C;TreewalkValue&gt; {
    use CompareOp::*;

    match op {
        In =&gt; self.invoke_method(&amp;#x26;left, &amp;#x26;Dunder::Contains, args![right]),
        NotIn =&gt; {
            let contains = self.invoke_method(&amp;#x26;left, &amp;#x26;Dunder::Contains, args![right])?;
            Ok(contains.not())
        }
        Equals =&gt; self.invoke_method(&amp;#x26;left, &amp;#x26;Dunder::Eq, args![right]),
        NotEquals =&gt; self.invoke_method(&amp;#x26;left, &amp;#x26;Dunder::Ne, args![right]),
        LessThan =&gt; self.invoke_method(&amp;#x26;left, &amp;#x26;Dunder::Lt, args![right]),
        GreaterThan =&gt; self.invoke_method(&amp;#x26;left, &amp;#x26;Dunder::Gt, args![right]),
        LessThanOrEqual =&gt; self.invoke_method(&amp;#x26;left, &amp;#x26;Dunder::Le, args![right]),
        GreaterThanOrEqual =&gt; self.invoke_method(&amp;#x26;left, &amp;#x26;Dunder::Ge, args![right]),
        Is =&gt; Ok(TreewalkValue::Bool(left.is(&amp;#x26;right))),
        IsNot =&gt; Ok(TreewalkValue::Bool(!left.is(&amp;#x26;right))),
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This part technically already existed, but there’s something satisfying about watching a new feature click into an old mechanism. These comparisons used to live inside my binary operator evaluation, back when &lt;code&gt;CompareOp&lt;/code&gt; didn’t exist and comparison chains and I didn’t yet understand each other.&lt;/p&gt;
&lt;p&gt;With the treewalk interpreter working smoothly, it was time to bring the same behavior to the bytecode VM.&lt;/p&gt;
&lt;h2&gt;Fixing the bytecode VM&lt;/h2&gt;
&lt;p&gt;When I sat down to write this section, I read through my code to begin to explain it. In classic rubber-duck fashion, I realized I’d overcomplicated the whole thing. I now present the simplified pseudocode of how the bytecode looks for operator chaining.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;evaluate left
for each (op, right):
    evaluate right
    compare op
    if false: goto end
    if not last iteration: pop true
    else: leave it
end:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remember how we had to save the right-hand side for the next comparison? That’s what we’re still missing in this pseudocode. The line &lt;code&gt;compare op&lt;/code&gt; will pop two operands off the stack and compare them, consuming both operands in the process. If we want the right operand to still be around afterwards, we’re gonna need to intervene.&lt;/p&gt;
&lt;p&gt;We can do this by running a &lt;code&gt;DupTop&lt;/code&gt; and a &lt;code&gt;RotThree&lt;/code&gt; before we do the actual comparison. (CPython uses &lt;code&gt;COPY&lt;/code&gt; and &lt;code&gt;SWAP&lt;/code&gt;.) The former makes a copy of the right-hand operand, while the latter moves it to the third spot on the stack (hence the rotate three).&lt;/p&gt;
&lt;p&gt;Here’s what this looks like in Rust.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;fn compile_comparison_chain(
    &amp;#x26;mut self,
    left: &amp;#x26;Expr,
    ops: &amp;#x26;[(CompareOp, Expr)],
) -&gt; CompilerResult&amp;#x3C;()&gt; {
    if ops.is_empty() {
        panic!(&quot;Comparison chain must have &gt;= 1 op.&quot;);
    }

    self.compile_expr(left)?;

    let mut false_jumps = vec![];
    for (i, (op, right)) in ops.iter().enumerate() {
        let last_op = i == ops.len() - 1;

        self.compile_expr(right)?;

        // Preserve the right-hand side for the next comparison
        if !last_op {
            self.emit(Opcode::DupTop)?;
            self.emit(Opcode::RotThree)?;
        }

        self.emit(Opcode::from(op))?;

        // If any comparison evaluates to False, jump to end.
        // Otherwise, pop the True and continue the chain.
        // Unless it&apos;s the last operation, and we should leave the result on the stack.
        if !last_op {
            // At the end of the loop, we will patch this with a JumpIfFalse
            false_jumps.push(self.emit_placeholder()?);

            self.emit(Opcode::PopTop)?;
        }
    }

    // Patch all fail jumps to here
    for placeholder in false_jumps {
        let offset = self.forward_offset_to(placeholder)?;
        self.emit_at(placeholder, Opcode::JumpIfFalse(offset))?;
    }

    Ok(())
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The updates to the VM itself were minimal, we just needed to support the new &lt;code&gt;DupTop&lt;/code&gt; and &lt;code&gt;RotThree&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;    Opcode::DupTop =&gt; {
        let x = self.peek()?;
        self.push(x)?;
    }
    Opcode::RotThree =&gt; {
        // Before:
        // c &amp;#x3C;- TOS
        // b
        // a
        //
        // After:
        // b &amp;#x3C;- TOS
        // a
        // c
        let c = self.pop()?;
        let b = self.pop()?;
        let a = self.pop()?;
        self.push(c)?;
        self.push(a)?;
        self.push(b)?;
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My initial implementation only worked when all the operands were equal, like &lt;code&gt;2 == 2 == 2&lt;/code&gt;. That told me I had a bug in the &lt;code&gt;RotThree&lt;/code&gt; implementation which I corrected (and left better notes behind for future-me).&lt;/p&gt;
&lt;h2&gt;Putting it all together&lt;/h2&gt;
&lt;p&gt;With both interpreter engines updated, it was time to add tests to &lt;a href=&quot;https://fromscratchcode.com/blog/verifying-two-interpreter-engines-with-one-test-suite/&quot;&gt;crosscheck&lt;/a&gt;. Here’s a subset of the chaining tests which are run through both engines.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;#[test]
fn operator_chaining() {
    let input = &quot;2 == 2 == 2&quot;;
    assert_crosscheck_return!(input, MemphisValue::Boolean(true));

    let input = &quot;2 == 2 == 22&quot;;
    assert_crosscheck_return!(input, MemphisValue::Boolean(false));

    let input = &quot;2 == 2 == 2 &amp;#x3C; 3&quot;;
    assert_crosscheck_return!(input, MemphisValue::Boolean(true));

    let input = &quot;1 &amp;#x3C; 2 &amp;#x3C; 3 &amp;#x3C; 4&quot;;
    assert_crosscheck_return!(input, MemphisValue::Boolean(true));

    let input = &quot;1 &amp;#x3C; 2 &amp;#x3C; -3 &amp;#x3C; 4&quot;;
    assert_crosscheck_return!(input, MemphisValue::Boolean(false));

    let input = &quot;1 &amp;#x3C; 2.0 &amp;#x3C; 3&quot;;
    assert_crosscheck_return!(input, MemphisValue::Boolean(true));

    let input = &quot;2 in [1,2,3] in [[1,2,3],[4,5,6]]&quot;;
    assert_crosscheck_return!(input, MemphisValue::Boolean(true));

    let input = &quot;2 in [1,2,3] in [[4,5,6]]&quot;;
    assert_crosscheck_return!(input, MemphisValue::Boolean(false));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;👉 Curious what these examples compile to? Try it live using Ozark, the interactive bytecode compiler: &lt;a href=&quot;https://fromscratchcode.com/ozark/?code=YSUyMCUzRCUyMDIlMjAlM0QlM0QlMjAyJTIwJTNEJTNEJTIwMiUyMCUyMyUyMFRydWUlMEFiJTIwJTNEJTIwMiUyMCUzRCUzRCUyMDIlMjAlM0QlM0QlMjAyMiUyMCUyMyUyMEZhbHNlJTBBYyUyMCUzRCUyMDIlMjAlM0QlM0QlMjAyJTIwJTNEJTNEJTIwMiUyMCUzQyUyMDMlMjAlMjMlMjBUcnVlJTBBZCUyMCUzRCUyMDElMjAlM0MlMjAyJTIwJTNDJTIwMyUyMCUzQyUyMDQlMjAlMjMlMjBUcnVlJTBBZSUyMCUzRCUyMDElMjAlM0MlMjAyJTIwJTNDJTIwLTMlMjAlM0MlMjA0JTIwJTIzJTIwRmFsc2UlMEFmJTIwJTNEJTIwMSUyMCUzQyUyMDIuMCUyMCUzQyUyMDMlMjAlMjMlMjBUcnVlJTBBZyUyMCUzRCUyMDIlMjBpbiUyMCU1QjElMkMyJTJDMyU1RCUyMGluJTIwJTVCJTVCMSUyQzIlMkMzJTVEJTJDJTVCNCUyQzUlMkM2JTVEJTVEJTIwJTIzJTIwVHJ1ZSUwQWglMjAlM0QlMjAyJTIwaW4lMjAlNUIxJTJDMiUyQzMlNUQlMjBpbiUyMCU1QiU1QjQlMkM1JTJDNiU1RCU1RCUyMCUyMyUyMEZhbHNl&quot;&gt;fromscratchcode.com/ozark&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sometimes I get lazy and/or scared and don’t add the full test suite to crosscheck, but I was determined to do this feature right. (hence the blog post.)&lt;/p&gt;
&lt;p&gt;Along the way, I realized I’d implemented &lt;code&gt;2 &amp;#x3C; 2.5&lt;/code&gt; but not &lt;code&gt;2.5 &amp;#x3C; 3&lt;/code&gt;. The first calls &lt;code&gt;Dunder::Lt&lt;/code&gt; on &lt;code&gt;int&lt;/code&gt;, while the second calls it on &lt;code&gt;float&lt;/code&gt;. It’s fun to catch and quickly fix oversights like this. In addition, &lt;code&gt;2 == 2.0&lt;/code&gt; never returned &lt;code&gt;True&lt;/code&gt;. This was because I’d assumed that any cross-type comparison should always return &lt;code&gt;False&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;The end&lt;/h2&gt;
&lt;p&gt;Operator chaining isn’t a flashy feature, yet implementing it reminded me what I love about continuing to develop Memphis: the more Python behaviors I properly model—across two engines—the purer my abstractions become. And what is expertise in software, if not fluency in abstractions? That’s what keeps me coming back to build this interpreter no one asked for.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[What I’ve learned from 200+ hours helping developers grow]]></title><link>https://fromscratchcode.com/blog/what-ive-learned-from-200-hours-helping-developers-grow</link><guid isPermaLink="true">https://fromscratchcode.com/blog/what-ive-learned-from-200-hours-helping-developers-grow</guid><pubDate>Mon, 14 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This isn’t a how-to guide. Just a few things I’ve noticed while mentoring people 1:1 over the last two years.&lt;/p&gt;
&lt;p&gt;One-on-one mentorship wasn’t even part of my career plan. I viewed myself as a quiet builder and, hopefully, a good teammate.&lt;/p&gt;
&lt;p&gt;This journey started as most things do in my life: an experiment because I was curious. I had an itch for more 1:1 conversation and a hope that I could help a person or two with Python, so I made a profile on Wyzant.&lt;/p&gt;
&lt;p&gt;Two years and more than 200 hours later, it’s become one of the most human parts of my work. I’ve worked with college students, self-taught developers, and software professionals, focusing on helping them build confidence and clarity in topics like Python, Rust, React, and even digital logic and computer architecture.&lt;/p&gt;
&lt;p&gt;Here’s what I’ve learned.&lt;/p&gt;
&lt;h2&gt;You don’t need to be extroverted&lt;/h2&gt;
&lt;p&gt;One of my early fears, both in mentorship and in starting my own business, was that I would fail if I wasn’t “on.” I wasn’t sales-y, and I worried I wouldn’t know how to manage a conversation that got away from me. I was even scared I wouldn’t know how to cut off a session at the end of the hour!&lt;/p&gt;
&lt;p&gt;But none of that turned out to matter.&lt;/p&gt;
&lt;p&gt;In fact, the structured sessions ended up being exactly what my brain needed. Already knowing someone wants to talk to me when I hop on a video call, even if it is loosely transactional, puts me at ease.&lt;/p&gt;
&lt;p&gt;This let me focus on the real work: showing up respectfully, staying curious about what brought someone in, and posing the right question at the right time.&lt;/p&gt;
&lt;h2&gt;If they’re not asking questions, that might be a good sign&lt;/h2&gt;
&lt;p&gt;Sometimes I’ll catch myself thinking, &lt;em&gt;Why aren’t they asking me anything?&lt;/em&gt; as I watch a mentee explore something on their own.&lt;/p&gt;
&lt;p&gt;But I stop myself, because that’s actually a good sign. It means they’ve taken the wheel, and something about our environment still feels useful to them. My challenge then becomes to provide encouragement and nudge them with bigger questions.&lt;/p&gt;
&lt;p&gt;A few months ago, one of my mentees was thinking aloud and said “I bet you’re gonna tell me to ask ChatGPT.” I smiled because it showed they had internalized when to use one of their tools. I was still there to help them interpret the next steps, but they were able to take the first step on their own.&lt;/p&gt;
&lt;h2&gt;Emotional safety looks different for everyone&lt;/h2&gt;
&lt;p&gt;Our first sessions are often knee-deep in technical detail, but as we zoom out, I try to get a sense of the environment around the work.&lt;/p&gt;
&lt;p&gt;How do they like their manager? For the college students, do they have friends in class?&lt;/p&gt;
&lt;p&gt;That stuff is often lurking beneath the surface. A lonely or unsupportive environment can be just as challenging as a gnarly stack trace. And I want to make space for both.&lt;/p&gt;
&lt;p&gt;Not everyone engages deeply on those questions, but it doesn’t mean they won’t come back. I’ve had consistent mentees where we go deep into mental health issues and others where we never get past the weather. Learning to be okay with that has been part of the work too.&lt;/p&gt;
&lt;h2&gt;Code is often just the entry point, we stay for the connection&lt;/h2&gt;
&lt;p&gt;You hear this kind of thing about food or music, but I can’t say I hear many people say, “I code to connect with people.” As a craft, it can be the ultimate solitude. Even when folks talk about using code “for good,” it’s often from a distance. Which is fine!&lt;/p&gt;
&lt;p&gt;But for me, it’s more personal. Nearly all of my close friends came from engineering school or work. It was an environment I felt confident in, and that confidence gave me a safe harbor from which to go meet people. Now I try to offer that same sense of support to people who might not have had it elsewhere.&lt;/p&gt;
&lt;p&gt;People often reach out to get unstuck technically, which gets us into the space together. But I really enjoy the chance to hear what’s going on in their lives. What’s coming up after graduation, what’s been weighing on them.&lt;/p&gt;
&lt;p&gt;I don’t get that deep with everyone I work with, but after three or so sessions we usually have some rapport. My goal is that they leave feeling more seen than when they came in, ideally with their code running.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;That’s everything I’ve learned. Nothing else! Zip, zero, zilch.&lt;/p&gt;
&lt;p&gt;Oh, and Vite replaced Create React App while I was asleep.&lt;/p&gt;
&lt;p&gt;If you’re looking for technical mentorship that includes both the code and the human behind it, I’d love to work with you. &lt;a href=&quot;https://cal.com/fromscratchcode/intro&quot;&gt;Book a free intro call&lt;/a&gt;, and let’s get started.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This was cross-posted on &lt;a href=&quot;https://fromscratchpress.com/what-ive-learned-from-200-hours-helping-developers-grow/&quot;&gt;From Scratch Press&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How global variables work in Python bytecode]]></title><link>https://fromscratchcode.com/blog/how-global-variables-work-in-python-bytecode</link><guid isPermaLink="true">https://fromscratchcode.com/blog/how-global-variables-work-in-python-bytecode</guid><pubDate>Mon, 16 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Think globally, act locally. Who knew this oft-touted phrase was referring to Python bytecode?&lt;/p&gt;
&lt;p&gt;Last time we acted on learning &lt;a href=&quot;https://fromscratchcode.com/blog/how-local-variables-work-in-python-bytecode/&quot;&gt;how local variables work&lt;/a&gt;, today let’s think about how global variables might work.&lt;/p&gt;
&lt;p&gt;I came into my own bytecode journey assuming that a “global” was no different than a “local,” it was just at the outermost scope of a module. And boy, was I mistaken.&lt;/p&gt;
&lt;p&gt;While the VM isn’t even aware of a local variable’s name, just its index, the VM performs dynamic name resolution to resolve each global variable. &lt;em&gt;This is a key part of Python’s dynamism&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Once again, this post will discuss the mechanics of &lt;a href=&quot;https://fromscratchcode.com/memphis/&quot;&gt;Memphis&lt;/a&gt;, my Python interpreter built in Rust. While it doesn’t match CPython 100%, the model is simple and maps closely to how most Python runtimes behave under the hood.&lt;/p&gt;
&lt;h2&gt;Bytecode Compilation&lt;/h2&gt;
&lt;p&gt;Consider the following Python program.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;# A global variable which we&apos;ll later reference inside our function
y = 11

# A function which adds an unknown number (via global var) to the input
# parameter x, returning the result.
def add_useless(x):
    return x + y
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;👉 Curious what this compiles to? Try it live using Ozark, the interactive bytecode compiler: &lt;a href=&quot;https://fromscratchcode.com/ozark/?code=eSUyMCUzRCUyMDExJTBBJTBBZGVmJTIwYWRkX3VzZWxlc3MoeCklM0ElMEElMjAlMjAlMjAlMjByZXR1cm4lMjB4JTIwJTJCJTIweQ%3D%3D&quot;&gt;fromscratchcode.com/ozark&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here is the bytecode for our function, &lt;code&gt;add_useless&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;LOAD_FAST    0 (x)
LOAD_GLOBAL  0 (y)
ADD
RETURN_VALUE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our &lt;code&gt;CodeObject&lt;/code&gt; looks like the one below. Again, pardon my pseudo-Rust syntax! Or...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CodeObject {
  name: &quot;add_useless&quot;,
  varnames: [&quot;x&quot;], // names of each local accessed by the function
  names: [&quot;y&quot;], // names of each global accessed by the function
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We don’t see a constant of 11 here because this code object is specific to our &lt;code&gt;add_useless&lt;/code&gt; function. The function will have to look up that value at runtime via the global store, since it isn’t embedded in the code object itself.&lt;/p&gt;
&lt;p&gt;If you haven’t read &lt;a href=&quot;https://fromscratchcode.com/blog/how-local-variables-work-in-python-bytecode/&quot;&gt;the previous post&lt;/a&gt;, I’d encourage you to pause here and give it a read. It walks through the evaluation stack step-by-step and shows how the &lt;code&gt;ADD&lt;/code&gt; operation gets its two operands off the stack.&lt;/p&gt;
&lt;h2&gt;VM Execution&lt;/h2&gt;
&lt;p&gt;What we’re interested in today is what, exactly, &lt;code&gt;LOAD_GLOBAL&lt;/code&gt; is doing. First, let’s define a runtime concept I blew past earlier.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Global Store&lt;/strong&gt;: a mapping from a variable name to an object reference. The object being referenced lives on the heap. This is roughly what you get when you call &lt;code&gt;globals()&lt;/code&gt; in Python or CPython.&lt;/p&gt;
&lt;p&gt;Let’s see what happens when the VM begins executing the bytecode. Like last time, we’ll ignore how the bytecode actually enters the function.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;LOAD_FAST 0&lt;/code&gt; means: read from slot 0 on the frame’s execution stack.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LOAD_GLOBAL 0&lt;/code&gt; means: look up the identifier in &lt;code&gt;names&lt;/code&gt; at index 0. It finds &lt;code&gt;y&lt;/code&gt;, then goes to the global store to look for the key &lt;code&gt;y&lt;/code&gt;, where it finds an object reference to the value of 11.&lt;/li&gt;
&lt;li&gt;The remaining instructions, &lt;code&gt;ADD&lt;/code&gt; and &lt;code&gt;RETURN_VALUE&lt;/code&gt;, work just as they did in the previous post: the VM pops two operands off the stack, computes the result, and returns it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You may have noticed: global variables don’t need to be defined when the function is defined, only when it is called. If &lt;code&gt;y&lt;/code&gt; doesn’t exist at function call time, a &lt;code&gt;NameError&lt;/code&gt; will be thrown. This differs from local variables, which must exist at the time they are first referenced. Otherwise, the bytecode compiler will assume you are referring to a global with that same name.&lt;/p&gt;
&lt;p&gt;See how there is an extra level of indirection when resolving the global compared to the local? This is key to Python’s dynamism. Here’s an example to illustrate this.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;y = 11

def add_useless(x):
    return x + y

print(add_useless(9)) # prints 20

y = 1

print(add_useless(9)) # prints 10
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This example highlights the perils of impure functions, but otherwise might not seem that surprising at first.&lt;/p&gt;
&lt;p&gt;Before we continue, it’s worth mentioning that the global store is specific to the module, while the heap is shared across modules. This means that multiple modules can have a global named &lt;code&gt;y&lt;/code&gt;, which matches what we expect as users. Imagine having to know if every other module in your project, including those from third-party libraries, had previously used a global identifier? That would be a nightmare!&lt;/p&gt;
&lt;h2&gt;Global Store Mutations&lt;/h2&gt;
&lt;p&gt;To further illustrate how globals can be modified, let’s consider two more examples. These below move away from my Memphis implementation and focus on how dynamic global behavior shows up in CPython.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;a = &quot;First&quot;
globals()[&apos;a&apos;] = &quot;Second&quot;
print(a) # prints &quot;Second&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This one accomplishes something similar to the previous example (reassigning a global), but by mutating the global store directly via &lt;code&gt;globals()&lt;/code&gt;. We see here that &lt;code&gt;a&lt;/code&gt; doesn’t point to a fixed memory location, but is resolved dynamically by name at runtime.&lt;/p&gt;
&lt;p&gt;To take it one step further, consider the same idea applied across modules. We’ll also reassign a function rather than an integer.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;# main.py
import other

def monkey_patch_foo():
    print(&quot;Got ya!&quot;)

other.foo = monkey_patch_foo
other.bar() # prints &quot;Got ya!&quot;

# other.py
def foo():
    print(&quot;I am foo.&quot;)

def bar():
    foo()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our &lt;code&gt;main&lt;/code&gt; module modifies the behavior of a function defined in an&lt;code&gt;other&lt;/code&gt; module, and that change affects the &lt;code&gt;other&lt;/code&gt; module itself. Said another way: you can change the behavior of code from the outside, and &lt;em&gt;the function itself doesn’t know&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This is referred to as monkey patching and is one of the most mind-bending things about Python. Monkey patching is often used to inject patch fixes or replace functionality at runtime. And it’s only possible because of Python’s dynamic name resolution for global variables.&lt;/p&gt;
&lt;p&gt;To be transparent, Memphis doesn’t fully support this yet! I&apos;m still working through object mutability and shared references, and how these vary across my treewalk implementation and bytecode VM. Monkey patching: WIP.&lt;/p&gt;
&lt;p&gt;You might wonder why Python treats locals and globals so differently in bytecode.&lt;/p&gt;
&lt;p&gt;The reason is to balance performance and flexibility. Functions are called frequently during the lifetime of a program, its locals are accessed often, so Python optimizes for speed using slot-based indexing. At the module level, the dynamic name resolution introduces a performance hit, but the language is more dynamic as a result.&lt;/p&gt;
&lt;h2&gt;The End&lt;/h2&gt;
&lt;p&gt;I didn’t fully appreciate this design until I implemented it myself. You really start to see how much of Python’s feel comes from how its variables are resolved in bytecode.&lt;/p&gt;
&lt;p&gt;If you’re curious to explore more, I recommend trying out the &lt;a href=&quot;https://docs.python.org/3/library/dis.html&quot;&gt;dis module&lt;/a&gt; to inspect your own functions! It’ll be confusing at first, but you may begin to pick up one new tidbit each time.&lt;/p&gt;
&lt;p&gt;Now that we’ve tackled &lt;a href=&quot;https://fromscratchcode.com/blog/how-local-variables-work-in-python-bytecode/&quot;&gt;locals&lt;/a&gt; and globals, we’re free to think about free variables next! I hope you’ll stay tuned.&lt;/p&gt;
&lt;p&gt;Lastly, &lt;strong&gt;I’m continuing to experiment with open office hours this week&lt;/strong&gt;. If you’re stuck in Python or Rust and want to talk through it live, &lt;a href=&quot;https://cal.com/fromscratchcode/intro&quot;&gt;here’s the booking link&lt;/a&gt;. The slots are pay-what-you-want, with zero pressure to tip. I’d love to meet you!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How local variables work in Python bytecode]]></title><link>https://fromscratchcode.com/blog/how-local-variables-work-in-python-bytecode</link><guid isPermaLink="true">https://fromscratchcode.com/blog/how-local-variables-work-in-python-bytecode</guid><pubDate>Mon, 02 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Local variables are stored on the stack.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This line tormented me for years. I could spout it in an interview, but I didn’t really know what it meant. I could pull up the diagram showing the stack growing upwards and the heap growing downwards. And I definitely didn’t want to think about what happened if they ever crossed.&lt;/p&gt;
&lt;p&gt;But to truly understand? I had to build it myself.&lt;/p&gt;
&lt;p&gt;This post explains how local variables work by walking through their mechanics in &lt;a href=&quot;https://fromscratchcode.com/memphis/&quot;&gt;Memphis&lt;/a&gt;, my Python interpreter built in Rust. While it doesn’t match CPython byte-for-byte, the model is simple and maps closely to how most Python runtimes behave under the hood.&lt;/p&gt;
&lt;p&gt;If you’ve read my previous posts, you’ll recall I have two implementations: a treewalk interpreter and a bytecode VM. This post focuses on the bytecode VM because it had a steeper learning curve, blew my mind more often, and is closer to CPython.&lt;/p&gt;
&lt;p&gt;Let’s begin with some definitions which will be used by our bytecode VM at runtime.&lt;/p&gt;
&lt;h2&gt;Bytecode VM concepts&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;CodeObject&lt;/code&gt;&lt;/strong&gt;: An immutable structure produced by the bytecode compiler and consumed by the VM.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;FunctionObject&lt;/code&gt;&lt;/strong&gt; : A runtime representation of a &lt;code&gt;CodeObject&lt;/code&gt; plus its captured environment. We’ll need this layer for when we introduce free variables, which underpin closures.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Frame&lt;/code&gt;&lt;/strong&gt;: A runtime representation of a &lt;code&gt;FunctionObject&lt;/code&gt;. It holds the program counter and the &lt;em&gt;evaluation stack&lt;/em&gt;, including the space reserved for local variables.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;CallStack&lt;/code&gt;&lt;/strong&gt;: A stack of &lt;code&gt;Frame&lt;/code&gt; objects, which tracks each execution context (i.e each new function call).&lt;/p&gt;
&lt;h2&gt;What the heap?!&lt;/h2&gt;
&lt;p&gt;Wait a minute. Are local variables, which so famously live on the stack, stored in the &lt;code&gt;Frame&lt;/code&gt; or the &lt;code&gt;CallStack&lt;/code&gt;? The answer is yes!&lt;/p&gt;
&lt;p&gt;Each &lt;code&gt;Frame&lt;/code&gt; contains its own evaluation stack, the bottom portion of which is reserved for local variables. This is what the term “stack-based virtual machine” refers to, and is in contrast to a register-based VM. Both are mechanisms for managing working memory during your program’s execution.&lt;/p&gt;
&lt;p&gt;Meanwhile, the &lt;code&gt;CallStack&lt;/code&gt; is one layer of abstraction higher. It holds a new &lt;code&gt;Frame&lt;/code&gt; for each new function which is entered.&lt;/p&gt;
&lt;p&gt;Before I go any farther, I want to address the elephant in the room. In addition to the incantation &lt;em&gt;local variables are stored on the stack&lt;/em&gt;, I was pretty sure that &lt;em&gt;objects in Python are stored on the heap&lt;/em&gt;. What gives?&lt;/p&gt;
&lt;p&gt;Both things are true. Local variables are stored on the stack, but most variables in Python are references. The stack holds the pointer to the object (which could an int, string, list, custom object, etc.), while the heap does hold the actual data.&lt;/p&gt;
&lt;p&gt;Let’s walk through a short function to see this in action.&lt;/p&gt;
&lt;h2&gt;The stack in action&lt;/h2&gt;
&lt;p&gt;Consider the following function. It takes in one parameter and defines one additional local variable. For the scope of this discussion, let’s assume our VM has already called our function, &lt;code&gt;add_useless&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;# A function which adds an unknown number (via local var) to the input
# parameter x, returning the result.
def add_useless(x):
    y = 11
    return x + y
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;👉 Curious what this compiles to? Try it live using Ozark, the interactive bytecode compiler: &lt;a href=&quot;https://fromscratchcode.com/ozark/?code=ZGVmJTIwYWRkX3VzZWxlc3MoeCklM0ElMEElMjAlMjAlMjAlMjB5JTIwJTNEJTIwMTElMEElMjAlMjAlMjAlMjByZXR1cm4lMjB4JTIwJTJCJTIweQ%3D%3D&quot;&gt;fromscratchcode.com/ozark&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Remember how we said the bottom portion of the &lt;code&gt;Frame&lt;/code&gt;&apos;s evaluation stack is reserved for local variables? We can visualize it like this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;┌────────────────┐
|   stack[1]     │
├────────────────┤
|   stack[0]     │
├────────────────┤
│   locals[1]    │
├────────────────┤
│   locals[0]    │
└────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before we set &lt;code&gt;y = 11&lt;/code&gt;, the stack would hold two reserved spaces for our two local variables, &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;┌──────────────────┐
│ empty slot for y │
├──────────────────┤
│    value of x    │
└──────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;During the evaluation of &lt;code&gt;y = 11&lt;/code&gt;, the stack briefly looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;┌──────────────────┐
│         11       │
├──────────────────┤
│ empty slot for y │
├──────────────────┤
│     value of x   │
└──────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After we set &lt;code&gt;y = 11&lt;/code&gt;, the stack would be:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;┌────────────┐
│    11 (y)  │
├────────────┤
│ value of x │
└────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;During the evaluation of &lt;code&gt;x + y&lt;/code&gt;, the stack briefly looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;┌────────────────┐
|       11       │
├────────────────┤
|   value of x   │
├────────────────┤
│      11 (y)    │
├────────────────┤
│   value of x   │
└────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After we add &lt;code&gt;x + y&lt;/code&gt;, but before the return the stack would be:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;┌────────────────┐
│ value of x + y │
├────────────────┤
│      11 (y)    │
├────────────────┤
│   value of x   │
└────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we return the sum, discard the frame, and hope nothing blows up. (Can you tell I don’t want to explain that part right now?)&lt;/p&gt;
&lt;p&gt;If you are screaming at your screen “why is only half of our stack &lt;em&gt;the&lt;/em&gt; stack,” I get it! It confused me too that is one piece of contiguous memory, but we reserve the bottom &lt;em&gt;n&lt;/em&gt; slots for &lt;em&gt;n&lt;/em&gt; local variables.&lt;/p&gt;
&lt;p&gt;So how do we know how many local variables to reserve? Because we already compiled the function’s bytecode before running it. Let’s take a look at how that works.&lt;/p&gt;
&lt;h2&gt;The bytecode in action&lt;/h2&gt;
&lt;p&gt;Before we look at the bytecode itself, let’s define a few more concepts. These are both fields on the &lt;code&gt;CodeObject&lt;/code&gt; and are populated during the bytecode compilation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;constants&lt;/code&gt;&lt;/strong&gt; : The list of constants taken directly from the user code. Think: any integer, float, bool, or string. Because &lt;code&gt;CodeObject&lt;/code&gt;s themselves are immutable, they can also appear as constants. In Rust, I represent this with an &lt;code&gt;enum&lt;/code&gt; to allow multiple types in the same list.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;varnames&lt;/code&gt;&lt;/strong&gt;: The list of identifiers for each local variable in a given &lt;code&gt;CodeObject&lt;/code&gt;. In &lt;a href=&quot;https://fromscratchcode.com/blog/how-i-added-support-for-nested-functions-in-python-bytecode/&quot;&gt;a previous post&lt;/a&gt;, I described how &lt;code&gt;varnames&lt;/code&gt; (for locals) differs from &lt;code&gt;names&lt;/code&gt; (for globals).&lt;/p&gt;
&lt;p&gt;During compilation, each identifier assigned to (either as a function parameter or a local variable) is pushed into the &lt;code&gt;varnames&lt;/code&gt; list of the current &lt;code&gt;CodeObject&lt;/code&gt;. (We can ignore the &lt;code&gt;global&lt;/code&gt; keyword for now.)&lt;/p&gt;
&lt;p&gt;Whenever the compiler encounters a new function definition, it creates a fresh &lt;code&gt;CodeObject&lt;/code&gt; and pushes it onto a &lt;em&gt;code stack&lt;/em&gt;. This stack is used only during compilation to track lexical scope, and is &lt;em&gt;not&lt;/em&gt; related to the runtime &lt;code&gt;CallStack&lt;/code&gt;. Once a function body is fully compiled, the corresponding &lt;code&gt;CodeObject&lt;/code&gt; is popped off and made available as a constant for its parent &lt;code&gt;CodeObject&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here is the bytecode for our &lt;code&gt;add_useless&lt;/code&gt; function. The data in the parentheses annotates what each index refers to. I added this manually from my Memphis output, but you can get a similar output in CPython by running &lt;code&gt;import dis; dis.dis(add_useless)&lt;/code&gt;, which uses the &lt;a href=&quot;https://docs.python.org/3/library/dis.html&quot;&gt;dis module&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;LOAD_CONST  0  (11)
STORE_FAST  1  (y)
LOAD_FAST   0  (x)
LOAD_FAST   1  (y)
ADD
RETURN_VALUE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’re indexing into two separate lists here, both of which live inside a &lt;code&gt;CodeObject&lt;/code&gt;. I write Rust, so naturally I visualized it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CodeObject {
  constants: [11],
  varnames: [&quot;x&quot;, &quot;y&quot;],
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;LOAD_CONST&lt;/code&gt; indexes into &lt;code&gt;constants&lt;/code&gt;, while &lt;code&gt;STORE_FAST&lt;/code&gt; and &lt;code&gt;LOAD_FAST&lt;/code&gt; index into &lt;code&gt;varnames&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The magic here is there’s no need for the runtime VM to know about the names of the local variables&lt;/strong&gt;. &lt;code&gt;varnames&lt;/code&gt; is used by the compiler to emit the right indices into the bytecode, but during execution, the VM just deals with stack slots.&lt;/p&gt;
&lt;p&gt;Once a runtime &lt;code&gt;Frame&lt;/code&gt; is initialized, the VM will execute each of these instructions and index into its own execution stack.&lt;/p&gt;
&lt;h2&gt;The End&lt;/h2&gt;
&lt;p&gt;If this felt a bit dense, I totally get it! It took me several tries to get this working &lt;em&gt;and&lt;/em&gt; clean in my Memphis implementation.&lt;/p&gt;
&lt;p&gt;I’m hoping to write similar posts covering &lt;a href=&quot;https://fromscratchcode.com/blog/how-global-variables-work-in-python-bytecode/&quot;&gt;global variables&lt;/a&gt; and free variables, so I hope you’ll stick around for those.&lt;/p&gt;
&lt;p&gt;Lastly, &lt;strong&gt;I’m experimenting with open office hours this week&lt;/strong&gt;. If you’re stuck in Python or Rust and want to talk through it live, &lt;a href=&quot;https://cal.com/fromscratchcode/intro&quot;&gt;here’s the booking link&lt;/a&gt;. The slots are pay-what-you-want, with zero pressure to tip. I’d love to meet you!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Verifying two interpreter engines with one test suite]]></title><link>https://fromscratchcode.com/blog/verifying-two-interpreter-engines-with-one-test-suite</link><guid isPermaLink="true">https://fromscratchcode.com/blog/verifying-two-interpreter-engines-with-one-test-suite</guid><pubDate>Mon, 05 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Crosscheck, my cross-engine testing framework for &lt;a href=&quot;https://fromscratchcode.com/memphis/&quot;&gt;Memphis&lt;/a&gt;, had been sitting atop my writing to-do list for a while.&lt;/p&gt;
&lt;p&gt;Instead, I deleted it and replaced it. Memphis can now test itself across all engines.&lt;/p&gt;
&lt;p&gt;What does this mean?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I can verify the treewalk interpreter and bytecode VM interpreter produce the same results (return value or symbol table entries) for a given snippet of Python.&lt;/li&gt;
&lt;li&gt;This functionality is available in unit tests, rather than only in integration tests. In Rust, this means in &lt;code&gt;src/&lt;/code&gt; rather than &lt;code&gt;tests/&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s look at a couple of examples.&lt;/p&gt;
&lt;h2&gt;Testing expressions&lt;/h2&gt;
&lt;p&gt;Any interpreter worth its table salt should be able to evaluate &lt;code&gt;2 + 2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;[An expression returns a value, whereas a statement modifies control flow or the symbol table. I couldn’t have defined this clearly before I began this project, so I hope this helps.]&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;#[test]
fn binary_expression() {
    let input = &quot;2 + 2&quot;;
    assert_crosscheck_return!(input, MemphisValue::Integer(4));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A few building blocks were necessary to get here.&lt;/p&gt;
&lt;p&gt;First, I needed to represent a Python value independent of the runtime engine. This isn’t exactly necessary for integers, but come lists or objects, my architecture needed each engine to be able to manage interior mutability differently.&lt;/p&gt;
&lt;p&gt;Second, I needed a simple initialization flow, which would hide the complexity of initializing two interpreter engines and running a snippet of code through both. I’d had a few builders over time, but they always did too much. I wanted to truly hide all this behind &lt;code&gt;assert_crosscheck_return!&lt;/code&gt;. Speaking of which, let’s look inside.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;macro_rules! assert_crosscheck_return {
    ($src:expr, $expected:expr) =&gt; {{
        let mut session =
            $crate::crosscheck::CrosscheckSession::new($crate::domain::Source::from_text($src));
        let (tw_val, vm_val) = session.eval();
        assert_eq!(
            tw_val, $expected,
            &quot;Treewalk return value did not match expected&quot;
        );
        assert_eq!(
            vm_val, $expected,
            &quot;Bytecode VM return value did not match expected&quot;
        );
    }};
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Awesome! It calls something else. Sounds and reads like software.&lt;/p&gt;
&lt;p&gt;It’s worth mentioning here why &lt;code&gt;assert_crosscheck_return&lt;/code&gt; is a macro rather than a helper function. When a panic happens inside a macro, it reports the line number of your test file, not of the macro itself. This is due to how Rust resolves them &lt;em&gt;before&lt;/em&gt; compile-time. Some of my macros call helper functions under the hood, but having the assertions at the macro-level makes debugging way simpler.&lt;/p&gt;
&lt;p&gt;We create a &lt;code&gt;CrosscheckSession&lt;/code&gt;, which creates two &lt;code&gt;MemphisContext&lt;/code&gt; objects. Since this is test-only, I don’t mind loudly failing with &lt;code&gt;expect&lt;/code&gt; inside of &lt;code&gt;eval&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;pub struct CrosscheckSession {
    treewalk: MemphisContext,
    vm: MemphisContext,
}

impl CrosscheckSession {
    pub fn new(source: Source) -&gt; Self {
        let treewalk = MemphisContext::new(Engine::Treewalk, source.clone());
        let vm = MemphisContext::new(Engine::BytecodeVm, source);
        Self { treewalk, vm }
    }

    /// Run both engines; confirm they return the same value, then return the value. Useful
    /// for evaluating expressions or statements which only return a single value.
    pub fn eval(&amp;#x26;mut self) -&gt; (MemphisValue, MemphisValue) {
        let tw_val = self.treewalk.run().expect(&quot;Treewalk run failed.&quot;);
        let vm_val = self.vm.run().expect(&quot;VM run failed.&quot;);

        (tw_val, vm_val)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each &lt;code&gt;MemphisContext&lt;/code&gt; manages the lexer/parser/interpreter lifetime for a single &lt;code&gt;Engine&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;pub struct MemphisContext {
    lexer: Lexer,
    interpreter: Box&amp;#x3C;dyn Interpreter&gt;,
}

impl MemphisContext {
    pub fn new(engine: Engine, source: Source) -&gt; Self {
        let lexer = Lexer::new(&amp;#x26;source);
        let interpreter = init_interpreter(engine, source.clone());

        Self { lexer, interpreter }
    }

    pub fn run(&amp;#x26;mut self) -&gt; Result&amp;#x3C;MemphisValue, MemphisError&gt; {
        let MemphisContext {
            lexer, interpreter, ..
        } = self;

        let mut parser = Parser::new(lexer);
        interpreter.run(&amp;#x26;mut parser)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Testing statements, classes, and more&lt;/h2&gt;
&lt;p&gt;Let’s look at a slightly more involved example, a test which defines a class with one attribute and a couple of methods.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;#[test]
fn method_call() {
    let mut session = crosscheck_eval!(
        r#&quot;
class Foo:
    def __init__(self, val):
        self.val = val

    def bar(self):
        return self.val

f = Foo(10)
b = f.bar()
&quot;#
    );
    assert_crosscheck_eq!(session, &quot;b&quot;, MemphisValue::Integer(10));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our pair of new macros here allows us to start a crosscheck session. Each session will run the provided code snippet through each engine, then stay alive so we can query its symbol table.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;macro_rules! crosscheck_eval {
    ($src:expr) =&gt; {{
        $crate::crosscheck::CrosscheckSession::new($crate::domain::Source::from_text($src))
            .run()
            .expect(&quot;Crosscheck session failed&quot;)
    }};
}

macro_rules! assert_crosscheck_eq {
    ($session:expr, $name:expr, $expected:expr) =&gt; {{
        let actual = $session.read($name).expect(&quot;Symbol not found&quot;);
        assert_eq!(actual, $expected);
    }};
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s look at the two new &lt;code&gt;CrosscheckSession&lt;/code&gt; methods, &lt;code&gt;run&lt;/code&gt; and &lt;code&gt;read&lt;/code&gt;. You’ll notice I break my own rule about keeping assertions at the top level inside the &lt;code&gt;read&lt;/code&gt; method. I’ll put a ticket in the backlog to fix that later.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;impl CrosscheckSession {
    /// Run both engines; discard the return value and return the session. Useful for
    /// later reads from the symbol table with `CrosscheckSession::read`.
    pub fn run(mut self) -&gt; Result&amp;#x3C;Self, MemphisError&gt; {
        self.treewalk.run()?;
        self.vm.run()?;
        Ok(self)
    }

    /// Read a value from both engines; confirm they return the same value, then
    /// return the value.
    pub fn read(&amp;#x26;mut self, name: &amp;#x26;str) -&gt; Option&amp;#x3C;MemphisValue&gt; {
        let a = self.treewalk.read(name)?;
        let b = self.vm.read(name)?;

        assert_eq!(a, b, &quot;Engines returned different values&quot;);
        Some(a)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that’s it! I have a few other macros and flows to validate an expected Python runtime error, but they follow the same ideas as what I’ve shown here.&lt;/p&gt;
&lt;h2&gt;Why did I delete the old &lt;code&gt;crosscheck&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;Because it sucked.&lt;/p&gt;
&lt;p&gt;Okay not really, it did its job for nearly a year! But it required &lt;em&gt;a lot&lt;/em&gt; of boilerplate code. It also expected each test file to manually define what engines to verify (using &lt;code&gt;TreewalkAdapter&lt;/code&gt; and &lt;code&gt;BytecodeVmAdapter&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Have a look for yourself:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;fn run_binary_expression_test&amp;#x3C;T: InterpreterTest&gt;(mut interpreter: T) {
    let input = &quot;2 + 2&quot;;
    match interpreter.evaluate(input) {
        Err(e) =&gt; panic!(&quot;Interpreter error: {:?}&quot;, e),
        Ok(result) =&gt; {
            assert_eq!(result, MemphisValue::Integer(4));
        }
    }
}

#[test]
fn test_treewalk_binary_expression() {
    run_binary_expression_test(TreewalkAdapter::new());
}

#[test]
fn test_bytecode_vm_binary_expression() {
    run_binary_expression_test(BytecodeVmAdapter::new());
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Early-2024-me was pleased with this, but it is &lt;em&gt;heavy&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I wrote the original as an integration test (where tests live in &lt;code&gt;tests/&lt;/code&gt; rather than &lt;code&gt;src/&lt;/code&gt;) because I didn’t know what I was doing. Rust produces a separate binary for integration tests, which was unnecessary for verifying cross-engine behavior.&lt;/p&gt;
&lt;p&gt;It also meant I was using &lt;code&gt;memphis&lt;/code&gt; as a library — interesting on its own (a Python interpreter you can embed in other Rust code! cool!), but not what I needed here.&lt;/p&gt;
&lt;h2&gt;The End&lt;/h2&gt;
&lt;p&gt;This work took roughly a year, though I took about 11 months off in the middle.&lt;/p&gt;
&lt;p&gt;If the founder of the Single Responsibility Principle reads this, I hope they are proud of me. Their principle served as a useful North Star.&lt;/p&gt;
&lt;p&gt;Along the way, I used &lt;code&gt;MemphisContext&lt;/code&gt; to support both engines in the REPL. That’s also the reason the &lt;code&gt;Lexer&lt;/code&gt; is kept alive but the &lt;code&gt;Parser&lt;/code&gt; isn’t—to support streaming input. But that&apos;s a story for another day.&lt;/p&gt;
&lt;p&gt;I hope you are well and your code returns the same value every time!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[I'm embarrassed by how much code I cut from my test suite]]></title><link>https://fromscratchcode.com/blog/im-embarrassed-by-how-much-code-i-cut-from-my-test-suite</link><guid isPermaLink="true">https://fromscratchcode.com/blog/im-embarrassed-by-how-much-code-i-cut-from-my-test-suite</guid><pubDate>Mon, 31 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My parser test suite was a house of cards I’ve never been prouder to see collapse. I found a pattern that worked, kept working, and then worked a little too well. Until rust-analyzer couldn&apos;t process my file anymore. Best practice says to refactor before your LSP craps out, but alas. Here&apos;s how I saved several thousand lines of test code in my &lt;a href=&quot;https://fromscratchcode.com/memphis/&quot;&gt;Memphis&lt;/a&gt; parser.&lt;/p&gt;
&lt;h2&gt;Designing an expressive parser test suite&lt;/h2&gt;
&lt;p&gt;I’m going to break all rules of writing and tell this story Benjamin Button style.&lt;/p&gt;
&lt;p&gt;We begin with this idyllic test case. Wouldn’t it be lovely to verify our AST in an expressive yet relaxed way? Yes, it is lovely.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;#[test]
fn expression() {
    let input = &quot;2 + 3 * (4 - 1)&quot;;
    let expected_ast = bin_op!(
        int!(2),
        Add,
        bin_op!(int!(3), Mul, bin_op!(int!(4), Sub, int!(1)))
    );

    assert_ast_eq!(input, expected_ast, Expr);

    let input = &quot;2 // 3&quot;;
    let expected_ast = bin_op!(int!(2), IntegerDiv, int!(3));

    assert_ast_eq!(input, expected_ast, Expr);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This test wasn’t all sunflowers and rainbows. This 16-line fact-checker used to come in at a bloated 38 lines.&lt;/p&gt;
&lt;p&gt;Have a peep yourself.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;#[test]
fn expression() {
    let input = &quot;2 + 3 * (4 - 1)&quot;;
    let context = init(input);

    let expected_ast = Expr::BinaryOperation {
        left: Box::new(Expr::Integer(2)),
        op: BinOp::Add,
        right: Box::new(Expr::BinaryOperation {
            left: Box::new(Expr::Integer(3)),
            op: BinOp::Mul,
            right: Box::new(Expr::BinaryOperation {
                left: Box::new(Expr::Integer(4)),
                op: BinOp::Sub,
                right: Box::new(Expr::Integer(1)),
            }),
        }),
    };

    match context.parse_oneshot::&amp;#x3C;Expr&gt;() {
        Err(e) =&gt; panic!(&quot;Parser error: {:?}&quot;, e),
        Ok(ast) =&gt; assert_eq!(ast, expected_ast),
    }

    let input = &quot;2 // 3&quot;;
    let context = init(input);

    let expected_ast = Expr::BinaryOperation {
        left: Box::new(Expr::Integer(2)),
        op: BinOp::IntegerDiv,
        right: Box::new(Expr::Integer(3)),
    };

    match context.parse_oneshot::&amp;#x3C;Expr&gt;() {
        Err(e) =&gt; panic!(&quot;Parser error: {:?}&quot;, e),
        Ok(ast) =&gt; assert_eq!(ast, expected_ast),
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The tool I used to reduce my boilerplate was declarative macros, Rust’s way of generating Rust code at compile time.&lt;/p&gt;
&lt;p&gt;By the end of my cleanup trance, I improved these areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;expressing Python types (int, str, bool, list, set, tuple)&lt;/li&gt;
&lt;li&gt;expressing operations (binary, unary, and logical ops)&lt;/li&gt;
&lt;li&gt;wrapping the actual entrypoint to parse the input&lt;/li&gt;
&lt;li&gt;wrapping error handling for the common case&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result? Shorter tests and clearer intentions.&lt;/p&gt;
&lt;h3&gt;Expressing Python types&lt;/h3&gt;
&lt;p&gt;I can now write &lt;code&gt;int!(3)&lt;/code&gt; instead of &lt;code&gt;Expr::Integer(3)&lt;/code&gt;. Blah blah blah so what.&lt;/p&gt;
&lt;p&gt;This one doesn’t save a whole lot, but let’s look at two more.&lt;/p&gt;
&lt;p&gt;I can now write &lt;code&gt;list![int!(1), int!(2), int!(3)]&lt;/code&gt; and &lt;code&gt;set![int!(1), int!(2), int!(3)]&lt;/code&gt;. Glancing at the implementation for those macros, we see another key.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;macro_rules! list {
    ($($expr:expr),* $(,)?) =&gt; {
        Expr::List(vec![
            $($expr),*
        ])
    };
}

macro_rules! set {
    ($($expr:expr),* $(,)?) =&gt; {
        Expr::Set(HashSet::from([
            $($expr),*
        ]))
    };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My parser tests no longer care that an &lt;code&gt;Expr::List&lt;/code&gt; accepts a &lt;code&gt;Vec&lt;/code&gt;, while a &lt;code&gt;Expr::Set&lt;/code&gt; accepts a &lt;code&gt;HashSet&lt;/code&gt;. One could argue I don’t need the &lt;code&gt;HashSet&lt;/code&gt; at all because this is just the AST, not the evaluation stage of the interpreter. In that case, I could change the underlying representation by updating the macro.&lt;/p&gt;
&lt;h3&gt;Expressing operations&lt;/h3&gt;
&lt;p&gt;This one starts to get &lt;em&gt;really&lt;/em&gt; fun. I can now write &lt;code&gt;bin_op!(var!(&quot;a&quot;), BitwiseAnd, var!(&quot;b&quot;))&lt;/code&gt;, which expands to the following.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;macro_rules! bin_op {
    ($left:expr, $op:ident, $right:expr) =&gt; {
        Expr::BinaryOperation {
            left: Box::new($left),
            op: BinOp::$op,
            right: Box::new($right),
        }
    };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not only does this macro hide the two &lt;code&gt;Box&lt;/code&gt; initializations (necessary in a recursive enum), it also allows us to write &lt;code&gt;BitwiseAnd&lt;/code&gt; rather than &lt;code&gt;BinOp::BitwiseAnd&lt;/code&gt;, all without sacrificing any type checking.&lt;/p&gt;
&lt;h3&gt;Wrapping the parser entrypoint&lt;/h3&gt;
&lt;p&gt;Previously, I had to write this, which isn’t awful, but is also awful.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;let context = init(&quot;2 + 3&quot;);

match context.parse_oneshot::&amp;#x3C;Expr&gt;() {
   ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’re working with a &lt;code&gt;MemphisContext&lt;/code&gt; object here, another bloated structure I use to orchestrate the whole evaluation flow. The problem is, this is an evolving interface. I learned the hard way that without a level of indirection in my tests, I’d have to tweak this pattern constantly. Across hundreds of tests, that is obnoxious.&lt;/p&gt;
&lt;p&gt;The new approach uses a straight forward &lt;code&gt;parse!&lt;/code&gt; macro.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;let ast = parse!($input, Statement);
assert_stmt_eq!(ast, $expected);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Wrapping the happy path error handling&lt;/h3&gt;
&lt;p&gt;As Yogi Berra famously said, 90% of unit tests are one-half mental.&lt;/p&gt;
&lt;p&gt;I applied this philosophy to design &lt;code&gt;parse!&lt;/code&gt; to handle the happy path, the roughly 90% of my parser tests I expect to be able to parse their Python input successfully. Since these are tests and nothing matters, we can fail loudly on any unexpected exceptions and return the AST quietly otherwise.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;macro_rules! parse {
    ($input:expr, $pattern:ident) =&gt; {
        match init($input).parse_oneshot::&amp;#x3C;$pattern&gt;() {
            Err(e) =&gt; panic!(&quot;Parser error: {:?}&quot;, e),
            Ok(ast) =&gt; ast,
        }
    };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And for those 10% of tests where we expect a parse error? This will do just fine.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;macro_rules! expect_error {
    ($input:expr, $pattern:ident) =&gt; {
        match init($input).parse_oneshot::&amp;#x3C;$pattern&gt;() {
            Ok(_) =&gt; panic!(&quot;Expected a ParserError!&quot;),
            Err(e) =&gt; e,
        }
    };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is what it now looks like to confirm an improperly structured &lt;code&gt;dict&lt;/code&gt;. While I love &lt;code&gt;match&lt;/code&gt; statements, I love even more no longer having to write them.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;let input = &quot;{ 2, **second }&quot;;
let e = expect_error!(input, Expr);
assert_eq!(e, ParserError::SyntaxError);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The End&lt;/h2&gt;
&lt;p&gt;I’m embarrassed I didn’t do these steps sooner. Please learn from my mistakes and treat your tests the way you wish to be treated.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[I left corporate and still do roadmaps + a Memphis update]]></title><link>https://fromscratchcode.com/blog/i-left-corporate-and-still-do-roadmaps-a-memphis-update</link><guid isPermaLink="true">https://fromscratchcode.com/blog/i-left-corporate-and-still-do-roadmaps-a-memphis-update</guid><pubDate>Mon, 03 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Happy March!&lt;/p&gt;
&lt;p&gt;This month I am putting a bow on my Q1 roadmap and continuing to unify the two &lt;a href=&quot;https://fromscratchcode.com/memphis/&quot;&gt;Memphis&lt;/a&gt; execution engines.&lt;/p&gt;
&lt;p&gt;I chose an update post because I want to “build in public.” What follows is what has been on my mind! I don’t write advice posts (Why you shouldn’t use &lt;code&gt;Vec&lt;/code&gt;) or random technical overviews (Vectors vs. Slices in Rust: A Complete Guide) because I am unable to do either with a straight face. If I could, I’d probably still work in a large organization where those kind of ambitious but safe norms are politely celebrated. Someone else will write those pieces and I support them in the same way I support someone running a marathon; I’m not against it, but that doesn’t mean I want to.&lt;/p&gt;
&lt;p&gt;It appears this “build in public” includes me breaking the fourth wall on my writing process! Can you tell I struggle in groups? If you’re still here, let’s continue.&lt;/p&gt;
&lt;h2&gt;Q1 Roadmap&lt;/h2&gt;
&lt;p&gt;I began Q1 with a list of 6 items I wanted to achieve for From Scratch. As someone who once flew to Chicago to write a list of personal goals on a public library whiteboard with a close friend, I’m no stranger to making goals. What felt new was doing them with that uniquely American fuel: a profit motive.&lt;/p&gt;
&lt;p&gt;The Original 6 (the little-known prequel to The Magnificent 7) were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Launch website testimonials (thanks Jakub!)&lt;/li&gt;
&lt;li&gt;Improve SEO for &lt;a href=&quot;https://fromscratchcode.com/&quot;&gt;fromscratchcode.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Launch lead magnet&lt;/li&gt;
&lt;li&gt;Run a small trial with paid ads&lt;/li&gt;
&lt;li&gt;Write chapters 3-5 of my novella&lt;/li&gt;
&lt;li&gt;File taxes and pay Q1 estimated taxes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I would later add two more:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add CTA to and modernize my personal site&lt;/li&gt;
&lt;li&gt;Create a runbook for follow-ups&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’m still working on Chapter 5 (Chapters 1-4 are on &lt;a href=&quot;https://fromscratchpress.com/essays/&quot;&gt;From Scratch Press&lt;/a&gt;) and the follow-up runbook, but everything else is &lt;code&gt;DONE&lt;/code&gt;. Who knew you could accomplish things without Jira for Enterprise.&lt;/p&gt;
&lt;p&gt;These records have unintentionally produced a fairly detailed account of the playbook I’m running to build my online business. Which I’m excited to share with you in case you too are interested in how to earn small amounts of money with only an internet connection.&lt;/p&gt;
&lt;p&gt;I feel pride looking at this list because it makes my efforts feel less random. I can reassure myself &lt;em&gt;Sure, I’m still growing, but here’s my strategy&lt;/em&gt;. I can (and have!) thrown this list into ChatGPT and asked what foundational pieces am I missing. And each time it says “Share your work on social media,” I close the tab and search for From Scratch on Google instead.&lt;/p&gt;
&lt;h2&gt;Multiple Execution Engines&lt;/h2&gt;
&lt;p&gt;Memphis has supported two execution engines for about a year now. But barely.&lt;/p&gt;
&lt;p&gt;The treewalk interpreter is farthest along and, if you wanted to try to run real code, what you should use. I’ve been strengthening the bytecode VM’s foundation and it’s coming along but slowly. Because I have no deadlines, I’m being deliberate to define Memphis capabilities versus treewalk capabilities versus bytecode VM capabilities. Did I mention I have no deadlines?&lt;/p&gt;
&lt;p&gt;The unification has proceeded in the following broad strokes.&lt;/p&gt;
&lt;h3&gt;Common Entrypoint&lt;/h3&gt;
&lt;p&gt;Initially, you could pick an engine like this.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Treewalk is default FOR NOW
memphis example.py

# VM selected using an environment variable
MEMPHIS_ENGINE=vm memphis example.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which works fine! But after kicking off the runtime, there was no coming back together. Or reunification.&lt;/p&gt;
&lt;p&gt;This remains the interface to select a non-default engine, but I’m gradually unifying more of the code behind the scenes. I’ve also floated the idea of using a Rust feature flag instead of an environment variable to produce a smaller binary.&lt;/p&gt;
&lt;h3&gt;Common Return Type&lt;/h3&gt;
&lt;p&gt;Because I respect those who came before me, I wanted to treat Python runtime errors as first-class. Meaning I wouldn’t trap them below deck after hitting an iceberg.&lt;/p&gt;
&lt;p&gt;Instead of separate error types for treewalk and VM errors, I would combine them. This would also be a chance to turn this dumping ground I aspirationally called &lt;code&gt;InterpreterError&lt;/code&gt; into a type which actually represented possible Python runtime errors.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;// The treewalk version was first so it is known as &quot;Interpreter&quot; here
pub enum InterpreterError {
    Exception(DebugCallStack),
    TypeError(Option&amp;#x3C;String&gt;, DebugCallStack),
    KeyError(String, DebugCallStack),
    ValueError(String, DebugCallStack),
    NameError(String, DebugCallStack),
    AttributeError(String, String, DebugCallStack),
    FunctionNotFound(String, DebugCallStack),
    MethodNotFound(String, DebugCallStack),
    ClassNotFound(String, DebugCallStack),
    ModuleNotFound(String, DebugCallStack),
    DivisionByZero(String, DebugCallStack),
    ExpectedVariable(DebugCallStack),
    ExpectedString(DebugCallStack),
    ExpectedInteger(DebugCallStack),
    ExpectedList(DebugCallStack),
    ExpectedTuple(DebugCallStack),
    ExpectedRange(DebugCallStack),
    ExpectedSet(DebugCallStack),
    ExpectedDict(DebugCallStack),
    ExpectedFloatingPoint(DebugCallStack),
    ExpectedBoolean(DebugCallStack),
    ExpectedObject(DebugCallStack),
    ExpectedClass(DebugCallStack),
    ExpectedFunction(DebugCallStack),
    ExpectedIterable(DebugCallStack),
    ExpectedCoroutine(DebugCallStack),
    WrongNumberOfArguments(usize, usize, DebugCallStack),
    StopIteration(DebugCallStack),
    AssertionError(DebugCallStack),
    MissingContextManagerProtocol(DebugCallStack),
    RuntimeError,
    EncounteredReturn(ExprResult),
    EncounteredRaise,
    EncounteredAwait,
    EncounteredSleep,
    EncounteredBreak,
    EncounteredContinue,
}

pub enum VmError {
    StackUnderflow,
    StackOverflow,
    NameError(String),
    RuntimeError,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I landed on this streamlined structure which could now be used on both engines. All my previous &lt;code&gt;ExpectedString&lt;/code&gt;, &lt;code&gt;ExpectedInteger&lt;/code&gt;, etc., variants are now just a &lt;code&gt;TypeError&lt;/code&gt;. This mirrors CPython, where an optional &lt;code&gt;String&lt;/code&gt; parameter provides more detail.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;pub struct ExecutionError {
    pub debug_call_stack: DebugCallStack,
    pub execution_error_kind: ExecutionErrorKind,
}

pub enum ExecutionErrorKind {
    RuntimeError,
    ImportError(String),
    TypeError(Option&amp;#x3C;String&gt;),
    KeyError(String),
    ValueError(String),
    NameError(String),
    AttributeError(String, String),
    DivisionByZero(String),
    StopIteration,
    AssertionError,
    MissingContextManagerProtocol,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this unified type, I was able to test for an expected &lt;code&gt;NameError&lt;/code&gt; in a crosscheck test. Which reminds me, I should really write more about crosscheck, my testing framework for both engines. I have a fun proc macro in the works there.&lt;/p&gt;
&lt;p&gt;Now that both engines returned an &lt;code&gt;ExecutionError&lt;/code&gt;, I needed to build an interface to push new stack frames to the &lt;code&gt;DebugCallStack&lt;/code&gt;. These would be displayed to the user whenever a Python runtime error occurs in their user code.&lt;/p&gt;
&lt;h3&gt;Common Debug Stack Trace&lt;/h3&gt;
&lt;p&gt;I’m still actively working on unifying stack traces, but I’m excited because it is me saying “Memphis supports a debug stack trace, regardless of what execution engine you choose,” rather than just “both engines have a stack trace.” Do you see the difference? It’s a pLaTfOrM. Or maybe I mean fRaMeWoRk? It’s something.&lt;/p&gt;
&lt;p&gt;I cleaned up my &lt;code&gt;DebugStackTrace&lt;/code&gt; and &lt;code&gt;DebugCallStack&lt;/code&gt; structs and moved them into a &lt;code&gt;domain&lt;/code&gt; module to indicate they represent a Python stack trace, but should &lt;strong&gt;NOT&lt;/strong&gt; be used as a source of runtime info for an engine.&lt;/p&gt;
&lt;p&gt;One challenge is giving each engine access to the right-sized shared state object. This would be how each engine could register new stack frames; think something like &lt;code&gt;state.push_stack_frame(function.to_stack_frame())&lt;/code&gt; when entering a new function context. I’m attempting to balance platform capabilities (&lt;code&gt;MemphisState&lt;/code&gt;) against freedom of implementation within each engine (&lt;code&gt;TreewalkState&lt;/code&gt; and &lt;code&gt;VmState&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The other challenge of implementing stack traces is it forces you to keep around your metadata for use at the right time. Metadata like file paths and line numbers aren’t necessary for actually running Python code, but they’re essential for debugging. When a statement is parsed and immediately evaluated (treewalk) or immediately compiled (bytecode VM, though this has a bug right now), we record the line number of the start of the current function and increment it each time we see a new line. This parser&amp;#x3C;&gt;runtime communication is only necessary for stack traces (for me. so far.).&lt;/p&gt;
&lt;p&gt;This work stream has been a crash course in applying the Single-Responsibility Principle a few decades after I first learned of it. It’s easy to understand the definition (”of course each function/struct/class only does one thing!”). But applying it? That’s harder (”we’ve got access to state here so I’ll put it there!”). The result is my code is GRADUALLY shifting from a medium number of medium-sized functions to a whole lot of tiny ones. I’ve always been an advocate for adding a second entrypoint, either through unit tests or &lt;a href=&quot;https://fromscratchcode.com/blog/a-repl-for-fat-finger-friendly-typing/&quot;&gt;a REPL&lt;/a&gt;, but Memphis has forced me to expand that thinking to an entirely different level.&lt;/p&gt;
&lt;h2&gt;The End&lt;/h2&gt;
&lt;p&gt;I’m putting the finishing touches on my Q2 roadmap, which should take my business to the max! Do people still say that?&lt;/p&gt;
&lt;p&gt;I recently finished &lt;em&gt;The Pathless Path&lt;/em&gt; by Paul Millerd and his message of finding the work each of us wants to do indefinitely resonated with me. With my Memphis engine work and my roadmap work, I believe I’m closer than ever to finding that. I’d also love to be a &lt;a href=&quot;https://fromscratchcode.com/mentorship/&quot;&gt;technical mentor&lt;/a&gt; to anyone reading this. Because money.&lt;/p&gt;
&lt;p&gt;Hope you are well!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Build a software career with meaning: a playbook]]></title><link>https://fromscratchcode.com/blog/build-a-software-career-with-meaning-playbook</link><guid isPermaLink="true">https://fromscratchcode.com/blog/build-a-software-career-with-meaning-playbook</guid><pubDate>Mon, 17 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Even with 20 fingers and toes, I barely have enough digits to count how many times during my 9-5 career I thought, “Wow, I feel frustrated/stuck/alone/hungry, maybe I should look for a new job.” A new job holds the allure of allowing you to let go of all your negative emotions (especially hunger) and start over.&lt;/p&gt;
&lt;p&gt;There are plenty of reasons to switch jobs, particularly when advocating for fairer compensation or a less-toxic environment. But if you&apos;re like me and looking to build a meaningful software career, where you can use your brain to its fullest and maybe do a bit of good, job hopping is more like a salve on a wound than a complete fix.&lt;/p&gt;
&lt;p&gt;With this in mind, I’m excited to announce my new email course: &lt;a href=&quot;https://fromscratchcode.com/courses/meaningful-career/&quot;&gt;Build a Software Career with Meaning&lt;/a&gt;. While under development, I called this “How I went from ‘Hello World!’ to ‘How can I help?’ in just 19 years,” which is clearly a cheeky title but captures the work-in-progress feel of my career.&lt;/p&gt;
&lt;p&gt;Did I mention it’s free? Over the course of 5 (business) days, you’ll receive a brief email with a piece of wisdom, along with an actionable thought experiment you can test on your career.&lt;/p&gt;
&lt;p&gt;I also want to be transparent: in email marketing speak, this is called a “lead magnet.” I learned this term from reading (does YouTube still exist?), so I can only assume this is referring to the element from the periodic table with the symbol &lt;code&gt;Pb&lt;/code&gt; (you know, the one pronounced ‘led’). And here I thought lead wasn&apos;t magnetic!&lt;/p&gt;
&lt;p&gt;I&apos;m offering the course for free because I want to share how I think about things. At the end of 5 (business) days, perhaps you’ll feel like you know a bit more about my values and want to work with me. If you don’t, that’s more than okay! You are welcome to stay on the list indefinitely, and the option to unsubscribe will always be at the bottom. There are no tricks here, just a marketing playbook I’ve been learning in between &lt;a href=&quot;https://fromscratchcode.com/blog/building-a-markdown-blog-with-links-optimized-for-gatsby/&quot;&gt;implementing my Markdown blog&lt;/a&gt; and &lt;a href=&quot;https://fromscratchcode.com/blog/how-i-added-support-for-nested-functions-in-python-bytecode/&quot;&gt;nested functions in Python bytecode&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you’ve ever felt stuck or disillusioned in your software career, I hope this course gives you a new perspective. If nothing else, it’ll be five (business) days of me popping into your email client with career advice just a tad more nuanced than “Quit your job!”&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;P.S. If you’re interested in reading the story about how my 9-5 career crashed-and-burned—and how I built something better from the wreckage—I have the complete account over on &lt;a href=&quot;https://fromscratchpress.com/why-i-left-my-9-5-for-good/&quot;&gt;From Scratch Press&lt;/a&gt;. I’d love to hear if any of my experiences and catatonic thought loops (the kind where you forget to eat) mirror your own!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Building a Markdown blog with links optimized for Gatsby]]></title><link>https://fromscratchcode.com/blog/building-a-markdown-blog-with-links-optimized-for-gatsby</link><guid isPermaLink="true">https://fromscratchcode.com/blog/building-a-markdown-blog-with-links-optimized-for-gatsby</guid><pubDate>Mon, 27 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;While I was hoping this website would build itself, it ended up taking a good amount of fine-tuning! I wanted to share how I built this blog and a few of the challenges I encountered.&lt;/p&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;My setup for this website is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;domain and DNS managed on GoDaddy&lt;/li&gt;
&lt;li&gt;code lives in a private GitHub repository (should I make this public?)&lt;/li&gt;
&lt;li&gt;static site written in React using Gatsby&lt;/li&gt;
&lt;li&gt;site deployed using the free tier on Netlify&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While &lt;a href=&quot;https://dev.to/jonesbeach&quot;&gt;Dev.to&lt;/a&gt; and &lt;a href=&quot;https://fromscratchcode.hashnode.dev/&quot;&gt;Hashnode&lt;/a&gt; have been helpful to get me flowing on my technical writing, I &lt;em&gt;really&lt;/em&gt; wanted a minimal blog over which I exercised full control. I wanted to be able style blockquotes the way I liked. I wanted the blog to reflect the &lt;strong&gt;From Scratch&lt;/strong&gt; ethos that less is better.&lt;/p&gt;
&lt;p&gt;The final straw: when I realized I was missing out on potential SEO improvements by not publishing my writing directly to &lt;code&gt;fromscratchcode.com&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Thus this silly blog that I’m moderately proud of was born!&lt;/p&gt;
&lt;h2&gt;List of Requirements&lt;/h2&gt;
&lt;p&gt;My requirements for the new blog were:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Posts shall be written in Markdown and be portable, meaning require no changes to be thrown into other editors (Dev.to, Hashnode, etc). As a bonus, I currently write these in Notion because they copy out already in Markdown.&lt;/li&gt;
&lt;li&gt;Posts shall support syntax highlighting for code blocks that actually look good. (This was a PAIN on my email provider. too bad this post isn’t about picking an email provider!)&lt;/li&gt;
&lt;li&gt;Internal links within posts shall be optimized for navigation and SEO.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Gatsby is modern, flexible, and I’d barely used it before, which gave me the confidence I could pull this off. I wrote this post because these requirements got more difficult as I went and I hope this can be a resource for someone else. Here’s what I learned!&lt;/p&gt;
&lt;h2&gt;Supporting Markdown Posts&lt;/h2&gt;
&lt;p&gt;My motivation for writing in Markdown will not surprise you: I wanted to spend more time writing and less time formatting. To move a piece (including code!) between platforms and have it &lt;code&gt;just work&lt;/code&gt;. Markdown checks all of those boxes.&lt;/p&gt;
&lt;p&gt;Gatsby’s ecosystem is rich with markdown support. Here’s how I leveraged it!&lt;/p&gt;
&lt;p&gt;I used the &lt;code&gt;gatsby-transformer-remark&lt;/code&gt; plugin to read the Markdown files. Next, I used the &lt;code&gt;createPages&lt;/code&gt; API in &lt;code&gt;gatsby-node.js&lt;/code&gt; to register a new page with slug (this is a URL that for some reason is named after an insect).&lt;/p&gt;
&lt;p&gt;Then, I used Gatsby’s &lt;code&gt;createNodeField&lt;/code&gt; to attach additional metadata to the Markdown node. This ensures this metadata is available to components via GraphQL queries. I also used the &lt;code&gt;reading-time&lt;/code&gt; library to calculate the reading time for each block of Markdown to give each post some fun deets and make this look like a real blog.&lt;/p&gt;
&lt;p&gt;Here are these pieces assembled together in my &lt;code&gt;gatsby-node.js&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;const BLOG_QUERY = `
    {
      allMarkdownRemark(
        filter: { fileAbsolutePath: { regex: &quot;/blog/&quot; } }
        sort: { frontmatter: { date: DESC } }
      ) {
        edges {
          node {
            frontmatter {
              slug
            }
          }
        }
      }
    }
  `

// This function runs a GraphQL query and creates pages at `${base_url}/${node.frontmatter.slug}`
// for each using the provided template.
// NOTE: each markdown file must provide its own slug in the frontmatter.
// TODO: could we provide a default in the case the frontmatter is not specified?
const createMarkdownPages = async (
  graphql,
  createPage,
  query,
  base_url,
  template
) =&gt; {
  const markdownFiles = await graphql(query)

  markdownFiles.data.allMarkdownRemark.edges.forEach(({ node }) =&gt; {
    const slug = `${base_url}/${node.frontmatter.slug}`
    createPage({
      path: slug,
      component: template,
      // The context is needed for the $slug param lookup in the query inside the
      // repsective template
      context: {
        slug,
      },
    })
  })
}

exports.createPages = async ({ graphql, actions }) =&gt; {
  const { createPage } = actions
  const blogPostTemplate = path.resolve(`src/templates/blogPostTemplate.js`)

  await createMarkdownPages(
    graphql,
    createPage,
    BLOG_QUERY,
    &quot;/blog&quot;,
    blogPostTemplate
  )
}

exports.onCreateNode = ({ node, actions, getNode }) =&gt; {
  const { createNodeField } = actions
  if (node.internal.type === &quot;MarkdownRemark&quot;) {
    let slug = node.frontmatter.slug

    // Determine the base path (e.g., &apos;policies&apos; or &apos;blog&apos;)
    // This is the name from the gatsby-source-filesystem config in gatsby-config.js
    const sourceInstanceName = getNode(node.parent).sourceInstanceName

    // Add the slug field to the node, which will become queryable from GraphQL. This is safer
    // than relying on frontmatter within the component pages because we have applied all the
    // necessary transformations here before we write to the field.
    createNodeField({
      node,
      name: &quot;slug&quot;,
      value: `/${sourceInstanceName}/${slug}`,
    })

    // Calculate the reading time of the markdown content and add it to the GraphQL
    const stats = readingTime(node.rawMarkdownBody)
    createNodeField({
      node,
      name: &quot;readingTime&quot;,
      value: stats.text, // Example: &quot;3 min read&quot;
    })
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I trimmed for brevity (HA!), but I process my &lt;a href=&quot;https://fromscratchcode.com/policies/terms-of-use/&quot;&gt;Terms of Use&lt;/a&gt; and &lt;a href=&quot;https://fromscratchcode.com/policies/privacy-policy/&quot;&gt;Privacy Policy&lt;/a&gt; using the same procedure. While those would not need syntax highlighting, requirement #3 about optimized internal links would still apply.&lt;/p&gt;
&lt;p&gt;Next, I used Gatsby’s GraphQL to query for the post content based on the slug of the rendered page.&lt;/p&gt;
&lt;p&gt;This is my first pass at &lt;code&gt;blogPostTemplate.js&lt;/code&gt;. I have stripped out a few pieces to focus on the GraphQL query.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import React from &quot;react&quot;
import { graphql } from &quot;gatsby&quot;

const BlogPost = ({ data }) =&gt; {
  const post = data.markdownRemark

  return (
    &amp;#x3C;Layout&gt;
      &amp;#x3C;article&gt;
        &amp;#x3C;h1&gt;{post.frontmatter.title}&amp;#x3C;/h1&gt;
        &amp;#x3C;div dangerouslySetInnerHTML={{ __html: post.html }} /&gt;
      &amp;#x3C;/article&gt;
    &amp;#x3C;/Layout&gt;
  )
}
export const query = graphql`
  query ($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      frontmatter {
        title
        date(formatString: &quot;MMM DD, YYYY&quot;)
      }
      fields {
        readingTime
      }
      html
    }
  }
`

export default BlogPost
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Consider the following piece of frontmatter, the metadata at the top of a Markdown file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;---
title: &quot;Introducing: From Scratch Code&quot;
date: &quot;2024-11-04&quot;
slug: &quot;introducing-from-scratch-code&quot;
---
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, we have created and populated a page at &lt;code&gt;/blog/introducing-from-scratch-code/&lt;/code&gt;. Awesome!&lt;/p&gt;
&lt;h2&gt;Enabling Syntax Highlighting&lt;/h2&gt;
&lt;p&gt;Syntax highlighting ended up taking two tries, both using PrismJS.&lt;/p&gt;
&lt;h3&gt;First Attempt: &lt;code&gt;gatsby-remark-prismjs&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;My first approach used the &lt;code&gt;gatsby-remark-prismjs&lt;/code&gt; plugin (yes, this is a plugin [prismjs] to a plugin [remark] to a framework [gatsby]) during the Gatsby build pipeline. Here was the addition to my &lt;code&gt;gatsby-config.js&lt;/code&gt; file.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;{
  resolve: `gatsby-transformer-remark`,
  options: {
    plugins: [
      {
        resolve: `gatsby-remark-prismjs`,
        options: {
          classPrefix: &quot;language-&quot;, // Set a class prefix
          inlineCodeMarker: null, // Marker for inline code
          // Do not use prism to highlight inline code
          noInlineHighlight: true,
        },
      },
    ],
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach does these steps behind the scenes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read markdown&lt;/li&gt;
&lt;li&gt;Convert it to HTML&lt;/li&gt;
&lt;li&gt;Apply syntax highlighting via CSS classes&lt;/li&gt;
&lt;li&gt;Let React render our HTML which we fetched by querying GraphQL&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This worked out of the box!&lt;/p&gt;
&lt;p&gt;To customize the theme, I added this to my &lt;code&gt;gatsby-browser.js&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;// Prism.js syntax highlighting
import &quot;prismjs/themes/prism-tomorrow.css&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I had some trouble styling line numbers and the Copy-to-Clipboard button. There seemed to be some complexity around PrismJS plugins inside a static site processing pipeline such as Remark in Gatsby, so I punted on those.&lt;/p&gt;
&lt;p&gt;This approached worked great until it didn’t, which brings me to requirement #3.&lt;/p&gt;
&lt;h2&gt;Optimizing Internal Links&lt;/h2&gt;
&lt;p&gt;When I say “internal link”, I mean a link on this website, such as &lt;code&gt;/blog&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I had two motivations for optimizing these:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;This is mostly me being particular, but in order to truly make my Markdown portable, I wanted to be able to write external links (such as &lt;code&gt;https://fromscratchcode.com/mentorship&lt;/code&gt;) in Markdown and have it be treated as the internal link &lt;code&gt;/mentorship&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;This is the biggie: Gatsby applies a magic touch using its &lt;code&gt;Link&lt;/code&gt; component, which consists of several things.
&lt;ol&gt;
&lt;li&gt;Under the hood, Gatsby prefetches any &lt;code&gt;Link&lt;/code&gt; destination when the page loads.&lt;/li&gt;
&lt;li&gt;When clicked, it uses &lt;code&gt;@reach/router&lt;/code&gt; to navigate to it using ultra-smooth client-side navigation.&lt;/li&gt;
&lt;li&gt;This is all while appearing to be an &lt;code&gt;&amp;#x3C;a&gt;&lt;/code&gt; component in the page source, which keeps these links *chef&apos;s kiss* perfect for SEO because they remain visible to crawlers such as Google’s.&lt;/li&gt;
&lt;li&gt;In addition, our React state persists across clicks. Without this optimization, the state of the dark mode toggle would persist when using the top nav, but not when clicking a link discovered in a blog post. That is not ideal!&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Second Attempt: Switching to Rehype&lt;/h3&gt;
&lt;p&gt;The challenge here is turning &lt;code&gt;[mentorship](/mentorship)&lt;/code&gt; into &lt;code&gt;&amp;#x3C;Link to=&quot;/mentorship&quot;&gt;mentorship&amp;#x3C;/Link&gt;&lt;/code&gt; and have it still be rendered as React.&lt;/p&gt;
&lt;p&gt;There is a strong ecosystem of JS libraries to help with this, but I needed to switch from using PrismJS in a Remark plugin to a Rehype plugin. That sentence would have been word salad to me a few &lt;del&gt;hours&lt;/del&gt; weeks ago, so please bear with me. Gatsby’s Remark pipeline converts Markdown to HTML before React sees it, so internal links get set before React can modify them. By using Rehype, we get a hook where we can dynamically swap &lt;code&gt;&amp;#x3C;a&gt;&lt;/code&gt; for &lt;code&gt;Link&lt;/code&gt;. Rehype also supports integrating PrismJS syntax highlighting. Here’s how I pulled it off!&lt;/p&gt;
&lt;p&gt;The function below does this whole shebang in several steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Parse our raw markdown content into an AST (Abstract Syntax Tree).&lt;/li&gt;
&lt;li&gt;Apply a plugin we would need to write to detect and convert external links to internal links.&lt;/li&gt;
&lt;li&gt;Convert the markdown AST (used by Remark) into an HTML AST (used by Rehype).&lt;/li&gt;
&lt;li&gt;Apply PrismJS syntax highlighting using a Rehype plugin.&lt;/li&gt;
&lt;li&gt;Render the Rehype AST as React, converting any &lt;code&gt;&amp;#x3C;a&gt;&lt;/code&gt; elements into &lt;code&gt;CustomLink&lt;/code&gt; components along the way. (&lt;code&gt;CustomLink&lt;/code&gt; is a wrapper I created around Gatsby’s &lt;code&gt;Link&lt;/code&gt; so that it works for internal or external links.)&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;// Render markdown to React
// This pipeline processes raw markdown and converts it into React components,
// applying syntax highlighting and internal link optimization.
const renderMarkdownToReact = markdown =&gt; {
  try {
    return unified()
      .use(remarkParse) // Parse markdown into an abstract syntax tree
      .use(optimizeInternalLinks) // Apply our plugin to detect internal links
      .use(remarkRehype) // Convert Markdown AST to Rehype AST
      .use(rehypePrism, { showLineNumbers: false }) // Add PrismJS syntax highlighting
      .use(rehypeReact, {
        jsx,
        jsxs,
        Fragment,
        components: {
          a: props =&gt; &amp;#x3C;CustomLink to={props.href} {...props} /&gt;,
        },
      })
      .processSync(markdown).result
  } catch (error) {
    console.error(&quot;Failed to render markdown:&quot;, error)
    return &amp;#x3C;div&gt;Error rendering markdown&amp;#x3C;/div&gt;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And our plugin to convert internal links:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;const optimizeInternalLinks = () =&gt; tree =&gt; {
  visit(tree, &quot;link&quot;, node =&gt; {
    const { url } = node
    if (url.startsWith(&quot;/&quot;) || url.startsWith(SITE_URL)) {
      const internalPath = url.replace(SITE_URL, &quot;&quot;)
      node.url = internalPath
    }
  })
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function calls &lt;code&gt;visit&lt;/code&gt; from &lt;code&gt;unist-util-visit&lt;/code&gt; to allow us to walk the AST. Its interface matches what is expected by a Remark plugin.&lt;/p&gt;
&lt;p&gt;PHEW. That is quite a pipeline. This is the type of problem I probably would have given up on pre-ChatGPT. With a tool that points me to exactly what libraries to use and gets me close on the initial syntax, I was able to take it the rest of the way.&lt;/p&gt;
&lt;p&gt;Now you can click around this blog and, when I reference &lt;a href=&quot;https://fromscratchcode.com/blog/introducing-from-scratch-code/&quot;&gt;a past post&lt;/a&gt;, it should load nearly instantaneously and preserve your dark mode settings. We did it!&lt;/p&gt;
&lt;h2&gt;What’s next?&lt;/h2&gt;
&lt;p&gt;I’m pleased with how the blog has turned out! I’d like to eventually add support for tags, table of contents for each post, and perhaps a series so to link to &lt;a href=&quot;https://fromscratchcode.hashnode.dev/series/memphis&quot;&gt;all the Memphis posts&lt;/a&gt;. I could add comments using Disqus but, then again, would you invite a troll into your living room?&lt;/p&gt;
&lt;p&gt;I’d love to hear from you! Please &lt;a href=&quot;https://fromscratchcode.com/contact/&quot;&gt;reach out&lt;/a&gt; if you find any bugs. I’m also curious what static site blog features have wow-ed you in the past, now that I am the proud owner of my own.&lt;/p&gt;
&lt;p&gt;I’m gonna go write some Rust now.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Typed integers in Rust for safer Python bytecode compilation]]></title><link>https://fromscratchcode.com/blog/typed-integers-in-rust-for-safer-python-bytecode-compilation</link><guid isPermaLink="true">https://fromscratchcode.com/blog/typed-integers-in-rust-for-safer-python-bytecode-compilation</guid><pubDate>Mon, 13 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Shortly after I shared &lt;a href=&quot;https://fromscratchcode.com/blog/how-i-added-support-for-nested-functions-in-python-bytecode&quot;&gt;my previous post&lt;/a&gt;, a helpful redditor &lt;a href=&quot;https://www.reddit.com/r/Python/comments/1hqkqxn/comment/m4sej1n/&quot;&gt;pointed out&lt;/a&gt; that the typed integers I alluded to is known as the &lt;a href=&quot;https://doc.rust-lang.org/rust-by-example/generics/new_types.html&quot;&gt;newtype pattern in Rust&lt;/a&gt;, a design that allows you to define types purely for compile-time safety and with no runtime hit.&lt;/p&gt;
&lt;p&gt;And here I thought I invented it! Alas. 😂&lt;/p&gt;
&lt;p&gt;I wanted to share a few details about what challenge I encountered and how I solved it.&lt;/p&gt;
&lt;h2&gt;The problem&lt;/h2&gt;
&lt;p&gt;Like I mentioned in &lt;a href=&quot;https://fromscratchcode.com/blog/how-i-added-support-for-nested-functions-in-python-bytecode&quot;&gt;the previous post&lt;/a&gt;, bytecode is a lower-level representation of your code created for efficient evaluation. One of the optimizations is that variable names are converted into indices during bytecode compilation, which supports faster lookup at runtime when the VM pumps through your bytecode.&lt;/p&gt;
&lt;p&gt;At one point while implementing this myself, I was debugging a sequence that felt like this. (I’m using the wishy-washy “felt like” here because I have no idea what the actual bytecode looked like.)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;LOAD_GLOBAL 0
LOAD_FAST 0
LOAD_CONST 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Okay, so we’re loading the first global, the first local, and the first constant. Given the ongoing evolution of my VM design and implementation, the data structures I’m using to communicate the mappings of these indices to their symbol names and/or values between the compiler and VM have been in a steady flux.&lt;/p&gt;
&lt;p&gt;I’d sit down to implement the execution of a given opcode in the VM, see that I was handed the value &lt;code&gt;0&lt;/code&gt;, and I’d think, “What do they want me to do with this?!” (“they” in the case being myself from 5 minutes prior hacking on the compiler stage.)&lt;/p&gt;
&lt;p&gt;After interpreting a &lt;code&gt;0&lt;/code&gt; wrong for the forth or fifth time, I decided THERE HAS TO BE A BETTER WAY. I asked myself, “Is there a way to get the compiler to tell me when I’m using a &lt;code&gt;0&lt;/code&gt; incorrectly?” In addition to compile-time checking, with &lt;code&gt;rust-analyzer&lt;/code&gt; configured in my Neovim, I should get a type error as soon as I made the mistake.&lt;/p&gt;
&lt;p&gt;Could I add types to my integers?!&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://docs.python.org/3/library/dis.html&quot;&gt;CPython bytecode docs&lt;/a&gt; even hint at these distinctions, but incorporating them into my own implementation wasn’t immediately clear until I experienced the pain firsthand. For my three opcodes above, the respective documentation is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;LOAD_GLOBAL(namei)
Loads the global named co_names[namei&gt;&gt;1] onto the stack.

LOAD_FAST(var_num)
Pushes a reference to the local co_varnames[var_num] onto the stack.

LOAD_CONST(consti)
Pushes co_consts[consti] onto the stack.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;co_names&lt;/code&gt;, &lt;code&gt;co_varnames&lt;/code&gt;, and &lt;code&gt;co_consts&lt;/code&gt; are three fields on a CPython code object, which is an immutable piece of compiled bytecode. In my interpreter, I’m using &lt;code&gt;names&lt;/code&gt; and &lt;code&gt;varnames&lt;/code&gt; to show my originality.&lt;/p&gt;
&lt;p&gt;(Note to self: I treat constants separately at the moments, but I should probably unify it as I solidify my understanding of code objects.)&lt;/p&gt;
&lt;p&gt;(Another note to self: how curious that they are shifting &lt;code&gt;namei&lt;/code&gt; to the right during &lt;code&gt;LOAD_GLOBAL&lt;/code&gt;. I hope to one day know why.)&lt;/p&gt;
&lt;h2&gt;The solution&lt;/h2&gt;
&lt;p&gt;I added typed integers to keep myself sane. They offered an additional benefit of providing me an opportunity to gain more experience with generics in Rust!&lt;/p&gt;
&lt;p&gt;Here is a subset of my &lt;code&gt;Opcode&lt;/code&gt; implementation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;pub enum Opcode {
    /// Push the value found at the specified index in the constant pool onto the stack.
    LoadConst(ConstantIndex),
    /// Read the local variable indicated by the specified index and push the value onto the stack.
    LoadFast(LocalIndex),
    /// Read the global variable indicated by the specified index and push the value onto the stack.
    LoadGlobal(NonlocalIndex),
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My hope here is these new types, &lt;code&gt;ConstantIndex&lt;/code&gt;, &lt;code&gt;LocalIndex&lt;/code&gt;, and &lt;code&gt;NonlocalIndex&lt;/code&gt;, would semantically illustrate the behavior of each opcode while providing type safety.&lt;/p&gt;
&lt;p&gt;Generics entered the scene next as I was hoping to implement this with minimal code reuse.&lt;/p&gt;
&lt;p&gt;Here is the next layer of the onion.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;pub type ConstantIndex = Index&amp;#x3C;ConstantMarker&gt;;
pub type LocalIndex = Index&amp;#x3C;LocalMarker&gt;;
pub type NonlocalIndex = Index&amp;#x3C;NonlocalMarker&gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’re beginning to see some code reuse–awesome!&lt;/p&gt;
&lt;p&gt;What are these marker types? They are truly that: a type which is literally just a marker with no other data. These empty types are enforced at compile-time and do nothing at runtime.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;pub struct ConstantMarker;
pub struct LocalMarker;
pub struct NonlocalMarker;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Rust, &lt;code&gt;PhantomData&amp;#x3C;T&gt;&lt;/code&gt; from &lt;code&gt;std::marker&lt;/code&gt; allows you to include a type in a struct purely for compile-time checks without affecting runtime performance—exactly the type safety for which we’re seeking! I’m using &lt;code&gt;usize&lt;/code&gt; for the value because my use-case was indices, which should never be negative.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;/// An unsigned integer wrapper which provides type safety. This is particularly useful when
/// dealing with indices used across the bytecode compiler and the VM as common integer values such
/// as 0, 1, etc, can be interpreted many different ways.
#[derive(Copy, Clone, PartialEq, Hash, Eq)]
pub struct Index&amp;#x3C;T&gt; {
    value: usize,
    _marker: PhantomData&amp;#x3C;T&gt;,
}

impl&amp;#x3C;T&gt; Index&amp;#x3C;T&gt; {
    pub fn new(value: usize) -&gt; Self {
        Self {
            value,
            _marker: PhantomData,
        }
    }
}

impl&amp;#x3C;T&gt; Deref for Index&amp;#x3C;T&gt; {
    type Target = usize;

    fn deref(&amp;#x26;self) -&gt; &amp;#x26;Self::Target {
        &amp;#x26;self.value
    }
}

impl&amp;#x3C;T&gt; Display for Index&amp;#x3C;T&gt; {
    fn fmt(&amp;#x26;self, f: &amp;#x26;mut Formatter) -&gt; Result&amp;#x3C;(), Error&gt; {
        write!(f, &quot;{}&quot;, self.value)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last piece of magic is &lt;code&gt;impl&amp;#x3C;T&gt; Deref for Index&amp;#x3C;T&gt;&lt;/code&gt;, which allows us to treat instances of our new type as integers when dereferenced.&lt;/p&gt;
&lt;p&gt;As a result, a piece of code like this would pass with flying colors.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;#[test]
fn test_dereference() {
    let index: LocalIndex = Index::new(4);
    assert_eq!(*index, 4)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By this point, we are free to use these in the bytecode compiler! Most places in the code don’t require specifying the generic because it will be inferred by the function signature, like in this example.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;fn get_or_set_local_index(&amp;#x26;mut self, name: &amp;#x26;str) -&gt; LocalIndex {
    if let Some(index) = self.get_local_index(name) {
        index
    } else {
        let code = self.ensure_code_object_mut();
        let new_index = code.varnames.len();
        code.varnames.push(name.to_string());
        Index::new(new_index)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We initialize a new index with &lt;code&gt;Index::new(new_index)&lt;/code&gt;, which the compiler knows to treat as an &lt;code&gt;Index&amp;#x3C;LocalMarker&gt;&lt;/code&gt;, which we have aliased to &lt;code&gt;LocalIndex&lt;/code&gt; to allow us to be blissfully ignorant VM developers.&lt;/p&gt;
&lt;h2&gt;The end&lt;/h2&gt;
&lt;p&gt;This is a small but powerful example of how I’m falling head-over-heels for Rust’s type system. I love writing expressive code to implement core features from scratch in a way which manages complexity and makes people smile.&lt;/p&gt;
&lt;p&gt;My mentor at my first big-boy job was a wizard at this and I credit him for showing me what is possible: spending more time thinking about what you’re trying to build rather than being bogged down by how you are trying to build it.&lt;/p&gt;
&lt;p&gt;Have you used Rust’s type system in any fun and creative ways? Or done something similar in another language? I’d love to hear your thoughts in the comments. Be well!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How I added support for nested functions in Python bytecode]]></title><link>https://fromscratchcode.com/blog/how-i-added-support-for-nested-functions-in-python-bytecode</link><guid isPermaLink="true">https://fromscratchcode.com/blog/how-i-added-support-for-nested-functions-in-python-bytecode</guid><pubDate>Mon, 30 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I wanted to share some &lt;strong&gt;pretty cool stuff&lt;/strong&gt; I’ve been learning about &lt;strong&gt;Python bytecode&lt;/strong&gt; with you, including how I added support for nested functions, but my guy at the printing press said I needed to keep it under 500 words.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;It’s a holiday week&lt;/em&gt;, he shrugged. &lt;em&gt;What do you expect me to do?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Excluding code snippets&lt;/em&gt;, I bargained.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Fine&lt;/em&gt;, he ceded.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Do you know why we use bytecode in the first place?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I just operate the printing press, I trust you though.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Fair enough. Let’s begin.&lt;/p&gt;
&lt;h2&gt;Why we use bytecode in the first place&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://fromscratchcode.com/memphis/&quot;&gt;Memphis&lt;/a&gt;, my Python interpreter written in Rust, has two execution engines. Neither can run all code but both can run some code.&lt;/p&gt;
&lt;p&gt;My &lt;strong&gt;treewalk interpreter&lt;/strong&gt; is what you would build if you didn’t know what you were doing. 🙋‍♂️ You tokenize the input Python code, generate an abstract syntax tree (AST), and then &lt;strong&gt;walk the tree&lt;/strong&gt; and evaluate each node. Expressions return values and statements modify the symbol table, which is implemented as a series of scopes which respect Python scoping rules. Just remember the easy pneumonic LEGB: local, enclosing, global, builtin.&lt;/p&gt;
&lt;p&gt;My &lt;strong&gt;bytecode VM&lt;/strong&gt; is what you would build if you didn’t know what you were doing but wanted to act like you did. Also 🙋‍♂️. For this engine, the tokens and AST work the same, but rather than walking we take off sprinting. We &lt;strong&gt;compile the AST into an intermediate representation (IR) hereafter known as bytecode&lt;/strong&gt;. We then create a stack-based virtual machine (VM), which conceptually acts like a CPU, executing bytecode instructions in sequence, but it’s implemented entirely in software.&lt;/p&gt;
&lt;p&gt;(For a complete guide of both approaches without the ramblings, &lt;a href=&quot;https://craftinginterpreters.com/&quot;&gt;Crafting Interpreters&lt;/a&gt; is &lt;strong&gt;excellent&lt;/strong&gt;.)&lt;/p&gt;
&lt;p&gt;Why do we do this in the first place? Just remember the two Ps: portability and performance. Remember how in the early 2000s nobody would shut up about how Java bytecode was portable? &lt;em&gt;All you need is a JVM and you can run a Java program compiled on any machine!&lt;/em&gt; Python chose not to go with this approach for both technical and marketing reasons, but in theory the same principles apply. (In practice, the compilation steps are different and I regret opening this can of worms.)&lt;/p&gt;
&lt;p&gt;Performance is the biggie though. Rather than traversing an AST multiple times during the lifetime of a program, the compiled IR is a more efficient representation. We see improved performance from avoiding the overhead of repeatedly traversing an AST, and its flat structure often results in better branch prediction and cache locality at runtime.&lt;/p&gt;
&lt;p&gt;(I don’t blame you for not thinking about caching if you don’t have a background in computer architecture—heck, I began my career in that industry and I think about caching far less than I think about how to avoid writing the same line of code twice. So just trust me on the performance piece. That’s my leadership style: blind trust.)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hey buddy, that’s 500 words. We need to load up the frame and let ‘er rip.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Already?! You excluded code snippets?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;There are no code snippets, my man.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Okay okay. Just 500 more. I promise.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Context matters for Python variables&lt;/h2&gt;
&lt;p&gt;I got kinda far before tabling my bytecode VM implementation about a year ago: I could define Python functions and classes and call those functions and instantiate those classes. I clamped down this behavior with some tests. But I knew my implementation was messy and that I’d need to revisit the fundamentals before adding more fun stuff. Now it’s Christmas week and I want to add fun stuff.&lt;/p&gt;
&lt;p&gt;Consider this snippet for calling a function, keeping an eye on the &lt;code&gt;TODO&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;fn compile_function_call(
    &amp;#x26;mut self,
    name: &amp;#x26;str,
    args: &amp;#x26;ParsedArguments)
) -&gt; Result&amp;#x3C;Bytecode, CompileError&gt; {
    let mut opcodes = vec![];

    // We push the args onto the stack in reverse call order so that we will pop
    // them off in call order.
    for arg in args.args.iter().rev() {
        opcodes.extend(self.compile_expr(arg)?);
    }

    let (_, index) = self.get_local_index(name);

    // TODO how does this know if it is a global or local index? this may not be the right
    // approach for calling a function
    opcodes.push(Opcode::Call(index));

    Ok(opcodes)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Are you done considering? We load the function arguments onto the stack and “call the function”. In bytecode, all names are converted into indices (because index access is faster during the VM runtime), but we don’t really have a way to know whether we are dealing with a local index or a global index here.&lt;/p&gt;
&lt;p&gt;Now consider the improved version.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;fn compile_function_call(
    &amp;#x26;mut self,
    name: &amp;#x26;str,
    args: &amp;#x26;ParsedArguments)
) -&gt; Result&amp;#x3C;Bytecode, CompileError&gt; {
    let mut opcodes = vec![self.compile_load(name)];

    // We push the args onto the stack in reverse call order so that we will pop
    // them off in call order.
    for arg in args.args.iter().rev() {
        opcodes.extend(self.compile_expr(arg)?);
    }

    let argc = opcodes.len() - 1;
    opcodes.push(Opcode::Call(argc));

    Ok(opcodes)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Thank you for considering that code.&lt;/p&gt;
&lt;p&gt;We now supported nested function calls! What changed?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;Call&lt;/code&gt; opcode now takes a number of positional arguments, rather than an index to the function. This instructs the VM how many arguments to pop off the stack before calling the function.&lt;/li&gt;
&lt;li&gt;After popping the arguments off the stack, the &lt;strong&gt;function itself will be left on the stack and &lt;code&gt;compile_load&lt;/code&gt; has already handled local versus global scope&lt;/strong&gt; for us.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;LOAD_GLOBAL versus LOAD_FAST&lt;/h2&gt;
&lt;p&gt;Let’s take a look at what &lt;code&gt;compile_load&lt;/code&gt; is doing.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;fn compile_load(&amp;#x26;mut self, name: &amp;#x26;str) -&gt; Opcode {
    match self.ensure_context() {
        Context::Global =&gt; Opcode::LoadGlobal(self.get_or_set_nonlocal_index(name)),
        Context::Local =&gt; {
            // Check locals first
            if let Some(index) = self.get_local_index(name) {
                return Opcode::LoadFast(index);
            }

            // If not found locally, fall back to globals
            Opcode::LoadGlobal(self.get_or_set_nonlocal_index(name))
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are several key principles in action here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We match based on the current context. Adhering to Python semantics, we can consider &lt;code&gt;Context::Global&lt;/code&gt; to be at the top level of any module (not just your script’s entrypoint), and &lt;code&gt;Context::Local&lt;/code&gt; is inside any block (i.e. function definition or class definition).&lt;/li&gt;
&lt;li&gt;We now differentiate between a local index and a non-local index. (Because I was going crazy trying to decipher what the index &lt;code&gt;0&lt;/code&gt; referred to in different places, I introduced typed-integers. &lt;code&gt;LocalIndex&lt;/code&gt; and &lt;code&gt;NonlocalIndex&lt;/code&gt; provide type-safety for otherwise untyped unsigned integers. I may write about this in the future!)&lt;/li&gt;
&lt;li&gt;We can tell at bytecode-compilation time whether a local variable exists with a given name, and if it does not, at runtime we will search for a global variable. This speaks to the dynamism built into Python: as long as a variable is present in the global scope of that module by the time a function executes, its value can be resolved at runtime. However, this dynamic resolution comes with a performance hit. While local variable lookups are optimized to use stack indices, global lookups require searching the global namespace dictionary, which is slower. This dictionary is a mapping of names to objects, which themselves may live on the heap. Who knew that the saying “Think globally, act locally.” was actually referring to Python scopes?&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;What’s in a varname?&lt;/h2&gt;
&lt;p&gt;The last thing I’ll leave you with today is a peek into how these variables names are mapped. In the code snippet below, you’ll notice that local indices are found in &lt;code&gt;code.varnames&lt;/code&gt; and nonlocal indices are found in &lt;code&gt;code.names&lt;/code&gt;. Both live on a &lt;code&gt;CodeObject&lt;/code&gt;, which contains the metadata for a block of Python bytecode, including its variable and name mappings.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;fn get_or_set_local_index(&amp;#x26;mut self, name: &amp;#x26;str) -&gt; LocalIndex {
    if let Some(index) = self.get_local_index(name) {
        index
    } else {
        let code = self.ensure_code_object_mut();
        let new_index = code.varnames.len();
        code.varnames.push(name.to_string());
        Index::new(new_index)
    }
}

fn get_local_index(&amp;#x26;self, name: &amp;#x26;str) -&gt; Option&amp;#x3C;LocalIndex&gt; {
    let code = self.ensure_code_object();
    find_index(&amp;#x26;code.varnames, name).map(Index::new)
}

fn get_or_set_nonlocal_index(&amp;#x26;mut self, name: &amp;#x26;str) -&gt; NonlocalIndex {
    let code = self.ensure_code_object_mut();
    if let Some(index) = find_index(&amp;#x26;code.names, name) {
        Index::new(index)
    } else {
        let new_index = code.names.len();
        code.names.push(name.to_string());
        Index::new(new_index)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The difference between &lt;code&gt;varnames&lt;/code&gt; and &lt;code&gt;names&lt;/code&gt; tormented me for weeks (CPython calls these &lt;code&gt;co_varnames&lt;/code&gt; and &lt;code&gt;co_names&lt;/code&gt;), but it’s actually fairly straightforward. &lt;code&gt;varnames&lt;/code&gt; holds the variable names for all local variables in a given scope, and &lt;code&gt;names&lt;/code&gt; does the same for all nonlocal.&lt;/p&gt;
&lt;p&gt;Once we properly track this, everything else &lt;strong&gt;just works&lt;/strong&gt;. At runtime, the VM sees a &lt;code&gt;LOAD_GLOBAL&lt;/code&gt; or a &lt;code&gt;LOAD_FAST&lt;/code&gt; and knows to look in the global namespace dictionary or the local stack, respectively.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Buddy! Mr Gutenberg is on the phone and says we can hold the presses no longer.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Okay! Fine! I get it! Let’s ship it.&lt;/em&gt; 🚢&lt;/p&gt;
&lt;h2&gt;What’s next for Memphis?&lt;/h2&gt;
&lt;p&gt;Shh! The printing press man doesn’t know I’m writing a conclusion, so I will be brief.&lt;/p&gt;
&lt;p&gt;With variable scoping and function calls in a solid place, I’m gradually turning my attention to features like stack traces and async support. If you’ve enjoyed this dive into bytecode or have questions about building your own interpreter, I’d love to hear from you—drop a comment!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Improving memory efficiency in a working interpreter]]></title><link>https://fromscratchcode.com/blog/improving-memory-efficiency-in-a-working-interpreter</link><guid isPermaLink="true">https://fromscratchcode.com/blog/improving-memory-efficiency-in-a-working-interpreter</guid><pubDate>Mon, 16 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Lifetimes are a fascinating feature of Rust and the human experience. This is a technical blog, so let’s focus on the former. I was admittedly a slow adopter for leveraging lifetimes to safely borrow data in Rust. In the treewalk implementation of &lt;a href=&quot;https://fromscratchcode.com/memphis/&quot;&gt;Memphis&lt;/a&gt;, my Python interpreter written in Rust, I hardly leverage lifetimes (by cloning incessantly) and I repeatedly elude the borrow checker (by using interior mutability, also incessantly) whenever possible.&lt;/p&gt;
&lt;p&gt;My fellow Rustaceans, I am here to today to tell you this ends now. Read my lips……no more shortcuts.&lt;/p&gt;
&lt;p&gt;Okay okay, let’s be real. What is a shortcut versus what’s the right way is a matter of priorities and perspective. We’ve all made mistakes, and I’m here to take accountability for mine.&lt;/p&gt;
&lt;p&gt;I began writing an interpreter six weeks after I first installed &lt;code&gt;rustc&lt;/code&gt; because I have no chill. With that haranguing and posturing out of the way, let’s begin today’s lecture on how we can use lifetimes as our lifeline to improve my bloated interpreter codebase.&lt;/p&gt;
&lt;h2&gt;Identifying and avoiding cloned data&lt;/h2&gt;
&lt;p&gt;A &lt;strong&gt;Rust lifetime is a mechanism which provides a compile-time guarantee that any references do not outlive the objects to which they refer&lt;/strong&gt;. They allow us to avoid the “dangling pointer” problem from C and C++.&lt;/p&gt;
&lt;p&gt;This is assuming you leverage them at all! Cloning is a convenient workaround when you want to avoid the complexities associated with managing lifetimes, though the downside is increased memory usage and a slight delay related to each time data is copied.&lt;/p&gt;
&lt;p&gt;Using lifetimes also forces you to think more idiomatically about owners and borrowing in Rust, which I was eager to do.&lt;/p&gt;
&lt;p&gt;I chose my first candidate as the tokens from a Python input file. My original implementation, which relied heavily on ChatGPT guidance while I was sitting on Amtrak, used this flow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;we pass our Python text to a Builder&lt;/li&gt;
&lt;li&gt;the Builder creates a Lexer, which tokenizes the input stream&lt;/li&gt;
&lt;li&gt;the Builder then creates a Parser, which clones the token stream to hold its own copy&lt;/li&gt;
&lt;li&gt;the Builder is used to create an Interpreter, which repeatedly asks the Parser for its next parsed statement and evaluates it until we reach the end of the token stream&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The convenient aspect of cloning the token stream is that the Lexer was free to be dropped after step 3. By updating my architecture to have the Lexer own the tokens and the Parser just borrow them, the Lexer would now be required to stay alive much longer. Rust lifetimes would guarantee this for us: as long as the Parser existed holding a reference to a borrowed token, the compiler would guarantee that the Lexer which own those tokens still existed, ensuring a valid reference.&lt;/p&gt;
&lt;p&gt;Like all code always, this ended up being a bigger change than I expected. Let’s see why!&lt;/p&gt;
&lt;h2&gt;The new parser&lt;/h2&gt;
&lt;p&gt;Before updating the Parser to borrow the tokens from the Lexer, it looked like this. The two fields of interest for today’s discussion are &lt;code&gt;tokens&lt;/code&gt; and &lt;code&gt;current_token&lt;/code&gt;. We have no idea how large the &lt;code&gt;Vec&amp;#x3C;Token&gt;&lt;/code&gt; is, but it is distinctly ours (i.e. we are not borrowing it).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;pub struct Parser {
    state: Container&amp;#x3C;State&gt;,
    tokens: Vec&amp;#x3C;Token&gt;,
    current_token: Token,
    position: usize,
    line_number: usize,
    delimiter_depth: usize,
}

impl Parser {
    pub fn new(tokens: Vec&amp;#x3C;Token&gt;, state: Container&amp;#x3C;State&gt;) -&gt; Self {
        let current_token = tokens.first().cloned().unwrap_or(Token::Eof);
        Parser {
            state,
            tokens,
            current_token,
            position: 0,
            line_number: 1,
            delimiter_depth: 0,
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After borrowing the tokens from the Lexer, it looks fairly similar, but now we see a LIFETIME! By connecting &lt;code&gt;tokens&lt;/code&gt; to the lifetime &lt;code&gt;&apos;a&lt;/code&gt;, the Rust compiler will not allow the owner of the tokens (which is our Lexer) and the tokens themselves to be dropped while our &lt;code&gt;Parser&lt;/code&gt; still references them. This feels safe and fancy!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;static EOF: Token = Token::Eof;

/// A recursive-descent parser which attempts to encode the full Python grammar.
pub struct Parser&amp;#x3C;&apos;a&gt; {
    state: Container&amp;#x3C;State&gt;,
    tokens: &amp;#x26;&apos;a [Token],
    current_token: &amp;#x26;&apos;a Token,
    position: usize,
    line_number: usize,
    delimiter_depth: usize,
}

impl&amp;#x3C;&apos;a&gt; Parser&amp;#x3C;&apos;a&gt; {
    pub fn new(tokens: &amp;#x26;&apos;a [Token], state: Container&amp;#x3C;State&gt;) -&gt; Self {
        let current_token = tokens.first().unwrap_or(&amp;#x26;EOF);
        Parser {
            state,
            tokens,
            current_token,
            position: 0,
            line_number: 1,
            delimiter_depth: 0,
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another small difference you may notice is this line:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;static EOF: Token = Token::Eof;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a small optimization that I began considering once my Parser was moving in the direction of “memory-efficient.” Rather than instantiating a new &lt;code&gt;Token::Eof&lt;/code&gt; each time the Parser needs to check if it is at the end of the text stream, the new model allowed me to instantiate only a single token and reference &lt;code&gt;&amp;#x26;EOF&lt;/code&gt; repeatedly.&lt;/p&gt;
&lt;p&gt;Again, this is a small optimization, but it speaks to the larger mindset of each piece of data existing only once in memory and every consumer just referencing it when needed, which Rust both encourages you to do and snugly holds your hand along the way.&lt;/p&gt;
&lt;p&gt;Speaking of optimization, I really should have benchmarked the memory usage before and after. Since I did not, I have nothing more to say on the matter.&lt;/p&gt;
&lt;p&gt;As I alluded to earlier, tying the lifetime of my Lexer and Parser together a large impact on my Builder pattern. Let’s see what that looks like!&lt;/p&gt;
&lt;h2&gt;The new Builder: MemphisContext&lt;/h2&gt;
&lt;p&gt;In the flow I described above, remember how I mentioned that the Lexer could be dropped as soon as the Parser created its own copy of the tokens? This had unintentionally influenced the design of my Builder, which was intended to be the component which supports orchestrating Lexer, Parser, and Interpreter interactions, whether you begin with a Python text stream or a path to a Python file.&lt;/p&gt;
&lt;p&gt;As you can see below, there are a few other non-ideal aspects to this design:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;needing to call a dangerous &lt;code&gt;downcast&lt;/code&gt; method to get the &lt;code&gt;Interpreter&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;why did I think it was okay to return a &lt;code&gt;Parser&lt;/code&gt; to &lt;em&gt;every&lt;/em&gt; unit test just to then pass it right back into &lt;code&gt;interpreter.run(&amp;#x26;mut parser)&lt;/code&gt;?!&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;fn downcast&amp;#x3C;T: InterpreterEntrypoint + &apos;static&gt;(input: T) -&gt; Interpreter {
    let any_ref: &amp;#x26;dyn Any = &amp;#x26;input as &amp;#x26;dyn Any;
    any_ref.downcast_ref::&amp;#x3C;Interpreter&gt;().unwrap().clone()
}

fn init(text: &amp;#x26;str) -&gt; (Parser, Interpreter) {
    let (parser, interpreter) = Builder::new().text(text).build();

    (parser, downcast(interpreter))
}


#[test]
fn function_definition() {
     let input = r#&quot;
def add(x, y):
    return x + y

a = add(2, 3)
&quot;#;
    let (mut parser, mut interpreter) = init(input);

    match interpreter.run(&amp;#x26;mut parser) {
        Err(e) =&gt; panic!(&quot;Interpreter error: {:?}&quot;, e),
        Ok(_) =&gt; {
            assert_eq!(
                interpreter.state.read(&quot;a&quot;),
                Some(ExprResult::Integer(5.store()))
            );
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Below is the new &lt;code&gt;MemphisContext&lt;/code&gt; interface. This mechanism manages the Lexer lifetime internally (to keep our references alive long enough to keep our Parser happy!) and only exposes what is needed to run this test.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;fn init(text: &amp;#x26;str) -&gt; MemphisContext {
    MemphisContext::from_text(text)
}

#[test]
fn function_definition() {
    let input = r#&quot;
def add(x, y):
    return x + y

a = add(2, 3)
&quot;#;
    let mut context = init(input);

    match context.run_and_return_interpreter() {
        Err(e) =&gt; panic!(&quot;Interpreter error: {:?}&quot;, e),
        Ok(interpreter) =&gt; {
            assert_eq!(
                interpreter.state.read(&quot;a&quot;),
                Some(ExprResult::Integer(5.store()))
            );
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;context.run_and_return_interpreter()&lt;/code&gt; is still a bit clunky and speaks to another design problem I may tackle down the road: when you run the interpreter, do you want to return only the final return value or something which lets you access arbitrary values from the symbol table? This method opts for the latter approach. I actually think there’s a case to do both, and will keep tweaking my API to allow for this as we go.&lt;/p&gt;
&lt;p&gt;Incidentally, this change improved my ability to evaluate an arbitrary piece of Python code. If you’ll recall from &lt;a href=&quot;https://fromscratchcode.com/blog/building-for-webassembly&quot;&gt;my WebAssembly saga&lt;/a&gt;, I had to rely on my crosscheck &lt;code&gt;TreewalkAdapter&lt;/code&gt; to do that at the time. Now, our Wasm interface is much cleaner.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;#[cfg(feature = &quot;wasm&quot;)]
mod wasm {
    use console_error_panic_hook::set_once;
    use wasm_bindgen::prelude::wasm_bindgen;

    use super::init::MemphisContext;

    #[wasm_bindgen]
    pub fn evaluate(code: String) -&gt; String {
        // Set the panic hook for better error messages in the browser console
        set_once();

        let mut context = MemphisContext::from_text(&amp;#x26;code);
        let result = context
            .evaluate_oneshot()
            .expect(&quot;Failed to evaluate expression.&quot;);
        format!(&quot;{}&quot;, result)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The interface &lt;code&gt;context.evaluate_oneshot()&lt;/code&gt; returns the expression result rather than a full symbol table. I wonder if there’s a better way to ensure any of the “oneshot” methods can only operate on a context once, ensuring that no consumers use them in a stateful context. I’ll keep simmering on that!&lt;/p&gt;
&lt;h2&gt;Was this worth it?&lt;/h2&gt;
&lt;p&gt;Memphis is first-and-foremost a learning exercise, so this was absolutely worth it!&lt;/p&gt;
&lt;p&gt;In addition to sharing the tokens between the Lexer and the Parser, I created an interface to evaluate Python code with significantly less boilerplate. While sharing data introduced additional complexity, these changes bring clear benefits: reduced memory usage, improved safety guarantees through stricter lifetime management, and a streamlined API that’s easier to maintain and extend.&lt;/p&gt;
&lt;p&gt;I’m choosing to believe this was the right approach, mostly to maintain my self-esteem. Ultimately, I aim to write code that clearly reflects the principles of software and computer engineering. We can now open the Memphis source, point to the single owner of the tokens, and sleep soundly at night!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[An interpreter inside an interpreter]]></title><link>https://fromscratchcode.com/blog/an-interpreter-inside-an-interpreter</link><guid isPermaLink="true">https://fromscratchcode.com/blog/an-interpreter-inside-an-interpreter</guid><pubDate>Mon, 25 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A few months into development, I decided my north star for &lt;a href=&quot;https://fromscratchcode.com/memphis/&quot;&gt;Memphis&lt;/a&gt; would be to run a Flask server entirely within my interpreter. I had no idea how much work this would entail, only that it sounded cool and would probably teach me a lot along the way. If I were making this goal today, I may pick FastAPI or nothing at all because that was silly of me.&lt;/p&gt;
&lt;h2&gt;Python stdlib&lt;/h2&gt;
&lt;p&gt;A big decision I encountered was how to deal with the Python standard lib. As you are likely familiar, the standard lib of a language is not technically part of the language definition or runtime. It is included with releases in order to make the language and runtime more useful. Imagine Python without threading or async support. You would still be able to evaluate expressions and instantiate classes, but most production-ready programs need some sort of concurrency support.&lt;/p&gt;
&lt;p&gt;One option would be to rewrite the entire standard lib myself. I’m building an interpreter, aren’t I? I believe this is the approach taken by RustPython, which is an admirable path. I figured I had enough on my plate getting the runtime to work, was looking for any and all corners to cut, and decided against this.&lt;/p&gt;
&lt;p&gt;The Python standard lib consists of two main parts: the parts implemented in Python and the parts implemented in C. Conveniently enough, I had my own Python interpreter. Could I just interpret the Python source file from the host machine to satisfy the former? Yes, I could. I’d need to support every syntax and feature they used, but after that, it would Just Work.&lt;/p&gt;
&lt;p&gt;The C part is where it gets interesting. Way back yonder in 2023, I made a decision to embed a Python interpreter inside my Python interpreter without fully understanding what that meant. Now it was time to wrap my head around this and decide if I wanted to stay with this approach or chose another path.&lt;/p&gt;
&lt;p&gt;The interop shop for Rust and Python is &lt;a href=&quot;https://github.com/PyO3/pyo3&quot;&gt;Pyo3&lt;/a&gt;. As the only game in town, Pyo3 uses the Foreign Function Interface (FFI) to allow your Rust code to make calls into the CPython binary. This works by agreeing on the Application Binary Interface (ABI), a concept I used during my career at AMD. Core software ftw!&lt;/p&gt;
&lt;h2&gt;Importing modules&lt;/h2&gt;
&lt;p&gt;My initial use-case was to run &lt;code&gt;import sys&lt;/code&gt; and have it give me an object on which I could perform a member access operation. I’m getting into interpreter-speak here, but this is the type of REPL session I’m talking about.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;Python 3.12.5 (main, Aug  6 2024, 19:08:49) [Clang 15.0.0 (clang-1500.3.9.4)]
Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.
&gt;&gt;&gt; import sys
&gt;&gt;&gt; sys
&amp;#x3C;module &apos;sys&apos; (built-in)&gt;
&gt;&gt;&gt; type(sys.modules)
&amp;#x3C;class &apos;dict&apos;&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Getting this functionality using Pyo3 was straightforward.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;pub struct CPythonModule(PyObject);

impl CPythonModule {
    pub fn new(name: &amp;#x26;str) -&gt; Self {
        pyo3::prepare_freethreaded_python();
        let pymodule = Python::with_gil(|py|
            PyModule::import(py, name).expect(&quot;Failed to import module&quot;).into()
        );

        Self(pymodule)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we can use this to drive a similar REPL session in Memphis, assuming you remember the cocktail of features flags to get this to run.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;memphis 0.1.0 REPL (Type &apos;exit()&apos; to quit)
&gt;&gt;&gt; import sys
&gt;&gt;&gt; sys
&amp;#x3C;module &apos;sys&apos; (built-in)&gt;
&gt;&gt;&gt; type(sys.modules)
&amp;#x3C;class &apos;dict&apos; (built-in)&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you’re asking yourself, couldn’t you just use this approach to import the entire standard lib (including the parts written in Python and C) and make your entire life, liberty, and the pursuit of happiness, easier, the answer is yes. That would be a valid approach! However, that would make my interpreter more of a shell around CPython than I would like. This is a learning exercise so I’m all for arbitrary decisions. For the purists out there who say loading any piece of CPython inside Memphis makes Memphis not a real interpreter, I would just say: please show me your interpreter.&lt;/p&gt;
&lt;p&gt;I conducted a quick test with htop by running &lt;code&gt;import sys&lt;/code&gt; inside a REPL session using both Memphis and CPython. On Memphis, because this load the CPython libraries into memory, it increased the RAM usage (Resident Set Size in htop) by about 5MB. For comparison, the Memphis REPL &lt;em&gt;after&lt;/em&gt; loading the &lt;code&gt;sys&lt;/code&gt; module uses about 9MB of RAM, while the Python REPL &lt;em&gt;before and after&lt;/em&gt; loading the &lt;code&gt;sys&lt;/code&gt; module uses about the same. I’m sure this isn’t an apples-to-apples comparison, but it at least told me that Memphis wasn’t gonna slowly choke my computer to death.&lt;/p&gt;
&lt;h2&gt;Converting objects and getting existential&lt;/h2&gt;
&lt;p&gt;The next complexity with this setup involves converting my Memphis object representation into CPython representations and vice versa. This is a work-in-progress and my primary directive was, initially, “do not fail” and, more recently, “show warnings when you do a lossy conversion.”&lt;/p&gt;
&lt;p&gt;Here is my conversion from a &lt;code&gt;PyObject&lt;/code&gt;, which is the object representation on the Pyo3 side, into an &lt;code&gt;ExprResult&lt;/code&gt;, my Memphis representation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;pub mod utils {
    pub fn from_pyobject(py: Python, py_obj: &amp;#x26;PyAny) -&gt; ExprResult {
        if let Ok(value) = py_obj.extract::&amp;#x3C;i64&gt;() {
            ExprResult::Integer(Container::new(value))
        } else if let Ok(value) = py_obj.extract::&amp;#x3C;f64&gt;() {
            ExprResult::FloatingPoint(value)
        } else if let Ok(value) = py_obj.extract::&amp;#x3C;&amp;#x26;str&gt;() {
            ExprResult::String(Str::new(value.to_string()))
        } else if let Ok(py_tuple) = py_obj.extract::&amp;#x3C;&amp;#x26;PyTuple&gt;() {
            let elements = py_tuple
                .iter()
                .map(|item| from_pyobject(py, item))
                .collect();
            ExprResult::Tuple(Container::new(Tuple::new(elements)))
        } else if let Ok(py_module) = py_obj.extract::&amp;#x3C;&amp;#x26;PyModule&gt;() {
            let mut module = Module::default();

            // Get the module&apos;s __dict__ to iterate over all attributes
            for (key, value) in py_module.dict() {
                let key_str: String =
                  key.extract().expect(&quot;Key is not a string&quot;);
                let expr_value = from_pyobject(py, value);
                module.insert(&amp;#x26;key_str, expr_value);
            }

            ExprResult::Module(Container::new(module))
        } else if let Ok(py_set) = py_obj.extract::&amp;#x3C;&amp;#x26;PySet&gt;() {
            let elements = py_set
                .iter()
                .map(|item| from_pyobject(py, item))
                .collect();
            ExprResult::Set(Container::new(Set::new(elements)))
        } else if let Ok(py_list) = py_obj.extract::&amp;#x3C;&amp;#x26;PyList&gt;() {
            let elements = py_list
                .iter()
                .map(|item| from_pyobject(py, item))
                .collect();
            ExprResult::List(Container::new(List::new(elements)))
        } else {
            // TODO think of a way to detect whether this is an object we can
            // convert or not
            // log(LogLevel::Warn, || {
            //     &quot;Potentially ambiguous CPythonObject instance.&quot;.to_string()
            // });
            ExprResult::CPythonObject(CPythonObject::new(py_obj.into_py(py)))
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here is the reverse comparison. Note that for both of these we must pass in a &lt;code&gt;Python&lt;/code&gt; object, which controls our access to the CPython GIL (global interpreter lock).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;impl ToPyObject for ExprResult {
    fn to_object(&amp;#x26;self, py: Python) -&gt; PyObject {
        match self {
            ExprResult::None =&gt; py.None(),
            ExprResult::Boolean(b) =&gt; b.to_object(py),
            ExprResult::Integer(i) =&gt; i.borrow().to_object(py),
            ExprResult::String(s) =&gt; s.as_str().to_object(py),
            ExprResult::List(l) =&gt; {
                let list = PyList::empty(py);
                for item in l.clone().into_iter() {
                    list.append(item).expect(&quot;Failed to append to PyList&quot;);
                }
                list.to_object(py)
            }
            ExprResult::Function(_) =&gt; {
                // TODO our PyCFunction implementation is a no-op, we need to find a way to pass
                // the interpreter into here.
                let callback = |_args: &amp;#x26;PyTuple, _kwargs: Option&amp;#x3C;&amp;#x26;PyDict&gt;| -&gt; PyResult&amp;#x3C;bool&gt; {
                    log(LogLevel::Warn, || {
                        &quot;Potentially lossy PyCFunction invocation.&quot;.to_string()
                    });
                    Ok(true)
                };
                // TODO use real function name
                let py_cfunc = PyCFunction::new_closure(
                    py,
                    Some(&quot;memphis_func&quot;),
                    None,
                    callback
                ).unwrap();
                py_cfunc.to_object(py)
            }
            ExprResult::Class(_) =&gt; {
                // TODO same here, our PyClass implementation does bring real fields
                Py::new(py, TestClass {}).unwrap().to_object(py)
            }
            ExprResult::Module(module) =&gt; {
                let py_module = PyModule::new(py, &amp;#x26;module.borrow().name()).unwrap();

                // Flatten all key-value pairs from scope into the module
                for (key, value) in module.borrow().dict() {
                    py_module.add(key, value.to_object(py)).unwrap();
                }

                py_module.to_object(py)
            }
            ExprResult::CPythonModule(module) =&gt; module.borrow().0.to_object(py),
            ExprResult::CPythonObject(object) =&gt; object.0.to_object(py),
            _ =&gt;
                unimplemented!(
                    &quot;Attempting to convert {} to a PyObject, but {} conversion is not implemented!&quot;,
                    self,
                    self.get_type()
                ),
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a rich area that I’d like to explore further. Here are some of the directions I’ve considered:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Convert each time an object crosses the FFI interface. (And yes, I realize that acronym expands to foreign function interface interface.) That’s roughly what I’m already doing, I would just need to own it and not feel like an imposter. This could be simple but inefficient.&lt;/li&gt;
&lt;li&gt;Keep a registry so that each object exists at most once on each side. This would be more efficient than (1), but it’d require a stable value which you could use to lookup and link up these objects.&lt;/li&gt;
&lt;li&gt;Aim for a single representation on the Rust side and use Pyo3 to proxy and lazily convert fields as needed. I believe this would still leverage the functionality of (1), but in a more efficient manner.&lt;/li&gt;
&lt;li&gt;Make the memory layout of a Memphis object match that of a &lt;code&gt;PyObject&lt;/code&gt;. Similar to how &lt;code&gt;#[repr(C)]&lt;/code&gt; already works in Rust, this would be similar to the role an ABI plays for a function call. I’m not even sure if this one is possible given the difference in what each side needs to do its evaluation, but this intrigues me.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I’m getting ahead of myself because I can barely load a C module right now, but there’s truly no end to where my curiosity could take me in this area.&lt;/p&gt;
&lt;h2&gt;The End&lt;/h2&gt;
&lt;p&gt;I continue to poke at this when I hit a new conversion failure while plodding along towards getting Flask to boot. This exercise is a good reminder that all objects (or classes, modules, etc) are a set of attributes that exist in a known format in memory. If we understand that format well enough, we should be able to do incredible things, regardless of whether it is on the Memphis or CPython side.&lt;/p&gt;
&lt;p&gt;This philosophy drives my work with From Scratch Code as well. If you are tired of being unable to get a library to work in your code, I encourage you to step back and ask: what the library is actually doing? Do you need it, or could a simpler solution work? I believe in cultivating this curiosity about software—and I’d be happy to help you incorporate this mindset into your toolbox.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Building for WebAssembly]]></title><link>https://fromscratchcode.com/blog/building-for-webassembly</link><guid isPermaLink="true">https://fromscratchcode.com/blog/building-for-webassembly</guid><pubDate>Mon, 18 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’m currently exploring two interesting topics for &lt;a href=&quot;https://fromscratchcode.com/memphis/&quot;&gt;Memphis&lt;/a&gt;, my Python interpreter in Rust: building for WebAssembly and embedding CPython. With no major milestones to report this week, I thought I’d share some in-progress thoughts. For me, Memphis is been a project for expanding my conceptual understanding through practical experiments—hopefully, this post can do the same for you as we walk through some of the design decisions I&apos;m exploring.&lt;/p&gt;
&lt;h2&gt;Python in the browser&lt;/h2&gt;
&lt;p&gt;Compiling Memphis to a WebAssembly target had been in the back of my mind for some time, and two Saturdays ago, I finally gave it a go. With a lukewarm cup of drip coffee on my coaster, I cracked my knuckles and began.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WebAssembly is a sandboxed execution environment inside modern web browsers&lt;/strong&gt; which complements the traditional JavaScript environment. The Wasm environment is closer to native code and can be used for tasks which benefit from a more performant CPU context; think number crunching or silly busy loops. I was interested in it less from a performance perspective and more because it was possible at all. One of Rust’s selling points (out of literally bajillions) is it can target Wasm. How do, one might ask? This is possible because Rust uses LLVM as its compiler backend. The Rust compiler frontend produces LLVM Intermediate Representation (IR) code and LLVM can compile this to native code for dozens of targets.&lt;/p&gt;
&lt;p&gt;That’s a pretty massive benefit and I was curious if it would Just Work for Memphis. I had given literally zero thought to running Python in the browser before, so this seemed like a perfect opportunity to test out the Wasm learning curve.&lt;/p&gt;
&lt;h2&gt;Setting Up Wasm-Pack and Building for WebAssembly&lt;/h2&gt;
&lt;p&gt;I fired up my AI assistant and asked for the launch sequence. It went &lt;code&gt;beep boop beep boop&lt;/code&gt;. Below are the steps annotated with my learnings along the way.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# wasm-pack helps compile our Rust code to WebAssembly and bundle it with JavaScript bindings we
# can call from our HTML/JavaScript page.
cargo install wasm-pack

# wasm-pack also downloads the wasm32-unknown-unknown target via rustup for us.
# If for whatever reason it does not, you can use this: rustup target add wasm32-unknown-unknown
# We must specify a feature flag because our wasm_bindgen interface is behind the wasm feature flag.
wasm-pack build --target web --out-dir wasm_ui/pkg -- --features wasm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The build succeeded on my first try! However, because we haven’t marked any functions in our Rust binary as being available to call from WebAssembly, it doesn’t do much.&lt;/p&gt;
&lt;p&gt;We can install the &lt;code&gt;wasm-bindgen&lt;/code&gt; crate to do this, which I put behind a feature flag. I added this to my &lt;code&gt;Cargo.toml&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ini&quot;&gt;[dependencies]
wasm-bindgen = { version = &quot;0.2&quot;, optional = true }

[features]
wasm = [&quot;wasm-bindgen&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here’s a small piece of code I added to my &lt;code&gt;src/lib.rs&lt;/code&gt; file, behind the &lt;code&gt;wasm&lt;/code&gt; feature flag. The &lt;code&gt;greet&lt;/code&gt; function is decorated with &lt;code&gt;#[wasm_bindgen]&lt;/code&gt; to make this symbol available in JavaScript.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;#[cfg(feature = &quot;wasm&quot;)]
mod wasm {
    use wasm_bindgen::prelude::wasm_bindgen;

    // Export a function to JavaScript
    #[wasm_bindgen]
    pub fn greet() -&gt; String {
        &quot;Hello from WebAssembly!&quot;.to_string()
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Creating a JavaScript Interface&lt;/h2&gt;
&lt;p&gt;I also asked my AI assistant for the smallest possible piece of JavaScript I could use to test my Wasm interface. When we call &lt;code&gt;init()&lt;/code&gt;, the browser loads the &lt;code&gt;.wasm&lt;/code&gt; file, performs a JIT compilation step to convert the portable WebAssembly binary into native code, and initializes memory for the WebAssembly runtime.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;#x3C;!DOCTYPE html&gt;
&amp;#x3C;html lang=&quot;en&quot;&gt;
  &amp;#x3C;head&gt;
    &amp;#x3C;meta charset=&quot;UTF-8&quot; /&gt;
    &amp;#x3C;title&gt;Wasm Test&amp;#x3C;/title&gt;
  &amp;#x3C;/head&gt;
  &amp;#x3C;body&gt;
    &amp;#x3C;script type=&quot;module&quot;&gt;
      import init, { greet } from &quot;./pkg/memphis.js&quot;

      async function run() {
        await init()
        console.log(greet())
      }

      run()
    &amp;#x3C;/script&gt;
  &amp;#x3C;/body&gt;
&amp;#x3C;/html&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Like a miracle among miracles, it Just Worked. Granted, I wasn’t running any Python code in the browser, but interfacing with my binary was a HUGE step that younger-me-who-could-barely-install-java did not want to undervalue.&lt;/p&gt;
&lt;p&gt;The next step was to give it a Python expression defined in JavaScript and have the Wasm binary crunch the numbers. As I mentioned in &lt;a href=&quot;https://fromscratchcode.com/blog/a-repl-for-fat-finger-friendly-typing&quot;&gt;my REPL post&lt;/a&gt;, every entry point in a software project is an opportunity to improve my abstractions, and it would certainly be the case again here. As I thumbed through my Memphis repo, I realized &lt;em&gt;Wow, I should really have a better interface to pass a string and evaluate it as Python.&lt;/em&gt; Like I said, I LOVE new entry points.&lt;/p&gt;
&lt;p&gt;For the time being, I would use my &lt;code&gt;crosscheck&lt;/code&gt; adapter. Crosscheck is my work-in-progress testing framework to validate the treewalk interpreter and bytecode VM produce the same behavior for a given Python input. It’s named after the thing flight attendants do.&lt;/p&gt;
&lt;p&gt;Here is my updated Rust code.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;#[cfg(feature = &quot;wasm&quot;)]
mod wasm {
    use wasm_bindgen::prelude::wasm_bindgen;

    use crosscheck::{InterpreterTest, TreewalkAdapter};

    // Export a function to JavaScript
    #[wasm_bindgen]
    pub fn greet() -&gt; String {
        &quot;Hello from WebAssembly!&quot;.to_string()
    }

    #[wasm_bindgen]
    pub fn evaluate(code: String) -&gt; String {
        let result = TreewalkAdapter.execute(&amp;#x26;code);
        format!(&quot;{}&quot;, result)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is my updated JavaScript code, which invokes the new Rust &lt;code&gt;evaluate&lt;/code&gt; function.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;#x3C;!DOCTYPE html&gt;
&amp;#x3C;html lang=&quot;en&quot;&gt;
  &amp;#x3C;head&gt;
    &amp;#x3C;meta charset=&quot;UTF-8&quot; /&gt;
    &amp;#x3C;title&gt;Wasm Test&amp;#x3C;/title&gt;
  &amp;#x3C;/head&gt;
  &amp;#x3C;body&gt;
    &amp;#x3C;script type=&quot;module&quot;&gt;
      import init, { greet, evaluate } from &quot;./pkg/memphis.js&quot;

      async function run() {
        await init()
        console.log(greet())
        const expr = &quot;[ 2 * i for i in range(5) if i % 2 == 0 ]&quot;
        console.log(expr, &quot;=&quot;, evaluate(expr))
      }

      run()
    &amp;#x3C;/script&gt;
  &amp;#x3C;/body&gt;
&amp;#x3C;/html&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Debugging WebAssembly Errors&lt;/h2&gt;
&lt;p&gt;Now when I ran it I got……… a console error. It crashed with an &lt;code&gt;unimplemented&lt;/code&gt; error.&lt;/p&gt;
&lt;p&gt;I poked around a bit and it was not clear what was causing this. You can click into the source but for a Wasm build that is just a block of assembly without references to the original Rust functions.&lt;/p&gt;
&lt;p&gt;I did some AI chatting/Googling and found two helpful approaches. One is &lt;a href=&quot;https://github.com/iamcodemaker/console_log&quot;&gt;console_log&lt;/a&gt; for use in Wasm builds, which displays log statements from your Rust code in your browser console. This helped some, but what I was really looking for was a stack trace. Enter &lt;a href=&quot;https://github.com/rustwasm/console_error_panic_hook&quot;&gt;console_error_panic_hook&lt;/a&gt;. It gave me the Rust stack trace immediately, which was CLUTCH. If you are doing your own Wasm build, stop reading this now and add this crate. I don’t even mind if you never finish reading this post. Ferris would want you to use this crate 🦀. Here’s how I added it to my Wasm interface.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;#[cfg(feature = &quot;wasm&quot;)]
mod wasm {
    use console_error_panic_hook::set_once;
    use wasm_bindgen::prelude::wasm_bindgen;

    #[wasm_bindgen]
    pub fn evaluate(code: String) -&gt; String {
        // Set the panic hook for better error messages in the browser console
        set_once();

        let result = TreewalkAdapter.execute(&amp;#x26;code);
        format!(&quot;{}&quot;, result)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My stack trace pointed me to my culprit: I was using &lt;code&gt;std::env&lt;/code&gt; to request some OS resources, which are not allowed in a Wasm runtime (that’s the sandboxed part). I put these calls behind a feature flag (they are related to how I hack-ily determine the location of the Python standard lib on the host machine) and fired up my build again. After a few small failures related to properly displaying my return types….&lt;/p&gt;
&lt;p&gt;IT WORKED. Here’s what I now see in my browser console.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;wasm_ui/:13: Hello from WebAssembly!
wasm_ui/:15: [ 2 * i for i in range(5) if i % 2 == 0 ] = [0, 4, 8]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;tldr I can run Python in the browser. (To their credit, &lt;a href=&quot;https://rustpython.github.io/demo/&quot;&gt;RustPython does this too&lt;/a&gt;. I haven’t looked deeply at their project but it seems comprehensive.) The Python list comprehension is defined in JavaScript in string form and the response list is evaluated by the Rust code compiled to Wasm and converted back into a string which can be displayed by JavaScript.&lt;/p&gt;
&lt;p&gt;This setup only supports expressions at the moment. To evaluate statements (and later read back their results), I will need to keep state on the Rust side. I also dream of building a JavaScript REPL. That sounds like a problem for future-me (and a boring dream tbh).&lt;/p&gt;
&lt;h2&gt;The End&lt;/h2&gt;
&lt;p&gt;I’ve been talking long enough, so I’m going to hold off on discussing embedded Python until next Monday.&lt;/p&gt;
&lt;p&gt;Apologies for the bait and switch. The content calendar waits for no one.&lt;/p&gt;
&lt;p&gt;To be clear, by embedded Python, I mean embedding a CPython interpreter inside of Memphis, not running Python in an “embedded systems” environment. That would be hard for no reason. Unlike Memphis, which is hard for FUN.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Introducing: From Scratch Code]]></title><link>https://fromscratchcode.com/blog/introducing-from-scratch-code</link><guid isPermaLink="true">https://fromscratchcode.com/blog/introducing-from-scratch-code</guid><pubDate>Mon, 04 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;THE BIG CITY—From Scratch Enterprises LLC (ticker: FSE) announced its newest venture Monday, &lt;a href=&quot;https://fromscratchcode.com/&quot;&gt;From Scratch Code&lt;/a&gt; (ticker: FSC). Members of the media gathered around the folding chair of its owlish founder, Jones Beach. Refreshments were not provided.&lt;/p&gt;
&lt;p&gt;Whispers circulated among the media contingent that this was the same desk which produced the not-a-non-profit, &lt;a href=&quot;https://fromscratchpress.com/&quot;&gt;From Scratch Press&lt;/a&gt; (ticker: FSP). The representative present could not confirm and barely glanced up from their phone.&lt;/p&gt;
&lt;p&gt;“After becoming the market leader in telling autism stories no one asked for, we stepped back and asked ourselves what was next,” said Beach. “It became clear that we could go beyond the abcs and move into 1s and 0s.”&lt;/p&gt;
&lt;p&gt;The event continued with a personal statement read aloud by Beach, which was a weird format, but seemed heartfelt.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;I never set out to make the killer app. When I was building an early project—a website about stadiums—people asked me when they could expect an app, I looked at them feeling under-appreciated and directed them to my clumsy website. I’m not motivated by attempting to build the next big thing, but by creating something genuine and functional.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;My skill set as a software engineer is typically valued through monetization rather than words of affirmation. I’m not asking for sympathy about this; I’m incredibly fortunate that people chose to pay me to write code for them for nearly a decade. But when this system&lt;/em&gt; &lt;a href=&quot;https://fromscratchpress.com/why-i-left-my-9-5-for-good/&quot;&gt;&lt;em&gt;stopped working for me&lt;/em&gt;&lt;/a&gt;&lt;em&gt;, I looked around for what else I could do with my skills. I’m driven by curiosity and a genuine desire to support others, bringing humor and understanding to my work. These values give me far more satisfaction than fitting into the small box an employer needs each quarter, so I set out to build a business that embraces them, with as little BS as possible.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;What mentally freed me to arrive at this point was letting go of the need to impress people who didn’t understand my tools, my craft, or my skill set. While that path works for some, it left me feeling unheard and used. Instead of building software to do something interesting, I chose to build software that is, itself, interesting—a kind of art for art’s sake and my personal rebellion against a system that seeks to control my time and monetize my output.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I landed on a service business because I crave 1:1 connection. My favorite moments in my 9-5 weren’t building products but nerding out with a colleague over an obscure programming language feature. Tutoring proved I could make non-zero dollars doing what I love most. Today, I’m expanding this into my own brand and platform, where creativity and emphasis on the individual can shine—free from high platform fees and other external constraints.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;From Scratch Code is for people who already know how to write code and want to learn how to write even better code. Who want to build their own libraries in Rust and Python and understand how programming languages and computers work together under the hood. Who want to have a technical support system which takes not being serious very seriously. I’ll continue to work with students and beginner developers on Wyzant, but here, you’ll find a space where creativity and curiosity are the main drivers.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If any of this resonates with you, I encourage you to&lt;/em&gt; &lt;a href=&quot;https://fromscratchcode.com/subscribe/&quot;&gt;&lt;em&gt;sign up for my email list&lt;/em&gt;&lt;/a&gt;&lt;em&gt;. There, I’ll be telling silly stories about the Rust and Python code I’m building—like my current interpreter project—and the things we could learn together, either through&lt;/em&gt; &lt;a href=&quot;https://fromscratchcode.com/mentorship/&quot;&gt;&lt;em&gt;mentorship&lt;/em&gt;&lt;/a&gt; &lt;em&gt;or&lt;/em&gt; &lt;a href=&quot;https://fromscratchcode.com/courses/&quot;&gt;&lt;em&gt;courses&lt;/em&gt;&lt;/a&gt;&lt;em&gt;. I’ll continue to discuss the mental health and adult-diagnosed autism side of my story on From Scratch Press. I can’t think of a better way to fully present the two sides of myself to the modern economy than by maintaining parallel newsletters!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I’ll wrap up with this: my inability to form even a single sentence of what feels like BS played a large role in my decision to leave corporate, so everything I’m building with From Scratch Code is genuine and designed to help you thrive in your technical work. On the flip side, I’m a firm believer that humor and creative absurdism can make people smile and expand what’s considered possible. As such, I found myself with no patience for people who (or systems which encourage people to) say “I’m working with person A on project B” when everyone in the room knows person A doesn’t respond to emails and project B will be scrapped. Perhaps this impatience with non-reality is an autism thing. I’m drawn to the person who says “what if we built project C on the moon?!” Those are the people stretching boundaries and refusing to live in the small boxes corporate life often imposes.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I want to be that person for you, your career, and your technical work. Your unlicensed technical therapist. A supportive listener who doesn’t take insurance but can debug your code.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Feel free to share this message with anyone who might enjoy some offbeat creativity alongside their technical growth—I’d love to connect with them!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This is going to be fun. I hope you’ll join me!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The event concluded ten minutes after it began.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;James Beach covers culture and satire in The Big City. He lives in The Big City alongside the rest of the literati. He is in no way related to Jones Beach and believes refreshments at press conferences pose an ethical dilemma.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;This is &lt;a href=&quot;https://fromscratchpress.com/introducing-from-scratch-code/&quot;&gt;cross-posted&lt;/a&gt; on &lt;strong&gt;From Scratch Press&lt;/strong&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[A REPL for fat-finger friendly typing]]></title><link>https://fromscratchcode.com/blog/a-repl-for-fat-finger-friendly-typing</link><guid isPermaLink="true">https://fromscratchcode.com/blog/a-repl-for-fat-finger-friendly-typing</guid><pubDate>Mon, 21 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My Python interpreter, &lt;a href=&quot;https://fromscrathcode.com/memphis/&quot;&gt;Memphis&lt;/a&gt;, has a REPL (read-eval-print loop)!&lt;/p&gt;
&lt;p&gt;This is old news. As long as you made zero mistakes while interacting with the wise old owl 🦉, you could interpret to your heart’s content. Assuming you never wanted to evaluate the same statement twice, or if you did, didn’t mind retyping it. Also with zero mistakes.&lt;/p&gt;
&lt;p&gt;I was perfectly content with this REPL. Thrilled even. I had written home about this REPL. But my bosses’s bosses’ bossi demanded we improve the REPL for the bottom line for the people. They called me into their office and nodded me into the chair across from their mahogany desk. “Some users are expecting the backspace key to work.” TO HELL WITH THE USERS! “The up arrow should bring up their last command.” ARROW KEY SUPPORT IS SO NINETIES! “Can you have this done by the end of Q5?” I QUIT!&lt;/p&gt;
&lt;p&gt;So I went back to my desk and improved the REPL.&lt;/p&gt;
&lt;p&gt;I improved it so much that all the keys worked. The backspace key, the up arrow, the down arrow, the left arrow, and last, but not least, the backspace arrow. An accountant could have a field day with the new REPL. tick tick tick tick tick tick tick tick tick. That’s the accountant typing numbers, not a bomb slowly diffusing.&lt;/p&gt;
&lt;p&gt;I sent the REPL down to the lab and told my main machinist to put a rush job on this order. It’s the REPL I said and from the look in their eyes I could tell they understood. 750ms later the build was complete and we had arrow key support. I took the product back to the big wigs, begged for my job back, and asked them what they thought. They ran a few commands, printed some prints, and added some adds. They made a mistake and hit the backspace key. I rolled my eyes because seriously who makes mistakes but they seemed satisfied. They realized they didn’t want to run a long command they had already typed out and this is where my life went to hell in a handbasket. They. hit. Ctrl. C. Seriously, who does that?! You know that ends the current process, right? RIGHT???&lt;/p&gt;
&lt;p&gt;“We need Ctrl-C support by the end of next year.” These people and their demands. I would add Ctrl-C support. But it absolutely would not be within the next two years.&lt;/p&gt;
&lt;p&gt;So I went back to my desk and added Ctrl-C support.&lt;/p&gt;
&lt;h2&gt;What made this REPL worthy of people with fat-fingered tendencies?&lt;/h2&gt;
&lt;h3&gt;Would I be a tool?&lt;/h3&gt;
&lt;p&gt;I have staked my entire professional and financial future on building things “from scratch,” so I faced a quandary on day 1 of this project. I chose to use &lt;a href=&quot;https://github.com/crossterm-rs/crossterm&quot;&gt;&lt;code&gt;crossterm&lt;/code&gt;&lt;/a&gt; for the key detection primarily because of the cross-platform support. Honestly though, &lt;code&gt;crossterm&lt;/code&gt; was very, very good. The API is intuitive and I was especially pleased with &lt;code&gt;KeyModifiers&lt;/code&gt; (which we needed to handle &lt;code&gt;Ctrl-C&lt;/code&gt;, which I thought was unnecessary, see above).&lt;/p&gt;
&lt;h3&gt;Raw mode is a pain&lt;/h3&gt;
&lt;p&gt;We needed it so that the terminal wouldn&apos;t handle special keys for us. But damn, I didn&apos;t realize it would turn our screen into a malfunctioning typewriter. Anyway, I had to normalize all strings to add a carriage return before any newline characters. Which worked fine and I&apos;m THRILLED about it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;/// When the terminal is in raw mode, we must emit a carriage return in addition to a newline,
/// because that does not happen automatically.
fn normalize&amp;#x3C;T: Display&gt;(err: T) -&gt; String {
    let formatted = format!(&quot;{}&quot;, err);
    if terminal::is_raw_mode_enabled().expect(&quot;Failed to query terminal raw mode&quot;) {
        formatted.replace(&quot;\n&quot;, &quot;\n\r&quot;)
    } else {
        formatted.to_string()
    }
}

/// Print command which will normalize newlines + carriage returns before printing.
fn print_raw&amp;#x3C;T: Display&gt;(val: T) {
    print!(&quot;{}&quot;, normalize(val));
    io::stdout().flush().expect(&quot;Failed to flush stdout&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Integration testing was fun&lt;/h3&gt;
&lt;p&gt;Under my old REPL (which I preferred, see above), I could test it integration-ally by just running the binary and passing in some Python code to stdin. That stopped working when using crossterm I think because of a contract dispute. I honestly can’t explain it fully, but event::read() would timeout and fail in the integration test provided with stdin input. So I mocked it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;pub trait TerminalIO {
    fn read_event(&amp;#x26;mut self) -&gt; Result&amp;#x3C;Event, io::Error&gt;;
    fn write&amp;#x3C;T: Display&gt;(&amp;#x26;mut self, output: T) -&gt; io::Result&amp;#x3C;()&gt;;
    fn writeln&amp;#x3C;T: Display&gt;(&amp;#x26;mut self, output: T) -&gt; io::Result&amp;#x3C;()&gt;;
}

/// A mock for testing that doesn&apos;t use `crossterm`.
struct MockTerminalIO {
    /// Predefined events for testing
    events: Vec&amp;#x3C;Event&gt;,

    /// Captured output for assertions
    output: Vec&amp;#x3C;String&gt;,
}

impl TerminalIO for MockTerminalIO {
    fn read_event(&amp;#x26;mut self) -&gt; Result&amp;#x3C;Event, io::Error&gt; {
        if self.events.is_empty() {
            Err(io::Error::new(io::ErrorKind::Other, &quot;No more events&quot;))
        } else {
            // remove from the front (semantically similar to VecDequeue::pop_front).
            Ok(self.events.remove(0))
        }
    }

    fn write&amp;#x3C;T: Display&gt;(&amp;#x26;mut self, output: T) -&gt; io::Result&amp;#x3C;()&gt; {
        self.output.push(format!(&quot;{}&quot;, output));
        Ok(())
    }

    fn writeln&amp;#x3C;T: Display&gt;(&amp;#x26;mut self, output: T) -&gt; io::Result&amp;#x3C;()&gt; {
        self.write(output)?;
        self.write(&quot;\n&quot;)?;
        Ok(())
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which resulted in the whole thing becoming a unit test? Honestly I don’t know. At this point, I call it an integration test if I either a) call a binary inside another binary, or 2) launch a server / open a port / listen on a socket inside a test. If you have another definition you’d like to leave in the comments, please don’t because that sounds annoying TBH.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;/// Run the complete flow, from input code string to return value string. If you need any Ctrl
/// modifiers, do not use this!
fn run_and_return(input: &amp;#x26;str) -&gt; String {
    let mut terminal = MockTerminalIO::from_str(input);
    Repl::new().run(&amp;#x26;mut terminal);
    terminal.return_val()
}

fn string_to_events(input: &amp;#x26;str) -&gt; Vec&amp;#x3C;Event&gt; {
    input
        .chars()
        .map(|c| {
            let key_code = match c {
                &apos;\n&apos; =&gt; KeyCode::Enter,
                _ =&gt; KeyCode::Char(c),
            };
            Event::Key(KeyEvent::new(key_code, KeyModifiers::NONE))
        })
        .collect()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now test these common scenarios with fairly little boilerplate.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;#[test]
fn test_repl_name_error() {
    let return_val = run_and_return(&quot;e\n&quot;);
    assert!(return_val.contains(&quot;NameError: name &apos;e&apos; is not defined&quot;));
}

#[test]
fn test_repl_expr() {
    let third_from_last = run_and_return(&quot;12345\n&quot;);
    assert_eq!(third_from_last, &quot;12345&quot;);
}

#[test]
fn test_repl_statement() {
    let return_val = run_and_return(&quot;a = 5.5\n&quot;);

    // empty string because a statement does not have a return value
    assert_eq!(return_val, &quot;&quot;);
}

#[test]
fn test_repl_function() {
    let code = r#&quot;
def foo():
    a = 10
    return 2 * a

foo()
&quot;#;
    let return_val = run_and_return(code);
    assert_eq!(return_val, &quot;20&quot;);
}

#[test]
fn test_repl_ctrl_c() {
    let mut events = string_to_events(&quot;123456789\n&quot;);
    let ctrl_c = Event::Key(KeyEvent::new(KeyCode::Char(&apos;c&apos;), KeyModifiers::CONTROL));
    events.insert(4, ctrl_c);
    let mut terminal = MockTerminalIO::new(events);

    Repl::new().run(&amp;#x26;mut terminal);
    assert_eq!(terminal.return_val(), &quot;56789&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Code entrypoints get me out of bed in the morning&lt;/h3&gt;
&lt;p&gt;One of my motivations in adding a REPL at all was because I believe you make your code better when you add a second entrypoint. You are essentially becoming the second user for your library, which helps you get closer to understanding The One Perfect Abstraction we are all poking at our keyboards in search of. I mean this point earnestly.&lt;/p&gt;
&lt;h3&gt;“Zero Dependencies” 😉&lt;/h3&gt;
&lt;p&gt;The REPL is now behind a feature flag as a way to get back at management. I am keeping alive the ability to interpret Python code with the help of zero third-party crates, which means crossterm would either need to be an exception or I would introduce a feature flag. Now, if you compile without the REPL enabled and run “memphis”, it will politely tell you “wrong build, dumbass.”&lt;/p&gt;
&lt;h2&gt;Goodbye&lt;/h2&gt;
&lt;p&gt;The REPL is &lt;a href=&quot;https://github.com/fromscratchcode/memphis/blob/main/src/repl/repl.rs&quot;&gt;here&lt;/a&gt;. You can run it &lt;a href=&quot;https://github.com/fromscratchcode/memphis/blob/main/docs/DEVELOPING.md#repl&quot;&gt;like this&lt;/a&gt;. If you want to buy it, that sounds like a scam. Be well &amp;#x26; Talk soon.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Declarative macro magic from Axum in Rust]]></title><link>https://fromscratchcode.com/blog/declarative-macro-magic-from-axum-in-rust</link><guid isPermaLink="true">https://fromscratchcode.com/blog/declarative-macro-magic-from-axum-in-rust</guid><pubDate>Mon, 07 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Take a quick glance at the code snippet below. Without thinking too hard, is &lt;code&gt;get&lt;/code&gt; a function or a method? How about &lt;code&gt;post&lt;/code&gt;?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;Router::new()
    .route(“/one”, get(get_handler).post(post_handler))
    .route(“/two”, post(post_handler).get(get_handler))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What did you decide: is &lt;code&gt;get&lt;/code&gt; a function or a method? It appears to be both! And the same with &lt;code&gt;post&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;If you are familiar with API development in Rust, you may recognize this syntax from &lt;a href=&quot;https://github.com/tokio-rs/axum&quot;&gt;Axum&lt;/a&gt;. This puzzling question piqued my curiosity and led me to build &lt;a href=&quot;https://github.com/fromscratchcode/cairo&quot;&gt;Cairo&lt;/a&gt;, where I re-implemented some key Axum concepts in a simpler way as a learning exercise. I’ve long been fascinated by the intermediate + advanced concepts library authors employ to make their interfaces feel frictionless. Rust sits in a brilliant space because of the way it melds high-level expressiveness with low-level control and performance. One of the ways it accomplishes this is using macros.&lt;/p&gt;
&lt;h2&gt;What is a macro in Rust?&lt;/h2&gt;
&lt;p&gt;Macros in Rust come in two key forms: declarative and procedural. This post will focus on declarative, but let’s briefly define both.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;declarative macro&lt;/strong&gt; in Rust uses the &lt;code&gt;function!(..)&lt;/code&gt; syntax, which you may be familiar with from &lt;code&gt;print!(“Hello World!”)&lt;/code&gt; or &lt;code&gt;panic!(“at the disco!”)&lt;/code&gt;. It is a form of metaprogramming which allows developers to reduce boilerplate code. It does this by evaluating the declarative macro at compile time, which produces more Rust code which ultimately ends up in the binary of the program.&lt;/p&gt;
&lt;p&gt;If you have wondered why &lt;code&gt;print!(..)&lt;/code&gt; in Rust is a macro, it is due to its dynamic nature. All of these are valid Rust:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;println!(&quot;Hello&quot;);
println!(&quot;Hello {}&quot;, &quot;World&quot;);
println!(&quot;Hello {}{}&quot;, &quot;World&quot;, &quot;!&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In a &lt;s&gt;language with rules&lt;/s&gt; strongly-typed language, a traditional function would typically not support a variable number of parameters like this (also called variable-arity). However, Rust’s declarative macros use a &lt;code&gt;$(..),*&lt;/code&gt; syntax to support variable-arity by expanding to different code based on the number of parameters passed. The compiled code may end up calling different function implementations or generating specific code for each unique pattern of parameters.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;procedural macro&lt;/strong&gt; in Rust uses the &lt;code&gt;#[...]&lt;/code&gt; syntax, which you may have seen in &lt;code&gt;#[tokio:main]&lt;/code&gt;. This macro is applied to a Rust function or struct, allowing the macro to modify the syntax tree of the annotated item at compile time. While it often helps reduce boilerplate code, it does so by transforming the function or struct as a whole rather than directly inserting statements into the function body. For example, &lt;code&gt;#[tokio::main]&lt;/code&gt; wraps the entire &lt;code&gt;main&lt;/code&gt; function in a &lt;code&gt;tokio&lt;/code&gt; async runtime, enabling it to block on the execution of async code in &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Code without macros can be repetitive and say the same thing in multiple ways&lt;/h2&gt;
&lt;p&gt;Axum is one of the most popular web frameworks in Rust. Its compatibility with the &lt;a href=&quot;https://github.com/tokio-rs/tokio&quot;&gt;Tokio&lt;/a&gt; ecosystem and its powerful syntax, among other features, keep it near the front of the pack.&lt;/p&gt;
&lt;p&gt;Skipping back to my original bafflement at whether &lt;code&gt;get&lt;/code&gt; is a function or a method, the answer is both and it does this using a declarative macro.&lt;/p&gt;
&lt;p&gt;If we look at our snippet of interest, &lt;code&gt;get&lt;/code&gt; must be a function available in the outer scope and a method on whatever type is returned by invoking &lt;code&gt;post&lt;/code&gt;. Similarly, &lt;code&gt;post&lt;/code&gt; must be a function available in the outer scope and a method on whatever type is returned by invoking &lt;code&gt;get&lt;/code&gt;. This symmetry pleases me. Let’s write some code that accomplishes exactly that and nothing more.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;use std::collections::HashMap;

/// Define a trait with a single method `handle`.
/// This trait allows different types to implement handler logic, which is useful in API routing
/// systems.
trait Handler {
    fn handle(&amp;#x26;self);
}

/// Define an enum representing HTTP methods.
/// Deriving `Hash`, `Eq`, and `PartialEq` allows this type to be used as a `HashMap` key.
#[derive(Hash, Eq, PartialEq, Debug)]
enum Method {
    Get,
    Post,
}

/// Start of a method-chaining function for the `GET` HTTP method.
fn get&amp;#x3C;H: Handler + &apos;static&gt;(handler: H) -&gt; InnerType {
    InnerType::new().on(Method::Get, handler)
}

/// Start of a method-chaining function for the `POST` HTTP method.
fn post&amp;#x3C;H: Handler + &apos;static&gt;(handler: H) -&gt; InnerType {
    InnerType::new().on(Method::Post, handler)
}

/// Define a struct that holds a collection of routes. We give it a silly name here to emphasize
/// this is internal to our Axum-like library. Specifically, each `Router` would hold several of
/// these struct instances, each mapped to a specific string-pattern representing a route. This is
/// outside the scope of this example, please see [Cairo](https://github.com/fromscratchcode/cairo)
/// if you are interested to learn more.
struct InnerType {
    /// `Box&amp;#x3C;dyn Handler&gt;` enables dynamic dispatch, allowing for different handler types in the
    /// `HashMap`.
    routes: HashMap&amp;#x3C;Method, Box&amp;#x3C;dyn Handler&gt;&gt;,
}

impl InnerType {
    /// Initialize our empty instance.
    fn new() -&gt; Self {
        Self {
            routes: HashMap::default(),
        }
    }

    /// Add a `Handler` for a specific HTTP method to the routes map.
    fn on&amp;#x3C;H: Handler + &apos;static&gt;(mut self, method: Method, handler: H) -&gt; Self {
        self.routes.insert(method, Box::new(handler));
        self
    }

    /// Method-chaining function for the `GET` HTTP method.
    fn get&amp;#x3C;H: Handler + &apos;static&gt;(self, handler: H) -&gt; Self {
        self.on(Method::Get, handler)
    }

    /// Method-chaining function for the `POST` HTTP method.
    fn post&amp;#x3C;H: Handler + &apos;static&gt;(self, handler: H) -&gt; Self {
        self.on(Method::Post, handler)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, we see some duplication, but the boilerplate isn’t overwhelmingly negative. However, what about when we add support for the remaining HTTP verbs: &lt;code&gt;OPTIONS&lt;/code&gt;, &lt;code&gt;HEAD&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, &lt;code&gt;PATCH&lt;/code&gt;? We’d find ourselves maintaining 7 functions and 7 methods. Given the choice between maintaining 14 blocks of code versus adding complexity to prove I know a language, I&apos;ll choose the latter every single time. Enter declarative macros.&lt;/p&gt;
&lt;h2&gt;Declarative Macros in Axum&lt;/h2&gt;
&lt;p&gt;The code below is a mix of Axum and Cairo. We introduce two macros: &lt;code&gt;add_http_function&lt;/code&gt; and &lt;code&gt;add_http_method&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;/// A declarative macro which will define a function `$name` which accepts a `Handler` to be
/// registered to a particular HTTP `Method::$method`.
macro_rules! add_http_function {
    (
        $name:ident, $method:ident
    ) =&gt; {
        fn $name&amp;#x3C;H: Handler + &apos;static&gt;(handler: H) -&gt; InnerType {
            on(Method::$method, handler)
        }
    };
}

/// A declarative macro which will define a method `$name` which accepts `self` and a `Handler` to
/// be registered to a particular HTTP `Method::$method`.
macro_rules! add_http_method {
    (
        $name:ident, $method:ident
    ) =&gt; {
        fn $name&amp;#x3C;H: Handler + &apos;static&gt;(self, handler: H) -&gt; Self {
            self.on(Method::$method, handler)
        }
    };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&apos;ll notice that these both accept two &lt;code&gt;ident&lt;/code&gt; parameters, a &lt;code&gt;$name&lt;/code&gt; and a &lt;code&gt;$method&lt;/code&gt;. The &lt;code&gt;$name&lt;/code&gt; becomes the function name, which will be &lt;code&gt;get&lt;/code&gt;, &lt;code&gt;post&lt;/code&gt;, etc., while the &lt;code&gt;$method&lt;/code&gt; is concatenated to &lt;code&gt;Method::&lt;/code&gt;, meaning we must give a valid &lt;code&gt;Method&lt;/code&gt; enum variant. It&apos;s okay if this is confusing at first—metaprogramming is a different way of thinking about programming. Instead of asking &quot;what should my code do?&quot; we are now asking &quot;what code should my code produce?&quot;&lt;/p&gt;
&lt;p&gt;You&apos;ll also notice that while they both define a &quot;function&quot; with the name &lt;code&gt;$name&lt;/code&gt;, &lt;code&gt;add_http_method&lt;/code&gt; accepts a parameter &lt;code&gt;self&lt;/code&gt;. This means we must invoke our macro (by calling &lt;code&gt;add_http_method!(get, Get)&lt;/code&gt;) in a context which is aware of a &lt;code&gt;self&lt;/code&gt;. For us, this will be inside our &lt;code&gt;impl InnerType&lt;/code&gt; block.&lt;/p&gt;
&lt;h2&gt;Putting it all together&lt;/h2&gt;
&lt;p&gt;Putting it all together, here is our new library with minimal boilerplate and support for method chaining for 7 HTTP methods.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;use std::collections::HashMap;

/// Define a trait with a single method `handle`.
/// This trait allows different types to implement handler logic, which is useful in API routing
/// systems.
trait Handler {
    fn handle(&amp;#x26;self);
}

/// Define an enum representing HTTP methods.
/// Deriving `Hash`, `Eq`, and `PartialEq` allows this type to be used as a `HashMap` key.
#[derive(Hash, Eq, PartialEq, Debug)]
enum Method {
    Get,
    Post,
    Options,
    Head,
    Put,
    Delete,
    Patch,
}

/// Define a function to be called by our `add_http_function` macro. For a given HTTP method, this
/// function will register a handler and return an type which supports method chaining.
fn on&amp;#x3C;H: Handler + &apos;static&gt;(method: Method, handler: H) -&gt; InnerType {
    InnerType::new().on(method, handler)
}

/// A declarative macro which will define a function `$name` which accepts a `Handler` to be
/// registered to a particular HTTP `Method::$method`.
macro_rules! add_http_function {
    (
        $name:ident, $method:ident
    ) =&gt; {
        fn $name&amp;#x3C;H: Handler + &apos;static&gt;(handler: H) -&gt; InnerType {
            on(Method::$method, handler)
        }
    };
}

// Invoke our declarative macro once for each HTTP `Method`.
add_http_function!(get, Get);
add_http_function!(post, Post);
add_http_function!(delete, Delete);
add_http_function!(head, Head);
add_http_function!(options, Options);
add_http_function!(patch, Patch);
add_http_function!(put, Put);

/// A declarative macro which will define a method `$name` which accepts `self` and a `Handler` to
/// be registered to a particular HTTP `Method::$method`.
macro_rules! add_http_method {
    (
        $name:ident, $method:ident
    ) =&gt; {
        fn $name&amp;#x3C;H: Handler + &apos;static&gt;(self, handler: H) -&gt; Self {
            self.on(Method::$method, handler)
        }
    };
}

/// Define a struct that holds a collection of routes. We give it a silly name here to emphasize
/// this is internal to our Axum-like library. Specifically, each `Router` would hold several of
/// these struct instances, each mapped to a specific string-pattern representing a route. This is
/// outside the scope of this example, please see [Cairo](https://github.com/fromscratchcode/cairo)
/// if you are interested to learn more.
struct InnerType {
    /// `Box&amp;#x3C;dyn Handler&gt;` enables dynamic dispatch, allowing for different handler types in the
    /// `HashMap`.
    routes: HashMap&amp;#x3C;Method, Box&amp;#x3C;dyn Handler&gt;&gt;,
}

impl InnerType {
    /// Initialize our empty instance.
    fn new() -&gt; Self {
        Self {
            routes: HashMap::default(),
        }
    }

    /// Add a `Handler` for a specific HTTP method to the routes map. This will be invoked from our
    /// `add_http_method` macro.
    fn on&amp;#x3C;H: Handler + &apos;static&gt;(mut self, method: Method, handler: H) -&gt; Self {
        self.routes.insert(method, Box::new(handler));
        self
    }

    // Invoke our declarative macro once for each HTTP `Method`.
    add_http_method!(get, Get);
    add_http_method!(post, Post);
    add_http_method!(delete, Delete);
    add_http_method!(head, Head);
    add_http_method!(options, Options);
    add_http_method!(patch, Patch);
    add_http_method!(put, Put);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The End&lt;/h2&gt;
&lt;p&gt;While we&apos;ve only scratched the surface of the metapossibilities of Rust&apos;s declarative macros, I hope this post has inspired you to explore more of what Rust can do at compile time. Procedural macros are another wonderful beast which I would encourage you to dig into if you are interested in ASTs and language development.&lt;/p&gt;
&lt;p&gt;If you are curious about more ways Axum and APIs work under-the-hood, I encourage you to check out my course &lt;a href=&quot;https://fromscratchcode.com/courses/rust-http-server&quot;&gt;HTTP Server in Rust&lt;/a&gt;. The final module is public as the repo &lt;a href=&quot;https://github.com/fromscratchcode/cairo&quot;&gt;Cairo&lt;/a&gt;, which shows the north star you will build towards throughout the course. In addition to the macro usage we described here, you’ll explore the details of HTTP and how to create a &lt;code&gt;Router&lt;/code&gt; which accepts handlers with variable parameters and return types using advanced type erasure.&lt;/p&gt;
&lt;p&gt;Now it&apos;s your turn! I&apos;d love your perspective in the comments on these two questions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;How have you used declarative macros in your own Rust projects to reduce boilerplate code?&lt;/li&gt;
&lt;li&gt;Are there any Rust crates you have used with an interfaces that makes you go &quot;how did they do that?!&quot;&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item></channel></rss>