Skip to content

Commit

Permalink
Function handling improvements (#18)
Browse files Browse the repository at this point in the history
Fixes several bugs that I didn't notice when initially implementing
functions.
Also adds many function-related tests which likely would've caught those
bugs (oops!).
  • Loading branch information
kdkasad authored Apr 6, 2024
2 parents b04c4ec + af11415 commit 7699509
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 154 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ jobs:

- name: Set up Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: clippy

- name: Cache build artifacts
uses: Swatinem/rust-cache@v2
Expand Down
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ The same applies for two units or variables, so a Newton can be represented as
`kg m/s^2` rather than `kg * m/s^2`. The expression `2 3` is also interpreted as
`2 * 3`, but that is disgusting syntax and should not be used.

The only caveat to this is that some implicit multiplications will be
interpreted as function calls. See the [Function call syntax
problems](#function-call-syntax-problems) section below for details.

### Variables

Values can be stored in variables. Variable names can contain letters, numbers,
Expand Down Expand Up @@ -209,14 +213,14 @@ decimal point contain zeros.

### Functions

Numbrs supoprt some built-in functions. Functions are called by preceding a
Numbrs supports some built-in functions. Functions are called by preceding a
group (e.g. parentheses) with the function identifier.

Example:

$ numbrs
> sin(0)
0
0.00000
> cos(180 degrees)
-1.00000

Expand All @@ -229,6 +233,27 @@ Currently, the following functions are supported:
| Absolute value | `abs` | 1 |
| Square root | `sqrt` | 1 |
| Natural logarithm | `ln` | 1 |
| Factorial | `factorial` | 1 |
| Combination | `choose` | 2 |
| Permutation | `permute` | 2 |

#### Function call syntax problems

Any group which comes after an identifier which matches the name of a function
will be treated as a function call. This means that functions will mask variable names.

Example:

$ numbrs
> foo = 5
5.00000
> ln = 5
5.00000
> foo(2)
10.00000
> ln(2)
0.69315


### Units

Expand Down
33 changes: 9 additions & 24 deletions src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use crate::{
affixes::{resolve_unit, try_get_prefix_scale},
ast::*,
dimension::Dimension,
functions::Function,
functions::FunctionCallError,
operation::Operation,
rat_util_macros::rat,
runtime::Runtime,
Expand Down Expand Up @@ -478,7 +478,9 @@ impl Node {
Value::Number(num) => args.push(num),
}
}
node.function.eval(args)
node.function
.eval(args)
.map_err(|func_err: FunctionCallError| EvalError::InvalidFunctionCall(func_err))
}
}
}
Expand Down Expand Up @@ -607,36 +609,19 @@ pub enum EvalError {
#[error("Invalid unary operation `{1}` for type `{0}`")]
InvalidUnaryOperation(Box<Value>, Operation),

/// # Wrong number of function arguments
/// # Invalid function call
///
/// This error occurs when a function is called with the wrong number of
/// arguments.
/// Returned when a function call fails.
///
/// The tuple fields of this error are:
/// 1. The function that was called
/// 2. The number of arguments expected
/// 3. The number of arguments given
#[error("Function `{0}` expects {1} arguments, got {2}")]
NumberOfFunctionArguments(Function, usize, usize),

/// # Non-integer argument given to a function which requires integers.
///
/// The referenced function and the offending argument are stored in this
/// error variant's tuple fields.
#[error("Function `{0}` requires integer arguments, got `{1}`")]
NonIntegerFunctionArgument(Function, Value),
/// Stores the error generated by the function call.
#[error("Invalid function call: {0}")]
InvalidFunctionCall(FunctionCallError),

/// # Non-pure value used in a context where a pure value is required
///
/// The invalid value is stored in the tuple field.
#[error("Non-number value `{0}` used in a context where a pure number value is required")]
NonPureValue(Value),

/// # Overflow occurred during evaluation
///
/// This should only be returned by [`Function`] calls.
#[error("Number `{0}` too large. Overflow occurred.")]
Overflow(BigRational),
}

#[cfg(test)]
Expand Down
Loading

0 comments on commit 7699509

Please sign in to comment.