1

I'm trying to call a Dyon built-in function (sin) from Rust:

use dyon::{Module, Runtime, Variable};
use std::sync::Arc;

fn main() {
    let mut dyon_runtime = Runtime::new();
    let module = Module::new();
    let dyon_module = Arc::new(module);
    let v = dyon_runtime.call_str_ret("sin", &[Variable::f64(0.0)], &dyon_module);
    match v {
        Err(e) => {
            eprintln!("Error: {:?}", e);
        }
        Ok(v) => {
            println!("Called sin - result {:?}", v);
        }
    };
}

However, I get

Error: "Could not find function `sin`"

What do I need to do to correctly call this function?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187

3 Answers3

2

I can't explain the design decisions here, but call_str_ret only handles functions that have been loaded, not external functions or intrinsics.

As a workaround, you can load a little shim function that just calls off to the appropriate function:

use dyon::{Module, Runtime, Variable};
use std::sync::Arc;

fn main() {
    let mut dyon_runtime = Runtime::new();
    let mut module = Module::new();

    let shim = Arc::new("do_it(x) = sin(x)".into());
    dyon::load_str("main.rs", shim, &mut module).expect("Unable to load shim function");
    let dyon_module = Arc::new(module);

    let v = dyon_runtime.call_str_ret("do_it", &[Variable::f64(90.0)], &dyon_module);
    match v {
        Err(e) => {
            eprintln!("Error: {:?}", e);
        }
        Ok(v) => {
            println!("Called sin - result {:?}", v);
        }
    };
}
Called sin - result F64(0.8939966636005579, None)
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
2

call_str() only cares about one type of function call. I don't know why they do this, but one solution would be to do it yourself:

use dyon::{ast, Module, Runtime, Variable};
use range::Range;
use std::cell::Cell;
use std::sync::Arc;

fn main() {
    let mut dyon_runtime = Runtime::new();
    let module = Module::new();

    let name: Arc<String> = Arc::new("sin".into());
    let f_index = Cell::new(module.find_function(&name, 0));
    let args = vec![ast::Expression::Variable(Box::new((
        Range::empty(0),
        Variable::F64(1.0, None),
    )))];
    let call = ast::Call {
        alias: None,
        name,
        f_index,
        args,
        custom_source: None,
        source_range: Range::empty(0),
    };
    let dyon_module = Arc::new(module);
    println!("{:?}", dyon_runtime.call(&call, &dyon_module));
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Stargateur
  • 24,473
  • 8
  • 65
  • 91
0

Both of the other answers led me to a solution that works cleanly for both cases: I took the Runtime.call_str_ret and modified it to use any non-None result from module.find_function. IMO the code is actually cleaner than the original version in Runtime. I've submitted a PR for this which has been merged, so releases of Dyon after 0.40.0 will have call_str_ret working for built-in functions.


If you can't use a more recent version of Dyon, then you could manually apply the patch from here: https://github.com/PistonDevelopers/dyon/pull/582.

Or alternatively you could use your own version of call_str_ret, like this:

use dyon::{Module, Runtime, Variable};
use std::sync::Arc;

extern crate range;

/// Call function by name, returning a value.
pub fn call_str_ret_ex(
    runtime:&mut Runtime,
    function: &str,
    args: &[Variable],
    module: &Arc<Module>
) -> Result<Variable, String> {
    use std::cell::Cell;
    use range::Range;
    use dyon::FnIndex;
    use dyon::runtime::Flow;
    use dyon::ast;

    let name: Arc<String> = Arc::new(function.into());
    let fn_index = module.find_function(&name, 0);
    if let FnIndex::None = fn_index {
        return Err(format!("Could not find function `{}`",function))
    }

    let call = ast::Call {
        alias: None,
        name: name.clone(),
        f_index: Cell::new(fn_index),
        args: args.iter()
                .map(|arg| ast::Expression::Variable(Box::new((
                            Range::empty(0), arg.clone()))))
                .collect(),
        custom_source: None,
        source_range: Range::empty(0),
    };
    match runtime.call(&call, &module) {
        Ok((Some(val), Flow::Continue)) => Ok(val),
        Err(err) => Err(err),
        _ => Err("Error during call".to_owned())
    }
}

This will let you write the original code as

def test_dyon_fn(
    dyon_runtime: &mut Runtime
    module: &Module,
    fn: &str,
)
    let v = call_str_ret_ex(&mut dyon_runtime, fn, &[Variable::f64(0.0)], &dyon_module);
match v {
    Err(e) => {
        eprintln!("Error: {:?}", e);
    }
    Ok(v) => {
        println!("Called {:?}  - result {:?}", fn, v);
    }
};

fn main() {
    let mut dyon_runtime = Runtime::new();
    let mut module = Module::new();
    let shim = Arc::new("sin_shim(x) = sin(x)".into());
    dyon::load_str("main.rs", shim, &mut module).expect("Unable to load shim function");
    let dyon_module = Arc::new(module);

    test_dyon_fn(&mut dyon_runtime, &dyon_module, "sin");
    test_dyon_fn(&mut dyon_runtime, &dyon_module, "sin_shim");
    test_dyon_fn(&mut dyon_runtime, &dyon_module, "no_such");
}

This prints:

Called sin - result F64(0.0, None)
Called sin_shim - result F64(0.0, None)
Error: "Could not find function `no_such`"
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187