-
Notifications
You must be signed in to change notification settings - Fork 26
/
Copy pathsymbol.rs
232 lines (225 loc) · 7.34 KB
/
symbol.rs
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
use crate::persistent_list_map::PersistentListMap;
use crate::traits;
use std::fmt;
use std::hash::{Hash, Hasher};
#[derive(Eq, Clone, Debug)]
pub struct Symbol {
pub name: String,
// @TODO Should this be an optional string?
// on one hand, playing with this is closer to the original,
// and slightly easier to read and understand (for me).
// But you might say it doesn't force you to cover the None
// route, the sort of invariants ADTs are good at.
// Most likely, we will reimplement this as Option<String>
pub ns: String,
pub meta: PersistentListMap,
}
#[macro_export]
macro_rules! sym {
($x:expr) => {
$crate::symbol::Symbol::intern($x)
}
}
impl Hash for Symbol {
fn hash<H: Hasher>(&self, state: &mut H) {
(&self.name,&self.ns).hash(state);
}
}
impl Symbol {
pub fn intern(name: &str) -> Symbol {
let mut ns = "";
let mut name = name;
// @TODO See if we will have any problems with manipulating
// text in other languages
// I think we will be ok here though
if let Some(ind) = name.chars().position(|c| c == '/') {
// @TODO Make sure that the index given by ^
// has the same meaning as the index
// we are giving to this range
// Ie, if the 6 in a[..6] refers to the 6th byte
// and if the 6 in Some(6) means the 6th character,
// we need to make sure each 'character' in this case
// is 1 byte, and not some other grapheme abstraction
// else,these are two different indexes
// support interning of the symbol '/' for division
if ind > 0 || name.len() > 1 {
ns = &name[..ind];
name = &name[ind + 1..];
}
}
Symbol::intern_with_ns(ns, name)
}
pub fn intern_with_ns(ns: &str, name: &str) -> Symbol {
Symbol {
name: String::from(name),
ns: String::from(ns),
meta: PersistentListMap::Empty,
}
}
pub fn unqualified(&self) -> Symbol {
// So we can keep the same meta
let mut retval = self.clone();
retval.ns = String::from("");
retval
}
pub fn has_ns(&self) -> bool {
self.ns != ""
}
pub fn name(&self) -> &str {
&self.name
}
// @TODO use IPersistentMap instead perhaps
pub fn meta(&self) -> PersistentListMap {
self.meta.clone()
}
pub fn with_meta(&self, meta: PersistentListMap) -> Symbol {
Symbol {
name: self.name.clone(), // String::from(self.name.clone()),
ns: self.ns.clone(), // String::from(self.ns.clone()),
meta,
}
}
}
impl PartialEq for Symbol {
// Remember; meta doesn't factor into equality
fn eq(&self,other: &Self) -> bool {
self.name == other.name && self.ns == other.ns
}
}
impl traits::IMeta for Symbol {
fn meta(&self) -> PersistentListMap {
self.meta()
}
}
impl traits::IObj for Symbol {
fn with_meta(&self,meta: PersistentListMap) -> Symbol {
self.with_meta(meta)
}
}
impl fmt::Display for Symbol {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.has_ns() {
write!(f, "{}/{}", self.ns, self.name)
} else {
write!(f, "{}", self.name)
}
}
}
mod tests {
mod symbol_tests {
use crate::keyword::Keyword;
use crate::maps::MapEntry;
use crate::persistent_list_map::ToPersistentListMapIter;
use crate::persistent_list_map::{PersistentListMap, PersistentListMapIter};
use crate::symbol::Symbol;
use crate::value::ToValue;
use crate::value::Value;
use std::collections::HashMap;
#[test]
fn test_intern() {
assert_eq!(
Symbol::intern("a"),
Symbol {
ns: String::from(""),
name: String::from("a"),
meta: PersistentListMap::Empty,
}
);
}
#[test]
fn test_intern_with_ns() {
assert_eq!(
Symbol::intern_with_ns("clojure.core", "a"),
Symbol {
ns: String::from("clojure.core"),
name: String::from("a"),
meta: PersistentListMap::Empty
}
);
assert_eq!(
Symbol::intern_with_ns("", "a"),
Symbol {
ns: String::from(""),
name: String::from("a"),
meta: PersistentListMap::Empty
}
);
assert_eq!(
Symbol::intern("a"),
Symbol {
ns: String::from(""),
name: String::from("a"),
meta: PersistentListMap::Empty
}
);
assert_eq!(
Symbol::intern("clojure.core/a"),
Symbol {
ns: String::from("clojure.core"),
name: String::from("a"),
meta: PersistentListMap::Empty
}
);
assert_eq!(
Symbol::intern("clojure/a"),
Symbol {
ns: String::from("clojure"),
name: String::from("a"),
meta: PersistentListMap::Empty,
}
);
assert_eq!(
Symbol::intern("/a"),
Symbol {
ns: String::from(""),
name: String::from("a"),
meta: PersistentListMap::Empty,
}
);
}
#[test]
fn test_with_meta() {
assert_eq!(
Symbol::intern_with_ns(
"namespace",
"name"
).with_meta(
persistent_list_map!(map_entry!("key", "value"))
),
Symbol {
ns: String::from("namespace"),
name: String::from("name"),
meta: persistent_list_map!(map_entry!("key", "value"))
}
);
assert_eq!(
Symbol::intern_with_ns(
"namespace",
"name"
).with_meta(
conj!(
PersistentListMap::Empty,
map_entry!("key", "value")
)
),
Symbol {
ns: String::from("namespace"),
name: String::from("name"),
meta: conj!(
PersistentListMap::Empty,
map_entry!("key", "value")
)
}
);
}
#[test]
fn test_work_with_hashmap() {
let mut hashmap = HashMap::new();
hashmap.insert(Symbol::intern("+"), 1_i32);
hashmap.insert(Symbol::intern("-"), 2_i32);
assert_eq!(1_i32, *hashmap.get(&Symbol::intern("+")).unwrap());
assert_eq!(2_i32, *hashmap.get(&Symbol::intern("-")).unwrap());
assert_eq!(None, hashmap.get(&Symbol::intern("*")));
}
}
}