Skip to content

Instantly share code, notes, and snippets.

@cschram
Created December 14, 2025 04:07
Show Gist options
  • Select an option

  • Save cschram/af7281e07de488f10e33dfd26595eff7 to your computer and use it in GitHub Desktop.

Select an option

Save cschram/af7281e07de488f10e33dfd26595eff7 to your computer and use it in GitHub Desktop.
Otter Pest Grammer
Module = { SOI ~ (Declaration* | EOI) }
// Indentation
// Uses the Pest stack to check indentation level
Indent = _{ PUSH(" "+ | "\t"+) }
Dedent = _{ NEWLINE ~ DROP }
CurrentIndent = _{ PEEK_ALL }
// Identifiers
TypeIdentifier = { Identifier ~ Generics? }
Identifier = @{ !Keyword ~ (NonDigitCharacter ~ (NonDigitCharacter | ASCII_DIGIT)*) }
NonDigitCharacter = { ASCII_ALPHA | "_" }
// Top-level Declarations
Declaration = {
(
ImportDeclaration |
StructDeclaration |
EnumDeclaration |
TraitDeclaration |
ImplDeclaration |
FunctionDeclaration |
TypeAliasDeclaration
) ~
(NEWLINE | EOI)
}
// Imports
ImportDeclaration = @{ Pub? ~ Use ~ WHITESPACE ~ Identifier ~ ( Colon ~ Identifier)* }
// Structs
StructDeclaration = {
Struct ~ WHITESPACE ~
TypeIdentifier ~
Colon ~ NEWLINE ~
StructBody
}
StructBody = { Indent ~ StructField ~ (NEWLINE ~ CurrentIndent ~ StructField)* ~ Dedent }
StructField = { Identifier ~ Colon ~ Identifier }
// Enums
EnumDeclaration = @{
Enum ~ WHITESPACE ~
TypeIdentifier ~
Colon ~ NEWLINE ~
EnumBody
}
EnumBody = { Indent ~ EnumVariant ~ (NEWLINE ~ CurrentIndent ~ EnumVariant)* ~ Dedent }
EnumVariant = { Identifier ~ (Colon ~ LParen ~ Params)? }
// Traits
TraitDeclaration = @{
Trait ~ WHITESPACE ~
Type ~
Colon ~ NEWLINE ~
TraitBody
}
TraitBody = { Indent ~ TraitMethod ~ (NEWLINE ~ CurrentIndent ~ TraitMethod)* ~ Dedent }
TraitMethod = { FunctionDeclaration | FunctionSignature }
// Impls
ImplDeclaration = @{
Impl ~ WHITESPACE ~
TypeIdentifier ~
(WHITESPACE ~ For ~ TypeIdentifier)? ~
Colon ~ NEWLINE ~
ImplBody
}
ImplBody = {
Indent ~ FunctionDeclaration ~
(NEWLINE ~ CurrentIndent ~ FunctionDeclaration)* ~
Dedent
}
// Functions
FunctionDeclaration = { FunctionSignature ~ Colon ~ NEWLINE ~ Block }
FunctionSignature = {
Pub? ~ Fn ~
Identifier ~
LParen ~ TypedParams ~ RParen ~
Arrow ~ Type
}
// Type aliases
TypeAliasDeclaration = { Type ~ WHITESPACE ~ TypeIdentifier ~ Eq ~ TypeIdentifier }
// Parameters
Params = { Identifier ~ (Comma ~ Identifier)* }
StructParams = { StructParam ~ (Comma ~ StructParam)* }
StructParam = { Identifier ~ Eq ~ Expression }
TypedParams = { TypedParam ~ (Comma ~ TypedParam)* }
TypedParam = { Identifier ~ Colon ~ TypeIdentifier }
// Generic parameters
Generics = { Lt ~ Params ~ Gt }
// Blocks
Block = { Indent ~ Statement ~ (CurrentIndent ~ Statement)* ~ Dedent }
// Statements
Statement = {
(
LetStatement |
Assignment |
IfStatement |
ForStatement |
WhileStatement |
ReturnStatement |
Break |
Continue |
Pass |
Expression
) ~ NEWLINE
}
LetStatement = { Let ~ WHITESPACE ~ Identifier ~ Eq ~ Expression }
Assignment = { Access ~ Eq ~ Expression }
IfStatement = {
If ~ WHITESPACE ~ Expression ~
Colon ~ NEWLINE ~
Block ~
ElIfStatement* ~
ElseStatement?
}
ElifStatement = {
ElIf ~ WHITESPACE ~ Expression ~
Colon ~ NEWLINE ~
Block
}
ElseStatement = { Else ~ Colon ~ NEWLINE ~ Block }
ForStatement = {
For ~ WHITESPACE ~
Identifier ~ WHITESPACE ~ In ~ WHITESPACE ~ Expression ~
Colon ~ NEWLINE ~
Block
}
WhileStatement = {
While ~ WHITESPACE ~ Expression ~
Colon ~ NEWLINE ~
Block
}
ReturnStatement = { Return ~ (WHITESPACE ~ Expression)? }
// Expressions
Expression = {
Literal |
BinaryExpression |
UnaryExpression |
CallExpression |
AccessExpression |
ParenExpression
}
BinaryExpression = { Expression ~ BinaryOp ~ Expression }
BinaryOp = {
Plus ~
Minus ~
Star ~
Slash ~
Percent ~
EqEq ~
Neq ~
Lt ~
Gt ~
LtEq ~
GtEq ~
(Is ~ Not) ~
Is ~
And ~
Or
}
UnaryExpression = { UnaryOp ~ Expression }
UnaryOp = { Bang ~ Not ~ Minus }
CallExpression = { AccessExpression ~ LParen ~ (StructParams | Params) ~ RParen }
AccessExpression = { Identifier ~ (Dot ~ Identifier)* }
ParenExpression = { LParen ~ Expression ~ RParen }
// Literals
Literal = {
UnitLiteral |
NumberLiteral |
StringLiteral |
BoolLiteral |
ArrayLiteral |
DictLiteral
}
UnitLiteral = { LParen ~ RParen }
NumberLiteral = { ASCII_DIGIT+ ( Dot ~ ASCII_DIGIT+) }
StringLiteral = { PUSH(Quote | DoubleQuote) ~ StringCharacter* ~ POP }
StringCharacter = {
!PEEK ~
(WHITESPACE | StringUnicode | EscapedQuote | AllowedQuote | EscapedNewline)
}
// Unicode allowed in stirngs
StringUnicode = { LETTER | MARK | NUMBER | PUNCTUATION | SYMBOL | SEPARATOR }
// Escaped version of the quote being used for the string
EscapedQuote = { Escape ~ PEEK }
// Matches whichever quote is not being used for the string
AllowedQuote = { !PEEK ~ (Quote | DoubleQuote) }
EscapedNewline = { Escape ~ NEWLINE }
BoolLiteral = { True | False }
ArrayLiteral = {
LBracket ~
(
Expression ~
(Comma ~ Expression)*
)? ~
RBracket
}
DictLiteral = {
LBrace ~
(
DictEntry ~
(Comma ~ DictEntry)*
)? ~
RBrace
}
DictEntry = { StringLiteral ~ Colon ~ Expression }
// Keywords
Keyword = {
Use |
As |
Pub |
Fn |
Struct |
Enum |
Trait |
Impl |
Return |
Let |
If |
Else |
Elif |
For |
While |
Break |
Continue |
Pass |
In |
Is |
Not |
And |
Or |
Match |
Case |
Await |
Spawn |
True |
False
}
Use = _{ "use" }
As = _{ "as" }
Pub = _{ "pub" }
Fn = _{ "fn" }
Struct = _{ "struct" }
Enum = _{ "enum" }
Trait = _{ "trait" }
Type = _{ "type" }
Impl = _{ "impl" }
Return = _{ "return" }
Let = _{ "let" }
If = _{ "if" }
Else = _{ "else" }
Elif = _{ "elif" }
For = _{ "for" }
While = _{ "while" }
Break = _{ "break" }
Continue = _{ "continue" }
Pass = _{ "pass" }
In = _{ "in" }
Is = _{ "is" }
Not = _{ "not" }
And = _{ "and" }
Or = _{ "or" }
Match = _{ "match" }
Case = _{ "case" }
Await = _{ "await" }
Spawn = _{ "spawn" }
True = _{ "true" }
False = _{ "false" }
// Structural characters
Colon = _{ ":" }
LParen = _{ "(" }
RParen = _{ ")" }
LBrace = _{ "{" }
RBrace = _{ "}" }
LBracket = { "[" }
RBracket = { "]" }
Comma = _{ "," }
Dot = _{ "." }
Quote = _{ "'" }
DoubleQuote = _{ '"' }
Escape = _{ "\\" }
// Operators
Arrow = _{ "->" }
Eq = _{ "=" }
EqEq = _@{ Eq ~ Eq }
Bang = _{ "!" }
Neq = _@{ Bang ~ Eq }
Lt = _{ "<" }
Gt = _{ ">" }
LtEq = _@{ Lt ~ Eq }
GtEq = _@{ Gt ~ Eq }
Plus = _{ "+" }
Minus = _{ "-" }
Star = _{ "*" }
Slash = _{ "/" }
Percent = _{ "%" }
Pipe = _{ "|" }
Amp = _{ "&" }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment