16个版本

0.0.17-alpha2024年8月12日
0.0.16-alpha2024年8月11日
0.0.8-alpha2024年3月10日
0.0.3-alpha2024年2月26日

#176编程语言

Download history 15/week @ 2024-05-20 12/week @ 2024-06-03 15/week @ 2024-06-10 8/week @ 2024-06-24 37/week @ 2024-07-01 14/week @ 2024-07-15 3/week @ 2024-07-22 71/week @ 2024-07-29 955/week @ 2024-08-05

1,043 每月下载
用于 9 个crate(8 直接使用)

自定义许可

47KB
775

Garnish Core — 构建状态

嵌入Garnish语言所需的核心库。这些是您将需要添加Garnish脚本到应用程序中的内容。

如果您想了解有关Garnish语言的信息,请访问演示网站

此仓库包含以下库crate。即使某个库没有更改,每个库的版本号也会与其他库保持同步。

特质

Traits Crate Traits Docs

包含用于其余核心库的基特质、结构体、枚举等。

简单数据

Simple Data Crate Simple Data Docs

使用标准Rust类型和数据结构实现的GarnishData

运行时

Runtime Crate Traits Docs

实现GarnishRuntime,该实现执行给定数据对象上的指令。

编译器

Compiler Crate Compiler Docs

包含用于分析输入字符串并将指令集构建到数据对象中的函数。

Garnish语言

Lang Crate Lang Docs

以上四个库的便利单依赖。

用法

以下示例使用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