Language Specification
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 19 identifiers are reserved as keywords:
nyan meow bring sniff scratch
purr paw nya lick picky
curl peek hiss nab flaunt
catnap yarn hairball kitty
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 } |
Type Annotations
Type annotations are optional but recommended. They appear after identifiers:
TypeExpr = "int" | "float" | "string" | "bool" | "furball" | "litter" .
Variable declaration with type:
VarStmt = "nyan" identifier [ TypeExpr ] "=" Expr .
Function with typed parameters and return type:
FuncStmt = "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, 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 = "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 + "!"
}
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
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 |