-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathrecord.rs
More file actions
148 lines (131 loc) · 3.97 KB
/
record.rs
File metadata and controls
148 lines (131 loc) · 3.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use crate::symbols::{Expr, LispResult, SymbolTable};
use anyhow::bail;
use core::hash::Hash;
use core::hash::Hasher;
use im::Vector;
use std::fmt;
use std::ops::Deref;
pub(crate) type RecordType = Box<dyn Record>;
/// Document Records. Used in the document_records! macro
/// to properly document your record type.
pub(crate) trait RecordDoc {
/// Public name of the the record.
fn name() -> &'static str;
/// Documentation for that record type.
fn type_doc() -> &'static str;
/// Documentation of the methods.
/// (method_name, method_doc)
fn method_doc() -> &'static [(&'static str, &'static str)];
}
/// Fundamental trait for records.
///
/// Records allow x7 to represent a variety of internally mutable types
/// while not expanding the Expr enum too much. These types are responsible for
/// implementing RecordDoc if they want to have documentation.
pub trait Record: Sync + Send + downcast_rs::DowncastSync {
/// Call a method on this record.
/// (.method_name <rec> arg1 arg2 arg3)
/// Becomes:
/// (&self: <rec>, sym: "method_name", args: vector![arg1, arg2, arg3])
fn call_method(
&self,
sym: &str,
args: Vector<Expr>,
symbol_table: &SymbolTable,
) -> LispResult<Expr>;
fn has_method(&self, sym: &str) -> bool;
fn id(&self) -> u64 {
0
}
/// Nicely display the record type.
fn display(&self) -> String;
/// Add more information for debug printing
fn debug(&self) -> String;
/// Clone the object.
fn clone(&self) -> RecordType;
/// Return the names of the methods for help messages.
fn methods(&self) -> Vec<String>;
/// Return the type name for nice help messages
fn type_name(&self) -> String;
// This method is used for bad_types error handling
fn get_type_str(&self) -> String {
self.type_name()
}
fn call_as_fn(&self, _args: Vector<Expr>, _symbol_table: &SymbolTable) -> LispResult<Expr> {
bail!("{:?} is not a function", self.debug())
}
fn defmethod(&self, _args: Vector<Expr>, _symbol_table: &SymbolTable) -> LispResult<Expr> {
bail!(
"Defining methods is not supported on this record: {}",
Record::type_name(self)
)
}
fn is_equal(&self, _other: &dyn Record) -> bool {
false
}
}
downcast_rs::impl_downcast!(Record);
impl fmt::Display for &dyn Record {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.display())
}
}
impl fmt::Display for RecordType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.display())
}
}
impl fmt::Debug for RecordType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.debug())
}
}
impl Hash for RecordType {
fn hash<H: Hasher>(&self, state: &mut H) {
self.deref().id().hash(state);
}
}
impl PartialEq for RecordType {
fn eq(&self, other: &RecordType) -> bool {
self.is_equal(other.as_ref())
}
}
impl Clone for RecordType {
fn clone(&self) -> RecordType {
Record::clone(self.as_ref())
}
}
#[macro_export]
macro_rules! record {
($e:expr) => {
Ok(Expr::Record(Box::new($e)))
};
}
#[macro_export]
macro_rules! unknown_method {
($self:expr, $method:expr) => {{
use itertools::Itertools;
Err(anyhow!(
"Unknown method `{}` on {}\n\nHelp! {} has the following methods:\n\n{}",
$method,
$self.display(),
$self.type_name(),
$self
.methods()
.iter()
.map(|s| format!("- {}", s))
.join("\n")
))
}};
}
#[macro_export]
macro_rules! try_call_method {
($self:expr, $sym:expr, $args:expr, $($method_name:ident),*) => {
match $sym {
$(
stringify!($method_name) => $self.$method_name($args),
)*
_ => unknown_method!($self, $sym)
}
}
}