1use sim_kernel::Symbol;
4use sim_lib_standard_core::{
5 LanguageRow, LanguageRowBuilder, SourceConformanceCase, SourceExpectation,
6};
7
8use crate::{
9 prolog_conformance_case_symbol, prolog_logic_organ_symbol, prolog_profile,
10 prolog_surface_fidelity_symbol,
11};
12
13pub fn prolog_matrix_row() -> LanguageRow {
15 LanguageRowBuilder::new(Symbol::new("prolog"), prolog_profile())
16 .with_cases(prolog_conformance_cases())
17 .build()
18}
19
20pub fn prolog_conformance_cases() -> Vec<SourceConformanceCase> {
22 let mut cases = vec![
23 prolog_case(
24 "fact",
25 "fact-color.prolog",
26 "color(red).",
27 "prolog:fact answers=1",
28 ),
29 prolog_case(
30 "rule",
31 "rule-painted.prolog",
32 "painted(X) :- color(X).",
33 "prolog:rule answers=1",
34 ),
35 prolog_case(
36 "query",
37 "query-color.prolog",
38 "?- color(X).",
39 "prolog:query answers=2",
40 ),
41 prolog_case(
42 "cut",
43 "cut-first-color.prolog",
44 "first_color(X) :- color(X), !.",
45 "prolog:cut answers=1 first=red",
46 ),
47 ];
48 cases.extend(prolog_arithmetic_cases());
49 cases.extend(prolog_list_cases());
50 cases.extend(prolog_all_solution_cases());
51 cases.extend(prolog_constraint_cases());
52 cases.extend(prolog_tabling_cases());
53 cases
54}
55
56fn prolog_case(
57 name: &str,
58 source_name: &str,
59 source: &str,
60 expected: &str,
61) -> SourceConformanceCase {
62 SourceConformanceCase {
63 symbol: prolog_conformance_case_symbol(name),
64 organ: prolog_logic_organ_symbol(),
65 source_name: source_name.to_owned(),
66 source: source.to_owned(),
67 expectation: SourceExpectation::LowersTo(expected.to_owned()),
68 affects_badge: Some(prolog_surface_fidelity_symbol()),
69 }
70}
71
72fn prolog_numbers_case(
73 name: &str,
74 source_name: &str,
75 source: &str,
76 expected: &str,
77) -> SourceConformanceCase {
78 SourceConformanceCase {
79 symbol: prolog_conformance_case_symbol(name),
80 organ: Symbol::qualified("numbers", "arith"),
81 source_name: source_name.to_owned(),
82 source: source.to_owned(),
83 expectation: SourceExpectation::LowersTo(expected.to_owned()),
84 affects_badge: Some(prolog_surface_fidelity_symbol()),
85 }
86}
87
88fn prolog_numbers_gap(
89 name: &str,
90 source_name: &str,
91 source: &str,
92 code: Symbol,
93 reason: &str,
94) -> SourceConformanceCase {
95 SourceConformanceCase {
96 symbol: prolog_conformance_case_symbol(name),
97 organ: Symbol::qualified("numbers", "arith"),
98 source_name: source_name.to_owned(),
99 source: source.to_owned(),
100 expectation: SourceExpectation::ExpectedGap {
101 code,
102 reason: reason.to_owned(),
103 },
104 affects_badge: Some(prolog_surface_fidelity_symbol()),
105 }
106}
107
108fn prolog_sequence_case(
109 name: &str,
110 source_name: &str,
111 source: &str,
112 expected: &str,
113) -> SourceConformanceCase {
114 SourceConformanceCase {
115 symbol: prolog_conformance_case_symbol(name),
116 organ: Symbol::new("sequence"),
117 source_name: source_name.to_owned(),
118 source: source.to_owned(),
119 expectation: SourceExpectation::LowersTo(expected.to_owned()),
120 affects_badge: Some(prolog_surface_fidelity_symbol()),
121 }
122}
123
124fn prolog_sequence_gap(
125 name: &str,
126 source_name: &str,
127 source: &str,
128 code: Symbol,
129 reason: &str,
130) -> SourceConformanceCase {
131 SourceConformanceCase {
132 symbol: prolog_conformance_case_symbol(name),
133 organ: Symbol::new("sequence"),
134 source_name: source_name.to_owned(),
135 source: source.to_owned(),
136 expectation: SourceExpectation::ExpectedGap {
137 code,
138 reason: reason.to_owned(),
139 },
140 affects_badge: Some(prolog_surface_fidelity_symbol()),
141 }
142}
143
144fn prolog_control_case(
145 name: &str,
146 source_name: &str,
147 source: &str,
148 expected: &str,
149) -> SourceConformanceCase {
150 SourceConformanceCase {
151 symbol: prolog_conformance_case_symbol(name),
152 organ: Symbol::new("control"),
153 source_name: source_name.to_owned(),
154 source: source.to_owned(),
155 expectation: SourceExpectation::LowersTo(expected.to_owned()),
156 affects_badge: Some(prolog_surface_fidelity_symbol()),
157 }
158}
159
160fn prolog_control_gap(
161 name: &str,
162 source_name: &str,
163 source: &str,
164 code: Symbol,
165 reason: &str,
166) -> SourceConformanceCase {
167 SourceConformanceCase {
168 symbol: prolog_conformance_case_symbol(name),
169 organ: Symbol::new("control"),
170 source_name: source_name.to_owned(),
171 source: source.to_owned(),
172 expectation: SourceExpectation::ExpectedGap {
173 code,
174 reason: reason.to_owned(),
175 },
176 affects_badge: Some(prolog_surface_fidelity_symbol()),
177 }
178}
179
180fn prolog_arithmetic_cases() -> Vec<SourceConformanceCase> {
181 vec![
182 prolog_numbers_case(
183 "is-promote",
184 "is-promote.prolog",
185 "X is 1 + 0.5.",
186 "prolog:is organ=numbers/arith answers=1 x=1.5",
187 ),
188 prolog_numbers_case(
189 "cmp-cross-domain",
190 "cmp-cross-domain.prolog",
191 "?- 2 =:= 2.0.",
192 "prolog:compare organ=numbers/arith answers=1",
193 ),
194 prolog_numbers_case(
195 "cmp-false",
196 "cmp-false.prolog",
197 "?- 3 < 2.",
198 "prolog:compare answers=0",
199 ),
200 prolog_numbers_gap(
201 "unbound-is",
202 "unbound-is.prolog",
203 "X is Y + 1.",
204 Symbol::qualified("prolog", "unbound-arithmetic"),
205 "is/2 requires the right side to be ground and evaluable",
206 ),
207 ]
208}
209
210fn prolog_list_cases() -> Vec<SourceConformanceCase> {
211 vec![
212 prolog_sequence_case(
213 "list-member",
214 "list-member.prolog",
215 "?- member(X, [a,b,c]).",
216 "prolog:member organ=sequence answers=3 xs=a,b,c",
217 ),
218 prolog_sequence_case(
219 "list-append",
220 "list-append.prolog",
221 "?- append([a], [b,c], Xs).",
222 "prolog:append organ=sequence answers=1 xs=(a b c)",
223 ),
224 prolog_sequence_gap(
225 "open-list",
226 "open-list.prolog",
227 "?- member(X, [a|Tail]).",
228 Symbol::qualified("prolog", "open-list"),
229 "open and improper lists are outside the closed Expr::List bridge",
230 ),
231 ]
232}
233
234fn prolog_all_solution_cases() -> Vec<SourceConformanceCase> {
235 vec![
236 prolog_sequence_case(
237 "findall-duplicates",
238 "findall-duplicates.prolog",
239 "findall(X, member(X, [a,b,a]), Xs).",
240 "prolog:findall organ=sequence answers=1 xs=(a b a)",
241 ),
242 prolog_sequence_case(
243 "bagof-groups",
244 "bagof-groups.prolog",
245 "bagof(Child, parent(Parent, Child), Children).",
246 "prolog:bagof organ=sequence answers=2 groups=alice:(bob bea);cara:(drew)",
247 ),
248 prolog_sequence_case(
249 "setof-sorted",
250 "setof-sorted.prolog",
251 "setof(X, member(X, [c,a,b,a]), Xs).",
252 "prolog:setof organ=sequence answers=1 xs=(a b c)",
253 ),
254 prolog_sequence_case(
255 "bagof-empty",
256 "bagof-empty.prolog",
257 "bagof(X, member(X, []), Xs).",
258 "prolog:bagof-empty organ=sequence answers=0",
259 ),
260 ]
261}
262
263fn prolog_constraint_cases() -> Vec<SourceConformanceCase> {
264 vec![
265 prolog_control_case(
266 "constraint-entailed",
267 "constraint-entailed.prolog",
268 "?- #=(2, 2).",
269 "prolog:constraint organ=control relation=#= verdict=entailed answers=1",
270 ),
271 prolog_control_case(
272 "constraint-disentailed",
273 "constraint-disentailed.prolog",
274 "?- #<(3, 2).",
275 "prolog:constraint organ=control relation=#< verdict=disentailed answers=0",
276 ),
277 prolog_control_gap(
278 "constraint-residual",
279 "constraint-residual.prolog",
280 "?- dif(X, 1).",
281 Symbol::qualified("prolog", "residual-constraint"),
282 "residual constraint demand is suspended on the control ledger",
283 ),
284 ]
285}
286
287fn prolog_tabling_cases() -> Vec<SourceConformanceCase> {
288 vec![prolog_sequence_case(
289 "tabling-left-recursive-path",
290 "tabling-left-recursive-path.prolog",
291 "table(path/2), path(a, Y).",
292 "prolog:tabling organ=sequence answers=2 ys=b,c",
293 )]
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299
300 #[test]
301 fn prolog_matrix_row_language_symbol_is_prolog() {
302 let row = prolog_matrix_row();
303
304 assert_eq!(row.language, Symbol::new("prolog"));
305 assert!(!row.is_empty());
306 assert_eq!(row.cases.len(), 19);
307 assert!(
308 row.cases
309 .iter()
310 .filter(|case| matches!(case.expectation, SourceExpectation::LowersTo(_)))
311 .count()
312 == 16
313 );
314 assert!(
315 row.cases
316 .iter()
317 .any(|case| matches!(case.expectation, SourceExpectation::ExpectedGap { .. }))
318 );
319 }
320}