@@ -306,6 +306,26 @@ ExprOpen: Expr = {
306306 // Parallel (brace-delimited)
307307 "parallel" <n:SpannedExprAtom> "{" <body:SpannedExpr> "}" =>
308308 Expr::Parallel(Box::new(n), Box::new(body)),
309+
310+ // Observe: `observe(dist, value)` - condition on observed data
311+ <l:@L> "observe" "(" <dist:SpannedExpr> "," <val:SpannedExpr> ")" <r:@R> =>
312+ Expr::Observe(Box::new(dist), Box::new(val)),
313+
314+ // Infer with parameters: `infer MCMC { steps = 1000, warmup = 500 } model`
315+ "infer" <method:InferMethod> "{" <params:InferParams> "}" <model:SpannedExprAtom> =>
316+ Expr::Infer(InferExpr {
317+ method,
318+ params,
319+ model: Box::new(model),
320+ }),
321+
322+ // Infer without parameters: `infer MCMC model`
323+ "infer" <method:InferMethod> <model:SpannedExprAtom> =>
324+ Expr::Infer(InferExpr {
325+ method,
326+ params: Vec::new(),
327+ model: Box::new(model),
328+ }),
309329};
310330
311331// Closed expressions: the operator precedence chain.
@@ -323,6 +343,13 @@ ExprOr: Expr = {
323343 Expr::BinOp(BinOp::Or,
324344 Box::new(Spanned::new(le, Span::new(l as u32, r as u32))),
325345 Box::new(Spanned::new(re, Span::new(l as u32, r as u32)))),
346+ // Pipe application: `x |> f` desugars to `f(x)`.
347+ // Sits at the same level as `||` so `a |> f || b` is `(a |> f) || b`.
348+ <l:@L> <arg:ExprOr> "|>" <f:ExprAnd> <r:@R> =>
349+ Expr::App(
350+ Box::new(Spanned::new(f, Span::new(l as u32, r as u32))),
351+ vec![Spanned::new(arg, Span::new(l as u32, r as u32))],
352+ ),
326353 ExprAnd,
327354};
328355
@@ -548,6 +575,34 @@ RecordTypeField: (Symbol, Spanned<Type>) = {
548575 <name:IDENT> ":" <ty:SpannedType> => (Symbol::intern(&name), ty),
549576};
550577
578+ // === Inference Helpers ===
579+
580+ InferMethod: InferMethod = {
581+ "MCMC" => InferMethod::MCMC,
582+ "HMC" => InferMethod::HMC,
583+ "SMC" => InferMethod::SMC,
584+ "VI" => InferMethod::VI,
585+ };
586+
587+ // Inference parameters: `steps = 1000, warmup = 500`.
588+ // Uses IDENT ":" SpannedExpr to avoid reduce/reduce with RecordField
589+ // (which uses IDENT "=" SpannedExpr).
590+ InferParams: Vec<(Spanned<Symbol>, Spanned<Expr>)> = {
591+ => Vec::new(),
592+ <v:InferParamsSep> => v,
593+ <v:InferParamsSep> "," => v,
594+ };
595+
596+ InferParamsSep: Vec<(Spanned<Symbol>, Spanned<Expr>)> = {
597+ <e:InferParam> => vec![e],
598+ <mut v:InferParamsSep> "," <e:InferParam> => { v.push(e); v },
599+ };
600+
601+ InferParam: (Spanned<Symbol>, Spanned<Expr>) = {
602+ <l:@L> <name:IDENT> <r:@R> ":" <val:SpannedExpr> =>
603+ (Spanned::new(Symbol::intern(&name), Span::new(l as u32, r as u32)), val),
604+ };
605+
551606// === Helpers ===
552607
553608Ident: String = {
0 commit comments