Teddy

Docs

About

Teddy is a small programming language inspired by the language-building chapter of Eloquent JavaScript by Marijn Haverbeke. The aim is to understand interpreters and compilers by building a language from first principles (my own lexer, parser, AST and code generator) rather than following the book verbatim. It diverges from the book’s example in design and execution, reflecting experiments and ideas formed along the way.

The name is not just because “teddy” is the canonical toy name, but because the word’s origin (President Teddy Roosevelt) is one I find interesting. It also feels fitting: an innocuous name with a hint of larger ambition beneath it.

I wrote the language in 2024 while learning JavaScript (see the original). This version keeps that code as it was, adds TypeScript types around it, and gives it a playground.

How it works

What the book calls a “parser” reads source code and produces a data structure (an AST) that reflects the structure of the program. Teddy splits that job into two steps, then adds a third that produces JavaScript:

  1. Lex: break the code into tokens (atoms).
  2. Parse: build an AST (Abstract Syntax Tree) from those tokens.
  3. Transpile: turn the AST into JavaScript. JS for now; an AST could become any language.

Isn’t it misleading to call this a language?

It just translates into JS, and I wouldn’t argue otherwise. It’s still worth doing, because it teaches how a language breaks code into tokens, how it builds an AST from them, and how an AST becomes something a machine can run.

Why “atoms”?

It isn’t poor poetry. An atom literally means “something that cannot be broken up any further”. In the same way, a token is the smallest piece of information a language considers legal.

A guide to Teddy

suppose votingAge -> 18, suppose age -> 10;
suppose name -> "john";

if(age < 15)
    print(name, "cannot vote");
elseIf(age < votingAge)
    print(name, "should vote soon");
else
    print(name, "can vote");

suppose greeting -> 'hello ${name}';
print(greeting);

suppose add -> function(x, y)
{
    return x + y;
};
print(add(2, 3));

Data types

TypeSyntaxExample
Number (primitive)Integers.18, -5
String (primitive)Characters between double quotes."john"
Template literal (primitive)Characters between single quotes, with ${…} interpolation.'hello ${name}'
Object (non-primitive)Key-value pairs between braces.{name: "john", age: 21}
Array (non-primitive)Values between brackets; nesting allowed.[1, 2, {name: "bob"}, ["x"]]

Keywords

KeywordMeaningExample
supposeDeclares a non-const, loosely typed binding.suppose x -> 1;
functionDeclares a function.function(x, y) { return x + y; };
returnReturns a value from a function.return x;
if, elseIf, elseLike if, else if and else in JS.elseIf(age < 18)

Operators

KindOperatorsNote
Assignment->
Arithmetic+ - * / %
Comparison= == != !== < > <= >== becomes JS ==, and == becomes JS ===.

Everything else

print
Teddy’s console.log.
JavaScript
JS is the proto-language behind Teddy. Inside expressions and blocks, JS functions and values work as they are (Math.max(1, 2), true). Only print can be called as a statement on its own.
Comments
Start with // and run to the end of the line.
Blocks
{ … } can be nested.
Bad code
Usually isn’t reported as a syntax error. It is mostly ignored, sometimes throws, and sometimes runs forever. The playground stops anything that runs longer than 2 seconds.