Skip to content

Commit 8c74c0f

Browse files
1BADragonCedarMatt
andauthored
Merge 0.3.0 work (1BADragon#15)
* Add more numeric builtins * Adding more builtins * Detour separating some lifetime things * Finished adding the math builtins * Fixed double ref clone * Added reduce (1BADragon#13) * Python bindings and better wasm type inference (1BADragon#14) * Working on adding python custom functions for the bindings * Have python function bindings working * Working on CelFloat for wasm bindings * Working on downcast issue * Added the new CelFloat type to force the use of a float * Removed wasm feature * Removed wasm downcast crate --------- Co-authored-by: matt <[email protected]> * Bump to 0.3.0 --------- Co-authored-by: matt <[email protected]>
1 parent 6cfb9f7 commit 8c74c0f

File tree

18 files changed

+800
-250
lines changed

18 files changed

+800
-250
lines changed

Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rscel"
3-
version = "0.2.0"
3+
version = "0.3.0"
44
edition = "2021"
55

66
[lib]
@@ -12,7 +12,10 @@ ast_ser = []
1212
program_cache = []
1313
python = ["dep:pyo3"]
1414
console_error_panic_hook = ["dep:console_error_panic_hook"]
15-
wasm = ["dep:wasm-bindgen", "dep:serde-wasm-bindgen", "console_error_panic_hook", "chrono/wasmbind"]
15+
wasm = [
16+
"dep:wasm-bindgen", "dep:serde-wasm-bindgen", "dep:js-sys",
17+
"console_error_panic_hook", "chrono/wasmbind"
18+
]
1619

1720

1821
[dependencies]
@@ -33,3 +36,4 @@ pyo3 = { version = "0.19.1", optional = true, features = ["extension-module", "c
3336
wasm-bindgen = { version = "0.2.87", optional = true}
3437
console_error_panic_hook = { version = "0.1.7", optional = true }
3538
serde-wasm-bindgen = { version = "0.5.0", optional = true }
39+
js-sys = { version = "0.3.64", optional = true }

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,7 @@ python-binding-release: .env
2626
source .env/bin/activate && maturin build --features python --release $(CARGO_ARGS)
2727

2828
wasm-example: wasm-binding
29-
cd examples/wasm && npm start
29+
cd examples/wasm && npm start
30+
31+
.PHONY: all
32+
all: wasm-binding python-binding

examples/eval.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import rscel
33

44
if len(sys.argv) == 2:
5-
print(rscel.eval(sys.argv[1], "{}"))
5+
print(rscel.eval(sys.argv[1], {}))
66
elif len(sys.argv) == 3:
77
print(rscel.eval(*sys.argv[1:]))
88
else:

examples/wasm/src/pages/CelComponent.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from "react";
22
import "./CelComponent.css";
33

4-
import init, { cel_eval, cel_details } from "rscel";
4+
import init, { cel_eval, cel_details, CelFloat } from "rscel";
55
import { useState } from "react";
66

77
export default function CelComponent() {
@@ -30,7 +30,7 @@ export default function CelComponent() {
3030
setParamVals((old: any) => {
3131
try {
3232
let newObj = { ...old };
33-
newObj[val] = JSON.parse(event.target.value);
33+
newObj[val] = Number(event.target.value);
3434
setErrorMessage("");
3535
return newObj;
3636
} catch (e) {
@@ -59,7 +59,6 @@ export default function CelComponent() {
5959
<button
6060
onClick={() => {
6161
const details = cel_details(prog);
62-
console.log(details);
6362

6463
if (details.success) {
6564
setParams(details.result.get("params"));
@@ -73,9 +72,7 @@ export default function CelComponent() {
7372
</button>
7473
<button
7574
onClick={() => {
76-
console.log(paramVals);
7775
const result = cel_eval(prog, paramVals);
78-
console.log(result);
7976

8077
if (result.success) {
8178
setErrorMessage(`Result: ${result.result.toString()}`);
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
use pyo3::{types::PyTuple, Py, PyAny, PyObject, Python, ToPyObject};
2+
3+
use crate::{CelError, CelResult, CelValue};
4+
5+
pub struct CelPyCallable {
6+
func: Py<PyAny>,
7+
}
8+
9+
impl CelPyCallable {
10+
pub fn new(func: Py<PyAny>) -> CelPyCallable {
11+
CelPyCallable { func }
12+
}
13+
}
14+
15+
impl FnOnce<(CelValue, &[CelValue])> for CelPyCallable {
16+
type Output = CelResult<CelValue>;
17+
18+
extern "rust-call" fn call_once(self, args: (CelValue, &[CelValue])) -> Self::Output {
19+
Python::with_gil(|py| {
20+
match self.func.call(
21+
py,
22+
PyTuple::new(
23+
py,
24+
&[args.0]
25+
.iter()
26+
.filter(|x| !x.is_null())
27+
.map(|x| x.to_object(py))
28+
.chain(args.1.into_iter().map(|x| x.to_object(py)))
29+
.collect::<Vec<PyObject>>(),
30+
),
31+
None,
32+
) {
33+
Ok(val) => Ok(val.extract(py).unwrap()),
34+
Err(val) => Err(CelError::runtime(&val.to_string())),
35+
}
36+
})
37+
}
38+
}
39+
40+
impl FnMut<(CelValue, &[CelValue])> for CelPyCallable {
41+
extern "rust-call" fn call_mut(&mut self, args: (CelValue, &[CelValue])) -> Self::Output {
42+
Python::with_gil(|py| {
43+
match self.func.call(
44+
py,
45+
PyTuple::new(
46+
py,
47+
&[args.0]
48+
.iter()
49+
.filter(|x| !x.is_null())
50+
.map(|x| x.to_object(py))
51+
.chain(args.1.into_iter().map(|x| x.to_object(py)))
52+
.collect::<Vec<PyObject>>(),
53+
),
54+
None,
55+
) {
56+
Ok(val) => Ok(val.extract(py).unwrap()),
57+
Err(val) => Err(CelError::runtime(&val.to_string())),
58+
}
59+
})
60+
}
61+
}
62+
63+
impl Fn<(CelValue, &[CelValue])> for CelPyCallable {
64+
extern "rust-call" fn call(&self, args: (CelValue, &[CelValue])) -> Self::Output {
65+
Python::with_gil(|py| {
66+
match self.func.call(
67+
py,
68+
PyTuple::new(
69+
py,
70+
&[args.0]
71+
.iter()
72+
.filter(|x| !x.is_null())
73+
.map(|x| x.to_object(py))
74+
.chain(args.1.into_iter().map(|x| x.to_object(py)))
75+
.collect::<Vec<PyObject>>(),
76+
),
77+
None,
78+
) {
79+
Ok(val) => Ok(val.extract(py).unwrap()),
80+
Err(val) => Err(CelError::runtime(&val.to_string())),
81+
}
82+
})
83+
}
84+
}

src/bindings/python/mod.rs

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,42 @@ use pyo3::{
88
};
99
use std::collections::HashMap;
1010

11+
mod celpycallable;
12+
13+
use celpycallable::CelPyCallable;
14+
1115
/* Eval entry point */
1216
#[pyfunction]
1317
fn eval(py: Python<'_>, prog_str: String, bindings: &PyDict) -> PyResult<PyObject> {
18+
let callables = {
19+
let mut callables = Vec::new();
20+
for keyobj in bindings.keys().iter() {
21+
let key = keyobj.downcast::<PyString>()?;
22+
let val = bindings.get_item(keyobj).unwrap();
23+
24+
if val.is_callable() {
25+
callables.push((key.to_str()?, CelPyCallable::new(val.into())));
26+
}
27+
}
28+
callables
29+
};
1430
let mut ctx = CelContext::new();
1531
let mut exec_ctx = BindContext::new();
1632

1733
ctx.add_program_str("entry", &prog_str).unwrap();
1834

1935
for keyobj in bindings.keys().iter() {
2036
let key = keyobj.downcast::<PyString>()?;
21-
exec_ctx.bind_param(key.to_str()?, bindings.get_item(keyobj).unwrap().extract()?)
37+
38+
let val = bindings.get_item(keyobj).unwrap();
39+
40+
if !val.is_callable() {
41+
exec_ctx.bind_param(key.to_str()?, val.extract()?)
42+
}
43+
}
44+
45+
for callable in callables.iter() {
46+
exec_ctx.bind_func(callable.0, &callable.1);
2247
}
2348

2449
let res = ctx.exec("entry", &exec_ctx);
@@ -56,7 +81,17 @@ impl PyCelContext {
5681
name: &str,
5782
bindings: &PyBindContext,
5883
) -> PyResult<PyObject> {
59-
match slf.ctx.exec(name, &bindings.ctx) {
84+
let mut bindctx = BindContext::new();
85+
86+
for (key, val) in bindings.bindings.iter() {
87+
bindctx.bind_param(&key, val.clone());
88+
}
89+
90+
for (key, val) in bindings.funcs.iter() {
91+
bindctx.bind_func(&key, val);
92+
}
93+
94+
match slf.ctx.exec(name, &bindctx) {
6095
Ok(val) => Ok(val.to_object(slf.py())),
6196
Err(err) => Err(PyValueError::new_err(err.to_string())),
6297
}
@@ -65,20 +100,37 @@ impl PyCelContext {
65100

66101
#[pyclass(name = "BindContext")]
67102
struct PyBindContext {
68-
ctx: BindContext,
103+
bindings: HashMap<String, CelValue>,
104+
funcs: HashMap<String, CelPyCallable>,
69105
}
70106

71107
#[pymethods]
72108
impl PyBindContext {
73109
#[new]
74110
pub fn new() -> PyBindContext {
75111
PyBindContext {
76-
ctx: BindContext::new(),
112+
bindings: HashMap::new(),
113+
funcs: HashMap::new(),
77114
}
78115
}
79116

80-
pub fn bind(mut slf: PyRefMut<'_, Self>, name: &str, val: CelValue) {
81-
slf.ctx.bind_param(name, val);
117+
pub fn bind_param(&mut self, name: &str, val: CelValue) {
118+
self.bindings.insert(name.to_owned(), val);
119+
}
120+
121+
pub fn bind_func(&mut self, name: &str, val: &PyAny) {
122+
self.funcs
123+
.insert(name.to_owned(), CelPyCallable::new(val.into()));
124+
}
125+
126+
pub fn bind(&mut self, name: &str, val: &PyAny) -> PyResult<()> {
127+
if val.is_callable() {
128+
self.bind_func(name, val);
129+
} else {
130+
self.bind_param(name, val.extract()?);
131+
}
132+
133+
Ok(())
82134
}
83135
}
84136

0 commit comments

Comments
 (0)