Garnish Core 
Core libraries needed to embed the garnish language. These are what you will need to add Garnish scripting to an application.
If your interested in learning about the Garnish Language, please visit the Demo Site.
Libraries
This repository contains the following library crates. Version numbers for each are kept in sync with one another even if it means no changes were made to that library.
Traits

Contains base traits, structs, enums, etc. for use by rest of the core libraries.
Simple Data

An implementation of GarnishData using standard Rust types and data structures.
Runtime

An implementation of GarnishRuntime which executes instructions upon given data object.
Compiler

Contains functions to lex and parse and input string and building that instruction set into a data object.
Garnish Lang

Convenience single dependency for above four libraries.
Usage
These examples use the Garnish Lang crate. If you plan to import the four individually, simply adjust the use statements accordingly.
Basic Compile and Execute
With just the core libraries this is a three-step process and a GarnishData object will need to be created for the third.
use garnish_lang::compiler::lex::{lex, LexerToken};
use garnish_lang::compiler::parse::{parse, ParseResult};
use garnish_lang::compiler::build::build_with_data;
use garnish_lang::simple::SimpleGarnishData;
const INPUT: &str = "5 + 5";
fn main() -> Result<(), String> {
let tokens: Vec<LexerToken> = lex(input).or_else(|e| Err(e.get_message().clone()))?;
let parse_result: ParseResult = parse(&tokens).or_else(|e| Err(e.get_message().clone()))?;
let mut data = SimpleGarnishData::new();
build_with_data(parse_result.get_root(), parse_result.get_nodes().clone(), &mut data)
.or_else(|e| Err(e.get_message().clone()))?;
let mut runtime = SimpleGarnishRuntime::new(data);
loop {
match runtime.execute_current_instruction(None) {
Err(e) => {
return Err(e.get_message().clone());
}
Ok(data) => match data.get_state() {
SimpleRuntimeState::Running => (),
SimpleRuntimeState::End => break,
},
}
}
runtime.get_data().get_current_value().and_then(|v| {
println!("Result: {:?}", runtime.get_data().get_raw_data(v))
});
Ok(())
}
Using Context
Providing a GarnishContext object during execution is a way to extend the functionality of a script.
This can be providing environment variables, methods for accessing a database, or customizing operations.
The following example provides two items to a script. A constant value for PI and a way to execute the trigonometric function sine.
use std::collections::HashMap;
use garnish_lang::{GarnishContext, GarnishData, RuntimeError};
use garnish_lang::simple::{
DataError,
SimpleData,
SimpleGarnishData,
SimpleNumber,
symbol_value
};
const MATH_FUNCTION_SINE: usize = 1;
pub struct MathContext {
symbol_to_data: HashMap<u64, SimpleData>
}
impl MathContext {
pub fn new() -> Self {
let mut symbol_to_data = HashMap::new();
symbol_to_data.insert(
symbol_value("Math::PI"),
SimpleData::Number(SimpleNumber::Float(std::f64::consts::PI))
);
symbol_to_data.insert(
symbol_value("sin"),
SimpleData::External(MATH_FUNCTION_SINE)
);
BrowserContext {
symbol_to_expression: HashMap::new(),
symbol_to_data
}
}
}
impl GarnishContext<SimpleGarnishData> for MathContext {
fn resolve(&mut self, symbol: u64, data: &mut SimpleGarnishData)
-> Result<bool, RuntimeError<DataError>> {
match self.symbol_to_data.get(&symbol) {
Some(v) => match v {
SimpleData::External(n) => {
data.add_external(*n).and_then(|addr| data.push_register(addr))?;
Ok(true)
},
SimpleData::Number(n) => {
data.add_number(*n).and_then(|addr| data.push_register(addr))?;
Ok(true)
},
_ => Ok(false)
}
None => Ok(false)
}
}
fn apply(
&mut self,
external_value: usize,
input_addr: usize,
data: &mut SimpleGarnishData,
) -> Result<bool, RuntimeError<DataError>> {
if external_value == MATH_FUNCTION_SINE {
let new_data = data.get_raw_data(input_addr).and_then(|d| Some(match d {
SimpleData::Number(num) => SimpleData::Number(SimpleNumber::Float(match num {
SimpleNumber::Integer(n) => f64::sin(n as f64),
SimpleNumber::Float(f) => f64::sin(f),
})),
_ => SimpleData::Unit
})).ok_or(
DataError::from("Failed to retrieve data during external apply 'sin'"
.to_string())
)?;
let addr = data.get_data().len();
data.get_data_mut().push(new_data);
data.push_register(addr)?;
Ok(true)
} else {
Ok(false)
}
}
}
Now we've implemented a GarnishContext we can pass it into the execute_current_instruction method instead of None.
let mut runtime = SimpleGarnishRuntime::new(data);
let mut context = MathContext::new();
loop {
match runtime.execute_current_instruction(Some(&mut context)) {
Err(e) => {
return Err(e.get_message().clone());
}
Ok(data) => match data.get_state() {
SimpleRuntimeState::Running => (),
SimpleRuntimeState::End => break,
},
}
}
Further Reading
The Browser Garnish project is the WebAssembly library used by the Demo Site.
Going through the demo and viewing the source will illustrate how it all links together.
API Documentation - For full descriptions and more examples. (Currently still working in progress)