Image modified from the original by Scott Goodwill on Unsplash

I'm Abid, and this is my website

Starsword

My in-development programming syntax, languages, and computing stack

Take the Wheel

Minimal coding agent (~400 LOC), my daily driver

Climate Nodes

Network model of climate tech + venture capital industry

Looking at the Sky

Video interviews showcasing the artwork and personal reality of creatives trying to make it in NYC

py-hamt v3 (src download)

For a previous job, I made a data structure in python that allows for S3-like mutable key-value storage on IPFS, normally an immutable content-addressed store. Includes a storage compatibility layer for Zarr v2. This is the source code of version 3, the last of my work on the project before maintenance was transferred. AGPLv3 License.

First arcsine in Solidity (Ethereum) full src download

Link is to an open source trig library where it was merged. First ever afaik.

First haversine distance in Solidity (Ethereum) full src download

The haversine function calculates the distance between two latitude+longitude points. First ever afaik.

Articles

Simple LRU cache with just a Python dict

Starsword 🔗

Starsword is my programming language project. My goal is to rebuild the traditional computing stack: hardware description language and chip layout engine, CPU + ISA, C-abstraction level language, operating system, common userland utilities like a terminal, some interpreted memory managed languages, and GUI userland programs like a web browser.

The guiding design decision for everything is aesthetics. I am ok with trading off almost anything if it means the language or tool will in some sense be more aesthetic. What this often will mean is a driven focus on simplicity and looks. The design of my website should give an idea of what that looks like in practice.

I've started by building a syntax flexible enough to serve as the foundation for several different programming languages. I'll first talk about the syntax, and then the ongoing current research work.

The Syntax

The goal is to represent, easily and aesthetically, any possible tree of strings.

I started from the base of S-expressions and Python as my inspiration. To keep the syntax feeling airy and light, I removed all the delimiters that S-expressions normally require. I replaced the nesting functionality that S-expressions provide with indentation.

The most basic element of the syntax is a string, and the next level up is lists with a mixed set of other lists and strings inside. This is all that's needed for representing trees.

Spaces separate list children, and any characters next to each other are concatenated into one string. Indentation marks the level of depth in the tree of a list, and an underscore tells the lexer where to find a missing embedded list.

Something I did not include was an easy way to include very short lists inline. A lot of syntax complexity, and thus lexer/parser complexity, can be avoided. Inline embedded lists also tended to look quite ugly. If I find a solution to the ugly appearance, then I would be willing to consider incorporating them one day.

The last feature to mention is the extremely powerful literal string facility. A string literal starts with a backtick ` and ends with a single quote '. The lexer will swallow everything between these two characters. Combined with the rule that strings next to each other are concatenated, this allows for strings that would normally be very hard to escape in most programming languages.

Let's show off some examples by translating some S-expression and Python syntax to the Starsword equivalent.

(+ 3 4) + 3 4 () just the empty line (define (square x) (* x x)) define square x * x x (+ (* x 3) 9) + _ 9 * x 3 more idiomatically: + 9 * x 3 indented lists are automatically embedded inside the end of a higher level list above them. ("") _ `' ("\n") _ ` ' (()) _ leave this line empty intentionally if x: foo() foo2() else: bar() if x _ foo foo2 bar

Hopefully you're starting to get the hang of it. Thinking in trees is very important for coming to grips with the syntax. The best way to learn is to create your own examples.

Current Research

The syntax needs to be exercised in a more real, practical, and alive environment. The strategy at the moment is to use it as a lisp-like python frontend, the way Hy does. This will function as a testing ground for the ergonomics of writing real programs. Typical lisp family features like macros will also be prototyped using this.

Take the Wheel 🔗

AI, take the wheel 😎 /s

Simple, opinionated coding agent for personal use. Focused on ease of maintenance, and speed through simplicity. Source code available under AGPLv3.

Key Differentiators:

Installation

uv tool install https://abidsikder.com/takethewheel-0.4.0-py3-none-any.whl

Usage

Make sure either an OPENROUTER_API_KEY or AWS_BEARER_TOKEN_BEDROCK is in your env. takethewheel kimi # Kimi K3 via OpenRouter takethewheel opus # opus 4.8 max via OpenRouter takethewheel aws-opus # opus 4.8 max via AWS Bedrock takethewheel model "prompt" # finish prompt and exit, noninteractive

Context via agents.md

Automatically pass context with an agents.md file. It must be in the directory you invoke the agent in.

To include the contents of other files, start a line with the @ symbol followed by the file path e.g.

@./README.md @./important_script.py Additional instructions...

Simple LRU cache with just a Python dict 🔗

Since Python 3.7+, insertion order is maintained in the python dict type. For example:

a = {} a[1] = 1 a[2] = 2 a[3] = 3

When you iterate over the keys and values, you will always get the pairing 1:1 first before 2:2. We can use this order to keep track of which key-value pairs were least recently used by moving used ones to the end of the insertion order. I'll show an example now with some python code, where the cache stores a mapping between some type ID and some type Obj.

# python 3.7+ from sys import getsizeof cache: dict[ID, Obj] = {} max_cache_size_bytes: int = 1_000_000 # e.g. 1 Megabyte def lru_eviction(): if getsizeof(cache) > max_cache_size_bytes: if len(cache) == 0: return stalest_key = next(iter(cache.keys())) del cache[stalest_key] def read(id: ID) -> Obj: result: Obj if id in cache: # Cache Hit result = cache[id] # move to the back in insertion order del cache[id] cache[id] = result else: # Cache Miss result = get(id) # Where get() is e.g. some network call cache[id] = result lru_eviction() return result

Based on this documentation for the time complexity of dict operations, cache reads are constant time complexity on average.