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 }