use std::cell::RefCell;
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use super::intern::{SymId, intern, resolve_sym};
use super::print::PrintOptions;
use super::value::{Value, ValueKind, VecLikeType};
use crate::emacs_core::eval::ResumeTarget;
use crate::window::WindowId;
thread_local! {
static FORMAT_OBJECT_STACK: RefCell<Vec<u64>> = const { RefCell::new(Vec::new()) };
}
#[derive(Clone, Debug)]
pub enum EvalError {
Signal {
symbol: SymId,
data: Vec<Value>,
raw_data: Option<Value>,
},
UncaughtThrow {
tag: Value,
value: Value,
},
}
impl Display for EvalError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Signal {
symbol,
data,
raw_data,
} => write!(
f,
"signal {} {}",
resolve_sym(*symbol),
format_signal_payload(raw_data.as_ref(), data),
),
Self::UncaughtThrow { tag, value } => write!(
f,
"uncaught throw tag={} value={}",
super::print::print_value(tag),
super::print::print_value(value),
),
}
}
}
impl Error for EvalError {}
#[derive(Clone, Debug)]
pub enum Flow {
Signal(Box<SignalData>),
Throw { tag: Value, value: Value },
}
#[derive(Clone, Debug)]
pub struct SignalData {
pub symbol: SymId,
pub data: Vec<Value>,
pub raw_data: Option<Value>,
pub(crate) suppress_signal_hook: bool,
pub(crate) selected_resume: Option<ResumeTarget>,
pub(crate) search_complete: bool,
}
impl SignalData {
pub fn symbol_name(&self) -> &str {
resolve_sym(self.symbol)
}
}
pub(crate) type EvalResult = Result<Value, Flow>;
pub(crate) fn signal(symbol: &str, data: Vec<Value>) -> Flow {
signal_internal(symbol, data, None, false)
}
pub(crate) fn signal_suppressed(symbol: &str, data: Vec<Value>) -> Flow {
signal_internal(symbol, data, None, true)
}
fn signal_internal(
symbol: &str,
data: Vec<Value>,
raw_data: Option<Value>,
suppress_signal_hook: bool,
) -> Flow {
if symbol == "void-variable" || symbol == "void-function" {
let data_strs: Vec<String> = data.iter().map(|v| super::print::print_value(v)).collect();
tracing::debug!("signal {symbol} ({})", data_strs.join(" "));
}
Flow::Signal(Box::new(SignalData {
symbol: intern(symbol),
data,
raw_data,
suppress_signal_hook,
selected_resume: None,
search_complete: false,
}))
}
pub(crate) fn signal_with_data(symbol: &str, data: Value) -> Flow {
signal_with_data_internal(symbol, data, false)
}
pub(crate) fn signal_with_data_suppressed(symbol: &str, data: Value) -> Flow {
signal_with_data_internal(symbol, data, true)
}
fn signal_with_data_internal(symbol: &str, data: Value, suppress_signal_hook: bool) -> Flow {
let normalized = super::value::list_to_vec(&data).unwrap_or_else(|| vec![data]);
signal_internal(symbol, normalized, Some(data), suppress_signal_hook)
}
pub fn map_flow(flow: Flow) -> EvalError {
match flow {
Flow::Signal(sig) => {
let SignalData {
symbol,
data,
raw_data,
..
} = *sig;
EvalError::Signal {
symbol,
data,
raw_data,
}
}
Flow::Throw { tag, value } => EvalError::UncaughtThrow { tag, value },
}
}
pub(crate) fn make_signal_binding_value(sig: &SignalData) -> Value {
if let Some(raw) = &sig.raw_data {
return Value::cons(Value::symbol(sig.symbol), *raw);
}
let mut values = Vec::with_capacity(sig.data.len() + 1);
values.push(Value::symbol(sig.symbol));
values.extend(sig.data.clone());
Value::list(values)
}
pub(crate) fn signal_from_binding_value(value: Value) -> Option<Flow> {
if !value.is_cons() {
return None;
};
let pair_car = value.cons_car();
let pair_cdr = value.cons_cdr();
let symbol = pair_car;
let tail = pair_cdr;
let symbol_name = symbol.as_symbol_name()?;
if tail.is_nil() {
return Some(signal(symbol_name, vec![]));
}
if let Some(items) = super::value::list_to_vec(&tail) {
return Some(signal(symbol_name, items));
}
Some(signal_with_data(symbol_name, tail))
}
pub fn format_eval_result(result: &Result<Value, EvalError>) -> String {
match result {
Ok(value) => format!("OK {}", super::print::print_value(value)),
Err(EvalError::Signal {
symbol,
data,
raw_data,
}) => {
let payload = format_signal_payload(raw_data.as_ref(), data);
format!("ERR ({} {})", resolve_sym(*symbol), payload)
}
Err(EvalError::UncaughtThrow { tag, value }) => {
format!(
"ERR (no-catch ({} {}))",
super::print::print_value(tag),
super::print::print_value(value),
)
}
}
}
fn format_signal_payload(raw_data: Option<&Value>, data: &[Value]) -> String {
if let Some(raw) = raw_data {
return super::print::print_value(raw);
}
if data.is_empty() {
"nil".to_string()
} else {
super::print::print_value(&Value::list(data.to_vec()))
}
}
fn format_opaque_handle_in_state(
buffers: &crate::buffer::BufferManager,
frames: &crate::window::FrameManager,
threads: &super::threads::ThreadManager,
value: &Value,
) -> Option<String> {
if let Some(handle) = super::terminal::pure::print_terminal_handle(value) {
return Some(handle);
}
if super::marker::is_marker(value) {
if let Some(marker) = value.as_marker_data() {
let mut out = String::from("#<marker ");
if marker.insertion_type {
out.push_str("(moves after insertion) ");
}
if let Some(buffer_id) = marker.buffer
&& let Some(buffer) = buffers.get(buffer_id)
{
out.push_str(&format!(
"at {} in {}",
marker.charpos + 1,
buffer.name_runtime_string_owned()
));
} else {
out.push_str("in no buffer");
}
out.push('>');
return Some(out);
}
}
if let Some(overlay) = value.as_overlay_data() {
if let Some(buffer_id) = overlay.buffer
&& let Some(buffer) = buffers.get(buffer_id)
{
return Some(format!(
"#<overlay from {} to {} in {}>",
buffer.text.emacs_byte_to_char(overlay.start) + 1,
buffer.text.emacs_byte_to_char(overlay.end) + 1,
buffer.name_runtime_string_owned()
));
}
return Some("#<overlay in no buffer>".to_string());
}
if let Some(id) = value.as_window_id() {
return Some(format_window_handle_in_state(buffers, frames, id));
}
if let Some(id) = threads.thread_id_from_handle(value) {
return Some(format!("#<thread {id}>"));
}
if let Some(id) = threads.mutex_id_from_handle(value) {
return Some(format!("#<mutex {id}>"));
}
if let Some(id) = threads.condition_variable_id_from_handle(value) {
return Some(format!("#<condvar {id}>"));
}
if let Some(buf_id) = value.as_buffer_id() {
if let Some(buf) = buffers.get(buf_id) {
return Some(format!("#<buffer {}>", buf.name_runtime_string_owned()));
}
if buffers.dead_buffer_last_name_value(buf_id).is_some() {
return Some("#<killed buffer>".to_string());
}
}
None
}
fn format_window_handle_in_state(
buffers: &crate::buffer::BufferManager,
frames: &crate::window::FrameManager,
id: u64,
) -> String {
let window_id = WindowId(id);
if let Some(frame_id) = frames.find_window_frame_id(window_id) {
if let Some(frame) = frames.get(frame_id) {
if let Some(window) = frame.find_window(window_id) {
if let Some(buffer_id) = window.buffer_id() {
if let Some(buffer) = buffers.get(buffer_id) {
return format!("#<window {id} on {}>", buffer.name_runtime_string_owned());
}
}
return format!("#<window {id} on {}>", frame.name_runtime_string_owned());
}
}
}
format!("#<window {id}>")
}
pub(crate) fn print_options_from_state(obarray: &super::symbol::Obarray) -> PrintOptions {
let print_gensym = obarray
.symbol_value("print-gensym")
.is_some_and(|v| v.is_truthy());
let print_circle = obarray
.symbol_value("print-circle")
.is_some_and(|v| v.is_truthy());
let print_escape_newlines = obarray
.symbol_value("print-escape-newlines")
.is_some_and(|v| v.is_truthy());
let print_level = obarray
.symbol_value("print-level")
.and_then(|v| v.as_fixnum())
.filter(|&n| n >= 0);
let print_length = obarray
.symbol_value("print-length")
.and_then(|v| v.as_fixnum())
.filter(|&n| n >= 0);
let print_escape_nonascii = obarray
.symbol_value("print-escape-nonascii")
.is_some_and(|v| v.is_truthy());
let print_escape_multibyte = obarray
.symbol_value("print-escape-multibyte")
.is_some_and(|v| v.is_truthy());
let print_escape_control_characters = obarray
.symbol_value("print-escape-control-characters")
.is_some_and(|v| v.is_truthy());
let print_continuous_numbering = obarray
.symbol_value("print-continuous-numbering")
.is_some_and(|v| v.is_truthy());
let print_number_table = if print_continuous_numbering {
obarray
.symbol_value("print-number-table")
.filter(|v| v.is_hash_table())
.copied()
} else {
None
};
let mut opts = PrintOptions::new(print_gensym, print_circle, print_level, print_length);
opts.print_escape_newlines = print_escape_newlines;
opts.print_escape_nonascii = print_escape_nonascii;
opts.print_escape_multibyte = print_escape_multibyte;
opts.print_escape_control_characters = print_escape_control_characters;
opts.print_continuous_numbering = print_continuous_numbering;
opts.print_number_table = print_number_table;
opts
}
pub(crate) fn print_value_in_state(
ctx: &crate::emacs_core::eval::Context,
value: &Value,
) -> String {
print_value_in_state_with_options(ctx, value, print_options_from_state(&ctx.obarray))
}
pub(crate) fn print_value_in_state_with_options(
ctx: &crate::emacs_core::eval::Context,
value: &Value,
options: PrintOptions,
) -> String {
format_value_in_state(
&ctx.obarray,
&ctx.buffers,
&ctx.frames,
&ctx.threads,
value,
options,
)
}
fn format_cycle_stack_index(value: &Value) -> Option<usize> {
let key = super::print::default_cycle_candidate_key(value)?;
FORMAT_OBJECT_STACK.with(|stack| stack.borrow().iter().position(|entry| *entry == key))
}
fn push_format_cycle_object(value: &Value) -> bool {
let Some(key) = super::print::default_cycle_candidate_key(value) else {
return false;
};
FORMAT_OBJECT_STACK.with(|stack| stack.borrow_mut().push(key));
true
}
fn pop_format_cycle_object(pushed: bool) {
if pushed {
FORMAT_OBJECT_STACK.with(|stack| {
stack.borrow_mut().pop();
});
}
}
fn format_object_stack_len() -> usize {
FORMAT_OBJECT_STACK.with(|stack| stack.borrow().len())
}
fn truncate_format_object_stack(len: usize) {
FORMAT_OBJECT_STACK.with(|stack| stack.borrow_mut().truncate(len));
}
fn format_value_in_state(
obarray: &super::symbol::Obarray,
buffers: &crate::buffer::BufferManager,
frames: &crate::window::FrameManager,
threads: &super::threads::ThreadManager,
value: &Value,
options: PrintOptions,
) -> String {
let _print_guard = super::print::enter_print_call(&options);
if let Some(handle) = format_opaque_handle_in_state(buffers, frames, threads, value) {
return handle;
}
if options.print_circle || options.print_level.is_some() || options.print_length.is_some() {
return super::print::print_value_stateful(value, options);
}
match value.kind() {
ValueKind::Cons | ValueKind::Veclike(VecLikeType::Vector) => {
if let Some(index) = format_cycle_stack_index(value) {
return format!("#{index}");
}
let pushed = push_format_cycle_object(value);
let rendered =
format_value_in_state_slow(obarray, buffers, frames, threads, value, options);
pop_format_cycle_object(pushed);
rendered
}
_ => super::print::print_value_with_options(value, options),
}
}
fn format_value_in_state_slow(
obarray: &super::symbol::Obarray,
buffers: &crate::buffer::BufferManager,
frames: &crate::window::FrameManager,
threads: &super::threads::ThreadManager,
value: &Value,
options: PrintOptions,
) -> String {
match value.kind() {
ValueKind::Cons => {
if let Some(shorthand) =
format_list_shorthand_in_state(obarray, buffers, frames, threads, value, options)
{
return shorthand;
}
let mut out = String::from("(");
format_cons_in_state(obarray, buffers, frames, threads, value, &mut out, options);
out.push(')');
out
}
ValueKind::Veclike(VecLikeType::Vector) => {
let mut out = String::from("[");
let items = value.as_vector_data().unwrap().clone();
for (idx, item) in items.iter().enumerate() {
if idx > 0 {
out.push(' ');
}
out.push_str(&format_value_in_state(
obarray, buffers, frames, threads, item, options,
));
}
out.push(']');
out
}
_ => super::print::print_value_with_options(value, options),
}
}
fn format_list_shorthand_in_state(
obarray: &super::symbol::Obarray,
buffers: &crate::buffer::BufferManager,
frames: &crate::window::FrameManager,
threads: &super::threads::ThreadManager,
value: &Value,
options: PrintOptions,
) -> Option<String> {
let items = super::value::list_to_vec(value)?;
if items.len() != 2 {
return None;
}
let head = match items[0].kind() {
ValueKind::Symbol(id) => resolve_sym(id),
_ => return None,
};
if head == "make-hash-table-from-literal" {
let payload = quote_payload(&items[1])?;
return Some(format!(
"#s{}",
format_value_in_state(obarray, buffers, frames, threads, &payload, options)
));
}
let (prefix, quoted, nested_options) = match head {
"quote" => Some(("'", &items[1], options)),
"function" => Some(("#'", &items[1], options)),
"`" => Some(("`", &items[1], options.enter_backquote())),
"," => {
options
.allow_unquote_shorthand()
.then_some((",", &items[1], options.exit_backquote()))
}
",@" => {
options
.allow_unquote_shorthand()
.then_some((",@", &items[1], options.exit_backquote()))
}
_ => None,
}?;
Some(format!(
"{prefix}{}",
format_value_in_state(obarray, buffers, frames, threads, quoted, nested_options)
))
}
fn format_cons_in_state(
obarray: &super::symbol::Obarray,
buffers: &crate::buffer::BufferManager,
frames: &crate::window::FrameManager,
threads: &super::threads::ThreadManager,
value: &Value,
out: &mut String,
options: PrintOptions,
) {
let mut cursor = *value;
let mut first = true;
let stack_len = format_object_stack_len();
loop {
match cursor.kind() {
ValueKind::Cons => {
if !first {
if let Some(index) = format_cycle_stack_index(&cursor) {
out.push_str(" . ");
out.push_str(&format!("#{index}"));
truncate_format_object_stack(stack_len);
return;
}
push_format_cycle_object(&cursor);
}
if !first {
out.push(' ');
}
let pair_car = cursor.cons_car();
let pair_cdr = cursor.cons_cdr();
out.push_str(&format_value_in_state(
obarray, buffers, frames, threads, &pair_car, options,
));
cursor = pair_cdr;
first = false;
}
ValueKind::Nil => {
truncate_format_object_stack(stack_len);
return;
}
_ => {
if !first {
out.push_str(" . ");
}
out.push_str(&format_value_in_state(
obarray, buffers, frames, threads, &cursor, options,
));
truncate_format_object_stack(stack_len);
return;
}
}
}
}
pub(crate) fn print_value_bytes_in_state(
obarray: &super::symbol::Obarray,
buffers: &crate::buffer::BufferManager,
frames: &crate::window::FrameManager,
threads: &super::threads::ThreadManager,
value: &Value,
) -> Vec<u8> {
if let Some(handle) = format_opaque_handle_in_state(buffers, frames, threads, value) {
return handle.into_bytes();
}
format_value_bytes_in_state_with_options(
obarray,
buffers,
frames,
threads,
value,
print_options_from_state(obarray),
)
}
pub(crate) fn format_value_bytes_in_state_with_options(
obarray: &super::symbol::Obarray,
buffers: &crate::buffer::BufferManager,
frames: &crate::window::FrameManager,
threads: &super::threads::ThreadManager,
value: &Value,
options: PrintOptions,
) -> Vec<u8> {
let _print_guard = super::print::enter_print_call(&options);
if let Some(handle) = format_opaque_handle_in_state(buffers, frames, threads, value) {
return handle.into_bytes();
}
if options.print_circle || options.print_level.is_some() || options.print_length.is_some() {
return super::print::print_value_stateful(value, options).into_bytes();
}
match value.kind() {
ValueKind::Cons => {
if let Some(index) = format_cycle_stack_index(value) {
return format!("#{index}").into_bytes();
}
let pushed = push_format_cycle_object(value);
let rendered =
format_cons_bytes_in_state(obarray, buffers, frames, threads, value, options);
pop_format_cycle_object(pushed);
rendered
}
ValueKind::Veclike(VecLikeType::Vector) => {
if let Some(index) = format_cycle_stack_index(value) {
return format!("#{index}").into_bytes();
}
let pushed = push_format_cycle_object(value);
let rendered =
format_vector_bytes_in_state(obarray, buffers, frames, threads, value, options);
pop_format_cycle_object(pushed);
rendered
}
_ => super::print::print_value_bytes_with_options(value, options),
}
}
fn format_cons_bytes_in_state(
obarray: &super::symbol::Obarray,
buffers: &crate::buffer::BufferManager,
frames: &crate::window::FrameManager,
threads: &super::threads::ThreadManager,
value: &Value,
options: PrintOptions,
) -> Vec<u8> {
if let Some(shorthand) =
format_list_shorthand_bytes_in_state(obarray, buffers, frames, threads, value, options)
{
return shorthand;
}
let mut out = Vec::new();
out.push(b'(');
append_cons_bytes_in_state(obarray, buffers, frames, threads, value, &mut out, options);
out.push(b')');
out
}
fn format_vector_bytes_in_state(
obarray: &super::symbol::Obarray,
buffers: &crate::buffer::BufferManager,
frames: &crate::window::FrameManager,
threads: &super::threads::ThreadManager,
value: &Value,
options: PrintOptions,
) -> Vec<u8> {
if super::chartable::bool_vector_length(value).is_some()
|| super::chartable::char_table_external_slots(value).is_some()
{
return super::print::print_value_bytes_with_options(value, options);
}
let mut out = Vec::new();
out.push(b'[');
let Some(values) = value.as_vector_data() else {
out.push(b']');
return out;
};
for (idx, item) in values.iter().enumerate() {
if idx > 0 {
out.push(b' ');
}
out.extend(format_value_bytes_in_state_with_options(
obarray, buffers, frames, threads, item, options,
));
}
out.push(b']');
out
}
fn format_list_shorthand_bytes_in_state(
obarray: &super::symbol::Obarray,
buffers: &crate::buffer::BufferManager,
frames: &crate::window::FrameManager,
threads: &super::threads::ThreadManager,
value: &Value,
options: PrintOptions,
) -> Option<Vec<u8>> {
let items = super::value::list_to_vec(value)?;
if items.len() != 2 {
return None;
}
let head = match items[0].kind() {
ValueKind::Symbol(id) => resolve_sym(id),
_ => return None,
};
if head == "make-hash-table-from-literal" {
let payload = quote_payload(&items[1])?;
let mut out = Vec::new();
out.extend_from_slice(b"#s");
out.extend(format_value_bytes_in_state_with_options(
obarray, buffers, frames, threads, &payload, options,
));
return Some(out);
}
let (prefix, quoted, nested_options) = match head {
"quote" => Some((b"'" as &[u8], &items[1], options)),
"function" => Some((b"#'" as &[u8], &items[1], options)),
"`" => Some((b"`" as &[u8], &items[1], options.enter_backquote())),
"," => options.allow_unquote_shorthand().then_some((
b"," as &[u8],
&items[1],
options.exit_backquote(),
)),
",@" => options.allow_unquote_shorthand().then_some((
b",@" as &[u8],
&items[1],
options.exit_backquote(),
)),
_ => None,
}?;
let mut out = Vec::new();
out.extend_from_slice(prefix);
out.extend(format_value_bytes_in_state_with_options(
obarray,
buffers,
frames,
threads,
quoted,
nested_options,
));
Some(out)
}
fn quote_payload(value: &Value) -> Option<Value> {
let items = super::value::list_to_vec(value)?;
if items.len() != 2 {
return None;
}
match items[0].kind() {
ValueKind::Symbol(id) if resolve_sym(id) == "quote" => Some(items[1]),
_ => None,
}
}
fn append_cons_bytes_in_state(
obarray: &super::symbol::Obarray,
buffers: &crate::buffer::BufferManager,
frames: &crate::window::FrameManager,
threads: &super::threads::ThreadManager,
value: &Value,
out: &mut Vec<u8>,
options: PrintOptions,
) {
let mut cursor = *value;
let mut first = true;
let stack_len = format_object_stack_len();
loop {
match cursor.kind() {
ValueKind::Cons => {
if !first {
if let Some(index) = format_cycle_stack_index(&cursor) {
out.extend_from_slice(b" . ");
out.extend_from_slice(format!("#{index}").as_bytes());
truncate_format_object_stack(stack_len);
return;
}
push_format_cycle_object(&cursor);
}
if !first {
out.push(b' ');
}
let pair_car = cursor.cons_car();
let pair_cdr = cursor.cons_cdr();
out.extend(format_value_bytes_in_state_with_options(
obarray, buffers, frames, threads, &pair_car, options,
));
cursor = pair_cdr;
first = false;
}
ValueKind::Nil => {
truncate_format_object_stack(stack_len);
return;
}
_ => {
if !first {
out.extend_from_slice(b" . ");
}
out.extend(format_value_bytes_in_state_with_options(
obarray, buffers, frames, threads, &cursor, options,
));
truncate_format_object_stack(stack_len);
return;
}
}
}
}
pub fn print_value_with_eval(eval: &super::eval::Context, value: &Value) -> String {
print_value_in_state(eval, value)
}
pub fn print_value_bytes_with_eval(eval: &super::eval::Context, value: &Value) -> Vec<u8> {
print_value_bytes_in_state(
&eval.obarray,
&eval.buffers,
&eval.frames,
&eval.threads,
value,
)
}
fn print_data_payload_with_eval(eval: &super::eval::Context, data: &[Value]) -> String {
if data.is_empty() {
"nil".to_string()
} else {
let parts = data
.iter()
.map(|v| print_value_with_eval(eval, v))
.collect::<Vec<_>>();
format!("({})", parts.join(" "))
}
}
fn print_signal_payload_with_eval(
eval: &super::eval::Context,
raw_data: Option<&Value>,
data: &[Value],
) -> String {
if let Some(raw) = raw_data {
return print_value_with_eval(eval, raw);
}
print_data_payload_with_eval(eval, data)
}
fn append_print_value_bytes_with_eval(
eval: &super::eval::Context,
value: &Value,
out: &mut Vec<u8>,
) {
out.extend_from_slice(&print_value_bytes_with_eval(eval, value));
}
pub fn format_eval_result_with_eval(
eval: &super::eval::Context,
result: &Result<Value, EvalError>,
) -> String {
match result {
Ok(value) => format!("OK {}", print_value_with_eval(eval, value)),
Err(EvalError::Signal {
symbol,
data,
raw_data,
}) => {
let payload = print_signal_payload_with_eval(eval, raw_data.as_ref(), data);
format!("ERR ({} {})", resolve_sym(*symbol), payload)
}
Err(EvalError::UncaughtThrow { tag, value }) => {
format!(
"ERR (no-catch ({} {}))",
print_value_with_eval(eval, tag),
print_value_with_eval(eval, value),
)
}
}
}
fn append_signal_payload_bytes_with_eval(
eval: &super::eval::Context,
raw_data: Option<&Value>,
data: &[Value],
out: &mut Vec<u8>,
) {
if let Some(raw) = raw_data {
append_print_value_bytes_with_eval(eval, raw, out);
} else if data.is_empty() {
out.extend_from_slice(b"nil");
} else {
out.push(b'(');
for (idx, item) in data.iter().enumerate() {
if idx > 0 {
out.push(b' ');
}
append_print_value_bytes_with_eval(eval, item, out);
}
out.push(b')');
}
}
pub fn format_eval_result_bytes_with_eval(
eval: &super::eval::Context,
result: &Result<Value, EvalError>,
) -> Vec<u8> {
let mut out = Vec::new();
match result {
Ok(value) => {
out.extend_from_slice(b"OK ");
append_print_value_bytes_with_eval(eval, value, &mut out);
}
Err(EvalError::Signal {
symbol,
data,
raw_data,
}) => {
out.extend_from_slice(b"ERR (");
out.extend_from_slice(resolve_sym(*symbol).as_bytes());
out.push(b' ');
append_signal_payload_bytes_with_eval(eval, raw_data.as_ref(), data, &mut out);
out.push(b')');
}
Err(EvalError::UncaughtThrow { tag, value }) => {
out.extend_from_slice(b"ERR (no-catch (");
append_print_value_bytes_with_eval(eval, tag, &mut out);
out.push(b' ');
append_print_value_bytes_with_eval(eval, value, &mut out);
out.extend_from_slice(b"))");
}
}
out
}
#[cfg(test)]
#[path = "error_test.rs"]
mod tests;