Skip to content

Commit 35b1073

Browse files
committed
environment now wraps values in Var, and we can inspect these vars with
the var macro
1 parent d6fe513 commit 35b1073

File tree

14 files changed

+300
-36
lines changed

14 files changed

+300
-36
lines changed

src/clojure/core.clj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,5 @@
6666

6767
(defn ffirst [x]
6868
(first (first x)))
69+
(defmacro var [name]
70+
(var-fn* name))

src/environment.rs

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ impl EnvironmentVal {
5555
fn has_namespace(&self, namespace: &Symbol) -> bool {
5656
self.namespaces.has_namespace(namespace)
5757
}
58+
fn get_var_from_namespace(&self, namespace: &Symbol, sym: &Symbol) -> Rc<Value> {
59+
self.namespaces.get_var(namespace, sym)
60+
}
5861
fn get_from_namespace(&self, namespace: &Symbol, sym: &Symbol) -> Rc<Value> {
5962
self.namespaces.get(namespace, sym)
6063
}
@@ -216,7 +219,33 @@ impl Environment {
216219
LocalEnvironment(parent_env, ..) => parent_env.get_main_environment(),
217220
}
218221
}
219-
222+
pub fn get_var(&self, sym: &Symbol) -> Rc<Value> {
223+
match self {
224+
MainEnvironment(env_val) => {
225+
// If we've recieved a qualified symbol like
226+
// clojure.core/+
227+
if sym.has_ns() {
228+
// Use that namespace
229+
env_val.get_var_from_namespace(&Symbol::intern(&sym.ns), sym)
230+
} else {
231+
env_val.get_var_from_namespace(
232+
&env_val.get_current_namespace(),
233+
&Symbol::intern(&sym.name),
234+
)
235+
}
236+
}
237+
LocalEnvironment(parent_env, mappings) => {
238+
if sym.ns != "" {
239+
return self.get_main_environment().get(sym);
240+
}
241+
match mappings.borrow().get(sym) {
242+
Some(val) => Rc::clone(val),
243+
None => parent_env.get(sym),
244+
}
245+
}
246+
}
247+
}
248+
// @TODO refactor to use ^
220249
// @TODO figure out convention for 'ns' vs 'namespace'
221250
/// Get closest value "around" us; try our local environment, then
222251
/// try our main environment (unless its namespace qualified)
@@ -307,6 +336,8 @@ impl Environment {
307336
let ns_macro = rust_core::NsMacro::new(Rc::clone(&environment));
308337
let load_file_fn = rust_core::LoadFileFn::new(Rc::clone(&environment));
309338
let refer_fn = rust_core::ReferFn::new(Rc::clone(&environment));
339+
//let meta_fn = rust_core::MetaFn::new(Rc::clone(&environment));
340+
let var_fn = rust_core::special_form::VarFn::new(Rc::clone(&environment));
310341
// @TODO after we merge this with all the other commits we have,
311342
// just change all the `insert`s here to use insert_in_namespace
312343
// I prefer explicity and the non-dependence-on-environmental-factors
@@ -325,6 +356,7 @@ impl Environment {
325356
environment.insert(Symbol::intern("fn"), fn_macro.to_rc_value());
326357
environment.insert(Symbol::intern("defmacro"), defmacro_macro.to_rc_value());
327358
environment.insert(Symbol::intern("eval"), eval_fn.to_rc_value());
359+
environment.insert(Symbol::intern("var-fn*"), var_fn.to_rc_value());
328360

329361
// Thread namespace
330362
environment.insert_into_namespace(
@@ -428,20 +460,13 @@ impl Environment {
428460
split_fn.to_rc_value(),
429461
);
430462

431-
environment.insert(Symbol::intern("+"), add_fn.to_rc_value());
432-
environment.insert(Symbol::intern("let"), let_macro.to_rc_value());
433-
environment.insert(Symbol::intern("str"), str_fn.to_rc_value());
434-
environment.insert(Symbol::intern("map"), map_fn.to_rc_value());
435463

436464
environment.insert(Symbol::intern("quote"), quote_macro.to_rc_value());
437465
environment.insert(Symbol::intern("do-fn*"), do_fn.to_rc_value());
438466
environment.insert(Symbol::intern("do"), do_macro.to_rc_value());
439467
environment.insert(Symbol::intern("def"), def_macro.to_rc_value());
440-
environment.insert(Symbol::intern("fn"), fn_macro.to_rc_value());
441468
environment.insert(Symbol::intern("if"), if_macro.to_rc_value());
442-
environment.insert(Symbol::intern("defmacro"), defmacro_macro.to_rc_value());
443469
environment.insert(Symbol::intern("ns"), ns_macro.to_rc_value());
444-
environment.insert(Symbol::intern("eval"), eval_fn.to_rc_value());
445470
environment.insert(
446471
Symbol::intern("lexical-eval"),
447472
lexical_eval_fn.to_rc_value(),

src/main.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ mod persistent_list;
1010
mod protocol;
1111
#[macro_use]
1212
mod symbol;
13+
#[macro_use]
14+
mod var;
1315
mod clojure_std;
1416
mod clojure_string;
1517
mod environment;
@@ -19,6 +21,7 @@ mod iterable;
1921
mod keyword;
2022
mod lambda;
2123
mod maps;
24+
mod meta;
2225
mod namespace;
2326
mod persistent_vector;
2427
mod reader;
@@ -28,7 +31,6 @@ mod type_tag;
2831
mod user_action;
2932
mod util;
3033
mod value;
31-
3234
mod protocols;
3335
mod traits;
3436
fn main() {

src/namespace.rs

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
use crate::persistent_list_map::PersistentListMap;
12
use crate::symbol::Symbol;
2-
use crate::value::Value;
3+
use crate::var::Var;
4+
use crate::value::{ToValue, Value};
35
use std::cell::RefCell;
46
use std::collections::HashMap;
57
use std::rc::Rc;
@@ -126,12 +128,18 @@ impl Default for Refers {
126128
#[derive(Debug, Clone)]
127129
pub struct Namespace {
128130
pub name: Symbol,
129-
mappings: RefCell<HashMap<Symbol, Rc<Value>>>,
130131
pub refers: RefCell<Refers>,
132+
// @TODO decide to make Var a Rc<Var> ?
133+
// On one hand, its internals (except its ns and name) are Rcs, so cloning is still
134+
// cloning a reference
135+
//
136+
// On the other hand, the Var itself, regardless, lives in one spot and is referenced by many
137+
// This explicitly shows its a reference
138+
mappings: RefCell<HashMap<Symbol, Var>>,
131139
}
132140

133141
impl Namespace {
134-
fn new(name: &Symbol, mappings: HashMap<Symbol, Rc<Value>>, refers: Refers) -> Namespace {
142+
fn new(name: &Symbol, mappings: HashMap<Symbol, Var>, refers: Refers) -> Namespace {
135143
Namespace {
136144
name: name.unqualified(),
137145
mappings: RefCell::new(mappings),
@@ -155,20 +163,47 @@ impl Namespace {
155163
.replace_with(|refers| refers.add_referred_namespaces(namespaces));
156164
}
157165

166+
//
167+
fn contains_key(&self,sym: &Symbol) -> bool {
168+
self.mappings.borrow_mut().contains_key(sym)
169+
}
158170
pub fn insert(&self, sym: &Symbol, val: Rc<Value>) {
159-
self.mappings.borrow_mut().insert(sym.unqualified(), val);
171+
if !self.contains_key(sym) {
172+
let var = var!(&self.name.name,&sym.name);
173+
var.bind_root(val);
174+
var.set_meta(sym.meta());
175+
self.mappings
176+
.borrow_mut()
177+
.insert(sym.unqualified(), var);
178+
} else {
179+
let mappings = self.mappings.borrow_mut();
180+
let var = mappings
181+
.get(&sym.unqualified())
182+
.unwrap();
183+
184+
var.bind_root(val);
185+
var.set_meta(sym.meta());
186+
}
187+
}
188+
189+
pub fn get_var(&self, sym: &Symbol) -> Rc<Value> {
190+
match self.mappings.borrow_mut().get(&sym.unqualified()).map(|var| var.clone()) {
191+
Some(var) => Rc::new(Value::Var(var)),
192+
None => Rc::new(Value::Condition(format!("Undefined symbol {}", sym.name))),
193+
}
160194
}
161195

162196
pub fn try_get(&self, sym: &Symbol) -> Option<Rc<Value>> {
163197
match self.mappings.borrow_mut().get(&sym.unqualified()) {
164-
Some(val) => Some(Rc::clone(val)),
198+
Some(var) => Some(var.deref()),
165199
None => None,
166200
}
167201
}
202+
168203
pub fn get(&self, sym: &Symbol) -> Rc<Value> {
169204
match self.try_get(sym) {
170205
Some(val) => val,
171-
None => Rc::new(Value::Condition(format!("1 Undefined symbol {}", sym.name))),
206+
None => Rc::new(Value::Condition(format!("Undefined symbol {}", sym.name))),
172207
}
173208
}
174209
}
@@ -260,7 +295,28 @@ impl Namespaces {
260295
}
261296
}
262297
}
298+
// TODO write this similar to try_get, and rewrite try_get in terms of this
299+
pub fn get_var(&self, namespace_sym: &Symbol, sym: &Symbol) -> Rc<Value> {
300+
// When storing / retrieving from namespaces, we want
301+
// namespace_sym unqualified keys
302+
let mut namespace_sym = namespace_sym.unqualified();
303+
304+
// @TODO just make it an Optional<String>
305+
// If our sym is namespace qualified, use that as our namespace
306+
if sym.has_ns() {
307+
namespace_sym = Symbol::intern(&sym.ns);
308+
}
309+
310+
let sym = sym.unqualified();
311+
let namespaces = self.0.borrow();
312+
let namespace = namespaces.get(&namespace_sym);
263313

314+
match namespace {
315+
Some(namespace) => namespace.get_var(&sym),
316+
// @TODO should this be a condition or nil?
317+
_ => Rc::new(Value::Condition(format!("Undefined symbol {}", sym.name))),
318+
}
319+
}
264320
/// Like get, but slightly lower level; returns a None on failure rather than a
265321
/// Value::Condition. See docs for get
266322
pub fn try_get(&self, namespace_sym: &Symbol, sym: &Symbol) -> Option<Rc<Value>> {
@@ -313,11 +369,11 @@ impl Namespaces {
313369
// { 'clojure.core ['a 'b 'c] ,
314370
// 'clojure.string ['x 'y 'z]}
315371
// referred_namespace_sym: Symbol::intern("clojure.core"),
316-
// referred_syms: vec![Symbol::intern("x"), .. Symbol::intern("z")]
372+
// referred_syms: vec![Symbol::intern("a"), .. Symbol::intern("c")]
317373
for (referred_namespace_sym, referred_syms) in referred_syms_map.iter() {
318374
// Ex: (if we're trying to get, say, '+)
319375
// Do we even refer a '+ from this namespace?
320-
// 'clojure.string ['x 'y 'z] <-- no
376+
// 'clojure.core ['a 'b 'c] <-- no
321377
// Continue then
322378
if !referred_syms.contains(&sym) {
323379
continue;
@@ -710,12 +766,12 @@ mod tests {
710766
namespace.insert(&Symbol::intern("a"), Rc::new(Value::Nil));
711767
namespace.insert(&Symbol::intern_with_ns("ns", "b"), Rc::new(Value::Nil));
712768
match &*namespace.get(&Symbol::intern("a")) {
713-
Value::Condition(_) => panic!("We are unable to get a symbol we've put into our namespace created with from_sym()"),
769+
Value::Condition(_) => panic!("We are unable to get a symbol (a) we've put into our namespace created with from_sym()"),
714770
_ => {}
715771
}
716772

717773
match &*namespace.get(&Symbol::intern("b")) {
718-
Value::Condition(_) => panic!("We are unable to get a symbol we've put into our namespace created with from_sym()"),
774+
Value::Condition(_) => panic!("We are unable to get a symbol (ns/b) we've put into our namespace created with from_sym()"),
719775
_ => {}
720776
}
721777

src/protocol.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ impl ProtocolCastable for Rc<Value> {
7878
macro_rules! define_protocol {
7979
// define_protocol!(Protocol = A | B)
8080
($protocol:ident = $($variant:ident) |*) => {
81-
#[derive(Debug, Clone)]
81+
#[derive(Hash,PartialEq,Eq,Debug, Clone)]
8282
pub struct $protocol {
8383
value: Rc<Value>
8484
}

src/protocols/imeta.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use crate::value::Value;
99
use std::rc::Rc;
1010

1111
define_protocol!(
12-
IMeta = PersistentList |
12+
IMeta = Var | // <-- where all the magic happens
13+
PersistentList |
1314
PersistentVector |
1415
PersistentListMap |
1516
Symbol // |
@@ -30,6 +31,9 @@ impl traits::IMeta for IMeta {
3031
Value::Symbol(val) => {
3132
val.meta()
3233
}
34+
Value::Var(var) => {
35+
var.meta()
36+
}
3337
_ => panic!("protocols::IMeta was wrapping an invalid type {} when calling meta()",self.value.type_tag())
3438
// Value::IFn(val) => {
3539
// val.with_meta(meta)

src/protocols/ipersistent_map.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::rc::Rc;
33
use crate::persistent_list_map;
44
use crate::protocol::ProtocolCastable;
55

6-
define_protocol!(IPersistentMap,PersistentListMap);
6+
define_protocol!(IPersistentMap = PersistentListMap);
77

88
impl persistent_list_map::IPersistentMap for IPersistentMap {
99
fn get(&self, key: &Rc<Value>) -> Rc<Value> {

src/rust_core.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
// This module will hold core function and macro primitives that aren't special cases
22
// (like the quote macro, or let), and can't be implemented in clojure itself
33

4+
// special forms
5+
pub(crate) mod special_form;
6+
pub use self::special_form::*;
7+
48
// language core functions
59
pub(crate) mod eval;
610
pub use self::eval::*;

src/rust_core/special_form.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
3+
Clojure Special Forms
4+
5+
(pprint (keys (. clojure.lang.Compiler specials)))
6+
7+
TODO: &
8+
TODO: monitor-exit
9+
TODO: case*
10+
TODO: try
11+
TODO: reify*
12+
TODO: finally
13+
TODO: loop*
14+
TODO: do
15+
TODO: letfn*
16+
TODO: if
17+
TODO: clojure.core/import*
18+
TODO: new
19+
TODO: deftype*
20+
TODO: let*
21+
TODO: fn*
22+
TODO: recur
23+
TODO: set!
24+
TODO: .
25+
* var
26+
TODO: quote
27+
TODO: catch
28+
TODO: throw
29+
TODO: monitor-enter
30+
TODO: def
31+
32+
*/
33+
34+
pub(crate) mod var;
35+
pub use self::var::*;

src/rust_core/special_form/var.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use crate::environment::Environment;
2+
use crate::error_message;
3+
use crate::ifn::IFn;
4+
use crate::type_tag::TypeTag;
5+
use crate::value::{ToValue, Value};
6+
use std::rc::Rc;
7+
8+
/// Returns var for symbol
9+
/// example (var println)
10+
#[derive(Debug, Clone)]
11+
pub struct VarFn {
12+
enclosing_environment: Rc<Environment>,
13+
}
14+
impl VarFn {
15+
pub fn new(enclosing_environment: Rc<Environment>) -> VarFn {
16+
VarFn {
17+
enclosing_environment,
18+
}
19+
}
20+
}
21+
impl ToValue for VarFn {
22+
fn to_value(&self) -> Value {
23+
Value::IFn(Rc::new(self.clone()))
24+
}
25+
}
26+
impl IFn for VarFn {
27+
fn invoke(&self, args: Vec<Rc<Value>>) -> Value {
28+
if args.len() != 1 {
29+
return error_message::wrong_arg_count(1, args.len());
30+
}
31+
32+
match args.get(0).unwrap().to_value() {
33+
Value::Symbol(s) => match &*self.enclosing_environment.get_var(&s) {
34+
var @ Value::Var(_) => var.clone(),
35+
error @ Value::Condition(_) => error.clone(),
36+
_ => panic!("environment.get_var didn't return Var or Condition in var special form")
37+
},
38+
_ => {
39+
error_message::type_mismatch(TypeTag::Symbol, args.get(0).unwrap())
40+
}
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)