Caw #
A small, interpreted programming language, built from scratch to understand how programming languages work. Named after the sound a crow makes.
Source files end in .caw.
// Count crows.
let crows = 3
fn describe(n) {
if n == 1 {
return "one crow"
} else {
return str(n) + " crows"
}
}
while crows > 0 {
print(describe(crows))
crows = crows - 1
}
Status #
Early. Very early. Currently building the lexer.
Caw does not run yet. This is deliberate: the repository exists from the beginning so the history shows how a language gets built, not just the finished result.
Why this exists #
This is a learning project. The goal is understanding — how text becomes behaviour, and how a pile of files becomes a project other people can join. Caw is not trying to be a good language. It is trying to be a legible one.
If you want a serious small language to learn from, read Crafting Interpreters — it's free, and it's the best book on the subject.
How it works #
Caw is an interpreter written in Python. It runs your code in three stages:
"let x = 1 + 2" raw text
| lexer caw/lexer.py
LET IDENT ASSIGN ... tokens: labelled chunks
| parser caw/parser.py
assign a syntax tree: structure
/ \
x add
/ \
1 2
| evaluator caw/interp.py
x is now 3 something happened
Each stage knows less than you'd think. The lexer has no idea what if means;
it only knows that if is a keyword. Structure is the parser's problem and
meaning is the evaluator's. That separation is the whole trick.
Layout #
| Path | What's in it |
|---|---|
SPEC.md |
the language specification — start here |
caw/tokens.py |
token types and the keyword table |
caw/lexer.py |
text into tokens |
examples/ |
example .caw programs |
tests/ |
automated tests |
Requirements #
Python 3.9 or newer. No dependencies.
Contributing #
Contributions welcome, especially from other people learning this stuff.
Because this is a learning project, explanations are as valuable as code. A pull request that makes a confusing function clearer, or adds a comment explaining why rather than what, is genuinely as useful here as a new feature.
If you're adding a language feature, update SPEC.md in the same change. The
spec is the contract; code that disagrees with it is a bug in one of the two.
Licence #
MIT — do what you like with it, just keep the copyright notice.