diff --git a/.ci/build.sh b/.ci/build.sh new file mode 100755 index 00000000..999f60c9 --- /dev/null +++ b/.ci/build.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -ex + +cargo build --verbose +cargo test --verbose + +if [ "$TRAVIS_RUST_VERSION" = "nightly" ] +then + cargo build --verbose --features no_stdlib + cargo test --verbose --features no_stdlib +fi + diff --git a/.travis.yml b/.travis.yml index 8c91a741..80702bdc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,3 +6,5 @@ rust: matrix: allow_failures: - rust: nightly + +script: bash ./.ci/build.sh \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 308685bf..f495d608 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,14 +15,34 @@ include = [ ] [dependencies] -num-traits = "*" +num-traits = "0.2.11" + +[dependencies.libm] +version = "0.2.1" +optional = true + +[dependencies.core-error] +version = "0.0.1-rc4" +features = ["alloc"] +optional = true + +[dependencies.hashbrown] +version = "0.7.1" +default-features = false +features = ["ahash", "nightly", "inline-more"] +optional = true + +[dependencies.ahash] +version = "0.3.2" +default-features = false +optional = true [features] #default = ["no_function", "no_index", "no_float", "only_i32", "no_stdlib", "unchecked", "no_optimize"] default = [ "optimize_full" ] debug_msgs = [] # print debug messages on function registrations and calls unchecked = [] # unchecked arithmetic -no_stdlib = [] # no standard library of utility functions +no_stdlib = ["num-traits/libm", "hashbrown", "core-error", "libm"] # no standard library of utility functions no_index = [] # no arrays and indexing no_float = [] # no floating-point no_function = [] # no script-defined functions diff --git a/src/any.rs b/src/any.rs index 96fe099b..927f2124 100644 --- a/src/any.rs +++ b/src/any.rs @@ -1,7 +1,8 @@ //! Helper module which defines the `Any` trait to to allow dynamic value handling. -use std::{ - any::{type_name, TypeId}, +use crate::stdlib::{ + any::{self, type_name, TypeId}, + boxed::Box, fmt, }; @@ -12,7 +13,7 @@ pub type Variant = dyn Any; pub type Dynamic = Box; /// A trait covering any type. -pub trait Any: std::any::Any { +pub trait Any: any::Any { /// Get the `TypeId` of this type. fn type_id(&self) -> TypeId; @@ -27,7 +28,7 @@ pub trait Any: std::any::Any { fn _closed(&self) -> _Private; } -impl Any for T { +impl Any for T { fn type_id(&self) -> TypeId { TypeId::of::() } diff --git a/src/api.rs b/src/api.rs index 3c248d5b..172e55d4 100644 --- a/src/api.rs +++ b/src/api.rs @@ -12,13 +12,15 @@ use crate::scope::Scope; #[cfg(not(feature = "no_optimize"))] use crate::optimize::optimize_ast; -use std::{ +use crate::stdlib::{ any::{type_name, TypeId}, - fs::File, - io::prelude::*, - path::PathBuf, + boxed::Box, + string::{String, ToString}, sync::Arc, + vec::Vec, }; +#[cfg(not(feature = "no_stdlib"))] +use crate::stdlib::{fs::File, io::prelude::*, path::PathBuf}; impl<'e> Engine<'e> { pub(crate) fn register_fn_raw( @@ -115,6 +117,7 @@ impl<'e> Engine<'e> { parse(&mut tokens_stream.peekable(), self, scope) } + #[cfg(not(feature = "no_stdlib"))] fn read_file(path: PathBuf) -> Result { let mut f = File::open(path.clone()) .map_err(|err| EvalAltResult::ErrorReadingScriptFile(path.clone(), err))?; @@ -127,12 +130,14 @@ impl<'e> Engine<'e> { } /// Compile a file into an AST. + #[cfg(not(feature = "no_stdlib"))] pub fn compile_file(&self, path: PathBuf) -> Result { self.compile_file_with_scope(&Scope::new(), path) } /// Compile a file into an AST using own scope. /// The scope is useful for passing constants into the script for optimization. + #[cfg(not(feature = "no_stdlib"))] pub fn compile_file_with_scope( &self, scope: &Scope, @@ -145,11 +150,13 @@ impl<'e> Engine<'e> { } /// Evaluate a file. + #[cfg(not(feature = "no_stdlib"))] pub fn eval_file(&mut self, path: PathBuf) -> Result { Self::read_file(path).and_then(|contents| self.eval::(&contents)) } /// Evaluate a file with own scope. + #[cfg(not(feature = "no_stdlib"))] pub fn eval_file_with_scope( &mut self, scope: &mut Scope, @@ -234,6 +241,7 @@ impl<'e> Engine<'e> { /// /// Note - if `retain_functions` is set to `true`, functions defined by previous scripts are _retained_ /// and not cleared from run to run. + #[cfg(not(feature = "no_stdlib"))] pub fn consume_file( &mut self, retain_functions: bool, @@ -247,6 +255,7 @@ impl<'e> Engine<'e> { /// /// Note - if `retain_functions` is set to `true`, functions defined by previous scripts are _retained_ /// and not cleared from run to run. + #[cfg(not(feature = "no_stdlib"))] pub fn consume_file_with_scope( &mut self, scope: &mut Scope, diff --git a/src/builtin.rs b/src/builtin.rs index b85cf306..1214c1b0 100644 --- a/src/builtin.rs +++ b/src/builtin.rs @@ -17,9 +17,12 @@ use num_traits::{ CheckedShr, CheckedSub, }; -use std::{ +use crate::stdlib::{ + boxed::Box, fmt::{Debug, Display}, + format, ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Range, Rem, Shl, Shr, Sub}, + string::{String, ToString}, {i32, i64, u32}, }; diff --git a/src/call.rs b/src/call.rs index 2a2822eb..a4dd7c90 100644 --- a/src/call.rs +++ b/src/call.rs @@ -1,6 +1,7 @@ //! Helper module which defines `FnArgs` to make function calling easier. use crate::any::{Any, Dynamic}; +use crate::stdlib::{string::String, vec, vec::Vec}; #[cfg(not(feature = "no_index"))] use crate::engine::Array; diff --git a/src/engine.rs b/src/engine.rs index bee73cff..9b239e96 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -11,12 +11,17 @@ use crate::optimize::OptimizationLevel; #[cfg(not(feature = "no_index"))] use crate::INT; -use std::{ +use crate::stdlib::{ any::{type_name, TypeId}, borrow::Cow, + boxed::Box, collections::HashMap, + format, iter::once, + string::{String, ToString}, sync::Arc, + vec, + vec::Vec, }; /// An dynamic array of `Dynamic` values. diff --git a/src/error.rs b/src/error.rs index a582bcb2..166108c8 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,7 +1,7 @@ //! Module containing error definitions for the parsing process. use crate::parser::Position; -use std::{char, error::Error, fmt}; +use crate::stdlib::{char, error::Error, fmt, string::String}; /// Error when tokenizing the script text. #[derive(Debug, Eq, PartialEq, Hash, Clone)] diff --git a/src/fn_register.rs b/src/fn_register.rs index 12f15047..d05048a9 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -4,7 +4,7 @@ use crate::any::{Any, Dynamic}; use crate::engine::{Engine, FnCallArgs}; use crate::parser::Position; use crate::result::EvalAltResult; -use std::any::TypeId; +use crate::stdlib::{any::TypeId, boxed::Box, string::ToString, vec}; /// A trait to register custom functions with the `Engine`. /// diff --git a/src/lib.rs b/src/lib.rs index ceb34396..1e8aa47c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ //! //! And the Rust part: //! -//! ```rust,no_run +//! ```rust,ignore //! use rhai::{Engine, EvalAltResult, RegisterFn}; //! //! fn main() -> Result<(), EvalAltResult> @@ -37,8 +37,12 @@ //! //! [Check out the README on GitHub for more information!](https://github.com/jonathandturner/rhai) +#![cfg_attr(feature = "no_stdlib", no_std)] #![allow(non_snake_case)] +#[cfg(feature = "no_stdlib")] +extern crate alloc; + // needs to be here, because order matters for macros macro_rules! debug_println { () => ( @@ -61,6 +65,8 @@ macro_rules! debug_println { ); } +#[macro_use] +mod stdlib; mod any; mod api; mod builtin; diff --git a/src/optimize.rs b/src/optimize.rs index 8a75a905..b3e11046 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -5,7 +5,11 @@ use crate::engine::{Engine, FnCallArgs, KEYWORD_DEBUG, KEYWORD_DUMP_AST, KEYWORD use crate::parser::{map_dynamic_to_expr, Expr, FnDef, Stmt, AST}; use crate::scope::{Scope, ScopeEntry, VariableType}; -use std::sync::Arc; +use crate::stdlib::{ + sync::Arc, + vec::Vec, string::{String, ToString}, + boxed::Box, vec, +}; /// Level of optimization performed #[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] diff --git a/src/parser.rs b/src/parser.rs index 366329c3..a66990b6 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -8,9 +8,19 @@ use crate::scope::{Scope, VariableType}; #[cfg(not(feature = "no_optimize"))] use crate::optimize::optimize_ast; -use std::{ - borrow::Cow, char, cmp::Ordering, fmt, iter::Peekable, str::Chars, str::FromStr, sync::Arc, - usize, +use crate::stdlib::{ + borrow::Cow, + boxed::Box, + char, + cmp::Ordering, + fmt, format, + iter::Peekable, + str::Chars, + str::FromStr, + string::{String, ToString}, + sync::Arc, + usize, vec, + vec::Vec, }; /// The system integer type. diff --git a/src/result.rs b/src/result.rs index faa8db7c..12eb9d78 100644 --- a/src/result.rs +++ b/src/result.rs @@ -4,7 +4,14 @@ use crate::any::Dynamic; use crate::error::ParseError; use crate::parser::{Position, INT}; -use std::{error::Error, fmt, path::PathBuf}; +use crate::stdlib::{ + error::Error, + fmt, + string::{String, ToString}, +}; + +#[cfg(not(feature = "no_stdlib"))] +use crate::stdlib::path::PathBuf; /// Evaluation result. /// @@ -47,6 +54,7 @@ pub enum EvalAltResult { /// Wrapped value is the type of the actual result. ErrorMismatchOutputType(String, Position), /// Error reading from a script file. Wrapped value is the path of the script file. + #[cfg(not(feature = "no_stdlib"))] ErrorReadingScriptFile(PathBuf, std::io::Error), /// Inappropriate member access. ErrorDotExpr(String, Position), @@ -93,6 +101,7 @@ impl EvalAltResult { } Self::ErrorAssignmentToConstant(_, _) => "Assignment to a constant variable", Self::ErrorMismatchOutputType(_, _) => "Output type is incorrect", + #[cfg(not(feature = "no_stdlib"))] Self::ErrorReadingScriptFile(_, _) => "Cannot read from script file", Self::ErrorDotExpr(_, _) => "Malformed dot expression", Self::ErrorArithmetic(_, _) => "Arithmetic error", @@ -127,6 +136,7 @@ impl fmt::Display for EvalAltResult { } Self::LoopBreak => write!(f, "{}", desc), Self::Return(_, pos) => write!(f, "{} ({})", desc, pos), + #[cfg(not(feature = "no_stdlib"))] Self::ErrorReadingScriptFile(path, err) => { write!(f, "{} '{}': {}", desc, path.display(), err) } @@ -199,7 +209,9 @@ impl> From for EvalAltResult { impl EvalAltResult { pub fn position(&self) -> Position { match self { - Self::ErrorReadingScriptFile(_, _) | Self::LoopBreak => Position::none(), + #[cfg(not(feature = "no_stdlib"))] + Self::ErrorReadingScriptFile(_, _) => Position::none(), + Self::LoopBreak => Position::none(), Self::ErrorParsing(err) => err.position(), @@ -226,7 +238,9 @@ impl EvalAltResult { pub(crate) fn set_position(&mut self, new_position: Position) { match self { - Self::ErrorReadingScriptFile(_, _) | Self::LoopBreak => (), + #[cfg(not(feature = "no_stdlib"))] + Self::ErrorReadingScriptFile(_, _) => (), + Self::LoopBreak => (), Self::ErrorParsing(ParseError(_, ref mut pos)) | Self::ErrorFunctionNotFound(_, ref mut pos) diff --git a/src/scope.rs b/src/scope.rs index 92c1fe14..e1bedde6 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -3,7 +3,12 @@ use crate::any::{Any, Dynamic}; use crate::parser::{map_dynamic_to_expr, Expr, Position}; -use std::borrow::Cow; +use crate::stdlib::{ + borrow::Cow, + iter, + string::{String, ToString}, + vec::Vec, +}; /// Type of a variable in the Scope. #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] @@ -193,7 +198,7 @@ impl<'a> Scope<'a> { } } -impl<'a, K> std::iter::Extend<(K, VariableType, Dynamic)> for Scope<'a> +impl<'a, K> iter::Extend<(K, VariableType, Dynamic)> for Scope<'a> where K: Into>, { diff --git a/src/stdlib.rs b/src/stdlib.rs new file mode 100644 index 00000000..fa147c7d --- /dev/null +++ b/src/stdlib.rs @@ -0,0 +1,23 @@ +#[cfg(feature = "no_stdlib")] +mod inner { + pub use core::{ + any, arch, array, ascii, cell, char, clone, cmp, convert, default, f32, f64, ffi, fmt, + future, hash, hint, i128, i16, i32, i64, i8, isize, iter, marker, mem, num, ops, option, + panic, pin, prelude, ptr, result, slice, str, task, time, u128, u16, u32, u64, u8, usize, + }; + + pub use alloc::{borrow, boxed, format, string, sync, vec}; + + pub use core_error as error; + + pub mod collections { + pub use hashbrown::HashMap; + } +} + +#[cfg(not(feature = "no_stdlib"))] +mod inner { + pub use std::*; +} + +pub use self::inner::*;