Skip to content

Commit d56172d

Browse files
authored
Merge pull request #37 from erkkikeranen/feature/sleep-and-nanoseconds
Thread/sleep, System/nanotime, time macro, subtract, multiply, divide, inc and dec
2 parents ae3c659 + 1f9bd38 commit d56172d

File tree

11 files changed

+440
-1
lines changed

11 files changed

+440
-1
lines changed

src/clojure/core.clj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,15 @@
1010

1111
(defn println [& more]
1212
(print-string (apply str more)))
13+
14+
(defn inc [x]
15+
(+ x 1))
16+
17+
(defn dec [x]
18+
(- x 1))
19+
20+
(defmacro time [expr]
21+
(list (quote let) [(quote start) (quote (System_nanotime)) (quote ret) expr]
22+
(quote (do
23+
(println (str "Elapsed time: " (_slash_ (- (System_nanotime) start) 1000000.0) " msecs"))
24+
ret))))

src/clojure_std.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub (crate) mod thread;
2+
pub (crate) mod time;

src/clojure_std/thread.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use crate::value::{ToValue, Value};
2+
use std::rc::Rc;
3+
use crate::ifn::IFn;
4+
use std::io::Read;
5+
use std::error::Error;
6+
use crate::error_message;
7+
use nom::lib::std::convert::TryFrom;
8+
use crate::type_tag::TypeTag;
9+
10+
use std::thread;
11+
use std::time;
12+
13+
/// provides a sleep function to sleep for given amount of ms
14+
#[derive(Debug, Clone)]
15+
pub struct SleepFn {}
16+
impl ToValue for SleepFn {
17+
fn to_value(&self) -> Value {
18+
Value::IFn(Rc::new(self.clone()))
19+
}
20+
}
21+
22+
impl IFn for SleepFn {
23+
fn invoke(&self, args: Vec<Rc<Value>>) -> Value {
24+
if args.len() == 1 {
25+
let arg = &**args.get(0).unwrap();
26+
match arg {
27+
Value::I32(i) => {
28+
std::thread::sleep(time::Duration::new(0, (*i as u32) * 1000000));
29+
return Value::Nil
30+
},
31+
_ => error_message::type_mismatch(TypeTag::I32, args.get(0).unwrap())
32+
}
33+
} else {
34+
error_message::wrong_arg_count(1, args.len());
35+
return Value::Nil
36+
}
37+
}
38+
}

src/clojure_std/time.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use crate::value::{ToValue, Value};
2+
use std::rc::Rc;
3+
use crate::ifn::IFn;
4+
use std::io::Read;
5+
use std::error::Error;
6+
use crate::error_message;
7+
use nom::lib::std::convert::TryFrom;
8+
use crate::type_tag::TypeTag;
9+
10+
use std::thread;
11+
use std::time::{SystemTime, UNIX_EPOCH};
12+
13+
/// provides a function to return current time in nanoseconds
14+
#[derive(Debug, Clone)]
15+
pub struct NanoTimeFn {}
16+
impl ToValue for NanoTimeFn {
17+
fn to_value(&self) -> Value {
18+
Value::IFn(Rc::new(self.clone()))
19+
}
20+
}
21+
22+
impl IFn for NanoTimeFn {
23+
fn invoke(&self, args: Vec<Rc<Value>>) -> Value {
24+
if args.len() == 0 {
25+
let ns = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos();
26+
return Value::F64(ns as f64)
27+
} else {
28+
error_message::wrong_arg_count(0, args.len());
29+
return Value::Nil
30+
}
31+
}
32+
}

src/environment.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::namespace::{Namespace, Namespaces};
22
use crate::repl;
33
use crate::repl::Repl;
44
use crate::rust_core;
5+
use crate::clojure_std;
56
use crate::symbol::Symbol;
67
use crate::value::{ToValue, Value};
78

@@ -70,13 +71,19 @@ impl Environment {
7071
pub fn clojure_core_environment() -> Rc<Environment> {
7172
// Register our macros / functions ahead of time
7273
let add_fn = rust_core::AddFn {};
74+
let subtract_fn = rust_core::SubtractFn {};
75+
let multiply_fn = rust_core::MultiplyFn {};
76+
let divide_fn = rust_core::DivideFn {};
7377
let str_fn = rust_core::StrFn {};
7478
let do_fn = rust_core::DoFn {};
7579
let nth_fn = rust_core::NthFn {};
7680
let do_macro = rust_core::DoMacro {};
7781
let concat_fn = rust_core::ConcatFn {};
7882
let print_string_fn = rust_core::PrintStringFn {};
7983
let assoc_fn = rust_core::AssocFn {};
84+
// clojure.std functions
85+
let thread_sleep_fn = clojure_std::thread::SleepFn {};
86+
let nanotime_fn = clojure_std::time::NanoTimeFn {};
8087
// Hardcoded fns
8188
let lexical_eval_fn = Value::LexicalEvalFn {};
8289
// Hardcoded macros
@@ -90,6 +97,9 @@ impl Environment {
9097
let eval_fn = rust_core::EvalFn::new(Rc::clone(&environment));
9198

9299
environment.insert(Symbol::intern("+"), add_fn.to_rc_value());
100+
environment.insert(Symbol::intern("-"), subtract_fn.to_rc_value());
101+
environment.insert(Symbol::intern("*"), multiply_fn.to_rc_value());
102+
environment.insert(Symbol::intern("_slash_"), divide_fn.to_rc_value());
93103
environment.insert(Symbol::intern("let"), let_macro.to_rc_value());
94104
environment.insert(Symbol::intern("str"), str_fn.to_rc_value());
95105
environment.insert(Symbol::intern("quote"), quote_macro.to_rc_value());
@@ -98,6 +108,11 @@ impl Environment {
98108
environment.insert(Symbol::intern("defmacro"), defmacro_macro.to_rc_value());
99109
environment.insert(Symbol::intern("eval"), eval_fn.to_rc_value());
100110

111+
// Thread namespace TODO / instead of _
112+
environment.insert(Symbol::intern("Thread_sleep"), thread_sleep_fn.to_rc_value());
113+
114+
environment.insert(Symbol::intern("System_nanotime"), nanotime_fn.to_rc_value());
115+
101116
environment.insert(Symbol::intern("+"), add_fn.to_rc_value());
102117
environment.insert(Symbol::intern("let"), let_macro.to_rc_value());
103118
environment.insert(Symbol::intern("str"), str_fn.to_rc_value());

src/error_message.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ pub fn wrong_varg_count(expected: &[usize], got: usize) -> Value {
2525
))
2626
}
2727

28+
pub fn zero_arg_count(got: usize) -> Value {
29+
Value::Condition(format!(
30+
"Wrong number of arguments given to function (Given: {})", got))
31+
}
32+
2833
pub fn index_out_of_bounds(ind: usize, count: usize) -> Value {
2934
Value::Condition(format!(
3035
"Index out of bounds: Index ({}), Length: ({})",

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ mod persistent_vector;
1313
mod reader;
1414
mod repl;
1515
mod rust_core;
16+
mod clojure_std;
1617
mod symbol;
1718
mod keyword;
1819
mod type_tag;

src/rust_core.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,18 @@ use crate::error_message;
1919

2020
use crate::util::IsEven;
2121

22+
pub(crate) mod _subtract_;
23+
pub use self::_subtract_::*;
24+
25+
pub(crate) mod _divide_;
26+
pub use self::_divide_::*;
27+
28+
pub(crate) mod _multiply_;
29+
pub use self::_multiply_::*;
30+
//
2231
// This module will hold core function and macro primitives that aren't special cases
2332
// (like the quote macro, or let), and can't be implemented in clojure itself
24-
33+
//
2534
#[derive(Debug, Clone)]
2635
pub struct StrFn {}
2736
impl ToValue for StrFn {

src/rust_core/_divide_.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
use crate::ifn::IFn;
2+
use crate::value::{Value, ToValue};
3+
use std::rc::Rc;
4+
5+
use crate::error_message;
6+
7+
/// (/ x y & xys)
8+
///
9+
#[derive(Debug, Clone)]
10+
pub struct DivideFn {}
11+
impl ToValue for DivideFn {
12+
fn to_value(&self) -> Value {
13+
Value::IFn(Rc::new(self.clone()))
14+
}
15+
}
16+
impl IFn for DivideFn {
17+
fn invoke(&self, args: Vec<Rc<Value>>) -> Value {
18+
match args.len() {
19+
0 => { error_message::zero_arg_count(args.len()) },
20+
1 => {
21+
let val = args.get(0).unwrap().to_value();
22+
match val {
23+
Value::I32(a_) => Value::F64(1.0 / a_ as f64),
24+
Value::F64(f_) => Value::F64(1.0 / f_),
25+
_ => Value::Condition(format!( // TODO: what error message should be returned regarding using typetags?
26+
"Type mismatch; Expecting: (i32 | i64 | f32 | f64), Found: {}",
27+
val.type_tag()
28+
))
29+
}
30+
},
31+
_ => {
32+
let mut args_iterator = args.into_iter();
33+
let first_arg = args_iterator.next().unwrap();
34+
args_iterator.fold(first_arg.to_value(), |a, b| match a {
35+
Value::I32(a_) => match *b {
36+
Value::I32(b_) => Value::I32(a_ / b_),
37+
Value::F64(b_) => Value::F64(a_ as f64 / b_),
38+
_ => Value::Condition(format!( // TODO: what error message should be returned regarding using typetags?
39+
"Type mismatch; Expecting: (i32 | i64 | f32 | f64), Found: {}",
40+
b.type_tag()
41+
)),
42+
},
43+
Value::F64(a_) => match *b {
44+
Value::I32(b_) => Value::F64(a_ / b_ as f64),
45+
Value::F64(b_) => Value::F64(a_ / b_),
46+
_ => Value::Condition(format!( // TODO: what error message should be returned regarding using typetags?
47+
"Type mismatch; Expecting: (i32 | i64 | f32 | f64), Found: {}",
48+
b.type_tag()
49+
)),
50+
},
51+
_ => Value::Condition(format!( // TODO: what error message should be returned regarding using typetags?
52+
"Type mismatch: Expecting: (i32 | i64 | f32 | f64), Found: {}",
53+
a.type_tag()
54+
)),
55+
})
56+
}
57+
}
58+
}
59+
}
60+
61+
#[cfg(test)]
62+
mod tests {
63+
mod Divide_tests {
64+
use crate::rust_core::_divide_::DivideFn;
65+
use crate::ifn::IFn;
66+
use crate::value::Value;
67+
use std::rc::Rc;
68+
69+
#[test]
70+
fn Divide_without_arguments_returns_one() {
71+
let Divide = DivideFn {};
72+
let args = vec![];
73+
assert_eq!(Value::Condition(String::from("Wrong number of arguments given to function (Given: 0)")),
74+
Divide.invoke(args));
75+
}
76+
77+
#[test]
78+
fn Divide_with_one_positive_argument_returns_reciprocal() {
79+
let divide = DivideFn {};
80+
let args = vec![Rc::new(Value::I32(5))];
81+
assert_eq!(Value::F64(1.0/5.0), divide.invoke(args));
82+
}
83+
84+
#[test]
85+
fn Divide_with_one_negative_argument_returns_reciprocal() {
86+
let divide = DivideFn {};
87+
let args = vec![Rc::new(Value::I32(-5))];
88+
assert_eq!(Value::F64(1.0 / -5.0), divide.invoke(args));
89+
}
90+
91+
#[test]
92+
fn Divide_with_two_integer_argument_returns_quotient() {
93+
let divide = DivideFn {};
94+
let args = vec![Rc::new(Value::I32(24)), Rc::new(Value::I32(6))];
95+
assert_eq!(Value::I32(4), divide.invoke(args));
96+
}
97+
98+
#[test]
99+
fn Divide_with_one_double_argument_returns_quotient() {
100+
let divide = DivideFn {};
101+
let args = vec![Rc::new(Value::I32(24)), Rc::new(Value::F64(1.5))];
102+
assert_eq!(Value::F64(16.0), divide.invoke(args));
103+
}
104+
105+
#[test]
106+
fn Divide_with_multiple_integer_arguments_returns_quotient() {
107+
let Divide = DivideFn {};
108+
let args = vec![
109+
Rc::new(Value::I32(100)),
110+
Rc::new(Value::I32(5)),
111+
Rc::new(Value::I32(4))];
112+
assert_eq!(Value::I32(5), Divide.invoke(args));
113+
}
114+
115+
#[test]
116+
fn Divide_with_multiple_mixed_arguments_returns_quotient() {
117+
let Divide = DivideFn {};
118+
let args = vec![
119+
Rc::new(Value::I32(100)),
120+
Rc::new(Value::I32(5)),
121+
Rc::new(Value::I32(4)),
122+
Rc::new(Value::F64(2.0))];
123+
assert_eq!(Value::F64(2.5), Divide.invoke(args));
124+
}
125+
126+
}
127+
}

src/rust_core/_multiply_.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
use crate::ifn::IFn;
2+
use crate::value::{Value, ToValue};
3+
use std::rc::Rc;
4+
5+
/// (* x y & xys)
6+
///
7+
#[derive(Debug, Clone)]
8+
pub struct MultiplyFn {}
9+
impl ToValue for MultiplyFn {
10+
fn to_value(&self) -> Value {
11+
Value::IFn(Rc::new(self.clone()))
12+
}
13+
}
14+
impl IFn for MultiplyFn {
15+
fn invoke(&self, args: Vec<Rc<Value>>) -> Value {
16+
args.into_iter().fold(1_i32.to_value(), |a, b| match a {
17+
Value::I32(a_) => match *b {
18+
Value::I32(b_) => Value::I32(a_ * b_),
19+
Value::F64(b_) => Value::F64(a_ as f64 * b_),
20+
_ => Value::Condition(format!( // TODO: what error message should be returned regarding using typetags?
21+
"Type mismatch; Expecting: (i32 | i64 | f32 | f64), Found: {}",
22+
b.type_tag()
23+
)),
24+
},
25+
Value::F64(a_) => match *b {
26+
Value::I32(b_) => Value::F64(a_ * b_ as f64),
27+
Value::F64(b_) => Value::F64(a_ * b_),
28+
_ => Value::Condition(format!( // TODO: what error message should be returned regarding using typetags?
29+
"Type mismatch; Expecting: (i32 | i64 | f32 | f64), Found: {}",
30+
b.type_tag()
31+
)),
32+
},
33+
_ => Value::Condition(format!( // TODO: what error message should be returned regarding using typetags?
34+
"Type mismatch: Expecting: (i32 | i64 | f32 | f64), Found: {}",
35+
a.type_tag()
36+
)),
37+
})
38+
}
39+
}
40+
41+
#[cfg(test)]
42+
mod tests {
43+
mod multiply_tests {
44+
use crate::rust_core::_multiply_::MultiplyFn;
45+
use crate::ifn::IFn;
46+
use crate::value::Value;
47+
use std::rc::Rc;
48+
49+
#[test]
50+
fn multiply_without_arguments_returns_one() {
51+
let multiply = MultiplyFn {};
52+
let args = vec![];
53+
assert_eq!(Value::I32(1), multiply.invoke(args));
54+
}
55+
56+
#[test]
57+
fn multiply_with_one_argument_returns_identity() {
58+
let multiply = MultiplyFn {};
59+
let args = vec![Rc::new(Value::I32(5))];
60+
assert_eq!(Value::I32(5), multiply.invoke(args));
61+
}
62+
63+
#[test]
64+
fn multiply_with_two_argument_returns_product() {
65+
let multiply = MultiplyFn {};
66+
let args = vec![Rc::new(Value::I32(5)), Rc::new(Value::I32(6))];
67+
assert_eq!(Value::I32(30), multiply.invoke(args));
68+
}
69+
70+
}
71+
}

0 commit comments

Comments
 (0)