M README.md => README.md +13 -0
@@ 108,6 108,7 @@ Cane is a reverse-polish-notation language. This means that the data comes *befo
| Name | Args | Notes |
| ---- | ---- | ----- |
| `print` | any | Prints the data as a string. Does not consume data |
+| `println` | any | Prints the data as a string with a newline appended. Does not consume data |
| `add` | any | Adds the data together. Not recursive. Consumes data. Based on type of first item. Adds second item to first, then third item to that, then so on. |
| `drop` | any | Deletes all previous data |
| `duplicate` | `(Data..., Number)` | Duplicates the data `N` times. `N` must be a non-negative integer. Does not consume data (it makes more of it) |
@@ 174,6 175,18 @@ Examples:
(1 2 8/2 -5439053/123129)
```
+| Name | Args | Notes |
+| ---- | ---- | ----- |
+| `mult` | `(Number...)` | Multiplies the numbers in order: `(1 2 3 mult)` is `1 * 2 * 3 = 6` |
+| `sub` | `(Number...)` | Subtracts the numbers in order: `(1 2 3 sub)` is `1 - 2 - 3 = -4` |
+| `div` | `(Number...)` | Divides the numbers in order: `(1 2 3 div)` is `(1/2)/3 = 1/6` |
+| `neg` | `Number` | Negates the previous number: `(1 neg)` is `(-1)` |
+| `floor` | `Number` | Rounds down the previous number: `(99/98 floor)` is `(1)` |
+| `ceil` | `Number` | Rounds up the previous number: `(99/98 ceil)` is `(2)` |
+| `abs` | `Number` | Finds the distance from zero of previous number: `(-99/98 abs)` is `(99/98)` |
+
+Note that `add` is not number-specific so it is documented in [basic actions](#basic-actions).
+
#### Casts
These can be converted between each other with casts. Each consumes the data preceding and replaces it with the new data.
M cane-lang/Cargo.toml => cane-lang/Cargo.toml +1 -0
@@ 16,3 16,4 @@ path = "src/cli.rs"
[dependencies]
malachite-q = "0.3"
+malachite-base = "0.3"
M cane-lang/src/lib.rs => cane-lang/src/lib.rs +311 -1
@@ 507,6 507,172 @@ where
)
.unwrap();
}
+ "println" => {
+ writeln!(
+ self.stdout,
+ "{}",
+ self.call_stack.last_mut().unwrap().get_data()
+ )
+ .unwrap();
+ }
+ "sub" => {
+ let data = self.call_stack.last_mut().unwrap().get_data();
+ let result_wrapped = data.get_list().pop_front();
+ if result_wrapped.is_none() {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ "Need data to subtract".to_string(),
+ ));
+ }
+ let mut result = result_wrapped.unwrap();
+ if result.get_kind() != Types::NUMBER {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ "Subtraction works on numbers only".to_string(),
+ ));
+ }
+ while let Some(mut var) = data.get_list().pop_front() {
+ if var.get_kind() != Types::NUMBER {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ "Subtraction works on numbers only".to_string(),
+ ));
+ }
+ result.get_number().subtract(var.get_number());
+ }
+ data.get_list().push(result);
+ }
+ "div" => {
+ let data = self.call_stack.last_mut().unwrap().get_data();
+ let result_wrapped = data.get_list().pop_front();
+ if result_wrapped.is_none() {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ "Need data to divide".to_string(),
+ ));
+ }
+ let mut result = result_wrapped.unwrap();
+ if result.get_kind() != Types::NUMBER {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ "Division works on numbers only.".to_string(),
+ ));
+ }
+ while let Some(mut var) = data.get_list().pop_front() {
+ if var.get_kind() != Types::NUMBER {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ "Division works on numbers only".to_string(),
+ ));
+ }
+ result.get_number().divide(var.get_number());
+ }
+ data.get_list().push(result);
+ }
+ "mult" => {
+ let data = self.call_stack.last_mut().unwrap().get_data();
+ let result_wrapped = data.get_list().pop_front();
+ if result_wrapped.is_none() {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ "Need data to multiply".to_string(),
+ ));
+ }
+ let mut result = result_wrapped.unwrap();
+ if result.get_kind() != Types::NUMBER {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ "Multiplication works on numbers only. Perhaps try `duplicate' instead?".to_string(),
+ ));
+ }
+ while let Some(mut var) = data.get_list().pop_front() {
+ if var.get_kind() != Types::NUMBER {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ "Multiplication works on numbers only. Perhaps try `duplicate' instead?".to_string(),
+ ));
+ }
+ result.get_number().multiply(var.get_number());
+ }
+ data.get_list().push(result);
+ }
+ "neg" => {
+ let data = self.call_stack.last_mut().unwrap().get_data();
+ let result_wrapped = data.get_list().pop_back();
+ if result_wrapped.is_none() {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ "Need data to negate".to_string(),
+ ));
+ }
+ let mut result = result_wrapped.unwrap();
+ if result.get_kind() != Types::NUMBER {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ "Negation works on numbers only.".to_string(),
+ ));
+ }
+ result.get_number().negate();
+ data.get_list().push(result);
+ }
+ "floor" => {
+ let data = self.call_stack.last_mut().unwrap().get_data();
+ let result_wrapped = data.get_list().pop_back();
+ if result_wrapped.is_none() {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ "Need data to round down".to_string(),
+ ));
+ }
+ let mut result = result_wrapped.unwrap();
+ if result.get_kind() != Types::NUMBER {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ "Rounding down works on numbers only.".to_string(),
+ ));
+ }
+ result.get_number().floor();
+ data.get_list().push(result);
+ }
+ "ceil" => {
+ let data = self.call_stack.last_mut().unwrap().get_data();
+ let result_wrapped = data.get_list().pop_back();
+ if result_wrapped.is_none() {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ "Need data to round up".to_string(),
+ ));
+ }
+ let mut result = result_wrapped.unwrap();
+ if result.get_kind() != Types::NUMBER {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ "Rounding up works on numbers only.".to_string(),
+ ));
+ }
+ result.get_number().ceiling();
+ data.get_list().push(result);
+ }
+ "abs" => {
+ let data = self.call_stack.last_mut().unwrap().get_data();
+ let result_wrapped = data.get_list().pop_back();
+ if result_wrapped.is_none() {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ "Need data to take the absolute value of".to_string(),
+ ));
+ }
+ let mut result = result_wrapped.unwrap();
+ if result.get_kind() != Types::NUMBER {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ "Taking the absolute value works on numbers only."
+ .to_string(),
+ ));
+ }
+ result.get_number().abs();
+ data.get_list().push(result);
+ }
"defact" => {
let key = self
.call_stack
@@ 846,8 1012,8 @@ where
#[cfg(test)]
mod tests {
- use crate::Interpreter;
use crate::stdtypes::{Data, Types};
+ use crate::Interpreter;
use std::collections::HashMap;
use std::io::BufReader;
use std::io::{Cursor, Seek, Write};
@@ 871,6 1037,24 @@ mod tests {
}
#[test]
+ fn test_println_string() {
+ let mut c = Cursor::new(Vec::new());
+ let mut stdout = Vec::new();
+ let mut variables = HashMap::new();
+ let args = Data::new(Types::LIST);
+
+ let test_input = b"(`hello, world!' println)" as &[u8];
+ c.write_all(test_input).unwrap();
+ let mut reader = BufReader::new(c);
+ reader.rewind().unwrap();
+
+ let mut runner = Interpreter::new(reader, &mut stdout, &mut variables, args);
+ runner.execute().unwrap();
+
+ assert_eq!(stdout, b"hello, world!\n");
+ }
+
+ #[test]
fn test_escape_string() {
let mut c = Cursor::new(Vec::new());
let mut stdout = Vec::new();
@@ 1488,4 1672,130 @@ mod tests {
assert_eq!(stdout, b"abc");
}
+
+ #[test]
+ fn test_subtract() {
+ let mut c = Cursor::new(Vec::new());
+ let mut stdout = Vec::new();
+ let mut variables = HashMap::new();
+ let args = Data::new(Types::LIST);
+
+ let test_input = b"(5 4 3 2 1 sub print)" as &[u8];
+ c.write_all(test_input).unwrap();
+ let mut reader = BufReader::new(c);
+ reader.rewind().unwrap();
+
+ let mut runner = Interpreter::new(reader, &mut stdout, &mut variables, args);
+ runner.execute().unwrap();
+
+ assert_eq!(stdout, b"-5");
+ }
+
+ #[test]
+ fn test_multiplication() {
+ let mut c = Cursor::new(Vec::new());
+ let mut stdout = Vec::new();
+ let mut variables = HashMap::new();
+ let args = Data::new(Types::LIST);
+
+ let test_input = b"(5 4 3 2 1 mult print)" as &[u8];
+ c.write_all(test_input).unwrap();
+ let mut reader = BufReader::new(c);
+ reader.rewind().unwrap();
+
+ let mut runner = Interpreter::new(reader, &mut stdout, &mut variables, args);
+ runner.execute().unwrap();
+
+ assert_eq!(stdout, b"120");
+ }
+
+ #[test]
+ fn test_division() {
+ let mut c = Cursor::new(Vec::new());
+ let mut stdout = Vec::new();
+ let mut variables = HashMap::new();
+ let args = Data::new(Types::LIST);
+
+ let test_input = b"(5 4 3 2 1 div print)" as &[u8];
+ c.write_all(test_input).unwrap();
+ let mut reader = BufReader::new(c);
+ reader.rewind().unwrap();
+
+ let mut runner = Interpreter::new(reader, &mut stdout, &mut variables, args);
+ runner.execute().unwrap();
+
+ assert_eq!(stdout, b"5/24");
+ }
+
+ #[test]
+ fn test_negation() {
+ let mut c = Cursor::new(Vec::new());
+ let mut stdout = Vec::new();
+ let mut variables = HashMap::new();
+ let args = Data::new(Types::LIST);
+
+ let test_input = b"(1 4 neg add print)" as &[u8];
+ c.write_all(test_input).unwrap();
+ let mut reader = BufReader::new(c);
+ reader.rewind().unwrap();
+
+ let mut runner = Interpreter::new(reader, &mut stdout, &mut variables, args);
+ runner.execute().unwrap();
+
+ assert_eq!(stdout, b"-3");
+ }
+
+ #[test]
+ fn test_floor() {
+ let mut c = Cursor::new(Vec::new());
+ let mut stdout = Vec::new();
+ let mut variables = HashMap::new();
+ let args = Data::new(Types::LIST);
+
+ let test_input = b"(5/4 floor 99/100 floor ` ' join print)" as &[u8];
+ c.write_all(test_input).unwrap();
+ let mut reader = BufReader::new(c);
+ reader.rewind().unwrap();
+
+ let mut runner = Interpreter::new(reader, &mut stdout, &mut variables, args);
+ runner.execute().unwrap();
+
+ assert_eq!(stdout, b"1 0");
+ }
+
+ #[test]
+ fn test_ceil() {
+ let mut c = Cursor::new(Vec::new());
+ let mut stdout = Vec::new();
+ let mut variables = HashMap::new();
+ let args = Data::new(Types::LIST);
+
+ let test_input = b"(5/4 ceil 99/100 ceil ` ' join print)" as &[u8];
+ c.write_all(test_input).unwrap();
+ let mut reader = BufReader::new(c);
+ reader.rewind().unwrap();
+
+ let mut runner = Interpreter::new(reader, &mut stdout, &mut variables, args);
+ runner.execute().unwrap();
+
+ assert_eq!(stdout, b"2 1");
+ }
+
+ #[test]
+ fn test_abs() {
+ let mut c = Cursor::new(Vec::new());
+ let mut stdout = Vec::new();
+ let mut variables = HashMap::new();
+ let args = Data::new(Types::LIST);
+
+ let test_input = b"(5/4 abs -99/100 abs ` ' join print)" as &[u8];
+ c.write_all(test_input).unwrap();
+ let mut reader = BufReader::new(c);
+ reader.rewind().unwrap();
+
+ let mut runner = Interpreter::new(reader, &mut stdout, &mut variables, args);
+ runner.execute().unwrap();
+
+ assert_eq!(stdout, b"5/4 99/100");
+ }
}
M cane-lang/src/stdtypes.rs => cane-lang/src/stdtypes.rs +33 -0
@@ 1,3 1,8 @@
+use malachite_base::num::arithmetic::traits::AbsAssign;
+use malachite_base::num::arithmetic::traits::CeilingAssign;
+use malachite_base::num::arithmetic::traits::FloorAssign;
+use malachite_base::num::arithmetic::traits::NegAssign;
+use malachite_base::num::arithmetic::traits::PowAssign;
use malachite_q::Rational;
use std::cmp::Ordering;
use std::collections::linked_list::{Iter, IterMut};
@@ 177,6 182,10 @@ impl List {
self.0.pop_back()
}
+ pub fn pop_front(&mut self) -> Option<Data> {
+ self.0.pop_front()
+ }
+
pub fn append(&mut self, mut data: Data) {
self.0.append(&mut data.val_list.0);
}
@@ 209,6 218,30 @@ impl Number {
pub fn decrement(&mut self) {
self.0 -= Rational::from_signeds(1, 1);
}
+ pub fn subtract(&mut self, other: &Number) {
+ self.0 -= &other.0;
+ }
+ pub fn multiply(&mut self, other: &Number) {
+ self.0 *= &other.0;
+ }
+ pub fn divide(&mut self, other: &Number) {
+ self.0 /= &other.0;
+ }
+ pub fn negate(&mut self) {
+ self.0.neg_assign();
+ }
+ pub fn floor(&mut self) {
+ self.0.floor_assign();
+ }
+ pub fn ceiling(&mut self) {
+ self.0.ceiling_assign();
+ }
+ pub fn abs(&mut self) {
+ self.0.abs_assign();
+ }
+ pub fn pow(&mut self, other: u64) {
+ self.0.pow_assign(other);
+ }
}
// Will use the first object to put data into
M demos/addition.cn => demos/addition.cn +1 -1
@@ 1,4 1,4 @@
; Adds together two rational numbers
Note that this language does NOT support floating point numbers, only rational numbers ~
-(1/2 3/4 add `\n' print)
+(1/2 3/4 add println)
M demos/args.cn => demos/args.cn +2 -2
@@ 6,7 6,7 @@ run this with arguments after it to see them echoed
ex: cane-lang args.cn hi there
~
-(args `\n' print)
+(args println)
;
this might have a strange output with all of the words meshed together. this is expected!
@@ 15,4 15,4 @@ when an array (args) is converted to a string, the elements are put together wit
This can be changed with the `join' action:
~
-(args ` ' join `\n' print)
+(args ` ' join println)
M demos/conditionals.cn => demos/conditionals.cn +3 -3
@@ 22,19 22,19 @@ If there is one data to be checked, it will always return true.
There must always be an "else"!
~
-(1 2 3 4 `(`this is ascending!\')' `(`this is not ascending :(\')' lt `\n' print)
+(1 2 3 4 `(`this is ascending!\')' `(`this is not ascending :(\')' lt println)
;
Strings don't have to be wrapped in parentheses when being executed!
~
-(1 2 3 4 ``this is ascending!\'' ``this is not ascending :(\'' lt `\n' print)
+(1 2 3 4 ``this is ascending!\'' ``this is not ascending :(\'' lt println)
;
They don't even need to be nested!
~
-(1 2 3 4 `this is ascending!' `this is not ascending :(' lt `\n' print)
+(1 2 3 4 `this is ascending!' `this is not ascending :(' lt println)
;
Note that all other data will still need to be nested.
M demos/hello.cn => demos/hello.cn +2 -2
@@ 1,4 1,4 @@
; Simple "Hello, world!" program ~
-(`hello, ' `world\n' add print)
-(`hello, world!' print)
+(`hello, ' `world' `\n' add print)
+(`hello, world!' println)
M demos/loop.cn => demos/loop.cn +1 -1
@@ 3,7 3,7 @@
(1 `count' defvar)
(
[
- (count `\n' print)
+ (count println)
(count 1 add `count' defvar)
]
100 repeat
A demos/math.cn => demos/math.cn +34 -0
@@ 0,0 1,34 @@
+; Cane can do some basic math! ~
+
+; Addition ~
+(1 2 3 4 add println)
+
+; Subtraction ~
+(5 4 4 sub println) ; Will subtract in order: 5 - 4 = 1, 1 - 4 = -3 ~
+
+; Multiplication ~
+(10 12 87 mult println)
+
+; Division ~
+(1 2 3 div println) ; will divide in order: 1 divided by 2 = 1/2, 1/2 divided by 3 is 1/6 ~
+
+; Negation ~
+(1 2 neg add println) ; neg will only negate the most recent number ~
+(1 -2 add println) ; though you could just do this instead ~
+
+; Floor ~
+(101/100 floor println) ; = 1, will always round down ~
+(99/100 floor println) ; = 0, will always round down ~
+
+; Ceiling ~
+(101/100 ceil println) ; = 2, will always round up ~
+(99/100 ceil println) ; = 1, will always round up ~
+
+; Absolute value ~
+(-3 abs println) ; = 3 ~
+(3 abs println) ; = 3 ~
+
+; Chaining operations ~
+(1 2 (3 4 mult)? add println) ; = 1 + 2 + (3 * 4) = 15 ~
+; The unwrap is important because otherwise it will try to add a list and not a number ~
+(1 2 (3 4 mult) add println) ; = 1 + 2 + len([3 * 4]) = 4 ~
M demos/recursiveloop.cn => demos/recursiveloop.cn +1 -1
@@ 4,7 4,7 @@
(1 `counter' defvar)
(
- [(counter 100 [(counter `\n' print) (1 counter add `counter' defvar) (body)] `' lte)]
+ [(counter 100 [(counter println) (1 counter add `counter' defvar) (body)] `' lte)]
`body' defact
)
(body)
M demos/unwrap.cn => demos/unwrap.cn +5 -5
@@ 1,19 1,19 @@
;
What are unwraps, you ask? Well, in a situation like this:
-(1 2 (3 4 add 1) add print)
+(1 2 (3 4 add 1) add println)
the language will add together the two numbers then add a list to it. Because this language is very poorly designed, the list will be coerced into a number. That will always return the size of the list. Therefore, this will return `5`.
Unwrapping would look something like this:
-(1 2 (3 4 add 1)? add print)
+(1 2 (3 4 add 1)? add println)
First `1` and `2` will be added, then `3 4` will be added, then the contents of that inner list will be appended to the end of the outer list:
-(1 2 7 1 add print)
+(1 2 7 1 add println)
which will act as expected. ~
-(1 2 (3 4 add 1) add `\n' print) ; will print 5 ~
-(1 2 (3 4 add 1)? add `\n' print) ; will print 11 ~
+(1 2 (3 4 add 1) add println) ; will print 5 ~
+(1 2 (3 4 add 1)? add println) ; will print 11 ~
M demos/variables.cn => demos/variables.cn +6 -6
@@ 4,14 4,14 @@ You need: the data, and a string as a name.
~
((1 2 3 4 add) `myvarname' defvar)
-(myvarname `\n' print)
-(myvarname `\n' print)
-(myvarname `\n' print)
-(myvarname `\n' print)
+(myvarname println)
+(myvarname println)
+(myvarname println)
+(myvarname println)
; All variables are immutable! You can replace the variable, but you can't edit it. ~
((1 2 3 4 add) `myvarname' defvar)
-(myvarname `\n' print)
+(myvarname println)
((1 2 3 4 5 add) `myvarname' defvar)
-(myvarname `\n' print)
+(myvarname println)