Full Language Reference
Parser-accurate specification of the Grove language. Optimized for LLM consumption: EBNF grammar, lexical rules, semantic constraints, and generation rules.
Purpose: Complete language description for machine generation and validation.
1. Grammar Overview
1.1 Module
Module := [ModuleHeader] Item*
ModuleHeader := "module" Ident
moduleis contextual (lexed as identifier, parsed specially at top of file).- Items can appear in mixed order.
1.2 Top-Level Items
Item := ImportDecl
| TypeDecl
| EnumDecl
| RecordDecl
| EventDecl
| ActionDecl
| ValidateDefDecl
| UpcastDecl
| FunctionDecl
| TriggerDecl
| ScheduleDecl
| WorkflowDecl
| ActivityDecl
| ServiceDecl
| RouteDecl
| QueryDecl
| WebhookDecl
| TestDecl
Lifecycle/delete annotation prefixes before action/event:
@create action ...@upsert action ...@delete action ...@delete event ...@delete(hard) action|event ...
2. Lexical Rules
2.1 Comments and Whitespace
- Whitespace: spaces, tabs, CR, LF are skipped.
- Comments: line comments
// ...(discarded). - No block comment form.
2.2 Identifiers
- Start:
[A-Za-z_] - Continue:
[A-Za-z0-9_]
2.3 Literals
- Integer:
123 - Decimal:
123.45 - Duration:
30s,5m,2h,7d - String:
"text" - Interpolated string:
"hello ${expr}" - Boolean:
true,false - Null:
null
String escapes: \\, \", \n, \t, \r, \$
2.4 Operators and Punctuation
Operators: + - * / % == != < <= > >= && || ! ?? ?. = -> => ...
Delimiters: () {} [] . , : ; @ ? |
2.5 Keywords
Hard keywords (dedicated token kinds):
- Declarations:
record event action validate upcast type enum import trigger schedule workflow activity service route query function webhook test - Control/expr:
let if else for in match emit fail return as true false null - Builtin types:
Int Decimal Bool String Id Date Time DateTime Duration Epoch Value Blob
Contextual keywords (lexed as Ident):
module apply state invariant node edge start spawn branch parallel map compensate search with operation source sink credentials auth protocol data when fetch publish invoke retry_policy after host host_fn signal run request expect assert ok error status errors headers input output timeout max_history backoff workflow on compensated index args sql gsql verify
3. Expression Grammar
3.1 Precedence (lowest to highest)
||&&== !=< > <= >=+ -* / %??- unary:
! - - postfix:
. ?. []and call chaining
3.2 Expression Forms
Expr := IntLit | DecimalLit | BoolLit | StringLit | InterpolatedString
| "null" | DurationLit | Ident
| FnCall | MethodCall | FieldAccess | OptionalChain | IndexExpr
| ListExpr | ObjectExpr
| UnaryExpr | BinaryExpr | NullCoalesce
| IfExpr | MatchExpr | LambdaExpr | SpreadExpr
| "(" Expr ")"
Concrete forms:
- Function call:
f(a, b) - Method call:
x.method(a) - Field access:
obj.field - Optional chain:
obj?.field - Index:
arr[i] - List literal:
[a, b, c] - Object literal:
{ k: v, ...rest } - If expression:
if cond { thenExpr } else { elseExpr } - Match expression:
match expr { pat -> body, _ -> fallback } - Lambda:
|x| x + 1or|x, y| x + y
Notes:
- Lambda syntax is pipe-based (
|...|), not=>. - Match wildcard is
_. - Enum matches must cover all variants or include
_. - Non-enum matches always require
_.
4. Type Grammar
TypeRef := BaseType ["?"]
BaseType := "Int" | "Decimal" | "Bool" | "String" | "Id"
| "Date" | "Time" | "DateTime" | "Duration" | "Epoch"
| "Value" | "Blob"
| "List" "<" TypeRef ">"
| "Map" ["<" TypeRef "," TypeRef ">"]
| "[" TypeRef "]"
| "{" FieldDecl* "}"
| QualifiedName
QualifiedName := Ident ("." Ident)*
- Bare
Mapdefaults toMap<String, Value>. - Optional type is postfix only (
T?).
5. Statement Grammar
Stmt := LetStmt | IfStmt | ForStmt | ReturnStmt
| EmitStmt | FailStmt | PublishStmt | ExprStmt
LetStmt := "let" Ident [":" TypeRef] "=" Expr
IfStmt := "if" Expr Block ["else" (IfStmt | Block)]
ForStmt := "for" [Ident ","] Ident "in" Expr Block
ReturnStmt := "return" Expr
EmitStmt := "emit" Ident [VersionTag] "{" FieldAssign* "}"
FailStmt := "fail" Expr Expr
PublishStmt:= "publish" Ident "{" FieldAssign* "}"
let bindings are immutable. Allowed in: pure functions, action apply, event apply, activity apply, route apply, upcast blocks. Not allowed in: edge mappings, trigger mappings.
6. Declaration Grammar
6.1 Import
ImportDecl := "import" ImportPath VersionTag ["as" Ident]
ImportPath := Ident ("/" Ident)*
VersionTag := "v" Int
grove/ prefix is reserved for standard library.
6.2 Type and Enum
TypeDecl := "type" Ident "{" FieldDecl* "}"
EnumDecl := "enum" Ident "{" Ident ("," Ident)* "}"
FieldDecl := Ident ":" TypeRef ["=" DefaultExpr] [Annotation*]
6.3 Record
RecordDecl := "record" [Ident] ["@parent" "(" Ident ")"] "{" RecordBody "}"
RecordBody := KindDecl? FieldDecl* (InvariantDecl | StateDecl)*
KindDecl := "kind" StringLiteral
InvariantDecl := "invariant" Ident "{" Expr "}"
StateDecl := "state" Ident "{" StateTransition* "}"
StateTransition := QualifiedName "->" QualifiedName "on" Ident [VersionTag]
At most one root record per module. Root records are unnamed and must declare a kind string (2-5 lowercase ASCII letters, not starting with mz, unique per project). Named records may use @parent(<root>) to mark themselves as child records; child records have no kind.
6.4 Event
EventDecl := [DeleteAnnotation] "event" Ident [VersionTag] "{" FieldDecl* [ApplyBlock] "}"
ApplyBlock := "apply" "{" Stmt* "}"
6.5 Action
ActionDecl := [LifecycleAnnotation] "action" Ident [VersionTag] "{"
FieldDecl*
ValidateRef*
ApplyBlock
"}"
LifecycleAnnotation := "@create" | "@upsert" | "@delete" | "@delete" "(" "hard" ")"
ValidateRef := "validate" Ident ["{" Expr "}"]
6.6 Validate
ValidateDefDecl := "validate" Ident "{" Expr "}"
Body must evaluate to boolean.
6.7 Upcast
UpcastDecl := "upcast" ("action" | "event") Ident VersionTag "->" VersionTag "{" Stmt* "}"
Source version must be lower than target version.
6.8 Function
FunctionDecl := "function" Ident "(" ParamList ")" ["->" TypeRef] "{" Stmt* "}"
ParamList := [Param ("," Param)*]
Param := Ident ":" TypeRef
6.9 Workflow
WorkflowDecl := "workflow" Ident VersionTag "{" WorkflowBody* "}"
WorkflowBody := NodeDecl | EdgeDecl | SearchDecl | CompensatedDecl
NodeDecl := "node" Ident "=" ActivityRef ["{" FieldMapping* "}"]
| "node" Ident "=" "branch" "{" BranchArm* "}"
| "node" Ident "=" "parallel" "{" ParallelItem* "}"
| "node" Ident "=" "map" Expr "{" ... "}"
| "node" Ident "=" "start" WorkflowRef "{" FieldMapping* "}"
| "node" Ident "=" "spawn" WorkflowRef "{" FieldMapping* "}"
EdgeDecl := "edge" Ident "->" Ident
SearchDecl := "search" "{" FieldMapping* "}"
CompensatedDecl := "on" "compensated" "->" TypeRef "{" Stmt* "}"
6.10 Activity
ActivityDecl := "activity" Ident VersionTag "{" DataBlock* [ApplyBlock] [RetryPolicy] "}"
DataBlock := "data" Ident "{" DataOp "}"
DataOp := FetchOp | InvokeOp | PublishOp | QueryOp | HostOp
| StartOp | SpawnOp | SignalOp
FetchOp := "fetch" QualifiedName "{" FieldMapping* "}"
InvokeOp := "invoke" QualifiedName "{" FieldMapping* "}"
RetryPolicy := "retry_policy" "{" RetryField* "}"
6.11 Service
ServiceDecl := "service" Ident [VersionTag] "{"
[CredentialsBlock]
[AuthBlock]
OperationDecl*
"}"
OperationDecl := "operation" Ident "{" FieldMapping* "}"
6.12 Route
RouteDecl := "route" StringLit "{" [VerifyBlock] RouteMethod* "}"
RouteMethod := HttpMethod "{" DataBlock* ApplyBlock "}"
HttpMethod := "GET" | "POST" | "PUT" | "PATCH" | "DELETE"
VerifyBlock := "verify" Ident "{" FieldMapping* "}"
6.13 Query
QueryDecl := "query" Ident "{" [ArgsBlock] (SqlBlock | GsqlBlock) [OutputDecl] [IndexBlock] "}"
ArgsBlock := "args" "{" FieldDecl* "}"
SqlBlock := "sql" "{" StringLit "}"
OutputDecl := "output" ":" TypeRef
IndexBlock := "index" "{" IndexCol* "}"
6.14 Trigger
TriggerDecl := "trigger" Ident "from" QualifiedName "{" TriggerBody "}"
TriggerBody := "start" Ident VersionTag "{" FieldMapping* "}"
6.15 Schedule
ScheduleDecl := "schedule" Ident "{" ScheduleBody "}"
ScheduleBody := "when" StringLit // cron expression
"start" Ident VersionTag "{" FieldMapping* "}"
6.16 Webhook
WebhookDecl := "webhook" Ident StringLit "{" [SecretDecl] WebhookEvent* "}"
WebhookEvent := "on" EventPattern "->" QualifiedName
EventPattern := Ident | Ident "." Ident | "*"
Built-in providers: github, stripe, slack, gitlab.
6.17 Test
TestDecl := "test" Ident "{" [TestWorkflow] TestRun* "}"
TestWorkflow := "workflow" Ident VersionTag
TestRun := "run" Ident "{" TestDirective* "}"
TestDirective := "request" "{" ... "}"
| "mock" Ident "{" ... "}"
| "expect" ("ok" | "error")
| "assert" Expr
| "advance_time" DurationLit
| "deliver_signal" Ident "{" ... "}"
| "cancel"
| "start_time" IntLit
7. Semantic Constraints
- At most one unnamed root
recordper module. emitonly in action apply context; forbidden in lambdas.forloops only in action apply; max nesting depth 3.- Validate references must resolve.
- Validate body must be boolean.
- Upcast source version < target version.
@create/@deletefor root records only.- Reserved fields:
pk id version created_at updated_at created_by updated_by. - Types are nominal -- identical structure, different name = not assignable.
Valuetype not allowed in user-declared record/event/action fields.- Enum match must cover all variants or include
_. - Non-enum match requires
_wildcard.
8. Method Chains
String: .trim(), .lower(), .upper(), .split(sep), .replace(old, new), .contains(sub), .startsWith(prefix), .endsWith(suffix), .substring(start, end), .padLeft(len, char), .padRight(len, char), .len
List: .map(|i| expr), .filter(|i| expr), .find(|i| expr), .contains(item), .flatten(), .unique(), .sort(|i| expr), .reverse(), .first(), .last(), .sum(), .join(sep), .append(item), .len
Map: .get(key), .keys(), .values(), .entries(), .contains_key(key), .merge(other), .remove(key), .len
Numeric: .round(), .ceil(), .floor(), .abs(), .min(other), .max(other), .toString(), .toDecimal()
Date/Time: .addDays(n), .addHours(n), .addMinutes(n), .addSeconds(n), .format(pattern), .diffDays(other), .startOfMonth(), .endOfMonth(), .toEpoch(), .toDateTime()
Crypto: .sha256(), .hmac(key), .base64Encode(), .base64Decode()
Coercion: .toInt(), .toDecimal(), .toBool(), .tryToInt(), .tryToDecimal(), .tryToBool()