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

TypeDescriptionExamples
int64-bit signed integer42, -7, 0
float64-bit floating-point3.14, -0.5
stringUTF-8 text"hello", ""
boolBooleanyarn (true), hairball (false)

Special Types

TypeDescription
furballError value carrying a message string
catnapThe nil/null value (singleton)

Composite Types

TypeDescriptionSyntax
litterOrdered collection of values[1, 2, 3]
MapString-keyed dictionary{"key": value}
kittyUser-defined structkitty Name { field: type }
breedType alias (transparent)breed Nickname = string
collarNewtype (nominal wrapper)collar UserId = int
poseInterface (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 an int or float.
  • ! 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) — iterates i from 0 to n-1.
  • Range form: purr i (a..b) — iterates i from a to b (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

FunctionSignatureDescription
nyanya(args...)Print values (space-separated) with trailing newline

Error Handling

FunctionSignatureDescription
hisshiss(args...)Raise error — panics with "Hiss! ..."
gaggag(fn) → value | furballCall fn(); recover from panic, return Furball on error
is_furballis_furball(v) → boolCheck if v is a Furball error value

Type Conversion

FunctionSignatureDescription
to_intto_int(v) → intConvert float, bool, or int to int
to_floatto_float(v) → floatConvert int to float
to_stringto_string(v) → stringConvert any value to its string representation

Collections

FunctionSignatureDescription
lenlen(v) → intLength of string or list
headhead(list) → valueFirst element of a list
tailtail(list) → listAll elements except the first
appendappend(list, value) → listNew list with value appended

Functional Operations

FunctionSignatureDescription
licklick(list, fn) → listMap: apply fn to each element
pickypicky(list, fn) → listFilter: keep elements where fn returns truthy
curlcurl(list, init, fn) → valueReduce: fold list with accumulator

Error Model

Errors in Meow use a panic/recover model:

  1. Raising errors: hiss("message") panics with the message "Hiss! message". Error messages are suffixed with ", nya~" when raised from runtime functions.

  2. Error values: When a panic is caught, it becomes a Furball — a value that carries the error message string.

  3. Catching errors: Three mechanisms:

    • gag(fn) — calls fn() and catches panics, returning a Furball on error.
    • expr ~> fallback — evaluates expr; if it panics, uses fallback instead.
    • expr ~> paw(err) { ... } — evaluates expr; if it panics, calls the handler with the Furball.
  4. Checking errors: is_furball(v) returns yarn if v is a Furball, hairball otherwise.

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:

ValueTruthy?
yarnyes
hairballno
catnapno
0 (int)no
0.0 (float)no
"" (empty string)no
non-zero int/floatyes
non-empty stringyes
empty list []no
non-empty listyes
empty map {}no
non-empty mapyes
kittyyes
furballno
funcyes