Skip to content

Commit 0f0fc05

Browse files
committed
Added bare basic referring of whole namespaces (with clojure.core referring by default)
1 parent d82ca34 commit 0f0fc05

File tree

5 files changed

+198
-37
lines changed

5 files changed

+198
-37
lines changed

src/environment.rs

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::clojure_std;
22
use crate::clojure_string;
3-
use crate::namespace::{Namespace, Namespaces};
3+
use crate::namespace::{Namespaces};
44
use crate::repl::Repl;
55
use crate::rust_core;
66
use crate::symbol::Symbol;
@@ -23,22 +23,42 @@ pub struct EnvironmentVal {
2323
namespaces: Namespaces,
2424
}
2525
impl EnvironmentVal {
26+
// @TODO is this wrapper really necessary, or is it just inviting an invariant break?
27+
/// Note; do not use. Does not enforce the invariant that namespace exist
28+
/// Use change_or_create_namespace instead
2629
fn change_namespace(&self, name: Symbol) {
2730
self.curr_ns_sym.replace(name);
2831
}
32+
fn change_or_create_namespace(&self, symbol: &Symbol)
33+
{
34+
if self.has_namespace(symbol) {
35+
self.change_namespace(symbol.unqualified());
36+
}
37+
else {
38+
self.create_namespace(symbol);
39+
self.change_namespace(symbol.unqualified());
40+
}
41+
}
2942
fn insert_into_namespace(&self, namespace: &Symbol, sym: Symbol, val: Rc<Value>) {
3043
self.namespaces.insert_into_namespace(namespace, &sym, val);
3144
}
3245
fn insert_into_current_namespace(&self, sym: Symbol, val: Rc<Value>) {
3346
self.namespaces
3447
.insert_into_namespace(&*self.curr_ns_sym.borrow(), &sym, val);
3548
}
49+
fn has_namespace(&self, namespace: &Symbol) -> bool {
50+
self.namespaces.has_namespace(namespace)
51+
}
3652
fn get_from_namespace(&self, namespace: &Symbol, sym: &Symbol) -> Rc<Value> {
3753
self.namespaces.get(namespace, sym)
3854
}
3955
fn get_current_namespace(&self) -> Symbol {
4056
self.curr_ns_sym.borrow().clone()
4157
}
58+
59+
fn create_namespace(&self,symbol: &Symbol) {
60+
self.namespaces.create_namespace(symbol);
61+
}
4262
// @TODO as mentioned, we've been working with a memory model where values exist
4363
// in our system once-ish and we reference them all over with Rc<..>
4464
// Look into possibly working this into that (if its even significant);
@@ -68,16 +88,27 @@ pub enum Environment {
6888
}
6989
use Environment::*;
7090
impl Environment {
71-
pub fn change_namespace(&self, symbol: Symbol) {
72-
let symbol = symbol.unqualified();
73-
91+
pub fn has_namespace(&self, symbol: &Symbol) -> bool {
7492
match self.get_main_environment() {
75-
MainEnvironment(EnvironmentVal { curr_ns_sym, .. }) => {
76-
curr_ns_sym.replace(symbol);
93+
MainEnvironment(env_val) => {
94+
env_val.has_namespace(symbol)
7795
}
7896
LocalEnvironment(..) => panic!(
7997
"get_main_environment() returns LocalEnvironment,\
80-
but by definition should only return MainEnvironment"
98+
but by definition should only return MainEnvironment"
99+
),
100+
}
101+
}
102+
/// Changes the current namespace, or creates one first if
103+
/// namespace doesn't already exist
104+
pub fn change_or_create_namespace(&self, symbol: &Symbol) {
105+
match self.get_main_environment() {
106+
MainEnvironment(env_val) => {
107+
env_val.change_or_create_namespace(symbol);
108+
}
109+
LocalEnvironment(..) => panic!(
110+
"get_main_environment() returns LocalEnvironment,\
111+
but by definition should only return MainEnvironment"
81112
),
82113
}
83114
}
@@ -155,13 +186,13 @@ impl Environment {
155186
MainEnvironment(env_val) => {
156187
// If we've recieved a qualified symbol like
157188
// clojure.core/+
158-
if sym.ns != "" {
189+
if sym.has_ns() {
159190
// Use that namespace
160191
env_val.get_from_namespace(&Symbol::intern(&sym.ns), sym)
161192
} else {
162193
env_val.get_from_namespace(
163194
&env_val.get_current_namespace(),
164-
&Symbol::intern(&sym.name),
195+
&sym,
165196
)
166197
}
167198
}
@@ -237,7 +268,7 @@ impl Environment {
237268
// @TODO after we merge this with all the other commits we have,
238269
// just change all the `insert`s here to use insert_in_namespace
239270
// I prefer explicity and the non-dependence-on-environmental-factors
240-
environment.change_namespace(Symbol::intern("clojure.core"));
271+
environment.change_or_create_namespace(&Symbol::intern("clojure.core"));
241272

242273
environment.insert(Symbol::intern("+"), add_fn.to_rc_value());
243274
environment.insert(Symbol::intern("-"), subtract_fn.to_rc_value());
@@ -395,7 +426,7 @@ impl Environment {
395426
let _ = Repl::new(Rc::clone(&environment)).try_eval_file("./src/clojure/core.clj");
396427

397428
// We can add this back once we have requires
398-
// environment.change_namespace(Symbol::intern("user"));
429+
// environment.change_or_create_namespace(Symbol::intern("user"));
399430

400431
environment
401432
}
@@ -421,11 +452,11 @@ mod tests {
421452

422453
assert_eq!(Symbol::intern("user"), env_val.get_current_namespace());
423454

424-
env_val.change_namespace(Symbol::intern("core"));
455+
env_val.change_or_create_namespace(&Symbol::intern("core"));
425456
assert_eq!(Symbol::intern("core"), env_val.get_current_namespace());
426457

427458
// @TODO add this invariant back next, and remove this comment; 5.9.2020
428-
// env_val.change_namespace(Symbol::intern_with_ns("not-ns","ns"));
459+
// env_val.change_or_create_namespace(Symbol::intern_with_ns("not-ns","ns"));
429460
// assert_eq!(Symbol::intern("ns"),env_val.get_current_namespace())
430461

431462
// @TODO add case for local environment

src/namespace.rs

Lines changed: 148 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,76 @@ use crate::value::Value;
33
use std::cell::RefCell;
44
use std::collections::HashMap;
55
use std::rc::Rc;
6+
/// This is a list of namespaces your namespace has completely referred, and the symbols
7+
/// that your namespace has individually referred
8+
/// Example:
9+
/// ```clojure
10+
/// (ns cats (:require [dogs :refer :all] [chickens :refer [a b c]))`
11+
/// ```
12+
/// =>
13+
/// ```
14+
/// Refers {
15+
/// refers: [
16+
/// Symbol::intern("clojure.core"),
17+
/// Symbol::intern("dogs")
18+
/// },
19+
/// syms: {
20+
/// Symbol::intern("chickens") : [ Symbol::intern("a"),..,Symbol::intern("c")]
21+
/// }
22+
/// }
23+
/// ``
24+
#[derive(Debug, Clone)]
25+
pub struct Refers {
26+
/// Namespaces that you have completely referred into your namespace;
27+
/// Basically, `[blah :refer :all]`
28+
pub namespaces: Vec<Symbol>,
29+
/// Symbols that you have individually referred into your namespace from another;
30+
/// Basically, `[blah :refer [a b c]]`
31+
pub syms: HashMap<Symbol,Vec<Symbol>>
32+
}
33+
impl Default for Refers {
34+
fn default() -> Self {
35+
Refers {
36+
namespaces: vec![Symbol::intern("clojure.core")],
37+
syms: HashMap::new()
38+
}
39+
}
40+
}
641

742
#[derive(Debug, Clone)]
843
pub struct Namespace {
944
pub name: Symbol,
1045
mappings: RefCell<HashMap<Symbol, Rc<Value>>>,
46+
pub refers: RefCell<Refers>
1147
}
1248
impl Namespace {
13-
pub fn new(name: &Symbol, mappings: RefCell<HashMap<Symbol, Rc<Value>>>) -> Namespace {
49+
fn new(name: &Symbol, mappings: HashMap<Symbol, Rc<Value>>,refers: Refers) -> Namespace {
1450
Namespace {
1551
name: name.unqualified(),
16-
mappings,
52+
mappings: RefCell::new(mappings),
53+
refers: RefCell::new(refers)
1754
}
1855
}
1956
pub fn from_sym(name: &Symbol) -> Namespace {
20-
Namespace::new(name, RefCell::new(HashMap::new()))
57+
Namespace::new(name, HashMap::new(), Refers::default())
2158
}
2259
pub fn insert(&self, sym: &Symbol, val: Rc<Value>) {
2360
self.mappings.borrow_mut().insert(sym.unqualified(), val);
2461
}
25-
pub fn get(&self, sym: &Symbol) -> Rc<Value> {
62+
pub fn try_get(&self, sym: &Symbol) -> Option<Rc<Value>> {
2663
match self.mappings.borrow_mut().get(&sym.unqualified()) {
27-
Some(val) => Rc::clone(val),
64+
Some(val) => Some(Rc::clone(val)),
65+
None => None
66+
}
67+
}
68+
pub fn get(&self, sym: &Symbol) -> Rc<Value> {
69+
match self.try_get(sym) {
70+
Some(val) => val,
2871
None => Rc::new(Value::Condition(format!("1 Undefined symbol {}", sym.name))),
2972
}
3073
}
3174
}
75+
3276
#[derive(Debug, Clone)]
3377
pub struct Namespaces(pub RefCell<HashMap<Symbol, Namespace>>);
3478

@@ -84,26 +128,88 @@ impl Namespaces {
84128
}
85129
}
86130
}
87-
/// Get value of sym at namespace
88-
pub fn get(&self, namespace_sym: &Symbol, sym: &Symbol) -> Rc<Value> {
131+
/// Like get, but slightly lower level; returns a None on failure rather than a
132+
/// Value::Condition. See docs for get
133+
pub fn try_get(&self, namespace_sym: &Symbol, sym: &Symbol) -> Option<Rc<Value>> {
89134
// When storing / retrieving from namespaces, we want
90135
// namespace_sym unqualified keys
91136
let mut namespace_sym = namespace_sym.unqualified();
92-
137+
// Ie, a scenario like get(.. , 'clojure.core/+) or get(.., 'shortcut/+)
138+
let mut grabbing_from_namespace_directly = false;
93139
// @TODO just make it an Optional<String>
94140
// If our sym is namespace qualified, use that as our namespace
95141
if sym.has_ns() {
142+
grabbing_from_namespace_directly = true;
96143
namespace_sym = Symbol::intern(&sym.ns);
97144
}
98145

99146
let sym = sym.unqualified();
100147
let namespaces = self.0.borrow();
101148
let namespace = namespaces.get(&namespace_sym);
102-
149+
//@TODO change to map
103150
match namespace {
104-
Some(namespace) => Rc::clone(&namespace.get(&sym)),
151+
Some(namespace) => {
152+
// If we cannot find the symbol, and its not a direct grab from a specific namespace,
153+
// we should see if we can find it in one of our referred namespaces or symbols
154+
let val = namespace.try_get(&sym);
155+
match val {
156+
Some(_) => val,
157+
None => {
158+
if grabbing_from_namespace_directly {
159+
return None;
160+
}
161+
let refers = namespace.refers.borrow();
162+
let referred_namespaces = &refers.namespaces;
163+
for referred_namespace_sym in referred_namespaces.into_iter() {
164+
if *referred_namespace_sym == namespace_sym {
165+
continue;
166+
}
167+
let try_get_sym_from_other_ns = self.try_get(&referred_namespace_sym,&sym);
168+
if let Some(val) = &try_get_sym_from_other_ns {
169+
return try_get_sym_from_other_ns;
170+
}
171+
}
172+
None
173+
//
174+
// Big @TODO
175+
// lookup iterating through hashmap
176+
//
177+
// let referred_syms = refers.syms;
178+
// for referred_sym in referred_syms.into_iter() {
179+
// }
180+
}
181+
}
182+
},
183+
None => None
184+
}
185+
}
186+
/// Get value of sym in namespace
187+
/// Note;
188+
/// ```
189+
/// get('clojure.core,'+)
190+
/// ```
191+
/// Will be asking what '+ means in 'clojure.core, so
192+
/// this will only return a value if there is a 'clojure.core/+
193+
/// But
194+
/// ```
195+
/// get('clojure.core, 'clojure.other/+)
196+
/// ```
197+
/// Will always mean the same thing, no matter what namespace we're in; it will mean
198+
/// the value '+ belonging to clojure.other, the namespace you're in is irrelevant
199+
///
200+
/// Finally,
201+
/// ```
202+
/// get('clojure.core, 'shortcut/+)
203+
/// ```
204+
/// Will depend on what shortcut expands to in clojure.core (assuming shortcut is not an actual namespace here)
205+
///
206+
/// As we can see, this is a relatively high level function meant to be getting _whatever_
207+
/// a user has typed in for a symbol while inside a namespace
208+
pub fn get(&self, namespace_sym: &Symbol, sym: &Symbol) -> Rc<Value> {
209+
match self.try_get(namespace_sym,sym) {
210+
Some(val) => val,
105211
// @TODO should this be a condition or nil?
106-
_ => Rc::new(Value::Condition(format!("Undefined symbol {}", sym.name))),
212+
None => Rc::new(Value::Condition(format!("Undefined symbol {}", sym.name)))
107213
}
108214
}
109215
}
@@ -115,15 +221,16 @@ mod tests {
115221
// a struct
116222
mod namespace_struct {
117223
use crate::namespace::Namespace;
224+
use crate::namespace::Refers;
118225
use crate::symbol::Symbol;
119226
use crate::value::Value;
120227
use std::cell::RefCell;
121228
use std::collections::HashMap;
122229
use std::rc::Rc;
123-
230+
124231
#[test]
125232
fn new() {
126-
let namespace = Namespace::new(&Symbol::intern("a"), RefCell::new(HashMap::new()));
233+
let namespace = Namespace::new(&Symbol::intern("a"), HashMap::new(), Refers::default());
127234
assert_eq!(namespace.name, Symbol::intern("a"));
128235
assert!(namespace.mappings.borrow().is_empty());
129236
}
@@ -132,18 +239,20 @@ mod tests {
132239
fn new_removes_namespace_from_qualified_symbol() {
133240
let namespace = Namespace::new(
134241
&Symbol::intern_with_ns("ns", "a"),
135-
RefCell::new(HashMap::new()),
242+
HashMap::new(),
243+
Refers::default()
136244
);
137245
assert_eq!(namespace.name, Symbol::intern("a"));
138246
assert!(namespace.name != Symbol::intern_with_ns("ns", "a"));
139247
assert!(namespace.mappings.borrow().is_empty());
140248
}
141249
#[test]
142250
fn new_namespace_starts_empty() {
143-
let namespace = Namespace::new(&Symbol::intern("a"), RefCell::new(HashMap::new()));
251+
let namespace = Namespace::new(&Symbol::intern("a"), HashMap::new(),Refers::default());
144252
let namespace2 = Namespace::new(
145253
&Symbol::intern_with_ns("ns", "b"),
146-
RefCell::new(HashMap::new()),
254+
HashMap::new(),
255+
Refers::default()
147256
);
148257
assert!(namespace.mappings.borrow().is_empty());
149258
assert!(namespace2.mappings.borrow().is_empty());
@@ -419,6 +528,29 @@ mod tests {
419528
}
420529
}
421530
}
531+
#[test]
532+
fn get_from_referred_namespace() {
533+
let namespaces = Namespaces::new();
534+
namespaces.insert_into_namespace(
535+
&Symbol::intern("clojure.core"),
536+
&Symbol::intern("+"),
537+
Rc::new(Value::Nil),
538+
);
539+
540+
namespaces.insert_into_namespace(
541+
&Symbol::intern("user"),
542+
&Symbol::intern("-"),
543+
Rc::new(Value::Nil),
544+
);
545+
546+
match &*namespaces.get(&Symbol::intern("user"),&Symbol::intern("+")) {
547+
Value::Condition(_) => {
548+
panic!("Namespace user failed to grab clojure.core/+ as a referred symbol");
549+
}
550+
_ => {}
551+
}
552+
553+
}
422554
////////////////////////////////////////////////////////////////////////////////////////////////////
423555
//
424556
////////////////////////////////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)