mirror of
https://github.com/eternal-flame-AD/unitdc-rs.git
synced 2025-02-01 19:10:16 -06:00
add documentation
This commit is contained in:
parent
6a6ebd315e
commit
fbde3594cd
9 changed files with 48 additions and 0 deletions
|
@ -17,6 +17,7 @@ fn main() {
|
|||
.run_str(include_str!("../../../unitdc.rc"))
|
||||
.expect("unitdc.rc should run");
|
||||
|
||||
// REPL loop
|
||||
for line in std::io::stdin().lines() {
|
||||
let line = line.expect("line should exist");
|
||||
if let Err(e) = interpreter.run_str(&line) {
|
||||
|
|
|
@ -13,6 +13,7 @@ extern "C" {
|
|||
fn log(s: String);
|
||||
}
|
||||
|
||||
/// Runs a string in the interpreter.
|
||||
#[wasm_bindgen]
|
||||
pub fn unitdc_input(input: String) -> Result<(), JsValue> {
|
||||
unsafe {
|
||||
|
@ -23,6 +24,7 @@ pub fn unitdc_input(input: String) -> Result<(), JsValue> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Initializes the interpreter, taking a callback function to send output to.
|
||||
#[wasm_bindgen]
|
||||
pub fn unitdc_init(js_output: Function) {
|
||||
unsafe {
|
||||
|
|
|
@ -13,8 +13,11 @@ use crate::{
|
|||
|
||||
use thiserror::Error;
|
||||
|
||||
/// All other operations.
|
||||
pub mod ops;
|
||||
/// Macro operations.
|
||||
pub mod ops_macros;
|
||||
/// Variable I/O operations.
|
||||
pub mod ops_variables;
|
||||
|
||||
pub struct Interpreter<'a> {
|
||||
|
@ -64,6 +67,7 @@ impl<'a> Interpreter<'a> {
|
|||
output,
|
||||
}
|
||||
}
|
||||
/// Reads all tokens from the tokenizer and processes them.
|
||||
pub fn process_tokens<R: Read>(
|
||||
&mut self,
|
||||
tokenizer: &mut Tokenizer<R>,
|
||||
|
|
|
@ -12,10 +12,17 @@ use crate::{
|
|||
use super::{Interpreter, InterpreterError, InterpreterResult, Output};
|
||||
|
||||
impl<'a> Interpreter<'a> {
|
||||
/// A literal number input, pushes a unit-less quantity to the stack.
|
||||
pub fn op_number(&mut self, number: BigRational) -> InterpreterResult<()> {
|
||||
self.stack.push(Quantity::new(number, UnitCombo::new()));
|
||||
Ok(())
|
||||
}
|
||||
/// A literal unit input.
|
||||
///
|
||||
/// - If the unit is unit-less (1), the top of the stack will be converted to a unit-less quantity.
|
||||
/// - If the top of the stack is a unit-less quantity, it will be converted to the given unit.
|
||||
/// - If the top of the stack is a quantity with equivalent units, it will be converted to the given unit.
|
||||
/// - Otherwise, an error will be returned.
|
||||
pub fn op_unit(&mut self, unit: &str) -> InterpreterResult<()> {
|
||||
let mut q = self.stack.pop().ok_or(InterpreterError::StackUnderflow)?;
|
||||
|
||||
|
@ -67,6 +74,7 @@ impl<'a> Interpreter<'a> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
/// Adds the top two quantities on the stack.
|
||||
pub fn op_add(&mut self) -> InterpreterResult<()> {
|
||||
let rhs = self.stack.pop().ok_or(InterpreterError::StackUnderflow)?;
|
||||
let lhs = self.stack.pop().ok_or(InterpreterError::StackUnderflow)?;
|
||||
|
@ -76,6 +84,7 @@ impl<'a> Interpreter<'a> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
/// Subtracts the top two quantities on the stack.
|
||||
pub fn op_sub(&mut self) -> InterpreterResult<()> {
|
||||
let rhs = self.stack.pop().ok_or(InterpreterError::StackUnderflow)?;
|
||||
let lhs = self.stack.pop().ok_or(InterpreterError::StackUnderflow)?;
|
||||
|
@ -85,6 +94,7 @@ impl<'a> Interpreter<'a> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
/// Multiplies the top two quantities on the stack.
|
||||
pub fn op_mul(&mut self) -> InterpreterResult<()> {
|
||||
let rhs = self.stack.pop().ok_or(InterpreterError::StackUnderflow)?;
|
||||
let lhs = self.stack.pop().ok_or(InterpreterError::StackUnderflow)?;
|
||||
|
@ -93,6 +103,7 @@ impl<'a> Interpreter<'a> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
/// Divides the top two quantities on the stack.
|
||||
pub fn op_div(&mut self) -> InterpreterResult<()> {
|
||||
let rhs = self.stack.pop().ok_or(InterpreterError::StackUnderflow)?;
|
||||
let lhs = self.stack.pop().ok_or(InterpreterError::StackUnderflow)?;
|
||||
|
@ -101,6 +112,7 @@ impl<'a> Interpreter<'a> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
/// Prints the top of the stack without altering it.
|
||||
pub fn op_p(&mut self) -> InterpreterResult<()> {
|
||||
let q = self.stack.pop().ok_or(InterpreterError::StackUnderflow)?;
|
||||
|
||||
|
@ -110,6 +122,7 @@ impl<'a> Interpreter<'a> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
/// Prints the top of the stack and pops it.
|
||||
pub fn op_n(&mut self) -> InterpreterResult<()> {
|
||||
let q = self.stack.pop().ok_or(InterpreterError::StackUnderflow)?;
|
||||
|
||||
|
@ -117,11 +130,13 @@ impl<'a> Interpreter<'a> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
/// Prints the entire stack.
|
||||
pub fn op_f(&mut self) -> InterpreterResult<()> {
|
||||
(self.output)(Output::QuantityList(self.stack.clone()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
/// Duplicates the top of the stack.
|
||||
pub fn op_d(&mut self) -> InterpreterResult<()> {
|
||||
let q = self.stack.pop().ok_or(InterpreterError::StackUnderflow)?;
|
||||
|
||||
|
@ -130,11 +145,13 @@ impl<'a> Interpreter<'a> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
/// Clears the stack.
|
||||
pub fn op_c(&mut self) -> InterpreterResult<()> {
|
||||
self.stack.clear();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
/// Swaps the top two elements of the stack.
|
||||
pub fn op_r(&mut self) -> InterpreterResult<()> {
|
||||
let a = self.stack.pop().ok_or(InterpreterError::StackUnderflow)?;
|
||||
let b = self.stack.pop().ok_or(InterpreterError::StackUnderflow)?;
|
||||
|
@ -144,6 +161,7 @@ impl<'a> Interpreter<'a> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
/// Prints a summary of the unit system, including all base units, derived units, and their scale and offset.
|
||||
pub fn op_upper_u(&mut self) -> InterpreterResult<()> {
|
||||
let mut output = String::from("Base units:\n");
|
||||
|
||||
|
@ -165,9 +183,11 @@ impl<'a> Interpreter<'a> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
/// Invokes the unit solver. See [here](https://github.com/eternal-flame-AD/unitdc-rs/wiki/The-Unit-Solver) for instructions.
|
||||
pub fn op_s(&mut self) -> InterpreterResult<()> {
|
||||
let target = self.stack.pop().ok_or(InterpreterError::StackUnderflow)?;
|
||||
|
||||
// first figure out how many known quantities we have
|
||||
let n_src_quantities = target.number_in_derived_unit().to_integer();
|
||||
if n_src_quantities.to_usize().unwrap_or(0) > self.stack.len() {
|
||||
return Err(InterpreterError::StackUnderflow);
|
||||
|
@ -176,6 +196,7 @@ impl<'a> Interpreter<'a> {
|
|||
.stack
|
||||
.split_off(self.stack.len() - n_src_quantities.to_usize().unwrap_or(0));
|
||||
let dst_unit = target.unit.reduce();
|
||||
// Check that all units involved are present
|
||||
let mut units_involved = Vec::new();
|
||||
for q in &src_quantities {
|
||||
for u in &q.unit.0 {
|
||||
|
|
|
@ -3,6 +3,10 @@ use crate::quantity::units::{BaseUnit, DerivedUnit};
|
|||
use super::{Interpreter, InterpreterError, InterpreterResult};
|
||||
|
||||
impl<'a> Interpreter<'a> {
|
||||
/// Defines a new base unit.
|
||||
///
|
||||
/// For example to define a unit "usd" (US Dollar), you would do:
|
||||
/// `@base(usd)`
|
||||
pub fn op_macro_baseunit(&mut self, arg: &str) -> InterpreterResult<()> {
|
||||
let symbol = arg.trim();
|
||||
|
||||
|
@ -16,6 +20,11 @@ impl<'a> Interpreter<'a> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
/// Defines a new derived unit.
|
||||
///
|
||||
/// This is done by popping a "scale" and then an "offset" from the stack.
|
||||
/// For example, to define a new unit "mpg" (miles per gallon), you would do:
|
||||
/// `0 (mi) 1 (gal) / 1 (mi) 1 (gal) / @derived(mpg)`
|
||||
pub fn op_macro_derivedunit(&mut self, arg: &str) -> InterpreterResult<()> {
|
||||
let symbol = arg.trim();
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use super::{Interpreter, InterpreterError, InterpreterResult};
|
||||
|
||||
impl<'a> Interpreter<'a> {
|
||||
/// Pops a quantity from the stack and stores it in a variable.
|
||||
pub fn op_store(&mut self, arg: &str) -> InterpreterResult<()> {
|
||||
let symbol = arg.trim();
|
||||
|
||||
|
@ -10,6 +11,7 @@ impl<'a> Interpreter<'a> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
/// Pushes a quantity from a variable onto the stack.
|
||||
pub fn op_recall(&mut self, arg: &str) -> InterpreterResult<()> {
|
||||
let symbol = arg.trim();
|
||||
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
/// Interpreter for UnitDC expressions.
|
||||
pub mod interpreter;
|
||||
/// A module for evaluating linear systems of equations.
|
||||
pub mod linear_system;
|
||||
/// A module for manipulating units and quantities.
|
||||
pub mod quantity;
|
||||
/// Tokenizer for the interpreter.
|
||||
pub mod tokenizer;
|
||||
|
|
|
@ -12,6 +12,7 @@ pub mod units;
|
|||
|
||||
use units::{DerivedUnit, UnitCombo};
|
||||
|
||||
/// A quantity of a combination of a number, the unit combination, and a list of derived units to use when formatting the quantity.
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||
pub struct Quantity {
|
||||
pub number: BigRational,
|
||||
|
@ -41,6 +42,7 @@ pub enum QuantityError {
|
|||
}
|
||||
|
||||
impl Quantity {
|
||||
/// Create a new quantity with the given number and unit combination.
|
||||
pub fn new(number: BigRational, unit: UnitCombo) -> Self {
|
||||
Quantity {
|
||||
number,
|
||||
|
@ -48,6 +50,7 @@ impl Quantity {
|
|||
use_derived_unit: Vec::new(),
|
||||
}
|
||||
}
|
||||
/// Computes the "user-facing" number of the quantity, considering the offset and scale of matching derived units.
|
||||
pub fn number_in_derived_unit(&self) -> BigRational {
|
||||
let mut number = self.number.clone();
|
||||
|
||||
|
|
|
@ -124,12 +124,14 @@ impl Display for DerivedUnit {
|
|||
}
|
||||
}
|
||||
|
||||
/// A `UnitExponent` is the combination of a base unit and an exponent.
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub struct UnitExponent {
|
||||
pub unit: BaseUnit,
|
||||
pub exponent: i32,
|
||||
}
|
||||
|
||||
/// A `UnitCombo` is a combination of `UnitExponent`s, which described an arbitrary "unit".
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UnitCombo(pub Vec<UnitExponent>);
|
||||
|
||||
|
|
Loading…
Reference in a new issue