1use std::sync::Arc;
2
3use sim_kernel::{
4 AbiVersion, Cx, Expr, Lib, LibManifest, LibTarget, Linker, Result, Symbol, Value, Version,
5 logic_db_write_capability,
6};
7
8use crate::{
9 codec::{consult_expr, consult_path},
10 error::logic_eval_error,
11 lisp_runtime::{
12 CONFIG_SYMBOL, DB_SYMBOL, LogicConfigState, LogicDbState, LogicFunction, config_value,
13 keyword, logic_config_state, logic_db_state, query_config, string_expr, symbol_expr,
14 unquote, usize_from_expr,
15 },
16 model::SearchStrategy,
17 query::{query, query_all, query_bool, query_one},
18 shapes::{register_logic_shapes, require_logic_stream},
19};
20
21const LOGIC_LIB_ID: &str = "logic";
22
23pub struct LogicLib;
29
30impl Lib for LogicLib {
31 fn manifest(&self) -> LibManifest {
32 LibManifest {
33 id: Symbol::new(LOGIC_LIB_ID),
34 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
35 abi: AbiVersion { major: 0, minor: 1 },
36 target: LibTarget::HostRegistered,
37 requires: Vec::new(),
38 capabilities: Vec::new(),
39 exports: logic_exports(),
40 }
41 }
42
43 fn load(&self, cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
44 register_logic_shapes(linker, cx)?;
45 register_logic_functions(cx, linker)?;
46 linker.value(
47 Symbol::qualified("logic", DB_SYMBOL),
48 cx.factory().opaque(Arc::new(LogicDbState::default()))?,
49 )?;
50 linker.value(
51 Symbol::qualified("logic", CONFIG_SYMBOL),
52 cx.factory().opaque(Arc::new(LogicConfigState::default()))?,
53 )?;
54 Ok(())
55 }
56}
57
58pub fn install_logic_lib(cx: &mut Cx) -> Result<()> {
62 sim_lib_core::install_once(cx, &LogicLib).map(|_| ())
63}
64
65pub fn realize_logic(
73 cx: &mut Cx,
74 goal: Expr,
75 answer_limit: Option<usize>,
76 stream_buffer: Option<usize>,
77 stream: bool,
78) -> Result<Value> {
79 install_logic_lib(cx)?;
80 let state = logic_config_state(cx)?;
81 let mut config = state.lock()?.clone();
82 if let Some(limit) = answer_limit {
83 config.limits.max_answers = Some(limit);
84 }
85 if let Some(buffer) = stream_buffer {
86 config.stream_buffer = buffer;
87 }
88 let db = logic_db_state(cx)?.lock()?.clone();
89 if stream {
90 let stream = query(cx, &db, &config, goal)?;
91 return cx.factory().opaque(Arc::new(stream));
92 }
93 match query_one(cx, &db, &config, goal)? {
94 Some(matched) => sim_kernel::shape_match_value(cx, matched),
95 None => cx.factory().nil(),
96 }
97}
98
99fn logic_exports() -> Vec<sim_kernel::Export> {
100 let mut exports = vec![
101 sim_kernel::Export::Value {
102 symbol: Symbol::qualified("logic", DB_SYMBOL),
103 },
104 sim_kernel::Export::Value {
105 symbol: Symbol::qualified("logic", CONFIG_SYMBOL),
106 },
107 ];
108 for symbol in [
109 Symbol::qualified("logic", "Var"),
110 Symbol::qualified("logic", "Goal"),
111 Symbol::qualified("logic", "Clause"),
112 Symbol::qualified("logic", "Fact"),
113 Symbol::qualified("logic", "Rule"),
114 Symbol::qualified("logic", "Answer"),
115 Symbol::qualified("logic", "Config"),
116 ] {
117 exports.push(sim_kernel::Export::Shape {
118 symbol,
119 shape_id: None,
120 });
121 }
122 for symbol in [
123 Symbol::qualified("logic", "config"),
124 Symbol::qualified("logic", "assert!"),
125 Symbol::qualified("logic", "retract!"),
126 Symbol::qualified("logic", "facts"),
127 Symbol::qualified("logic", "consult"),
128 Symbol::qualified("logic", "consult!"),
129 Symbol::qualified("logic", "stream-next"),
130 Symbol::qualified("logic", "stream-close"),
131 Symbol::qualified("logic", "query"),
132 Symbol::qualified("logic", "query/one"),
133 Symbol::qualified("logic", "query/all"),
134 Symbol::qualified("logic", "query?"),
135 Symbol::qualified("logic", "predicate?"),
136 ] {
137 exports.push(sim_kernel::Export::Function {
138 symbol,
139 function_id: None,
140 });
141 }
142 exports
143}
144
145fn register_logic_functions(cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
146 for (symbol, implementation) in [
147 (
148 Symbol::qualified("logic", "config"),
149 logic_config_fn as fn(&mut Cx, &[Expr]) -> Result<Value>,
150 ),
151 (Symbol::qualified("logic", "assert!"), logic_assert_fn),
152 (Symbol::qualified("logic", "retract!"), logic_retract_fn),
153 (Symbol::qualified("logic", "facts"), logic_facts_fn),
154 (Symbol::qualified("logic", "consult"), logic_consult_fn),
155 (
156 Symbol::qualified("logic", "consult!"),
157 logic_consult_bang_fn,
158 ),
159 (
160 Symbol::qualified("logic", "stream-next"),
161 logic_stream_next_fn,
162 ),
163 (
164 Symbol::qualified("logic", "stream-close"),
165 logic_stream_close_fn,
166 ),
167 (Symbol::qualified("logic", "query"), logic_query_fn),
168 (Symbol::qualified("logic", "query/one"), logic_query_one_fn),
169 (Symbol::qualified("logic", "query/all"), logic_query_all_fn),
170 (Symbol::qualified("logic", "query?"), logic_query_bool_fn),
171 (Symbol::qualified("logic", "predicate?"), logic_predicate_fn),
172 ] {
173 linker.function_value(
174 symbol.clone(),
175 cx.factory().opaque(Arc::new(LogicFunction {
176 symbol,
177 implementation,
178 }))?,
179 )?;
180 }
181 Ok(())
182}
183
184fn logic_config_fn(cx: &mut Cx, args: &[Expr]) -> Result<Value> {
185 let state = logic_config_state(cx)?;
186 let mut config = state.lock()?.clone();
187 if !args.len().is_multiple_of(2) {
188 return Err(logic_eval_error(
189 "logic/config options must be key/value pairs",
190 ));
191 }
192 for pair in args.chunks(2) {
193 let key = keyword(&pair[0])?;
194 match key.as_str() {
195 "max-depth" => config.limits.max_depth = usize_from_expr(cx, &pair[1])?,
196 "stream-buffer" => config.stream_buffer = usize_from_expr(cx, &pair[1])?,
197 "answer-limit" => config.limits.max_answers = Some(usize_from_expr(cx, &pair[1])?),
198 "strategy" => {
199 let symbol = symbol_expr(cx, &pair[1])?;
200 config.strategy = SearchStrategy::from_symbol(&symbol)
201 .ok_or_else(|| logic_eval_error(format!("unsupported strategy {symbol}")))?;
202 }
203 other => {
204 return Err(logic_eval_error(format!(
205 "logic/config does not support :{other}"
206 )));
207 }
208 }
209 }
210 *state.lock()? = config.clone();
211 config_value(cx, &config)
212}
213
214fn logic_assert_fn(cx: &mut Cx, args: &[Expr]) -> Result<Value> {
215 cx.require(&logic_db_write_capability())?;
216 let [expr] = args else {
217 return Err(logic_eval_error("logic/assert! expects one quoted clause"));
218 };
219 let clause_expr = unquote(expr);
220 logic_db_state(cx)?
221 .lock()?
222 .assert_clause_expr(clause_expr)?;
223 cx.factory().bool(true)
224}
225
226fn logic_retract_fn(cx: &mut Cx, args: &[Expr]) -> Result<Value> {
227 cx.require(&logic_db_write_capability())?;
228 let [expr] = args else {
229 return Err(logic_eval_error("logic/retract! expects one quoted clause"));
230 };
231 let removed = logic_db_state(cx)?
232 .lock()?
233 .retract_clause_expr(&unquote(expr))?;
234 cx.factory().bool(removed)
235}
236
237fn logic_facts_fn(cx: &mut Cx, args: &[Expr]) -> Result<Value> {
238 let [expr] = args else {
239 return Err(logic_eval_error("logic/facts expects one predicate symbol"));
240 };
241 let predicate = symbol_expr(cx, expr)?;
242 let facts = logic_db_state(cx)?.lock()?.facts(&predicate);
243 cx.factory().list(
244 facts
245 .into_iter()
246 .map(|expr| cx.factory().expr(expr))
247 .collect::<Result<Vec<_>>>()?,
248 )
249}
250
251fn logic_consult_fn(cx: &mut Cx, args: &[Expr]) -> Result<Value> {
252 cx.require(&logic_db_write_capability())?;
253 let [path_expr] = args else {
254 return Err(logic_eval_error("logic/consult expects one path"));
255 };
256 let path = string_expr(cx, path_expr)?;
257 let state = logic_db_state(cx)?;
258 let mut db = state.lock()?;
259 let count = consult_path(cx, &mut db, &path)?;
260 drop(db);
261 cx.factory().string(count.to_string())
262}
263
264fn logic_consult_bang_fn(cx: &mut Cx, args: &[Expr]) -> Result<Value> {
265 cx.require(&logic_db_write_capability())?;
266 let [expr] = args else {
267 return Err(logic_eval_error(
268 "logic/consult! expects quoted clause data",
269 ));
270 };
271 let state = logic_db_state(cx)?;
272 let mut db = state.lock()?;
273 let count = consult_expr(&mut db, unquote(expr))?;
274 drop(db);
275 cx.factory().string(count.to_string())
276}
277
278fn logic_query_fn(cx: &mut Cx, args: &[Expr]) -> Result<Value> {
279 let [goal, rest @ ..] = args else {
280 return Err(logic_eval_error("query expects a goal"));
281 };
282 let config = query_config(cx, rest)?;
283 let goal = unquote(goal);
284 let db = logic_db_state(cx)?.lock()?.clone();
285 let stream = query(cx, &db, &config, goal)?;
286 cx.factory().opaque(Arc::new(stream))
287}
288
289fn logic_query_one_fn(cx: &mut Cx, args: &[Expr]) -> Result<Value> {
290 let [goal, rest @ ..] = args else {
291 return Err(logic_eval_error("query/one expects a goal"));
292 };
293 let config = query_config(cx, rest)?;
294 let db = logic_db_state(cx)?.lock()?.clone();
295 match query_one(cx, &db, &config, unquote(goal))? {
296 Some(matched) => sim_kernel::shape_match_value(cx, matched),
297 None => cx.factory().nil(),
298 }
299}
300
301fn logic_query_all_fn(cx: &mut Cx, args: &[Expr]) -> Result<Value> {
302 let [goal, rest @ ..] = args else {
303 return Err(logic_eval_error("query/all expects a goal"));
304 };
305 let config = query_config(cx, rest)?;
306 let db = logic_db_state(cx)?.lock()?.clone();
307 let answers = query_all(cx, &db, &config, unquote(goal), config.limits.max_answers)?;
308 let mut values = Vec::with_capacity(answers.len());
309 for matched in answers {
310 values.push(sim_kernel::shape_match_value(cx, matched)?);
311 }
312 cx.factory().list(values)
313}
314
315fn logic_query_bool_fn(cx: &mut Cx, args: &[Expr]) -> Result<Value> {
316 let [goal, rest @ ..] = args else {
317 return Err(logic_eval_error("query? expects a goal"));
318 };
319 let config = query_config(cx, rest)?;
320 let db = logic_db_state(cx)?.lock()?.clone();
321 let accepted = query_bool(cx, &db, &config, unquote(goal))?;
322 cx.factory().bool(accepted)
323}
324
325fn logic_predicate_fn(cx: &mut Cx, args: &[Expr]) -> Result<Value> {
326 let [expr] = args else {
327 return Err(logic_eval_error("predicate? expects a predicate symbol"));
328 };
329 let predicate = symbol_expr(cx, expr)?;
330 let exists = logic_db_state(cx)?.lock()?.predicate_exists(&predicate);
331 cx.factory().bool(exists)
332}
333
334fn logic_stream_next_fn(cx: &mut Cx, args: &[Expr]) -> Result<Value> {
335 let [stream_expr] = args else {
336 return Err(logic_eval_error("logic/stream-next expects a stream"));
337 };
338 let stream = cx.eval_expr(stream_expr.clone())?;
339 match sim_kernel::Stream::next(require_logic_stream(&stream)?, cx)? {
340 Some(value) => Ok(value),
341 None => cx.factory().nil(),
342 }
343}
344
345fn logic_stream_close_fn(cx: &mut Cx, args: &[Expr]) -> Result<Value> {
346 let [stream_expr] = args else {
347 return Err(logic_eval_error("logic/stream-close expects a stream"));
348 };
349 let stream = cx.eval_expr(stream_expr.clone())?;
350 sim_kernel::Stream::close(require_logic_stream(&stream)?, cx)?;
351 cx.factory().nil()
352}