Coder Social home page Coder Social logo

cel-rust's People

Contributors

clarkmcc avatar fore5fire avatar hardbyte avatar inikolaev avatar lfbrehm avatar orf avatar progval avatar russellhaering avatar waywardmonkeys avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

cel-rust's Issues

Support thread-safe program execution

I'm experimenting with writing a Python extension for this library using pyo3 and running into issues when it comes down to concurrency. I'm not really well-versed in Rust, but I asked about it here. As far as I understand it boils down to using Arc instead of Rc.

I'm currently using a Python version of CEL interpreter, but its performance leaves a lot to be desired, so I'm looking for an alternatives. I use CEL for feature flags so we have multiple compiled expressions which are evaluated from different threads.

What are your thoughts about it? What would it take to make the interpreter thread-safe?

I'm willing to help, but my Rust knowledge if very very limited :)

Wrong evaluation result

I've been playing around with this CEL implementation and I noticed one odd thing with the following expressions:

b && (c == "string")

b && c == "string"

c == "string" && b

Given this context

{"b": True, "c": "string"}

they should all evaluate to true, but this is not what's happening:

True <= b && (c == "string")
False <= b && c == "string"
True <= c == "string" && b

Here's a simple reproducer:

use cel_interpreter::{Context,Program, Value};

fn main() {
    let expressions = [
        "b && (c == \"string\")",
        "b && c == \"string\"",
        "c == \"string\" && b",
    ];

    for expression in expressions {
        let program = Program::compile(expression).unwrap();
        let mut context = Context::default();
        context.add_variable("b", Value::Bool(true));
        context.add_variable("c", Value::String(String::from("string").into()));

        let result = program.execute(&context);

        println!("{:?} <= {}", result, expression)
    }
}

It produces the following output:

Ok(Bool(true)) <= b && (c == "string")
Ok(Bool(false)) <= b && c == "string"
Ok(Bool(true)) <= c == "string" && b

It seems like in the case of b && c == "string" the interpreter effectively evaluates this expression

(b && c) == "string"

I'm also using a Python version of CEL interpreter and it evaluates it properly:

import celpy

expressions = [
    'b && (c == "string")',
    'b && c == "string"',
    'c == "string" && b',
]

for expression in expressions:
    env = celpy.Environment()
    ast = env.compile(expression)
    prgm = env.program(ast)

    activation = celpy.json_to_cel({"a": 1, "b": True, "c": "string"})
    result = prgm.evaluate(activation)
    print(f"{result} <= {expression}")

Produces

True <= b && (c == "string")
True <= b && c == "string"
True <= c == "string" && b

The type of serialized unsigned integer data does not match the default type of numbers in the expression.

#[derive(Serialize)]
struct MidType<'a> {
    body: &'a [u8],
    raw: &'a [u8]
}

fn main() {
    let program = Program::compile("foo.body.contains(1)").unwrap();
    let mut context = Context::default();
    context.add_variable("foo", MidType {
        body: &[1,2,3],
        raw: &[]
    }).unwrap();
    let v = program.execute(&context).unwrap();
    println!("{:?}",v);
}

the body will be serialized as List[UInt], but the number 1 is Int type. output: Bool(false)

Indexed-based map access is not supported

fn main() {
    let program = Program::compile("headers[\"Content-Type\"].contains(\"application/json\")").unwrap();
    let mut context = Context::default();
    let mut headers = HashMap::new();
    headers.insert("Content-Type","application/json".to_string());
    println!("{}",headers["Content-Type"]);
    context.add_variable("headers", headers);
    let value = program.execute(&context).unwrap();
    assert_eq!(value, true.into());
}

headers["Content-Type"] 这样会报错,thread 'main' panicked at 'not implemented'
该如何实现呢,我知道headers.status这种写法可以,但是需要修改表达式

Switch to Chumsky for parsing

Benefits of using chumsky for parsing:

  • Easier to read and modify than LALRPOP grammar
  • Much better error reporting and syntax assistance

High level plan:

  • Migrate the parser in hardbyte/common-expression-language to use the same AST as this repository.
  • Implement parser for anything missing in https://github.com/hardbyte/common-expression-language
  • Resolve any remaining differences between the existing LALRPOP parser and chumsky one.
    • Switch this repo's integer implementations to 64bit. #16
    • Remove the DoubleMinus and DoubleNot. Should be e.g. Unary(Negative, Unary(Negative, member)). #17
    • others ?
  • a small refactor PR in this repository to hide larlpop internals and expose a parser function and parser errors. #12
  • more parser tests to increase confidence that a replaced parser doesn't break anything. #13
  • PR to add the chumsky parser as an option perhaps not exposed by default or an optional opt-in?
  • Improvements to error reporting from parser - consider https://github.com/zesterer/ariadne
  • Compare and decide whether to make chumsky parser the default/only parser

Do you want to keep both parsers? If so, how should the API work to pick between them? Assume it wouldn't be too tricky to add unsigned ints and un-escaped strings to the current lalrpop version?

Serialize Value to JSON String

I’m fairly new to Rust and am using this project. I’d like to convert the executed Value into a String so I can serialize it to JSON.

Does Value need to implement From for this to work?

Context.clone() method used by macros is private to the crate

The official cel-spec has some additional macros / functions that might be useful for some of our use cases like: exists or exists_one.

Unfortunately I am not able to implement them by myself because: Context.clone() is pub(crate) and not pub:

pub(crate) fn clone(&self) -> Context {

Am I missing something or wouldn't it be useful to provide the clone functionality and context shadowing also for custom extension functions?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.