#nalgebra #latex #matrix

execute_evcxr

A library for execution of evcxr-supported syntax via building and executing a binary crate

2 releases

0.1.1 Jul 9, 2022
0.1.0 Jul 9, 2022

#491 in Procedural macros


Used in nalgebra_latex

MIT/Apache

21KB
284 lines

crates.io crates.io

Execution of evcxr-flavored Rust

Further evcxr can refer to the evaluation context, to Jupyter kernel for Rust programming language, or to its supported script-like syntax.

Example of evcxr-flavored Rust

:dep image = "0.23"
:dep evcxr_image = "1.1"
// In a pure Rust project, the dependencies above would be specified in Cargo.toml
use evcxr_image::ImageDisplay;

image::ImageBuffer::from_fn(256, 256, |x, y| {
    if (x as i32 - y as i32).abs() < 3 {
        image::Rgb([0, 0, 255])
    } else {
        image::Rgb([0, 0, 0])
    }
})

Example for Jupyter Notebook with evcxr kernel

:dep execute_evcxr = { version = "0.1.2" }

use execute_evcxr::{execute_evcxr, Config};

// This way it's possible to specify the non-default values
let config = Config { verbose: false, ..Default::default() };
execute_evcxr(r#"
:dep nalgebra = "0.31.0"
:dep nalgebra_latex = { version = "0.1.6", features = ["lin_sys", "evcxr"] }
 
use nalgebra::{matrix, Const};
use nalgebra_latex::{
    lin_sys::{
        LinSys,
        unknowns::SingleLetterBoldfaceVecOfUnknowns,
        numbering::Numbering,
        fmt::CasesLinSysFormatter,
    },
    fmt::EvcxrOutputFormatter,
};
use std::io::{stdout, Write};

let mut s = String::new();
let m = matrix!(
    1,2,3;
    4,5,6;
    7,8,9;
);
let vec_of_unknowns = SingleLetterBoldfaceVecOfUnknowns::<_,{Numbering::OneBased}>::new('x', Const::<3>);
let ls = LinSys::new(m, vec_of_unknowns);
CasesLinSysFormatter::write_evcxr_output(&mut s, &ls).unwrap();
stdout().write_all(s.as_bytes()).unwrap();
"#, config);

Example for Rust project

extern crate execute_evcxr;

use execute_evcxr::{execute_evcxr, Config};

fn main() {
    let config = Config { ..Config::default() };
    execute_evcxr(r#"
:dep nalgebra = "0.31.0"
:dep nalgebra_latex = { version = "0.1.6", features = ["lin_sys", "evcxr"] }

use nalgebra::{matrix, Const};
use nalgebra_latex::{
    lin_sys::{
        LinSys,
        unknowns::SingleLetterBoldfaceVecOfUnknowns,
        numbering::Numbering,
        fmt::CasesLinSysFormatter,
    },
    fmt::EvcxrOutputFormatter,
};
use std::io::{stdout, Write};

let mut s = String::new();
let m = matrix!(
    1,2,3;
    4,5,6;
    7,8,9;
);
let vec_of_unknowns = SingleLetterBoldfaceVecOfUnknowns::<_,{Numbering::OneBased}>::new('x', Const::<3>);
let ls = LinSys::new(m, vec_of_unknowns);
CasesLinSysFormatter::write_evcxr_output(&mut s, &ls).unwrap();
stdout().write_all(s.as_bytes()).unwrap();
"#, config);
}

At the moment of writing, the original evcxr kernel supports a lot but not all 1 features of Rust. This crate has been born as a temporary solution to the problem.

Note on evcxr-flavored Rust

The syntax supported by evcxr kernel, as opposed to pure Rust, is implementation-defined. As a result, the same evcxr-flavored script can be executed differently by execute_evcxr and evcxr itself. In case of discrepancies, the behavior of evcxr is considered correct and the deviation on the side of execute_evcxr must be treated a bug, unless stated otherwise.

Note on limitations

  • As its name suggests, execute_evcxr is not an evaluation context in contrast to evcxr. It does not "return" a value. It executes a given script supplied in the form of string instead of attempting to evaluate it, unlike evcxr.

  • execute_evcxr gets its job done by parsing the supplied script only. It does not analyze the source code of the crates-dependencies. This fact will become important further in the text.

  • Because execute_evcxr is a temporary solution, it does not try to memoize which macros were defined in the script and to which kinds of syntactic entities it would expand. This fact will become important further in the text.

  • execute_evcxr works by parsing evcxr-flavored Rust, building a binary crate from it in the temporary dir, executing the binary crate, and cleaning up. Therefore, it must know which syntactic entities (or, more precisely, "statements") should go inside the main() function. This fact will become important further in the text.

  • Due to the last three limitations above, the developer might need to annotate every macro invocation that eventually expands to "items" using #[expands_only_to_items] attribute. Otherwise, they will be placed inside main function. Luckily, the most common macros do not require the attribute and in many cases even if the macros do expands to "items" in the main(), the binary crate can still work as expected.

  • Execution speed of the scripts is bounded to building them anew, including downloading dependencies from the internet. It can be a wise idea to set up caching. The library author hasn't faced the need for caching, yet.

For library developers

This is a README for library users. There's a separate DEV-README.md with information that can be relevant only to library developers.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~1.5MB
~37K SLoC