1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
//! SBrain, or Semantic Brain, is a set of extensions to the famous language by Urban Müller
//! designed to make it more amenable to genetic programming. Additions include a stack, a general-
//! purpose register, and single-instruction arithmetic.
//!
//! This crate provides an implementation of the SBrain specification designed to be used for
//! genetic programming. See the `specification` pseudomodule for the complete specification.
//!
//! Here's a quick example:
//!
//! ```
//! # use sbrain::*;
//! let result = evaluate("[.>]@@Test Data to Echo", None);
//! assert_eq!("Test Data to Echo", &tape_to_string(result.output));
//! // This program terminates after 52 cycles
//! assert_eq!(52, result.cycles);
//!
//! // In this case, the program is interruped before completion.
//! let result = evaluate("[.>]@@Test Data to Echo", Some(32));
//! // The program doesn't finish, because it would take more than 32 cycles.
//! assert_eq!("Test Data t", &tape_to_string(result.output));
//! assert_eq!(false, result.halted);
//! ```

pub mod specification;
mod machine;
mod source;

pub use machine::*;
pub use source::source_to_tapes;

/// Represents the outcome of an evaluation by the SBrain VM.
pub struct EvalResult {
    /// The output of the computation
    pub output: Vec<MData>,
    /// The number of cycles for which the machine ran
    pub cycles: u32,
    /// Whether or not the machine halted on its own. False means it was interrupted.
    pub halted: bool,
}

/// Run the program represented by the given source on a new Semantic Brain VM.
/// If Limit is None, this may never return; if it is Some(n), the machine will run for at most n
/// cycles, then stop.
///
/// # Panics
/// This function panics if the source evaluates to tapes that exceed the maximum size of the
/// VM's tapes (2^16 )
///
/// # Examples
/// This simple program reads data off the tape until it encounters a 0. Here, `evaluate()` is used
/// without an execution limit; this is because it's easy to reason about when the program will
/// end. This isn't recommended for any but the simplest programs.
///
/// ```
/// # use sbrain::*;
/// let result = evaluate("[.>]@@Test Data to Echo", None);
///
/// assert_eq!("Test Data to Echo", &tape_to_string(result.output));
/// // This program terminates after 52 cycles
/// assert_eq!(52, result.cycles);
/// ```
/// In this case, the program is interruped before completion.
///
/// ```
/// # use sbrain::*;
/// let result = evaluate("[.>]@@Test Data to Echo", Some(32));
/// // The program doesn't finish, because it would take more than 32 cycles.
/// assert_eq!("Test Data t", &tape_to_string(result.output));
/// assert_eq!(false, result.halted);
/// ```
pub fn evaluate(source: &str, limit: Option<u32>) -> EvalResult {
    // Transliterate the source code, creating Vec<MData> tapes.
    let (program, data) = source_to_tapes(&source);
    // Create a machine with no input tape.
    let mut machine = SBrainVM::new(None);
    // Load the program and data tapes.
    machine.load_program(&program).unwrap();
    machine.load_data(&data).unwrap();

    let (cycles, halted) = machine.run(limit);

    EvalResult {
        output: machine.get_output(),
        cycles: cycles,
        halted: halted,
    }

}

/// Convert a tape of MData cells into Unicode chars. Invalid chars are excluded, which could have
/// some unintended side effects for genesis based on string comparisons.
pub fn tape_to_string(tape: Vec<MData>) -> String {
    use std::char;
    let mut result = String::with_capacity(tape.len());
    for cell in tape {
        if let Some(c) = char::from_u32(cell) {
            result.push(c);
        };
    }
    result
}