How AI Coding Assistants Use Code Context: LSP, ASTs, Search, and Retrieval

In the previous post, the important idea was that an editor is not a magical code mind… Visual Studio, VS Code, Neovim, and Emacs know how to display files and react to cursor positions. The deeper code knowledge usually lives in language services: parsers, syntax trees, symbol tables, type checkers, diagnostics, caches, and sometimes language servers speaking LSP. That explains how an editor can answer precise questions: what symbol is under the cursor? where is it defined? what type does this expression have? which references should change during a rename? where should this diagnostic squiggle appear? AI coding assistants start from a different place. ...

August 11, 2026 · Giulia

How Visual Studio Understands Your Code: LSP, Syntax Trees, and Language Services Behind the Curtain

Autocomplete feels like the editor understands the code.🙂 Visual Studio, VS Code, Neovim, and Emacs know about files, tabs, selections, cursor positions, and how to draw the UI. The deeper language knowledge lives in a separate language layer: sometimes an integrated IDE service, sometimes an external language server process. That language layer is the part that parses code, tracks names, builds semantic information, and answers questions like: what is under the cursor? where is this function defined? which completions are valid here? is this variable unused? what edit would safely rename this symbol? The editor asks, and the language layer answers: when that layer is an external process speaking a standard protocol, the protocol is often the Language Server Protocol, or LSP. So LSP is the message format that lets an editor talk to the language-specific system that has the intelligence. ...

August 5, 2026 · Giulia

Nolan, AI, and the Responsibility of Intelligence

I walked out of the cinema expecting to think about Homer. Instead, I couldn’t stop thinking about AI. Not because Christopher Nolan’s The Odyssey is secretly about machine learning. It is not. Nor because every old story must now be converted into a parable about large language models. The connection is more interesting than that. Nolan keeps returning to people who push intelligence past an old boundary and then discover that getting there was only half the problem. ...

July 24, 2026 · Giulia

Building an AI Agent: RAG, Hybrid Search, Semantic Cache, and Memory

Most RAG demos stop at the happy path: split a PDF, embed the chunks, retrieve the nearest neighbors, put them in a prompt, call a model. That is the right starting point, but it leaves out the parts that make the system feel like an actual assistant instead of a stateless search box. I built a small agent in Python to explore those missing pieces. It is a FastAPI service with two main endpoints: /ingest for uploading documentation and /ask for asking questions against it. Under the hood it uses Redis Stack via redisvl for vector and full-text search, OpenAI for embeddings and answer generation, and Redis again for sessions, semantic cache entries, long-term memory, and metrics. ...

July 7, 2026 · Giulia

Writing One Attention Head in NumPy

I had read about attention more times than I can count. Watched the 3Blue1Brown video, skimmed the Illustrated Transformer, half-read the paper. I could recite the slogan queries, keys, values but when I tried to picture what a single forward pass actually looked like in memory, I got fuzzy. Specifically: I was never sure, in a given diagram, whether each row of the attention matrix corresponded to one query or one key. That tiny ambiguity told me I did not really understand it yet. ...

May 15, 2026 · Giulia

Writing a Huffman Compressor: Prefix Codes, Bit Packing, and the Header Problem

Compression is one of those topics that sits in a peculiar spot: everyone uses it constantly, almost nobody writes one. The algorithms behind gzip, zip, zstd get treated as a black box you pipe bytes through. Huffman coding is the smallest interesting member of that family, and writing one by hand is the cleanest way to surface the parts of compression that aren’t really about compression at all: how you pack variable-length codes into fixed-size bytes, how the decoder finds the table, and what “the end of the file” even means when your last byte is half real data and half padding. ...

May 12, 2026 · Giulia

Writing a JSON Parser From Scratch: Lexer, Recursive Descent, and the Cases You Forget

I’ve been reading JSON for years without ever writing the code that parses it. That’s a strange gap: it’s a format we touch most days, and the one whose internals I’d thought about least. So I sat down one evening and wrote one from scratch in Python, no json.loads, no regex shortcuts, just the spec and a blank file. The structure is the textbook one: a lexer that turns characters into tokens, and a recursive-descent parser that turns tokens into a tree. What I want to write about is less the structure itself (that part is well-trodden) and more the places where the spec is sharper than it looks, and where a reasonable-seeming shortcut quietly produces a parser that accepts invalid input. ...

May 10, 2026 · Giulia

Reimplementing wc: Where Text Abstractions Leak

Reimplementing wc is less about counting and more about confronting the places where “text” stops being a clean abstraction. The interface is trivial; the semantics are not. Most of the interesting behavior lives at the boundary between bytes, encodings, and Unix I/O conventions. Below is a minimal clone. It’s deliberately scoped: correct along a few dimensions, incomplete along others. #!/usr/bin/env python3 import argparse, sys def get_bytes(raw): return len(raw) def get_lines(text): return text.count('\n') def get_words(text): return len(text.split()) def get_chars(text): return len(text) def main(): parser = argparse.ArgumentParser() parser.add_argument('file_path', nargs='?') parser.add_argument('-c', action="store_true") parser.add_argument('-l', action="store_true") parser.add_argument('-w', action="store_true") parser.add_argument('-m', action="store_true") args = parser.parse_args() if args.file_path: with open(args.file_path, "rb") as f: raw = f.read() label = args.file_path elif not sys.stdin.isatty(): raw = sys.stdin.buffer.read() label = None else: return text = raw.decode('utf-8') if not any([args.c, args.l, args.w, args.m]): args.c = args.l = args.w = True results = [] if args.l: results.append(str(get_lines(text))) if args.w: results.append(str(get_words(text))) if args.c: results.append(str(get_bytes(raw))) if args.m: results.append(str(get_chars(text))) parts = ' '.join(results) print(f"{parts} {label}" if label else parts) if __name__ == "__main__": main() Bytes vs characters is an interface boundary, not trivia The -c / -m distinction is where most implementations quietly diverge from spec. ...

May 4, 2026 · Giulia

C: The Memory Model and Stack Frames

This is part two of two. Part one covers variables, symbolic constants, and the preprocessor. C always evaluates expressions before executing C evaluates every expression before running it. If main is missing, the linker signals an error, that’s the entry point contract. Parameters to main (argc, argv) behave like local variables. No garbage collector In C there’s no garbage collector. Local variables are not “objects” managed by a runtime; they’re temporary allocations on registers or the stack. When the function returns, they’re gone. There’s no tracing, no finalization, no safety net. This is by design. ...

March 28, 2026 · Giulia

C: Variables, Symbolic Constants, and the Preprocessor

This is part one of two. Part two covers the C memory model and stack frames. -Wall If I use the -Wall flag, the compiler emits warnings. Simple rule: always use it. 0 means more than zero The value 0 is equivalent to false in C boolean expressions. It also shows up in shell short-circuit evaluation. In an expression like ./a.out && ls, ls only executes if ./a.out returns 0. That’s because 0 is the success exit code, and && short-circuits on failure. So ./a.out returning 0 makes ls run. The 0 in the exit code sense is “success / true” from the shell’s perspective, opposite of the C boolean convention, which trips people up. ...

March 22, 2026 · Giulia