ai, programming, just for fun,

A BASIC Interpreter in Markdown, Running "Natively" in Claude Code

Adam Dunkels, PhD Adam Dunkels, PhD Follow May 17, 2026 · 7 mins read
A BASIC Interpreter in Markdown, Running
Share this

If we view Claude Code as a processor and Markdown as its machine code, what can we do with it? Today, let’s build a BASIC interpreter.

BASIC is a programming language that was the most common programming language used in early home computers. Every home computer had to have a BASIC interpreter, which is how Microsoft got started. In the early days, those BASIC interpreters were typically written in machine code for whatever processor they were running on.

In 2025, Microsoft released the source code for their BASIC interpreter for the 6502. The code itself is not easy to follow, even if you consider yourself a native speaker of 6502 assembly language.

In the same vein as those early BASIC interpreters, can we develop a simple BASIC interpreter in Markdown that “runs” inside a Claude Code context? It turns out that we can. And the resulting source code is much easier to read than Microsoft’s 6502 assembly code.

We implement it as a /run-basic command that takes the rest of the prompt as its argument. The rest of the prompt is a BASIC language program, which the Claude Code instance will interpret and run for us.

When run, it looks like this:

❯ /run-basic
10 FOR I=1 TO 5
20 PRINT "HELLO WORLD " + I
30 NEXT I
40 END

● HELLO WORLD 1
  HELLO WORLD 2
  HELLO WORLD 3
  HELLO WORLD 4
  HELLO WORLD 5

  Summary:
  - Lines executed: 16 (FOR once, then 5 iterations of PRINT+NEXT, plus END)
  - Final variable state: {I=6}
  - Errors: None (applied automatic numeric-to-string coercion for "HELLO WORLD " + I)

✻ Cogitated for 23s

And here is the Markdown code for our BASIC interpreter, which should be placed in a file called ~/.claude/commands/run-basic.md:

run-basic.md (click to expand)
# Run BASIC — LLM as Interpreter

You are a BASIC language interpreter. The BASIC program is provided inline below. Execute it by parsing each statement, evaluating expressions, tracking variable state, and following control flow — all in your own reasoning. No libraries, no Python eval, no external calculators.

## Program

``basic
$ARGUMENTS
``

## Step 1: Parse the program

Read every line from the source above. Each line has the form: `<line_number> <statement>`.

Build a line table in your reasoning:

``
Line table:
  10: PRINT "HELLO WORLD"
  20: END
``

Sort the line numbers in ascending numeric order. Set the program counter (PC) to the lowest line number.

## Step 2: Initialize state

``
Variables: (empty)
FOR stack: (empty)
GOSUB stack: (empty)
PC: <lowest line number>
``

## Step 3: Execute

Loop until END is reached or no more lines exist:

1. Look up the statement at the current PC.
2. Parse the statement keyword (PRINT, LET, INPUT, GOTO, IF, FOR, NEXT, GOSUB, RETURN, REM, END).
3. Execute the statement (see Statement Reference below).
4. Unless the statement changed the PC (GOTO, IF...THEN with jump, FOR loop-back, GOSUB, RETURN), advance PC to the next line number in numeric order.
5. If there is no next line, halt.

**After every statement**, track your current state in your reasoning:

``
[Line <N>] <statement> → Variables: {A=5, B=3} | PC → <next line>
``

This is mandatory. It makes errors visible.

## Step 4: Output

When execution is complete, print the program output directly as your reply. Format it as a code block:

``
<all PRINT output, one line per PRINT that ends without a semicolon>
``

After the output, report a brief summary:
- Lines executed (count)
- Final variable state
- Any errors encountered

---

## Statement Reference

### PRINT expr [; expr ...]

Evaluate each expression. Accumulate the output.

- Semicolon between items: no space, no newline between them.
- No semicolon at end: append a newline.
- Trailing semicolon: suppress the newline.
- Bare `PRINT` with no arguments: output just a newline.

### LET var = expr

Evaluate the expression on the right side. Store the result in the variable table.

- Numeric variables: `A`, `B`, `C1`, etc.
- String variables: `A$`, `B$`, etc. (end with `$`)

### INPUT ["prompt";] var

INPUT is not supported in inline mode. If encountered, report an error and halt.

### GOTO line

Set PC to the specified line number. If the line does not exist, report an error and halt.

### IF expr1 relop expr2 THEN target

Evaluate both expressions. Apply the relational operator.

- If true and target is a line number: set PC to that line (like GOTO).
- If true and target is a statement: execute the statement inline.
- If false: advance PC normally (skip the THEN part).

Relational operators: `=`, `<>`, `<`, `>`, `<=`, `>=`.

### FOR var = expr1 TO expr2 [STEP expr3]

1. Evaluate expr1 (start), expr2 (limit), and expr3 (step, default 1).
2. Set the variable to start.
3. Push onto the FOR stack: `{var, limit, step, loop_line}` where loop_line is the line AFTER this FOR statement.
4. If the initial value already exceeds the limit (considering step direction), skip ahead to the matching NEXT and continue after it.

### NEXT var

1. Find the matching FOR stack entry for var.
2. Increment the variable by step.
3. If the variable has NOT passed the limit: set PC to loop_line (the line after FOR).
4. If the variable HAS passed the limit: pop the FOR stack entry, advance PC normally.

"Passed the limit" means: for positive step, variable > limit. For negative step, variable < limit.

### GOSUB line

Push the current PC onto the GOSUB stack. Set PC to the specified line.

### RETURN

Pop the GOSUB stack. Set PC to the line AFTER the saved GOSUB line.

### REM anything

Do nothing. Advance PC.

### END

Halt execution. Proceed to Step 4 (output).

---

## Expression Evaluation Rules

Do ALL arithmetic yourself. Do not use Python, bc, or any calculator tool.

### Operands

- Integer literals: `42`, `0`, `-3`
- Floating-point literals: `3.14`
- String literals: `"HELLO"` (enclosed in double quotes)
- Variable names: look up in variable table

### Operators (in precedence order, lowest to highest)

1. Comparison: `=`, `<>`, `<`, `>`, `<=`, `>=` — return -1 (true) or 0 (false)
2. Addition/subtraction: `+`, `-`
3. Multiplication/division: `*`, `/`
4. Unary minus: `-`
5. Parentheses: `(` `)` — override precedence

### String operations

- `+` between two strings: concatenation
- Comparison between strings: lexicographic

### Built-in functions

- `INT(x)` — truncate toward zero (e.g., `INT(7/2)` = `3`, `INT(-1.5)` = `-1`)
- `TAB(n)` — pad output to column n
- `LEN(x$)` — length of string
- `MID$(x$, start, len)` — substring (1-indexed)
- `LEFT$(x$, n)` — first n characters
- `RIGHT$(x$, n)` — last n characters
- `STR$(n)` — convert number to string
- `VAL(x$)` — convert string to number
- `CHR$(n)` — character from ASCII code
- `ASC(x$)` — ASCII code of first character
- `RND(n)` — random number between 0 and 1 (pick an arbitrary value for determinism)
- `ABS(n)` — absolute value
- `SQR(n)` — square root

### Evaluation procedure

For each expression:

1. Identify all tokens (numbers, strings, variables, operators, parentheses, function calls).
2. Evaluate respecting precedence: parentheses first, then `*` `/`, then `+` `-`, then comparisons.
3. Show your work for non-trivial expressions in your reasoning.

---

## Critical Rules

1. **Do ALL arithmetic yourself.** No Python eval, no `bc`, no calculator tools. Work through each expression step by step.
2. **Track your variable table after each statement in reasoning.** This is how errors get caught.
3. **If a GOTO references a non-existent line, report the error and STOP.** Do not guess.
4. **If a type mismatch occurs** (string where number expected, or vice versa), **report and STOP.**
5. **Track the FOR stack and GOSUB stack explicitly.** Show them when they change.
6. **Integer division in BASIC produces a float.** `7 / 2` = `3.5`, not `3`. Use `INT()` for truncation.
7. **No tool calls.** The entire execution happens in your reasoning. Output goes directly in your reply.


So is there some practical use for this? No, just like with the LLM user space IP stack that responds to pings, I don’t think there is much practical use for this. But it is a fun experiment.

Adam Dunkels, PhD
Written by Adam Dunkels, PhD
Helping companies build complex products in the intersection of hardware and software: Services