Skip to content

Commit eace596

Browse files
committed
More docs
1 parent ba54825 commit eace596

File tree

5 files changed

+214
-129
lines changed

5 files changed

+214
-129
lines changed

README.md

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,36 @@ In order to evaluate expressions, three things must happen;
1414
## Data Provider
1515

1616
A data provider is an object which converts addresses into values. Addresses are arbitrary text tokens wrapped in
17-
braces. The provider determines their meaning.
17+
braces. The provider determines their meaning. [See the `repl.rs`](examples/repl.rs) for a feature-complete REPL.
1818

1919
```rust
20+
#[derive(Copy, Clone)]
2021
struct Provider;
2122

2223
impl expression::DataSource for Provider {
2324
fn query(&self, query: impl AsRef<str>) -> Option<expression::Object> {
2425
// Parse the query however you need to.
2526
// For example, the format `column:row`
26-
27-
let (column, row) = query.as_ref().split_at(query.as_ref().rfind(':'));
28-
27+
28+
Some(Object::string("Hey!"))
2929
}
3030
}
31+
```
32+
33+
## Context
34+
35+
Next you'll need a context object which holds state and variables and acts as an API to the expression engine. You can
36+
define your own functions, globals and operators here.
37+
38+
```rust
39+
fn main() {
40+
let mut cx = expression::Context::new(Provider)
41+
.with_global("twelve", expression::Object::Number(12))
42+
.with_fn("print", |_cx, args| {
43+
println!("{:?}", args);
44+
Ok(expression::Object::Nothing)
45+
});
46+
47+
assert_eq!(cx.evaluate("twelve"), expression::Object::Number(12));
48+
}
3149
```

examples/repl.rs

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,9 @@
1-
use expression::Context;
2-
use expression::DataSource;
3-
use expression::Object;
4-
use std::fmt::Debug;
5-
use std::io::BufRead;
6-
use std::io::BufReader;
7-
use std::io::BufWriter;
8-
use std::io::Write;
9-
101
/// A simple REPL to demonstrate the use of the expression crate.
112
/// # Features:
12-
/// - Basic expression evaluation
13-
/// - Simple table for demonstrating addresses
14-
/// - Demonstrate complex usage of the API
3+
/// - Basic expression evaluation: [`repl.rs:169`](./#L169)
4+
/// - Simple table for demonstrating addresses: [`repl.rs:30`](./#L30)
5+
/// - A top-level `eval` function to demonstrate programmatic evaluation: [`repl.rs:137`](./#L137)
6+
/// - A `<<` operator to demonstrate operator registration: [`repl.rs:144`](./#L144)
157
///
168
/// Type an expression into the REPL and press enter.
179
/// The following commands are available:
@@ -23,6 +15,16 @@ use std::io::Write;
2315
/// # Addresses:
2416
/// Addresses are of the form `column:row` where `column` is the name of the column and `row` is the row number. Therefore, columns may contain `:`.
2517
18+
use std::any::Any;
19+
use expression::Context;
20+
use expression::OperatorBuilder;
21+
use expression::DataSource;
22+
use expression::Object;
23+
use std::fmt::Debug;
24+
use std::io::BufRead;
25+
use std::io::BufReader;
26+
use std::io::Write;
27+
2628
#[derive(Debug, Clone)]
2729
struct Table {
2830
columns: Vec<String>,
@@ -31,6 +33,10 @@ struct Table {
3133

3234
impl DataSource for Table {
3335
fn query(&self, query: impl AsRef<str>) -> Option<Object> {
36+
37+
// Parse the address into a usable format.
38+
// Since the address is of the form `column:row`, we can split the address at the last `:`.
39+
3440
let (column, row) = query.as_ref().split_at(query.as_ref().rfind(':')?);
3541
let col = self.columns.iter().position(|c| c == column)?;
3642

@@ -126,12 +132,34 @@ mod commands {
126132
}
127133

128134
pub fn main() {
129-
let mut cx = Context::new(Table::empty(["a", "b", "c"]));
135+
let mut cx = Context::new(Table::empty(["a", "b", "c"]))
136+
.with_fn("eval", |cx, args| {
137+
if let Some(Object::String(s)) = args.get(0) {
138+
cx.evaluate(s)
139+
} else {
140+
Ok(Object::Nothing)
141+
}
142+
})
143+
.with_operator(OperatorBuilder::new()
144+
.operands(2)
145+
.symbol("<<")
146+
.handler(|args| {
147+
let (Some(Object::Number(base)), Some(Object::Number(shift))) = (args.get(0), args.get(1)) else {
148+
return Err(expression::Error::other("<< requires two numbers"))
149+
};
150+
151+
let base = *base as i64;
152+
let shift = *shift as i64;
153+
154+
Ok(Object::Number((base << shift) as f64))
155+
})
156+
.build());
130157

131158
loop {
132159
let cmd = prompt("> ");
133160

134161
match cmd.trim() {
162+
"" => continue,
135163
"/exit" => break,
136164
"/dump" => println!("{:#?}", cx.provider()),
137165
cmd if cmd.starts_with("/set ") => commands::set(&mut cx, cmd[5..].trim()),

src/error.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,16 @@ multi_error! { global();
7070

7171
pub type Result<T> = core::result::Result<T, Error>;
7272

73-
use alloc::string::String;
73+
use alloc::borrow::ToOwned;
74+
use alloc::string::{String, ToString};
7475
pub use global::Error;
7576

77+
impl global::Error {
78+
pub fn other(message: impl AsRef<str>) -> Self {
79+
Self::from(ManualError::OtherError(message.as_ref().to_owned()))
80+
}
81+
}
82+
7683
/// TODO: Document the error types used throughout the expression parser below.
7784
#[derive(Debug, Clone)]
7885
pub enum ManualError {
@@ -85,6 +92,8 @@ pub enum ManualError {
8592
ConversionFailed,
8693
ExpectedType(String),
8794
EmptyResultSet(String),
95+
96+
OtherError(String)
8897
}
8998

9099
impl core::error::Error for ManualError {}

0 commit comments

Comments
 (0)