/* * ____ * * Copyright ____ Manos Pitsidianakis * * This file is part of ____. * * ____ is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ____ is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ____. If not, see . */ #[cfg(test)] #[test] fn test_starlark() { use starlark::{ environment::{Globals, Module}, eval::Evaluator, syntax::{AstModule, Dialect}, values::Value, }; let content = r#" def hello(): return "hello" hello() + " world!" "#; // We first parse the content, giving a filename and the Starlark // `Dialect` we'd like to use (we pick standard). let ast: AstModule = AstModule::parse("hello_world.star", content.to_owned(), &Dialect::Standard).unwrap(); // We create a `Globals`, defining the standard library functions available. // The `standard` function uses those defined in the Starlark specification. let globals: Globals = Globals::standard(); // We create a `Module`, which stores the global variables for our calculation. let module: Module = Module::new(); // We create an evaluator, which controls how evaluation occurs. let mut eval: Evaluator = Evaluator::new(&module); // And finally we evaluate the code using the evaluator. let res: Value = eval.eval_module(ast, &globals).unwrap(); assert_eq!(res.unpack_str(), Some("hello world!")); } #[cfg(test)] #[test] fn test_starlark_2() { use starlark::{ environment::{GlobalsBuilder, Module}, eval::Evaluator, starlark_module, syntax::{AstModule, Dialect}, }; // This defines the function that is visible to Starlark #[starlark_module] fn starlark_quadratic(builder: &mut GlobalsBuilder) { fn quadratic(a: i32, b: i32, c: i32, x: i32) -> starlark::Result { Ok(a * x * x + b * x + c) } } // We build our globals to make the function available in Starlark let globals = GlobalsBuilder::new().with(starlark_quadratic).build(); let module = Module::new(); let mut eval = Evaluator::new(&module); // Let's test calling the function from Starlark code let starlark_code = r#" quadratic(4, 2, 1, x = 8) "#; let ast = AstModule::parse( "quadratic.star", starlark_code.to_owned(), &Dialect::Standard, ) .unwrap(); let res = eval.eval_module(ast, &globals).unwrap(); assert_eq!(res.unpack_i32(), Some(273)); // Verify that we got an `int` // return value of 4 * 8^2 + 2 * 8 // + 1 = 273 } #[cfg(test)] #[test] fn test_starlark_3() { use std::cell::RefCell; use starlark::{ any::ProvidesStaticType, environment::{GlobalsBuilder, Module}, eval::Evaluator, starlark_module, syntax::{AstModule, Dialect}, values::{none::NoneType, Value}, }; let content = r#" emit(1) emit(["test"]) emit({"x": "y"}) "#; // Define a store in which to accumulate JSON strings #[derive(Debug, ProvidesStaticType, Default)] struct Store(RefCell>); impl Store { fn add(&self, x: String) { self.0.borrow_mut().push(x) } } #[starlark_module] fn starlark_emit(builder: &mut GlobalsBuilder) { fn emit(x: Value, eval: &mut Evaluator) -> starlark::Result { // We modify extra (which we know is a Store) and add the JSON of the // value the user gave. eval.extra .unwrap() .downcast_ref::() .unwrap() .add(x.to_json()?); Ok(NoneType) } } let ast = AstModule::parse("json.star", content.to_owned(), &Dialect::Standard).unwrap(); // We build our globals adding some functions we wrote let globals = GlobalsBuilder::new().with(starlark_emit).build(); let module = Module::new(); let store = Store::default(); { let mut eval = Evaluator::new(&module); // We add a reference to our store eval.extra = Some(&store); eval.eval_module(ast, &globals).unwrap(); } assert_eq!(&*store.0.borrow(), &["1", "[\"test\"]", "{\"x\":\"y\"}"]); } #[cfg(test)] #[test] fn test_starlark_4() { use starlark::{ environment::{Globals, Module}, eval::Evaluator, syntax::{AstModule, Dialect, DialectTypes}, }; let content = r#" def takes_int(x: int): pass takes_int("test") "#; // Make the dialect enable types let dialect = Dialect { enable_types: DialectTypes::Enable, ..Dialect::Standard }; // We could equally have done `dialect = Dialect::Extended`. let ast = AstModule::parse("json.star", content.to_owned(), &dialect).unwrap(); let globals = Globals::standard(); let module = Module::new(); let mut eval = Evaluator::new(&module); let res = eval.eval_module(ast, &globals); // We expect this to fail, since it is a type violation assert!(res .unwrap_err() .to_string() .contains("Value `test` of type `string` does not match the type annotation `int`")); } #[cfg(test)] #[test] fn test_starlark_5() { use starlark::{ environment::{FrozenModule, Globals, Module}, eval::{Evaluator, ReturnFileLoader}, syntax::{AstModule, Dialect}, }; // Get the file contents (for the demo), in reality use `AstModule::parse_file`. fn get_source(file: &str) -> &str { match file { "a.star" => "a = 7", "b.star" => "b = 6", _ => { r#" load('a.star', 'a') load('b.star', 'b') ab = a * b "# } } } fn get_module(file: &str) -> starlark::Result { let ast = AstModule::parse(file, get_source(file).to_owned(), &Dialect::Standard)?; // We can get the loaded modules from `ast.loads`. // And ultimately produce a `loader` capable of giving those modules to // Starlark. let mut loads = Vec::new(); for load in ast.loads() { loads.push((load.module_id.to_owned(), get_module(load.module_id)?)); } let modules = loads.iter().map(|(a, b)| (a.as_str(), b)).collect(); let mut loader = ReturnFileLoader { modules: &modules }; let globals = Globals::standard(); let module = Module::new(); { let mut eval = Evaluator::new(&module); eval.set_loader(&mut loader); eval.eval_module(ast, &globals)?; } // After creating a module we freeze it, preventing further mutation. // It can now be used as the input for other Starlark modules. Ok(module.freeze()?) } let ab = get_module("ab.star").unwrap(); assert_eq!(ab.get("ab").unwrap().unpack_i32(), Some(42)); } #[cfg(test)] #[test] fn test_starlark_6() { use starlark::{ environment::{Globals, Module}, eval::Evaluator, syntax::{AstModule, Dialect}, }; let content = r#" def quadratic(a, b, c, x): return a*x*x + b*x + c quadratic "#; let ast = AstModule::parse("quadratic.star", content.to_owned(), &Dialect::Extended).unwrap(); let globals = Globals::standard(); let module = Module::new(); let mut eval = Evaluator::new(&module); let quad = eval.eval_module(ast, &globals).unwrap(); let heap = module.heap(); let res = eval .eval_function( quad, &[heap.alloc(4), heap.alloc(2), heap.alloc(1)], &[("x", heap.alloc(8))], ) .unwrap(); assert_eq!(res.unpack_i32(), Some(273)); } #[cfg(test)] #[test] fn test_starlark_datetime_fmt() { use starlark::{ environment::{GlobalsBuilder, Module}, eval::Evaluator, starlark_module, syntax::{AstModule, Dialect}, values::Value, }; let content = r#" def datetime_fmt(): now_val = now() print("now returned ", now_val) n = since(now_val) print("since returned ", n) if n < 60 * 60: return "{} minute{} ago".format((n / (60)), ("" if n / 60 == 1 else "s")) elif n < 24 * 60 * 60: return "{} hour{} ago".format( n / (60 * 60), "" if n / (60 * 60) == 1 else "s") elif n < 7 * 24 * 60 * 60: return "{} day{} ago".format( n / (24 * 60 * 60), "" if n / (24 * 60 * 60) == 1 else "s") else: return "{}".format(n) datetime_fmt "#; // This defines the function that is visible to Starlark #[starlark_module] fn starlark_datetime_fmt(builder: &mut GlobalsBuilder) { fn print(s: Value, val: u64) -> starlark::Result { let ss: &str = s.unpack_str().unwrap_or(""); println!("{ss}{val}"); Ok(1) } fn now() -> starlark::Result { let d = std::time::UNIX_EPOCH; let now: std::time::Duration = std::time::SystemTime::now().duration_since(d).unwrap(); Ok(now.as_secs()) } fn since(epoch: u64) -> starlark::Result { let d = std::time::UNIX_EPOCH + std::time::Duration::from_secs(epoch); let now: std::time::Duration = std::time::SystemTime::now() .duration_since(d) .unwrap_or_else(|_| std::time::Duration::new(std::u64::MAX, 0)); Ok(now.as_secs()) } } let ast = AstModule::parse("datetime_fmt.star", content.to_owned(), &Dialect::Extended).unwrap(); let globals = GlobalsBuilder::new().with(starlark_datetime_fmt).build(); let module = Module::new(); let mut eval = Evaluator::new(&module); let quad = eval.eval_module(ast, &globals).unwrap(); let res = eval.eval_function(quad, &[], &[]).unwrap(); assert_eq!( res.unpack_str(), Some("0.0 minutes ago".to_string()).as_deref() ); } #[cfg(test)] #[test] fn test_starlark_7() { /* use starlark::starlark_module; use std::fmt::Display; use std::fmt::Write; use std::fmt::{self}; use allocative::Allocative; use starlark::environment::Globals; use starlark::environment::Module; use starlark::eval::Evaluator; use starlark::starlark_simple_value; use starlark::syntax::AstModule; use starlark::syntax::Dialect; use starlark::values::Heap; use starlark::values::NoSerialize; use starlark::values::ProvidesStaticType; use starlark::values::StarlarkValue; use starlark::values::Value; use starlark::values::ValueError; use starlark::values::ValueLike; use starlark_derive::starlark_value; // Define complex numbers #[derive(Debug, PartialEq, Eq, ProvidesStaticType, NoSerialize, Allocative)] struct Complex { real: i32, imaginary: i32, } starlark_simple_value!(Complex); impl Display for Complex { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{} + {}i", self.real, self.imaginary) } } #[starlark_value(type = "complex")] impl<'v> StarlarkValue<'v> for Complex { // How we add them fn add(&self, rhs: Value<'v>, heap: &'v Heap) -> Option>> { if let Some(rhs) = rhs.downcast_ref::() { Some(Ok(heap.alloc(Complex { real: self.real + rhs.real, imaginary: self.imaginary + rhs.imaginary, }))) } else { None } } } let content = "str(a + b)"; let ast = AstModule::parse("complex.star", content.to_owned(), &Dialect::Standard).unwrap(); let globals = Globals::standard(); let module = Module::new(); // We inject some complex numbers into the module before we start. let a = module.heap().alloc(Complex { real: 1, imaginary: 8, }); module.set("a", a); let b = module.heap().alloc(Complex { real: 4, imaginary: 2, }); module.set("b", b); let mut eval = Evaluator::new(&module); let res = eval.eval_module(ast, &globals).unwrap(); assert_eq!(res.unpack_str(), Some("5 + 10i")); */ }