16个版本
新 0.0.17-alpha | 2024年8月12日 |
---|---|
0.0.16-alpha | 2024年8月11日 |
0.0.8-alpha | 2024年3月10日 |
0.0.3-alpha | 2024年2月26日 |
#176 在 编程语言
1,043 每月下载
用于 9 个crate(8 直接使用)
47KB
775 行
Garnish Core —
嵌入Garnish语言所需的核心库。这些是您将需要添加Garnish脚本到应用程序中的内容。
如果您想了解有关Garnish语言的信息,请访问演示网站。
库
此仓库包含以下库crate。即使某个库没有更改,每个库的版本号也会与其他库保持同步。
特质
包含用于其余核心库的基特质、结构体、枚举等。
简单数据
使用标准Rust类型和数据结构实现的GarnishData
。
运行时
实现GarnishRuntime
,该实现执行给定数据对象上的指令。
编译器
包含用于分析输入字符串并将指令集构建到数据对象中的函数。
Garnish语言
以上四个库的便利单依赖。
用法
以下示例使用Garnish语言 crate。如果您计划单独导入四个库,只需相应调整use
语句。
基本编译和执行
仅使用核心库时,这是一个三步过程,第三步需要创建一个GarnishData
对象。
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);
// SimpleGarnishRuntime only provides method to execute instructions 1 at a time,
// so we loop until finished
loop {
// this None argument is where a GarnishContext would be passed
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,
},
}
}
// Result of an execution is a data objects current value
runtime.get_data().get_current_value().and_then(|v| {
// get_raw_data is not a trait member of GarnishData,
// but a convenience function of SimpleGarnishData
println!("Result: {:?}", runtime.get_data().get_raw_data(v))
});
Ok(())
}
使用上下文
在执行期间提供GarnishContext
对象是一种扩展脚本功能的方式。这可以提供环境变量、访问数据库的方法或自定义操作。
以下示例向脚本提供了两个项目。PI的常量值以及执行正弦函数的方法。
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 {
// This method is called when ever a script has an unresolved identifier during runtime
fn resolve(&mut self, symbol: u64, data: &mut SimpleGarnishData)
-> Result<bool, RuntimeError<DataError>> {
// lookup given symbol to see if we have a value for it
// returning true tells runtime that the symbol was resolved
// and not to do any more checks
// returning false will let the runtime check additional resolve methods,
// resulting in a Unit value if nothing resolves it
match self.symbol_to_data.get(&symbol) {
Some(v) => match v {
SimpleData::External(n) => {
// using GarnishData trait methods, add_* for each GarnishDataType
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)
}
}
// This method is called when ever an External type value
// is used with Garnish's 'apply' type operations
fn apply(
&mut self,
external_value: usize,
input_addr: usize,
data: &mut SimpleGarnishData,
) -> Result<bool, RuntimeError<DataError>> {
// check that the external value given is actually supported
if external_value == MATH_FUNCTION_SINE {
// using some non trait methods, whether to use trait methods or
// implementation specific methods will depend on your use case
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),
})),
// sin function only supports numbers, all other values result in Unit
_ => SimpleData::Unit
})).ok_or(
DataError::from("Failed to retrieve data during external apply 'sin'"
.to_string())
)?;
// need to add new data
// then push its address to registers for next operation to use
// failure to not push expected values and still returning true,
// could cause script to fail due to empty registers
let addr = data.get_data().len();
data.get_data_mut().push(new_data);
data.push_register(addr)?;
Ok(true)
} else {
// return value signifies same as resolve method's return value
Ok(false)
}
}
}
现在我们实现了GarnishContext
,我们可以将其传递给execute_current_instruction
方法,而不是传递None。
// ...
let mut runtime = SimpleGarnishRuntime::new(data);
let mut context = MathContext::new();
loop {
// add new context object
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,
},
}
}
// ...
进一步阅读
Browser Garnish项目是演示网站使用的WebAssembly库。通过查看演示和源代码,可以了解它们是如何相互关联的。
API文档 - 提供完整的描述和更多示例。(目前仍在进行中)
依赖项
~170KB