~fd/cane-lang

aa2d1d1b8d35277d20692ae4b17de8547e01c52e — Ersei Saggi 10 months ago ecfe393
Add math, println
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)