Meow Programming Language Specification: Syntax, Types, Functions, and Pattern Matching
This document defines the syntax and semantics of the Meow Programming Language.
Meow is a cat-themed programming language that transpiles to Go. Source files use the .nyan extension and are encoded in UTF-8.
Notation
This specification uses Extended Backus-Naur Form (EBNF) for grammar productions:
Production = name "=" Expression "." .
Expression = Term { "|" Term } .
Term = Factor { Factor } .
Factor = name | literal | "(" Expression ")" | "[" Expression "]" | "{" Expression "}" .
[ ... ]denotes optional (0 or 1).{ ... }denotes repetition (0 or more)." ... "denotes terminal symbols.
Source Code Representation
Source code is UTF-8 encoded text in .nyan files. Newlines serve as statement terminators (semicolons are not used). The compiler processes a single .nyan file at a time.
Lexical Elements
Comments
Two forms of comments:
LineComment = "#" { any_char } newline .
BlockComment = "-~" { any_char } "~-" .
Line comments start with # and extend to the end of the line. Block comments start with -~ and end with ~-, and may span multiple lines. Comments are treated as whitespace by the parser.
Keywords
The following 25 identifiers are reserved as keywords:
nyan meow bring sniff scratch
purr paw nya lick picky
curl peek hiss nab flaunt
catnap yarn hairball kitty breed
collar pose groom self trill
Type Keywords
The following 6 identifiers are reserved as type keywords:
int float string bool furball litter
Identifiers
identifier = letter { letter | digit | "_" } .
letter = "a" ... "z" | "A" ... "Z" | "_" .
digit = "0" ... "9" .
Identifiers name variables, functions, types, and struct fields. By convention, all user-facing identifiers use snake_case.
Integer Literals
int_lit = digit { digit } .
Integer literals are sequences of decimal digits representing 64-bit signed integers.
Float Literals
float_lit = digit { digit } "." digit { digit } .
Float literals contain a decimal point and represent 64-bit IEEE 754 floating-point numbers.
String Literals
string_lit = '"' { char | escape } '"' .
escape = "\" ( '"' | "\" | "n" | "t" | "r" ) .
String literals are enclosed in double quotes. Supported escape sequences: \", \\, \n, \t, \r.
Operators and Delimiters
Operators:
+ - * / %
= == != < > <= >=
&& || !
|=| ~> . .. =>
Delimiters:
( ) { } [ ] , :
Types
Meow uses a gradual type system. Values are dynamically typed at runtime (boxed as Value), but optional static type annotations enable compile-time checking and optimized code generation.
Primitive Types
| Type | Description | Examples |
|---|---|---|
int | 64-bit signed integer | 42, -7, 0 |
float | 64-bit floating-point | 3.14, -0.5 |
string | UTF-8 text | "hello", "" |
bool | Boolean | yarn (true), hairball (false) |
Special Types
| Type | Description |
|---|---|
furball | Error value carrying a message string |
catnap | The nil/null value (singleton) |
Composite Types
| Type | Description | Syntax |
|---|---|---|
litter | Ordered collection of values | [1, 2, 3] |
| Map | String-keyed dictionary | {"key": value} |
kitty | User-defined struct | kitty Name { field: type } |
breed | Type alias (transparent) | breed Nickname = string |
collar | Newtype (nominal wrapper) | collar UserId = int |
pose | Interface (method signatures) | pose Showable { meow show() string } |
Type Alias (breed)
A breed declaration creates a transparent alias for an existing type. The alias is fully interchangeable with the original type in all operations.
BreedStmt = "breed" identifier "=" TypeExpr newline .
breed Nickname = string
nyan name Nickname = "Nyantyu" # string and Nickname are interchangeable
nya(name + " chan") # string operations work directly
breed is a compile-time construct only — it leaves no trace in the generated code.
Newtype (collar)
A collar declaration creates a distinct new type that wraps an existing type. Values must be constructed explicitly, and the inner value is accessed via .value.
CollarStmt = "collar" identifier "=" TypeExpr newline .
collar UserId = int
nyan id = UserId(42) # constructor wraps the value
nya(id.value) # .value unwraps it => 42
nya(id) # => UserId{value: 42}
Two collar types with the same underlying type are distinct:
collar Temperature = int
collar Humidity = int
nyan temp = Temperature(72)
nyan humid = Humidity(72)
# temp != humid — different collar types are never equal
Type Annotations
Type annotations are optional but recommended. They appear after identifiers:
TypeExpr = "int" | "float" | "string" | "bool" | "furball" | "litter" | identifier .
Variable declaration with type:
VarStmt = "nyan" identifier [ TypeExpr ] "=" Expr .
Function with typed parameters and return type:
FuncStmt = [ "trill" ] "meow" identifier "(" [ ParamList ] ")" [ TypeExpr ] Block .
ParamList = Param { "," Param } .
Param = identifier [ TypeExpr ] .
Go-style grouped types propagate right-to-left: in (a, b int), both a and b receive type int.
Expressions
Literal Expressions
IntLit = int_lit .
FloatLit = float_lit .
StringLit = string_lit .
BoolLit = "yarn" | "hairball" .
NilLit = "catnap" .
ListLit = "[" [ Expr { "," Expr } [ "," ] ] "]" .
MapLit = "{" [ MapEntry { "," MapEntry } [ "," ] ] "}" .
MapEntry = StringLit ":" Expr .
Identifier Expression
Ident = identifier .
Evaluates to the value bound to the identifier in the current scope.
Unary Expressions
UnaryExpr = ( "-" | "!" ) Expr .
-negates anintorfloat.!inverts truthiness.
Binary Expressions
BinaryExpr = Expr op Expr .
Arithmetic operators (+, -, *, /, %) require operands of the same type. + also concatenates strings.
Comparison operators (<, >, <=, >=) work on int and float. Equality operators (==, !=) work on all types.
Logical operators (&&, ||) use short-circuit evaluation. && returns the left operand if falsy, otherwise the right. || returns the left operand if truthy, otherwise the right.
Call Expression
CallExpr = Expr "(" [ Expr { "," Expr } ] ")" .
Calls a function, lambda, or built-in. Also used to construct kitty instances by calling the type name.
Lambda Expression
LambdaExpr = "paw" "(" [ ParamList ] ")" "{" Expr "}" .
Creates an anonymous function. The body is a single expression (not a block of statements).
paw(x int) { x * 2 }
Index Expression
IndexExpr = Expr "[" Expr "]" .
Accesses a list element by zero-based index.
Member Expression
MemberExpr = Expr "." identifier .
Accesses a field on a kitty instance, calls a method defined by groom, or calls a function in an imported package.
Pipe Expression
PipeExpr = Expr "|=|" Expr .
Passes the left expression as the first argument to the right expression. If the right side is a function call, the left value is prepended to its arguments:
x |=| f(y) # equivalent to f(x, y)
x |=| f # equivalent to f(x)
Catch Expression
CatchExpr = Expr "~>" Expr .
If the left expression panics, the right side is used as a fallback. If the right side is a function, it receives the Furball error as its argument:
risky() ~> 0 # fallback value
risky() ~> paw(err) { handle(err) } # handler function
Match Expression
MatchExpr = "peek" "(" Expr ")" "{" { MatchArm } "}" .
MatchArm = Pattern "=>" Expr [ "," ] .
Pattern = LitPattern | RangePattern | WildcardPattern .
LitPattern = Expr .
RangePattern = Expr ".." Expr .
WildcardPattern = "_" .
Evaluates the subject and tests it against each pattern in order. Returns the body of the first matching arm.
peek(n) {
0 => "zero",
1..10 => "low",
_ => "other"
}
Statements
Variable Declaration
VarStmt = "nyan" identifier [ TypeExpr ] "=" Expr newline .
Declares a variable and binds it to a value.
nyan x int = 42
nyan name = "Nyantyu"
Reassignment
AssignStmt = identifier "=" Expr newline .
Rebinds an existing variable to a new value.
Function Declaration
FuncStmt = [ "trill" ] "meow" identifier "(" [ ParamList ] ")" [ TypeExpr ] Block .
Block = "{" { Stmt } "}" .
Declares a named function. Functions that don’t explicitly bring a value implicitly return catnap.
meow greet(name string) string {
bring "Hello, " + name + "!"
}
Pure Functions (trill)
Prefixing a declaration with trill opts the function into a compile-time purity check. Inside a trill function the body may only call other trill functions and side-effect-free builtins (arithmetic/comparison operators, len, to_int, to_float, to_string, to_bytes, to_runes, is_furball, head, tail, append, lick, picky, curl). Calling nya, hiss, gag, an imported-package member, or a non-trill user function is a compile error. Lambda bodies are scanned recursively, so an impure lambda passed to lick/picky/curl is also rejected.
trill meow add(a int, b int) int {
bring a + b
}
Known limitation (step 1): passing a non-pure function as a bare value (without calling it) is not yet detected.
Return Statement
ReturnStmt = "bring" [ Expr ] newline .
Returns a value from the enclosing function.
Conditional Statement
IfStmt = "sniff" "(" Expr ")" Block [ "scratch" ( IfStmt | Block ) ] .
Evaluates the condition. If truthy, executes the body. Optional scratch provides else/else-if branches.
sniff (x > 0) {
nya("positive")
} scratch sniff (x == 0) {
nya("zero")
} scratch {
nya("negative")
}
Loop Statement
RangeStmt = "purr" identifier "(" RangeExpr ")" Block .
RangeExpr = Expr [ ".." Expr ] .
Two forms:
- Count form:
purr i (n)— iteratesifrom0ton-1. - Range form:
purr i (a..b)— iteratesifromatob(inclusive).
purr i (5) { nya(i) } # 0, 1, 2, 3, 4
purr i (1..5) { nya(i) } # 1, 2, 3, 4, 5
Nab Statement
NabStmt = "nab" string_lit newline .
Imports a standard library package. Available packages: "file", "http", "testing".
nab "http"
Kitty Statement
KittyStmt = "kitty" identifier "{" { KittyField } "}" .
KittyField = identifier ":" TypeExpr [ "," ] newline .
Defines a struct type with named, typed fields. A constructor function with the same name is automatically created.
kitty Point {
x: int
y: int
}
nyan p = Point(3, 7)
nya(p.x) # => 3
Breed Statement
BreedStmt = "breed" identifier "=" TypeExpr newline .
Declares a type alias. See Type Alias (breed) above.
Collar Statement
CollarStmt = "collar" identifier "=" TypeExpr newline .
Declares a newtype wrapper. See Newtype (collar) above.
Pose Statement
PoseStmt = "pose" identifier "{" { PoseMethod } "}" .
PoseMethod = "meow" identifier "(" [ ParamList ] ")" [ TypeExpr ] newline .
Defines an interface — a named set of method signatures. Types structurally satisfy a pose if they have all required methods with matching signatures.
pose Showable {
meow show() string
}
A pose is a compile-time construct used for structural type checking. It does not generate runtime code.
Groom Statement
GroomStmt = "groom" identifier "{" { FuncStmt } "}" .
Adds methods to an existing kitty or collar type. Each method is a meow function that receives the instance as self implicitly.
kitty Cat { name: string, age: int }
groom Cat {
meow show() string {
bring self.name + " (age " + to_string(self.age) + ")"
}
meow is_kitten() bool {
bring self.age < 1
}
}
nyan c = Cat("Nyantyu", 3)
nya(c.show()) # => Nyantyu (age 3)
The self keyword refers to the instance the method is called on. For kitty types, self.field accesses fields. For collar types, self.value accesses the wrapped value.
Self Expression
SelfExpr = "self" .
Refers to the current instance within a groom block. Only valid inside method bodies defined by groom.
Expression Statement
ExprStmt = Expr newline .
Any expression can appear as a statement. The result is discarded.
Built-in Functions
I/O
| Function | Signature | Description |
|---|---|---|
nya | nya(args...) | Print values (space-separated) with trailing newline |
Error Handling
| Function | Signature | Description |
|---|---|---|
hiss | hiss(args...) | Raise error — panics with "Hiss! ..." |
gag | gag(fn) → value | furball | Call fn(); recover from panic, return Furball on error |
is_furball | is_furball(v) → bool | Check if v is a Furball error value |
Type Conversion
| Function | Signature | Description |
|---|---|---|
to_int | to_int(v) → int | Convert float, bool, or int to int |
to_float | to_float(v) → float | Convert int to float |
to_string | to_string(v) → string | Convert any value to its string representation |
Collections
| Function | Signature | Description |
|---|---|---|
len | len(v) → int | Length of string or list |
head | head(list) → value | First element of a list |
tail | tail(list) → list | All elements except the first |
append | append(list, value) → list | New list with value appended |
Functional Operations
| Function | Signature | Description |
|---|---|---|
lick | lick(list, fn) → list | Map: apply fn to each element |
picky | picky(list, fn) → list | Filter: keep elements where fn returns truthy |
curl | curl(list, init, fn) → value | Reduce: fold list with accumulator |
Error Model
Errors in Meow use a panic/recover model:
Raising errors:
hiss("message")panics with the message"Hiss! message". Error messages are suffixed with", nya~"when raised from runtime functions.Error values: When a panic is caught, it becomes a
Furball— a value that carries the error message string.Catching errors: Three mechanisms:
gag(fn)— callsfn()and catches panics, returning aFurballon error.expr ~> fallback— evaluatesexpr; if it panics, usesfallbackinstead.expr ~> paw(err) { ... }— evaluatesexpr; if it panics, calls the handler with theFurball.
Checking errors:
is_furball(v)returnsyarnifvis aFurball,hairballotherwise.
Program Structure
A Meow program is a single .nyan file containing a sequence of top-level statements. The generated Go code follows this structure:
package main
import meow "github.com/135yshr/meow/runtime/meowrt"
import meow_file "github.com/135yshr/meow/runtime/file" // from nab "file"
import meow_http "github.com/135yshr/meow/runtime/http" // from nab "http"
import meow_testing "github.com/135yshr/meow/runtime/testing" // from nab "testing"
// user-defined functions
func main() {
// top-level statements
}
Truthiness
All values have a truthiness used by sniff conditions and logical operators:
| Value | Truthy? |
|---|---|
yarn | yes |
hairball | no |
catnap | no |
0 (int) | no |
0.0 (float) | no |
"" (empty string) | no |
| non-zero int/float | yes |
| non-empty string | yes |
empty list [] | no |
| non-empty list | yes |
empty map {} | no |
| non-empty map | yes |
| kitty | yes |
| furball | no |
| func | yes |