lib/pluck/src/topics.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 
  3 pub const HelpTopic = struct {
  4     title: []const u8,
  5     lines: []const []const u8,
  6 };
  7 
  8 pub const help_topics = struct {
  9     pub const primitives = [_][]const u8{ "flip", "factor", "discrete", "uniform", "bgeom", "geom" };
 10     pub const queries = [_][]const u8{ "Marginal", "Posterior", "PosteriorSamples", "AdaptiveRejection" };
 11     pub const syntax = [_][]const u8{ "syntax-overview", "if", "case", "let", "lambda", "operators" };
 12     pub const types = [_][]const u8{ "Bool", "Nat", "List", "Pair", "SuspendibleBool", "Unit" };
 13     pub const stdlib = [_][]const u8{ "fst", "snd", "and", "or", "not", "iff", "given", "geom", "bgeom", "randnat", "randnatlist", "inc", "dec", "add", "+", "mul", "*", "sub", "-", "mod", "eq_nat", "nat=?", "lt_nat", "<", "gt_nat", ">", "le_nat", "<=", "ge_nat", ">=", "iseven", "car", "cdr", "cdr_safe", "isempty", "fold", "length", "append", "append_one", "map", "mapi", "filter", "filteri", "range", "zip_with", "take", "index", "list_eq", "list=?", "any", "all", "constructors_equal", "constructor=?", "adt_eq", "=?", "==", "susp_list_eq", "suspendible-list=?", "int1=?", "int2=?", "int3=?", "int4=?", "head-or", "float-of-nat", "make-uniform", "normalize-seq", "sample-seq" };
 14     pub const commands = [_][]const u8{ ":help", ":quit", ":reset", ":defs", ":type" };
 15 };
 16 
 17 pub fn getHelpTopic(name: []const u8) ?HelpTopic {
 18     const topics = std.StaticStringMap(HelpTopic).initComptime(.{
 19         .{ "flip", HelpTopic{
 20             .title = "flip : Prob -> Bool",
 21             .lines = &.{
 22                 "  Bernoulli random variable.",
 23                 "  (flip p) returns True with probability p, else False.",
 24                 "",
 25                 "  Example:",
 26                 "    (Marginal (flip 0.5))",
 27             },
 28         } },
 29         .{ "factor", HelpTopic{
 30             .title = "factor : Weight -> Unit",
 31             .lines = &.{
 32                 "  Soft conditioning by multiplying path weights.",
 33                 "  (factor 0) prunes a path.",
 34             },
 35         } },
 36         .{ "discrete", HelpTopic{
 37             .title = "discrete : (value, prob)... -> value",
 38             .lines = &.{
 39                 "  Finite categorical distribution.",
 40                 "",
 41                 "  Example:",
 42                 "    (Marginal (discrete (True 0.3) (False 0.7)))",
 43             },
 44         } },
 45         .{ "uniform", HelpTopic{
 46             .title = "uniform : value... -> value",
 47             .lines = &.{
 48                 "  Uniform choice among provided values.",
 49                 "",
 50                 "  Example:",
 51                 "    (Marginal (uniform Red Green Blue))",
 52             },
 53         } },
 54         .{ "bgeom", HelpTopic{
 55             .title = "bgeom : Prob -> Nat -> Nat",
 56             .lines = &.{
 57                 "  Bounded geometric distribution.",
 58                 "  Exact-inference friendly alternative to unbounded geom.",
 59             },
 60         } },
 61         .{ "geom", HelpTopic{
 62             .title = "geom : Prob -> Nat",
 63             .lines = &.{
 64                 "  Unbounded geometric distribution.",
 65             },
 66         } },
 67 
 68         .{ "Marginal", HelpTopic{
 69             .title = "Marginal : a -> Dist a",
 70             .lines = &.{
 71                 "  Compute the full distribution of an expression.",
 72                 "",
 73                 "  Example:",
 74                 "    (Marginal (flip 0.5))",
 75             },
 76         } },
 77         .{ "Posterior", HelpTopic{
 78             .title = "Posterior : a -> Bool -> Dist a",
 79             .lines = &.{
 80                 "  Condition expression on evidence.",
 81                 "",
 82                 "  Example:",
 83                 "    (Posterior burglar called)",
 84             },
 85         } },
 86         .{ "PosteriorSamples", HelpTopic{
 87             .title = "PosteriorSamples : a -> Bool -> Nat -> Dist a",
 88             .lines = &.{
 89                 "  Draw N samples from the posterior.",
 90                 "",
 91                 "  Example:",
 92                 "    (PosteriorSamples x True 100)",
 93             },
 94         } },
 95         .{ "AdaptiveRejection", HelpTopic{
 96             .title = "AdaptiveRejection : a -> Bool -> a",
 97             .lines = &.{
 98                 "  Draw one sample from the posterior.",
 99             },
100         } },
101         .{ "syntax-overview", HelpTopic{
102             .title = "Pluck Language Syntax Overview",
103             .lines = &.{
104                 "  Pluck uses S-expression syntax.",
105                 "",
106                 "  Top-level forms:",
107                 "    (define name expr)",
108                 "    (define (name args...) body)",
109                 "    (define-type TypeName (Ctor ...))",
110                 "    (query expr)",
111                 "    (query name expr)",
112                 "",
113                 "  Core expression forms:",
114                 "    (if cond then else)",
115                 "    (lam x -> body)",
116                 "    (let ((x e1) (y e2)) body)",
117                 "    (case expr of Ctor arg => result | Other => fallback)",
118             },
119         } },
120         .{ "if", HelpTopic{
121             .title = "if : conditional expression",
122             .lines = &.{
123                 "  Form:",
124                 "    (if condition then-expr else-expr)",
125             },
126         } },
127         .{ "case", HelpTopic{
128             .title = "case : pattern matching",
129             .lines = &.{
130                 "  Form:",
131                 "    (case expr of Ctor arg1 arg2 => result | Other => fallback)",
132             },
133         } },
134         .{ "let", HelpTopic{
135             .title = "let : local bindings",
136             .lines = &.{
137                 "  Form:",
138                 "    (let ((x e1) (y e2)) body)",
139             },
140         } },
141         .{ "lambda", HelpTopic{
142             .title = "lambda : anonymous functions",
143             .lines = &.{
144                 "  Form:",
145                 "    (lam x -> body)",
146             },
147         } },
148         .{ "operators", HelpTopic{
149             .title = "operators",
150             .lines = &.{
151                 "  Prefer stdlib/operator forms in S-expression syntax:",
152                 "    (and a b)",
153                 "    (or a b)",
154                 "    (not a)",
155                 "    (add x y)",
156                 "    (+ x y)",
157                 "    (mul x y)",
158                 "    (* x y)",
159             },
160         } },
161 
162         .{ "Bool", HelpTopic{ .title = "type Bool : True | False", .lines = &.{"  Boolean type."} } },
163         .{ "Nat", HelpTopic{ .title = "type Nat : O | S(Nat)", .lines = &.{"  Natural numbers."} } },
164         .{ "List", HelpTopic{ .title = "type List a : Nil | Cons(a, List a)", .lines = &.{"  Linked list type."} } },
165         .{ "Pair", HelpTopic{ .title = "type Pair a b : Pair(a, b)", .lines = &.{"  Pair/product type."} } },
166         .{ "SuspendibleBool", HelpTopic{ .title = "type SuspendibleBool : FinallyTrue | FinallyFalse | Suspend(SuspendibleBool)", .lines = &.{"  Suspendible Boolean computations for LPSMC workflows."} } },
167         .{ "Unit", HelpTopic{ .title = "type Unit : Unit", .lines = &.{"  Unit value type."} } },
168 
169         .{ "fst", HelpTopic{ .title = "fst : Pair a b -> a", .lines = &.{"  First projection."} } },
170         .{ "snd", HelpTopic{ .title = "snd : Pair a b -> b", .lines = &.{"  Second projection."} } },
171         .{ "and", HelpTopic{ .title = "and : Bool -> Bool -> Bool", .lines = &.{"  Boolean conjunction."} } },
172         .{ "or", HelpTopic{ .title = "or : Bool -> Bool -> Bool", .lines = &.{"  Boolean disjunction."} } },
173         .{ "not", HelpTopic{ .title = "not : Bool -> Bool", .lines = &.{"  Boolean negation."} } },
174         .{ "iff", HelpTopic{ .title = "iff : Bool -> Bool -> Bool", .lines = &.{"  Boolean equivalence."} } },
175         .{ "given", HelpTopic{ .title = "given : Bool -> a -> a", .lines = &.{"  Hard conditioning helper."} } },
176         .{ "randnat", HelpTopic{ .title = "randnat : Unit -> Nat", .lines = &.{"  Geometric random natural number."} } },
177         .{ "randnatlist", HelpTopic{ .title = "randnatlist : Unit -> List Nat", .lines = &.{"  Random list of geometric random natural numbers."} } },
178         .{ "inc", HelpTopic{ .title = "inc : Nat -> Nat", .lines = &.{"  Successor."} } },
179         .{ "dec", HelpTopic{ .title = "dec : Nat -> Nat", .lines = &.{"  Saturating predecessor."} } },
180         .{ "add", HelpTopic{ .title = "add : Nat -> Nat -> Nat", .lines = &.{"  Nat addition."} } },
181         .{ "+", HelpTopic{ .title = "+ : Nat -> Nat -> Nat", .lines = &.{"  Operator form for add."} } },
182         .{ "mul", HelpTopic{ .title = "mul : Nat -> Nat -> Nat", .lines = &.{"  Nat multiplication."} } },
183         .{ "*", HelpTopic{ .title = "* : Nat -> Nat -> Nat", .lines = &.{"  Operator form for mul."} } },
184         .{ "sub", HelpTopic{ .title = "sub : Nat -> Nat -> Nat", .lines = &.{"  Saturating Nat subtraction."} } },
185         .{ "-", HelpTopic{ .title = "- : Nat -> Nat -> Nat", .lines = &.{"  Operator form for sub."} } },
186         .{ "mod", HelpTopic{ .title = "mod : Nat -> Nat -> Nat", .lines = &.{"  Nat remainder."} } },
187         .{ "eq_nat", HelpTopic{ .title = "eq_nat : Nat -> Nat -> Bool", .lines = &.{"  Nat equality."} } },
188         .{ "nat=?", HelpTopic{ .title = "nat=? : Nat -> Nat -> Bool", .lines = &.{"  Operator-style name for eq_nat."} } },
189         .{ "lt_nat", HelpTopic{ .title = "lt_nat : Nat -> Nat -> Bool", .lines = &.{"  Strict Nat less-than."} } },
190         .{ "<", HelpTopic{ .title = "< : Nat -> Nat -> Bool", .lines = &.{"  Operator form for lt_nat."} } },
191         .{ "gt_nat", HelpTopic{ .title = "gt_nat : Nat -> Nat -> Bool", .lines = &.{"  Strict Nat greater-than."} } },
192         .{ ">", HelpTopic{ .title = "> : Nat -> Nat -> Bool", .lines = &.{"  Operator form for gt_nat."} } },
193         .{ "le_nat", HelpTopic{ .title = "le_nat : Nat -> Nat -> Bool", .lines = &.{"  Nat less-than-or-equal."} } },
194         .{ "<=", HelpTopic{ .title = "<= : Nat -> Nat -> Bool", .lines = &.{"  Operator form for le_nat."} } },
195         .{ "ge_nat", HelpTopic{ .title = "ge_nat : Nat -> Nat -> Bool", .lines = &.{"  Nat greater-than-or-equal."} } },
196         .{ ">=", HelpTopic{ .title = ">= : Nat -> Nat -> Bool", .lines = &.{"  Operator form for ge_nat."} } },
197         .{ "iseven", HelpTopic{ .title = "iseven : Nat -> Bool", .lines = &.{"  True for even natural numbers."} } },
198         .{ "car", HelpTopic{ .title = "car : List a -> a", .lines = &.{"  List head."} } },
199         .{ "cdr", HelpTopic{ .title = "cdr : List a -> List a", .lines = &.{"  List tail, or Nil for Nil."} } },
200         .{ "cdr_safe", HelpTopic{ .title = "cdr_safe : List a -> List a", .lines = &.{"  Safe list tail."} } },
201         .{ "isempty", HelpTopic{ .title = "isempty : List a -> Bool", .lines = &.{"  True for Nil."} } },
202         .{ "fold", HelpTopic{ .title = "fold : (b -> a -> b) -> b -> List a -> b", .lines = &.{"  Fold a list from tail to head."} } },
203         .{ "length", HelpTopic{ .title = "length : List a -> Nat", .lines = &.{"  List length."} } },
204         .{ "append", HelpTopic{ .title = "append : List a -> List a -> List a", .lines = &.{"  List concatenation."} } },
205         .{ "append_one", HelpTopic{ .title = "append_one : List a -> a -> List a", .lines = &.{"  Append a single element."} } },
206         .{ "map", HelpTopic{ .title = "map : (a -> b) -> List a -> List b", .lines = &.{"  Map a function over a list."} } },
207         .{ "mapi", HelpTopic{ .title = "mapi : (a -> Nat -> b) -> List a -> List b", .lines = &.{"  Map with zero-based element indexes."} } },
208         .{ "filter", HelpTopic{ .title = "filter : (a -> Bool) -> List a -> List a", .lines = &.{"  Keep matching elements."} } },
209         .{ "filteri", HelpTopic{ .title = "filteri : (a -> Nat -> Bool) -> List a -> List a", .lines = &.{"  Keep matching indexed elements."} } },
210         .{ "range", HelpTopic{ .title = "range : Nat -> List Nat", .lines = &.{"  Naturals from zero up to the bound."} } },
211         .{ "zip_with", HelpTopic{ .title = "zip_with : (a -> b -> c) -> List a -> List b -> List c", .lines = &.{"  Zip two lists with a binary function."} } },
212         .{ "take", HelpTopic{ .title = "take : Nat -> List a -> List a", .lines = &.{"  Prefix of a list."} } },
213         .{ "index", HelpTopic{ .title = "index : Nat -> List a -> a", .lines = &.{"  Zero-based list lookup."} } },
214         .{ "list_eq", HelpTopic{ .title = "list_eq : (a -> a -> Bool) -> List a -> List a -> Bool", .lines = &.{"  Elementwise list equality."} } },
215         .{ "list=?", HelpTopic{ .title = "list=? : (a -> a -> Bool) -> List a -> List a -> Bool", .lines = &.{"  Operator-style name for list_eq."} } },
216         .{ "any", HelpTopic{ .title = "any : (a -> Bool) -> List a -> Bool", .lines = &.{"  True when any element matches."} } },
217         .{ "all", HelpTopic{ .title = "all : (a -> Bool) -> List a -> Bool", .lines = &.{"  True when all elements match."} } },
218         .{ "constructors_equal", HelpTopic{ .title = "constructors_equal : a -> b -> Bool", .lines = &.{"  Compare ADT constructor names."} } },
219         .{ "constructor=?", HelpTopic{ .title = "constructor=? : a -> b -> Bool", .lines = &.{"  Operator-style name for constructors_equal."} } },
220         .{ "adt_eq", HelpTopic{ .title = "adt_eq : a -> a -> Bool", .lines = &.{"  Recursive ADT equality."} } },
221         .{ "=?", HelpTopic{ .title = "=? : a -> a -> Bool", .lines = &.{"  Operator-style name for adt_eq."} } },
222         .{ "==", HelpTopic{ .title = "== : a -> a -> Bool", .lines = &.{"  Operator-style name for adt_eq."} } },
223         .{ "susp_list_eq", HelpTopic{ .title = "susp_list_eq : (a -> a -> Bool) -> List a -> List a -> SuspendibleBool", .lines = &.{"  Suspendible elementwise list equality."} } },
224         .{ "suspendible-list=?", HelpTopic{ .title = "suspendible-list=? : (a -> a -> Bool) -> List a -> List a -> SuspendibleBool", .lines = &.{"  Reference name for suspendible elementwise list equality."} } },
225         .{ "int1=?", HelpTopic{ .title = "int1=? : int1 -> int1 -> Bool", .lines = &.{"  Equality for one-bit integer records."} } },
226         .{ "int2=?", HelpTopic{ .title = "int2=? : int2 -> int2 -> Bool", .lines = &.{"  Equality for two-bit integer records."} } },
227         .{ "int3=?", HelpTopic{ .title = "int3=? : int3 -> int3 -> Bool", .lines = &.{"  Equality for three-bit integer records."} } },
228         .{ "int4=?", HelpTopic{ .title = "int4=? : int4 -> int4 -> Bool", .lines = &.{"  Equality for four-bit integer records."} } },
229         .{ "head-or", HelpTopic{ .title = "head-or : List a -> a -> a", .lines = &.{"  List head with fallback."} } },
230         .{ "float-of-nat", HelpTopic{ .title = "float-of-nat : Nat -> Float", .lines = &.{"  Convert a Nat to a float."} } },
231         .{ "make-uniform", HelpTopic{ .title = "make-uniform : List a -> List (Pair a Float)", .lines = &.{"  Build uniform weighted pairs."} } },
232         .{ "normalize-seq", HelpTopic{ .title = "normalize-seq : List (Pair a Float) -> List (Pair a Float)", .lines = &.{"  Normalize weights for sequential sampling."} } },
233         .{ "sample-seq", HelpTopic{ .title = "sample-seq : List (Pair a Float) -> a", .lines = &.{"  Sample from normalized sequential weights."} } },
234 
235         .{ ":help", HelpTopic{ .title = ":help [topic]", .lines = &.{"  Show help for a topic."} } },
236         .{ ":quit", HelpTopic{ .title = ":quit", .lines = &.{"  Exit interactive mode."} } },
237         .{ ":reset", HelpTopic{ .title = ":reset", .lines = &.{"  Clear user definitions."} } },
238         .{ ":defs", HelpTopic{ .title = ":defs", .lines = &.{"  List user definitions."} } },
239         .{ ":type", HelpTopic{ .title = ":type <name>", .lines = &.{"  Show type information for a symbol."} } },
240 
241         .{ "primitives", HelpTopic{ .title = "=== Primitives ===", .lines = &.{"  flip, factor, discrete, uniform, bgeom, geom"} } },
242         .{ "queries", HelpTopic{ .title = "=== Queries ===", .lines = &.{"  Marginal, Posterior, PosteriorSamples, AdaptiveRejection"} } },
243         .{ "syntax", HelpTopic{ .title = "=== Language Syntax ===", .lines = &.{"  syntax-overview, if, case, let, lambda, operators"} } },
244         .{ "types", HelpTopic{ .title = "=== Types ===", .lines = &.{"  Bool, Nat, List, Pair, SuspendibleBool, Unit"} } },
245         .{ "stdlib", HelpTopic{ .title = "=== Standard Library ===", .lines = &.{ "  fst, snd, and, or, not, iff, given, geom, bgeom, randnat, randnatlist", "  inc, dec, add, +, mul, *, sub, -, mod, eq_nat, nat=?, lt_nat, <, gt_nat, >", "  le_nat, <=, ge_nat, >=, iseven, car, cdr, cdr_safe, isempty, fold, length", "  append, append_one, map, mapi, filter, filteri, range, zip_with, take, index", "  list_eq, list=?, any, all, constructors_equal, constructor=?, adt_eq, =?, ==", "  susp_list_eq, suspendible-list=?, int1=?, int2=?, int3=?, int4=?", "  head-or, float-of-nat, make-uniform, normalize-seq, sample-seq" } } },
246         .{ "commands", HelpTopic{ .title = "=== Commands ===", .lines = &.{"  :help, :quit, :reset, :defs, :type"} } },
247     });
248 
249     return topics.get(name);
250 }
251 
252 test "help_topics lists match getHelpTopic entries" {
253     for (help_topics.primitives) |name| try std.testing.expect(getHelpTopic(name) != null);
254     for (help_topics.queries) |name| try std.testing.expect(getHelpTopic(name) != null);
255     for (help_topics.syntax) |name| try std.testing.expect(getHelpTopic(name) != null);
256     for (help_topics.types) |name| try std.testing.expect(getHelpTopic(name) != null);
257     for (help_topics.stdlib) |name| try std.testing.expect(getHelpTopic(name) != null);
258     for (help_topics.commands) |name| try std.testing.expect(getHelpTopic(name) != null);
259 }
260 
261 test "getHelpTopic returns null for unknown topic" {
262     try std.testing.expect(getHelpTopic("nonexistent") == null);
263 }