1use crate::cell::*;
10use crate::copyterm::copy_to_buf;
11use crate::machine::{Machine, RtError};
12use crate::render::format_term;
13
14pub fn set_formal(m: &mut Machine, formal: Word, context: &str, uncatchable: bool) {
17 let mark = m.heap.len().min(payload_start(formal, m));
18 let ctx_atom = make_atom(m.atoms.intern(context));
19 let err_f = m.atoms.intern("error");
20 let idx = m.heap.len();
21 m.heap.push(pack_functor(err_f, 2));
22 m.heap.push(formal);
23 m.heap.push(ctx_atom);
24 let ball_word = make(TAG_STR, idx as u64);
25 let ball = copy_to_buf(m, ball_word);
26 let mut message = String::new();
27 format_term(m, ball_word, &mut message);
28 if let Some((file, line, col)) = m.site_location(m.error_site) {
38 use std::fmt::Write as _;
39 let _ = write!(message, " at {file}:{line}:{col}");
40 }
41 m.heap.truncate(mark.max(idx)); m.error = Some(RtError {
43 ball,
44 message,
45 uncatchable,
46 });
47}
48
49fn payload_start(w: Word, m: &Machine) -> usize {
51 match tag_of(w) {
52 TAG_STR | TAG_LST | TAG_FLT | TAG_BIG => payload(w) as usize,
53 _ => m.heap.len(),
54 }
55}
56
57pub fn instantiation(m: &mut Machine, context: &str) {
59 let f = make_atom(m.atoms.intern("instantiation_error"));
60 set_formal(m, f, context, false);
61}
62
63pub fn type_error(m: &mut Machine, expected: &str, culprit: Word, context: &str) {
65 let te = m.atoms.intern("type_error");
66 let ty = make_atom(m.atoms.intern(expected));
67 let idx = m.heap.len();
68 m.heap.push(pack_functor(te, 2));
69 m.heap.push(ty);
70 m.heap.push(culprit);
71 set_formal(m, make(TAG_STR, idx as u64), context, false);
72}
73
74pub fn domain_error(m: &mut Machine, domain: &str, culprit: Word, context: &str) {
76 let de = m.atoms.intern("domain_error");
77 let d = make_atom(m.atoms.intern(domain));
78 let idx = m.heap.len();
79 m.heap.push(pack_functor(de, 2));
80 m.heap.push(d);
81 m.heap.push(culprit);
82 set_formal(m, make(TAG_STR, idx as u64), context, false);
83}
84
85pub fn evaluation(m: &mut Machine, kind: &str, context: &str) {
87 let ee = m.atoms.intern("evaluation_error");
88 let k = make_atom(m.atoms.intern(kind));
89 let idx = m.heap.len();
90 m.heap.push(pack_functor(ee, 1));
91 m.heap.push(k);
92 set_formal(m, make(TAG_STR, idx as u64), context, false);
93}
94
95pub fn resource(m: &mut Machine, kind: &str, context: &str, uncatchable: bool) {
97 let re = m.atoms.intern("resource_error");
98 let k = make_atom(m.atoms.intern(kind));
99 let idx = m.heap.len();
100 m.heap.push(pack_functor(re, 1));
101 m.heap.push(k);
102 set_formal(m, make(TAG_STR, idx as u64), context, uncatchable);
103}
104
105pub fn existence_procedure(m: &mut Machine, name: &str, arity: u32) {
109 let ee = m.atoms.intern("existence_error");
110 let proc = make_atom(m.atoms.intern("procedure"));
111 let slash = m.atoms.intern("/");
112 let name_atom = make_atom(m.atoms.intern(name));
113 let pi = m.heap.len();
114 m.heap.push(pack_functor(slash, 2));
115 m.heap.push(name_atom);
116 m.heap.push(make_int(arity as i64));
117 let idx = m.heap.len();
118 m.heap.push(pack_functor(ee, 2));
119 m.heap.push(proc);
120 m.heap.push(make(TAG_STR, pi as u64));
121 let context = format!("Undefined procedure: {name}/{arity}");
122 set_formal(m, make(TAG_STR, idx as u64), &context, false);
123}
124
125pub fn throw_term(m: &mut Machine, ball_word: Word) {
128 let ball_word = m.deref(ball_word);
129 if tag_of(ball_word) == TAG_REF {
130 instantiation(m, "throw/1 requires a bound argument");
132 return;
133 }
134 let ball = copy_to_buf(m, ball_word);
135 let mut message = String::new();
136 format_term(m, ball_word, &mut message);
137 m.error = Some(RtError {
138 ball,
139 message,
140 uncatchable: false,
141 });
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use plg_shared::StringInterner;
148
149 fn machine() -> Box<Machine> {
150 Machine::new(StringInterner::new(), Vec::new())
151 }
152
153 #[test]
154 fn existence_message_matches_v1_bytes() {
155 let mut m = machine();
156 existence_procedure(&mut m, "nosuch", 1);
157 assert_eq!(
158 m.error.as_ref().unwrap().message,
159 "error(existence_error(procedure, /(nosuch, 1)), Undefined procedure: nosuch/1)"
160 );
161 }
162
163 #[test]
164 fn evaluation_message_matches_v1_bytes() {
165 let mut m = machine();
166 evaluation(
167 &mut m,
168 "zero_divisor",
169 "Division by zero (integer division)",
170 );
171 assert_eq!(
172 m.error.as_ref().unwrap().message,
173 "error(evaluation_error(zero_divisor), Division by zero (integer division))"
174 );
175 }
176
177 #[test]
178 fn existence_suffix_appears_when_site_resolves() {
179 use crate::machine::SrcLoc;
180 let mut m = machine();
181 m.set_provenance(
182 vec![SrcLoc {
183 file: 0,
184 line: 12,
185 col: 7,
186 }],
187 vec!["examples/family.pl".to_string()],
188 );
189 m.error_site = 0;
190 existence_procedure(&mut m, "foo", 1);
191 assert_eq!(
193 m.error.as_ref().unwrap().message,
194 "error(existence_error(procedure, /(foo, 1)), Undefined procedure: foo/1) \
195 at examples/family.pl:12:7"
196 );
197 }
198
199 #[test]
200 fn ball_survives_heap_truncation() {
201 let mut m = machine();
202 let heap_mark = m.heap.len();
203 existence_procedure(&mut m, "gone", 2);
204 m.heap.truncate(heap_mark); let err = m.error.take().unwrap();
206 let w = crate::copyterm::restore_from_buf(&mut m, &err.ball);
207 let mut s = String::new();
208 format_term(&m, w, &mut s);
209 assert!(s.starts_with("error(existence_error(procedure"), "{s}");
210 }
211}