# Caw — language specification, v0.1 Caw is a small, interpreted programming language. It is named after the sound a crow makes. Source files end in `.caw`. This document is the agreement between the person writing Caw code and the program that runs it. If the interpreter disagrees with this document, one of the two is wrong and we have to decide which. ## Design principles 1. **Small enough to hold in your head.** Every feature must justify itself. 2. **Explicit over convenient.** Prefer catching a mistake over saving a keystroke. 3. **Readable errors.** A confusing error message is a bug. ## Lexical structure - **Comments** start with `//` and run to the end of the line. Discarded entirely. - **Whitespace** (spaces, tabs) is insignificant except as a separator between tokens. - **Newlines** end a statement. There are no semicolons. - **Identifiers** start with a letter or `_`, then any letters, digits or `_`. Case-sensitive: `count` and `Count` are different variables. - **Numbers** are decimal, with an optional fractional part: `42`, `3.14`. Internally all numbers are floating point. (See "Known limitations".) - **Strings** are double-quoted: `"hello"`. Escapes: `\n`, `\t`, `\"`, `\\`. ## Keywords Reserved, and therefore unusable as variable names: let fn return if else while true false nil and or not ## Types | Type | Examples | Notes | |----------|-----------------------|----------------------------------------| | number | `42`, `3.14`, `-7` | all numbers are floating point | | string | `"caw"` | immutable | | boolean | `true`, `false` | | | nil | `nil` | the absence of a value | | function | `fn (x) { ... }` | created with `fn` | ## Truthiness Only `false` and `nil` are falsy. Everything else — including `0` and `""` — is truthy. This is a deliberate choice: it means `if x` asks "does x exist?" rather than "is x non-zero?", which is the question people usually mean. ## Operators, loosest to tightest binding or and == != < > <= >= + - * / not - (prefix, applied to one value) () (function call) `+` adds numbers and concatenates strings. Mixing a number and a string is an error rather than a silent conversion — silent conversion is a famous source of bugs in other languages. ## Statements let NAME = EXPRESSION // declare a new variable NAME = EXPRESSION // reassign an existing variable if EXPRESSION { ... } else { ... } while EXPRESSION { ... } fn NAME(A, B) { ... } // declare a function return EXPRESSION EXPRESSION // evaluated, result discarded `let` declares; bare `=` reassigns. Reassigning a variable that was never declared is an error. This catches typos that would otherwise silently create a second variable. ## Scope Each `{ ... }` block creates a new scope. Names declared inside a block are invisible outside it. An inner scope can see and modify names from enclosing scopes. ## Built-in functions | Name | Purpose | |-----------|------------------------------------------| | `print(x)`| write x followed by a newline to output | | `len(x)` | length of a string | | `str(x)` | convert any value to a string | | `num(x)` | convert a string to a number, or error | ## Example program ```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 } print("a murder of nothing") ``` Expected output: ``` 3 crows 2 crows one crow a murder of nothing ``` ## Known limitations (v0.1) Deliberately out of scope for now. Listed so they're decisions, not oversights: - No integers, only floats. `1/3` will look ugly. - No lists, no maps, no objects. - No modules or imports — one file at a time. - No closures capturing state after the enclosing function returns. - Very slow. It's an interpreter written in an interpreted language.