ketos 0.5.0

Lisp dialect scripting and extension language
Documentation
#[macro_use] extern crate assert_matches;
extern crate ketos;

use std::time::Duration;

use ketos::{
    Builder,
    Error,
    RestrictConfig,
    RestrictError,
};

fn run(restrict: RestrictConfig, code: &str) -> Result<(), Error> {
    let interp = Builder::new()
        .restrict(restrict)
        .finish();

    try!(interp.run_code(code, None));
    Ok(())
}

macro_rules! assert_matches_re {
    ( $e:expr , $re:expr ) => {
        assert_matches!($e, Error::RestrictError(e) if e == $re)
    }
}

#[test]
fn test_restrict_time() {
    assert_matches_re!(run(
        RestrictConfig{
            execution_time: Some(Duration::from_millis(100)),
            .. RestrictConfig::permissive()
        },
        "
        (define (foo) (foo))
        (foo)
        ").unwrap_err(),
        RestrictError::ExecutionTimeExceeded);
}

#[test]
fn test_restrict_stack() {
    assert_matches_re!(run(
        RestrictConfig{
            call_stack_size: 100,
            .. RestrictConfig::permissive()
        },
        "
        (define (foo) (bar))
        (define (bar) (foo))
        (foo)
        ").unwrap_err(),
        RestrictError::CallStackExceeded);

    assert_matches_re!(run(
        RestrictConfig{
            value_stack_size: 100,
            .. RestrictConfig::permissive()
        },
        "
        (define (foo)
          (let ((a ()) (a ()) (a ()) (a ()) (a ()) (a ()) (a ()) (a ())
                (a ()) (a ()) (a ()) (a ()) (a ()) (a ()) (a ()) (a ())
                (a ()) (a ()) (a ()) (a ()) (a ()) (a ()) (a ()) (a ())
                (a ()) (a ()) (a ()) (a ()) (a ()) (a ()) (a ()) (a ()))
            (bar)))
        (define (bar) (foo))
        (foo)
        ").unwrap_err(),
        RestrictError::ValueStackExceeded);
}

#[test]
fn test_restrict_namespace() {
    { // This block is just so text editors can hide this nonsense.
        assert_matches_re!(run(
            RestrictConfig{
                namespace_size: 20,
                .. RestrictConfig::permissive()
            },
            "
            (define a-1 ())
            (define a-2 ())
            (define a-3 ())
            (define a-4 ())
            (define a-5 ())
            (define a-6 ())
            (define a-7 ())
            (define a-8 ())
            (define a-9 ())
            (define a-10 ())
            (define a-11 ())
            (define a-12 ())
            (define a-13 ())
            (define a-14 ())
            (define a-15 ())
            (define a-16 ())
            (define a-17 ())
            (define a-18 ())
            (define a-19 ())
            (define a-20 ())
            (define a-21 ())
            ").unwrap_err(),
            RestrictError::NamespaceSizeExceeded);
    }
}

#[test]
fn test_restrict_memory() {
    assert_matches_re!(run(
        RestrictConfig{
            memory_limit: 100,
            .. RestrictConfig::permissive()
        },
        "
        (define (foo a) (foo (list () a)))
        (foo ())
        ").unwrap_err(),
        RestrictError::MemoryLimitExceeded);
}

#[test]
fn test_restrict_integer() {
    let cfg = RestrictConfig{
        max_integer_size: 100,
        .. RestrictConfig::permissive()
    };

    assert_matches_re!(run(cfg.clone(), "
        0x10000000000000000000000000
        ").unwrap_err(),
        RestrictError::IntegerLimitExceeded);

    assert_matches_re!(run(cfg.clone(), "
        (^ 2 1_000_000)
        ").unwrap_err(),
        RestrictError::IntegerLimitExceeded);

    assert_matches_re!(run(cfg.clone(), "
        (<< 1 1_000_000)
        ").unwrap_err(),
        RestrictError::IntegerLimitExceeded);

    assert_matches_re!(run(cfg.clone(), "
        (define (foo n) (foo (* n 2)))
        (foo 1)
        ").unwrap_err(),
        RestrictError::IntegerLimitExceeded);

    assert_matches_re!(run(cfg.clone(), "
        (define (foo n) (foo (/ n 2)))
        (foo 1)
        ").unwrap_err(),
        RestrictError::IntegerLimitExceeded);
}

#[test]
fn test_restrict_syntax() {
    assert_matches_re!(run(
        RestrictConfig{
            max_syntax_nesting: 50,
            .. RestrictConfig::permissive()
        },
        "
        ((((((((((((((((((((((((((((((((((((((((((((((((((
        ((((((((((((((((((((((((((((((((((((((((((((((((((
        ))))))))))))))))))))))))))))))))))))))))))))))))))
        ))))))))))))))))))))))))))))))))))))))))))))))))))
        ").unwrap_err(),
        RestrictError::MaxSyntaxNestingExceeded);
}