Files use the .slop extension and run with:
uv run sloperator.py path/to/program.slop— Comments start with an em dash. Statements end with sparkles.
clanker describe(count) {
vibe count > 1 {
ship “big slop”✨
} nah {
ship “small slop”✨
}
}
slop MAX = 3✨
slop count = 0✨
ralph count < MAX {
hallucinate(describe(count))✨
count = count + 1✨
}
Prints small slop twice, then big slop.
| SlopLang | Conventional | Meaning |
|---|---|---|
slop |
let |
Declare a mutable variable |
clanker |
fn |
Define a function |
vibe |
if |
Conditional |
nah |
else |
Else branch |
ralph |
while |
Loop while a condition is ✅ |
hallucinate |
print |
Print one value, then a newline |
ship |
return |
Return one value from a function |
✨ |
; |
Statement terminator (required) |
— |
// |
Start a line comment |
“ ” |
" " |
String delimiters (straight quotes are a lexical error) |
✅ |
true |
Boolean true literal |
❌ |
false |
Boolean false literal |
There are no other reserved words, and no break, continue, for, or else if.
UTF-8, case-sensitive. Identifiers match [A-Za-z_][A-Za-z0-9_]*. Spaces, tabs, and newlines are whitespace; newlines do not end statements.
Emoji tokens are ✨ (U+2728), ✅ (U+2705), and ❌ (U+274C), each optionally followed by the variation selector U+FE0F, which some keyboards emit. ✨ must follow variable declarations, assignments, ship, and expression statements. It never follows a block's closing brace. — (U+2014) starts a comment that runs to end of line.
Strings open with “ (U+201C), close with ” (U+201D), and support escapes \n, \t, \”, and \\. A string cannot contain a raw newline. Unknown escapes and unterminated strings are lexical errors.
| Type | Examples | Notes |
|---|---|---|
| Number | 0, 42, 9001 |
Arbitrary-precision integers |
| String | “hello” |
Immutable Unicode text |
| Boolean | ✅, ❌ |
Required by conditions |
| Nil | Result of a function with no ship |
No source literal |
| Function | hallucinate, describe |
Built-in or user-defined |
No implicit conversions.
slop declares a mutable variable and requires an initializer. A scope cannot declare a name twice, but an inner block may shadow an outer name.
slop score = 10✨
slop LIMIT = 100✨
score = score + 5✨
score = score + 1✨
Assignment is a statement, not an expression: name = expression✨. It updates the nearest existing slop variable. Reading or assigning an undefined name is a runtime error.
Every brace block is a lexical scope. Assignment searches the current scope, then each enclosing scope.
Conditions take no parentheses and must be booleans; there is no truthiness.
vibe score >= 10 {
hallucinate(“ship it”)✨
} nah {
hallucinate(“keep working”)✨
}
ralph score > 0 {
score = score - 1✨
}
nah must be followed by a block; nest a vibe for another condition. ralph checks its condition before each iteration.
clanker declares a named function at the top level. Calls require exactly the declared number of arguments. Parameters and locals belong to the call's scope.
clanker add(left, right) {
ship left + right✨
}
hallucinate(add(20, 22))✨
ship returns one value; reaching the end of the body returns nil. ship outside a function and duplicate parameter names are syntax errors.
A clanker name is an immutable binding in the global scope: it cannot be redeclared or assigned, and it is a value, so slop f = add✨ followed by f(1, 2)✨ works. Calling a non-function is a runtime error. Functions must be declared before they are called; mutual recursion works when both are declared before the first call. No nested or anonymous functions.
Call depth is capped at 500 nested calls. Exceeding it is a runtime error (stack overflow), never a Python traceback.
hallucinate(value) is the only built-in. It accepts exactly one argument, prints it followed by a newline, and returns nil. Numbers print as base-10 digits, strings without quotes, booleans as ✅/❌, nil as nil, and functions as <clanker name>.
hallucinate(3)✨ — 3
+ adds numbers or concatenates strings. -, *, / accept only numbers; division truncates toward zero (-7 / 2 is -3, not floor division's -4) and division by zero is a runtime error. <, <=, >, >= compare numbers. ==, != compare any values; different types are unequal, and functions are equal only to themselves. && and || require booleans and short-circuit. Unary - negates a number.
Precedence, lowest to highest:
||&&==,!=<,<=,>,>=+,-*,/- Unary
- - Calls
- Literals, names,
( )
program -> topLevel* EOF ;
topLevel -> functionDecl | declaration ;
functionDecl -> "clanker" IDENTIFIER "(" parameters? ")" block ;
parameters -> IDENTIFIER ( "," IDENTIFIER )* ;
declaration -> variableDecl | statement ;
variableDecl -> "slop" IDENTIFIER "=" expression "✨" ;
statement -> assignStmt | ifStmt | loopStmt | returnStmt
| block | expressionStmt ;
assignStmt -> IDENTIFIER "=" expression "✨" ;
ifStmt -> "vibe" expression block ( "nah" block )? ;
loopStmt -> "ralph" expression block ;
returnStmt -> "ship" expression "✨" ;
block -> "{" declaration* "}" ;
expressionStmt -> expression "✨" ;
expression -> logicOr ;
logicOr -> logicAnd ( "||" logicAnd )* ;
logicAnd -> equality ( "&&" equality )* ;
equality -> comparison ( ( "==" | "!=" ) comparison )* ;
comparison -> term ( ( "<" | "<=" | ">" | ">=" ) term )* ;
term -> factor ( ( "+" | "-" ) factor )* ;
factor -> unary ( ( "*" | "/" ) unary )* ;
unary -> "-" unary | call ;
call -> primary ( "(" arguments? ")" )* ;
arguments -> expression ( "," expression )* ;
primary -> NUMBER | STRING | "✅" | "❌"
| IDENTIFIER | "(" expression ")" ;
The interpreter stops at the first lexical, syntax, or runtime error, writes the diagnostic to stderr, exits with status 1, and never shows a Python traceback. Output produced before a runtime error stays on stdout.
SlopLang lexical error at line 1: straight quotes are not supported, use “ and ” for strings
SlopLang syntax error at line 2: expected ✨ after variable declaration, found 'slop'
SlopLang runtime error at line 3: division by zero
The smart quotes, em dash, and emoji are part of the language, so you will type them a lot.
| Character | macOS | Windows | Linux (GTK) |
|---|---|---|---|
“ |
Option + [ |
Alt + 0147 |
Ctrl+Shift+U 201C |
” |
Option + Shift + [ |
Alt + 0148 |
Ctrl+Shift+U 201D |
— |
Option + Shift + - |
Alt + 0151 |
Ctrl+Shift+U 2014 |
✨ ✅ ❌ |
Ctrl + Cmd + Space |
Win + . |
Ctrl + . or Ctrl+Shift+U 2728 / 2705 / 274C |
The interpreter recognizes the common mistakes (", ;, //, and the en dash –) and tells you what to use instead. If an emoji shows up blank in your editor, the file is fine; the editor's font is missing the glyph. In Zed, set an explicit buffer_font_family (the bundled default has no ❌) and add "buffer_font_fallbacks": ["Apple Color Emoji"] or your platform's emoji font.
No floats, arrays, maps, indexing, nested functions, classes, modules, imports, file access, user input, exceptions, or built-ins beyond hallucinate.