Use SmartString for buffers and literal_syntax can panic.

This commit is contained in:
Stephen Chung 2022-11-22 23:30:43 +08:00
parent 05c7d00a8e
commit d911327242
16 changed files with 225 additions and 174 deletions

View File

@ -163,25 +163,18 @@ impl Engine {
// Active standard keywords cannot be made custom // Active standard keywords cannot be made custom
// Disabled keywords are OK // Disabled keywords are OK
Some(token) if token.is_standard_keyword() => { Some(token) if token.is_standard_keyword() => {
if self.disabled_symbols.is_empty() if !self.disabled_symbols.contains(token.literal_syntax()) {
|| !self.disabled_symbols.contains(&*token.syntax())
{
return Err(format!("'{keyword}' is a reserved keyword")); return Err(format!("'{keyword}' is a reserved keyword"));
} }
} }
// Active standard symbols cannot be made custom // Active standard symbols cannot be made custom
Some(token) if token.is_standard_symbol() => { Some(token) if token.is_standard_symbol() => {
if self.disabled_symbols.is_empty() if !self.disabled_symbols.contains(token.literal_syntax()) {
|| !self.disabled_symbols.contains(&*token.syntax())
{
return Err(format!("'{keyword}' is a reserved operator")); return Err(format!("'{keyword}' is a reserved operator"));
} }
} }
// Active standard symbols cannot be made custom // Active standard symbols cannot be made custom
Some(token) Some(token) if !self.disabled_symbols.contains(token.literal_syntax()) => {
if self.disabled_symbols.is_empty()
|| !self.disabled_symbols.contains(&*token.syntax()) =>
{
return Err(format!("'{keyword}' is a reserved symbol")) return Err(format!("'{keyword}' is a reserved symbol"))
} }
// Disabled symbols are OK // Disabled symbols are OK

View File

@ -688,8 +688,7 @@ impl Engine {
name: &str, name: &str,
module: SharedModule, module: SharedModule,
) { ) {
let separator = crate::tokenizer::Token::DoubleColon.syntax(); let separator = crate::tokenizer::Token::DoubleColon.literal_syntax();
let separator = separator.as_ref();
if name.contains(separator) { if name.contains(separator) {
let mut iter = name.splitn(2, separator); let mut iter = name.splitn(2, separator);

View File

@ -361,7 +361,7 @@ impl fmt::Debug for Expr {
#[cold] #[cold]
#[inline(never)] #[inline(never)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut display_pos = format!(" @ {:?}", self.start_position()); let mut display_pos = self.start_position();
match self { match self {
Self::DynamicConstant(value, ..) => write!(f, "{value:?}"), Self::DynamicConstant(value, ..) => write!(f, "{value:?}"),
@ -395,7 +395,7 @@ impl fmt::Debug for Expr {
write!(f, "{}{}", x.1, Token::DoubleColon.literal_syntax())?; write!(f, "{}{}", x.1, Token::DoubleColon.literal_syntax())?;
let pos = x.1.position(); let pos = x.1.position();
if !pos.is_none() { if !pos.is_none() {
display_pos = format!(" @ {pos:?}"); display_pos = pos;
} }
} }
f.write_str(&x.3)?; f.write_str(&x.3)?;
@ -413,7 +413,7 @@ impl fmt::Debug for Expr {
Self::Stmt(x) => { Self::Stmt(x) => {
let pos = x.span(); let pos = x.span();
if !pos.is_none() { if !pos.is_none() {
display_pos = format!(" @ {pos:?}"); display_pos = pos.start();
} }
f.write_str("ExprStmtBlock")?; f.write_str("ExprStmtBlock")?;
f.debug_list().entries(x.iter()).finish() f.debug_list().entries(x.iter()).finish()
@ -421,7 +421,7 @@ impl fmt::Debug for Expr {
Self::FnCall(x, ..) => fmt::Debug::fmt(x, f), Self::FnCall(x, ..) => fmt::Debug::fmt(x, f),
Self::Index(x, options, pos) => { Self::Index(x, options, pos) => {
if !pos.is_none() { if !pos.is_none() {
display_pos = format!(" @ {pos:?}"); display_pos = *pos;
} }
let mut f = f.debug_struct("Index"); let mut f = f.debug_struct("Index");
@ -434,7 +434,7 @@ impl fmt::Debug for Expr {
} }
Self::Dot(x, options, pos) => { Self::Dot(x, options, pos) => {
if !pos.is_none() { if !pos.is_none() {
display_pos = format!(" @ {pos:?}"); display_pos = *pos;
} }
let mut f = f.debug_struct("Dot"); let mut f = f.debug_struct("Dot");
@ -454,7 +454,7 @@ impl fmt::Debug for Expr {
}; };
if !pos.is_none() { if !pos.is_none() {
display_pos = format!(" @ {pos:?}"); display_pos = *pos;
} }
f.debug_struct(op_name) f.debug_struct(op_name)
@ -466,7 +466,7 @@ impl fmt::Debug for Expr {
Self::Custom(x, ..) => f.debug_tuple("Custom").field(x).finish(), Self::Custom(x, ..) => f.debug_tuple("Custom").field(x).finish(),
}?; }?;
f.write_str(&display_pos) write!(f, " @ {display_pos:?}")
} }
} }

View File

@ -317,7 +317,7 @@ impl Engine {
scope: &mut Scope, scope: &mut Scope,
this_ptr: &mut Dynamic, this_ptr: &mut Dynamic,
expr: &Expr, expr: &Expr,
new_val: &mut Option<(Dynamic, &OpAssignment)>, new_val: Option<(Dynamic, &OpAssignment)>,
) -> RhaiResult { ) -> RhaiResult {
let chain_type = ChainType::from(expr); let chain_type = ChainType::from(expr);
@ -507,7 +507,7 @@ impl Engine {
target: &mut Target, target: &mut Target,
rhs: &Expr, rhs: &Expr,
idx_values: &mut FnArgsVec<Dynamic>, idx_values: &mut FnArgsVec<Dynamic>,
new_val: &mut Option<(Dynamic, &OpAssignment)>, new_val: Option<(Dynamic, &OpAssignment)>,
) -> RhaiResultOf<(Dynamic, bool)> { ) -> RhaiResultOf<(Dynamic, bool)> {
let is_ref_mut = target.is_ref(); let is_ref_mut = target.is_ref();
let op_pos = parent.position(); let op_pos = parent.position();
@ -576,7 +576,7 @@ impl Engine {
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
self.run_debugger(global, caches, scope, this_ptr, parent)?; self.run_debugger(global, caches, scope, this_ptr, parent)?;
let (new_val, op_info) = new_val.take().expect("`Some`"); let (new_val, op_info) = new_val.expect("`Some`");
let idx_val = &mut idx_values.pop().unwrap(); let idx_val = &mut idx_values.pop().unwrap();
let idx = &mut idx_val.clone(); let idx = &mut idx_val.clone();
@ -686,12 +686,12 @@ impl Engine {
unreachable!("function call in dot chain should not be namespace-qualified") unreachable!("function call in dot chain should not be namespace-qualified")
} }
// {xxx:map}.id op= ??? // {xxx:map}.id op= ???
Expr::Property(x, pos) if target.is_map() && new_val.is_some() => { Expr::Property(x, pos) if new_val.is_some() && target.is_map() => {
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
self.run_debugger(global, caches, scope, this_ptr, rhs)?; self.run_debugger(global, caches, scope, this_ptr, rhs)?;
let index = &mut x.2.clone().into(); let index = &mut x.2.clone().into();
let (new_val, op_info) = new_val.take().expect("`Some`"); let (new_val, op_info) = new_val.expect("`Some`");
{ {
let val_target = &mut self.get_indexed_mut( let val_target = &mut self.get_indexed_mut(
global, caches, target, index, *pos, op_pos, true, false, global, caches, target, index, *pos, op_pos, true, false,
@ -720,7 +720,7 @@ impl Engine {
self.run_debugger(global, caches, scope, this_ptr, rhs)?; self.run_debugger(global, caches, scope, this_ptr, rhs)?;
let ((getter, hash_get), (setter, hash_set), name) = &**x; let ((getter, hash_get), (setter, hash_set), name) = &**x;
let (mut new_val, op_info) = new_val.take().expect("`Some`"); let (mut new_val, op_info) = new_val.expect("`Some`");
if op_info.is_op_assignment() { if op_info.is_op_assignment() {
let args = &mut [target.as_mut()]; let args = &mut [target.as_mut()];

View File

@ -8,7 +8,7 @@ use std::borrow::Borrow;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
impl Engine { impl Dynamic {
/// Recursively calculate the sizes of a value. /// Recursively calculate the sizes of a value.
/// ///
/// Sizes returned are `(` [`Array`][crate::Array], [`Map`][crate::Map] and [`String`] `)`. /// Sizes returned are `(` [`Array`][crate::Array], [`Map`][crate::Map] and [`String`] `)`.
@ -16,20 +16,20 @@ impl Engine {
/// # Panics /// # Panics
/// ///
/// Panics if any interior data is shared (should never happen). /// Panics if any interior data is shared (should never happen).
pub(crate) fn calc_data_sizes(value: &Dynamic, _top: bool) -> (usize, usize, usize) { pub(crate) fn calc_data_sizes(&self, _top: bool) -> (usize, usize, usize) {
match value.0 { match self.0 {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(ref arr, ..) => { Union::Array(ref arr, ..) => {
arr.iter() arr.iter()
.fold((0, 0, 0), |(ax, mx, sx), value| match value.0 { .fold((0, 0, 0), |(ax, mx, sx), value| match value.0 {
Union::Array(..) => { Union::Array(..) => {
let (a, m, s) = Self::calc_data_sizes(value, false); let (a, m, s) = value.calc_data_sizes(false);
(ax + a + 1, mx + m, sx + s) (ax + a + 1, mx + m, sx + s)
} }
Union::Blob(ref a, ..) => (ax + 1 + a.len(), mx, sx), Union::Blob(ref a, ..) => (ax + 1 + a.len(), mx, sx),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(..) => { Union::Map(..) => {
let (a, m, s) = Self::calc_data_sizes(value, false); let (a, m, s) = value.calc_data_sizes(false);
(ax + a + 1, mx + m, sx + s) (ax + a + 1, mx + m, sx + s)
} }
Union::Str(ref s, ..) => (ax + 1, mx, sx + s.len()), Union::Str(ref s, ..) => (ax + 1, mx, sx + s.len()),
@ -44,13 +44,13 @@ impl Engine {
.fold((0, 0, 0), |(ax, mx, sx), value| match value.0 { .fold((0, 0, 0), |(ax, mx, sx), value| match value.0 {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(..) => { Union::Array(..) => {
let (a, m, s) = Self::calc_data_sizes(value, false); let (a, m, s) = value.calc_data_sizes(false);
(ax + a, mx + m + 1, sx + s) (ax + a, mx + m + 1, sx + s)
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Blob(ref a, ..) => (ax + a.len(), mx, sx), Union::Blob(ref a, ..) => (ax + a.len(), mx, sx),
Union::Map(..) => { Union::Map(..) => {
let (a, m, s) = Self::calc_data_sizes(value, false); let (a, m, s) = value.calc_data_sizes(false);
(ax + a, mx + m + 1, sx + s) (ax + a, mx + m + 1, sx + s)
} }
Union::Str(ref s, ..) => (ax, mx + 1, sx + s.len()), Union::Str(ref s, ..) => (ax, mx + 1, sx + s.len()),
@ -59,17 +59,17 @@ impl Engine {
} }
Union::Str(ref s, ..) => (0, 0, s.len()), Union::Str(ref s, ..) => (0, 0, s.len()),
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(..) if _top => { Union::Shared(..) if _top => self.read_lock::<Dynamic>().unwrap().calc_data_sizes(true),
Self::calc_data_sizes(&*value.read_lock::<Dynamic>().unwrap(), true)
}
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(..) => { Union::Shared(..) => {
unreachable!("shared values discovered within data: {}", value) unreachable!("shared values discovered within data: {}", self)
} }
_ => (0, 0, 0), _ => (0, 0, 0),
} }
} }
}
impl Engine {
/// Raise an error if any data size exceeds limit. /// Raise an error if any data size exceeds limit.
/// ///
/// [`Position`] in [`EvalAltResult`][crate::EvalAltResult] is always [`NONE`][Position::NONE] /// [`Position`] in [`EvalAltResult`][crate::EvalAltResult] is always [`NONE`][Position::NONE]
@ -125,7 +125,7 @@ impl Engine {
return Ok(value); return Ok(value);
} }
let sizes = Self::calc_data_sizes(value.borrow(), true); let sizes = value.borrow().calc_data_sizes(true);
self.raise_err_if_over_data_size_limit(sizes) self.raise_err_if_over_data_size_limit(sizes)
.map(|_| value) .map(|_| value)

View File

@ -307,7 +307,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if self.has_data_size_limit() { if self.has_data_size_limit() {
let val_sizes = Self::calc_data_sizes(&value, true); let val_sizes = value.calc_data_sizes(true);
total_data_sizes = ( total_data_sizes = (
total_data_sizes.0 + val_sizes.0, total_data_sizes.0 + val_sizes.0,
@ -339,7 +339,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if self.has_data_size_limit() { if self.has_data_size_limit() {
let delta = Self::calc_data_sizes(&value, true); let delta = value.calc_data_sizes(true);
total_data_sizes = ( total_data_sizes = (
total_data_sizes.0 + delta.0, total_data_sizes.0 + delta.0,
total_data_sizes.1 + delta.1, total_data_sizes.1 + delta.1,
@ -411,13 +411,11 @@ impl Engine {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(..) => { Expr::Index(..) => {
self.eval_dot_index_chain(global, caches, scope, this_ptr, expr, &mut None) self.eval_dot_index_chain(global, caches, scope, this_ptr, expr, None)
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(..) => { Expr::Dot(..) => self.eval_dot_index_chain(global, caches, scope, this_ptr, expr, None),
self.eval_dot_index_chain(global, caches, scope, this_ptr, expr, &mut None)
}
_ => unreachable!("expression cannot be evaluated: {:?}", expr), _ => unreachable!("expression cannot be evaluated: {:?}", expr),
} }

View File

@ -21,7 +21,9 @@ pub use eval_context::EvalContext;
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
pub use global_state::GlobalConstants; pub use global_state::GlobalConstants;
pub use global_state::GlobalRuntimeState; pub use global_state::GlobalRuntimeState;
pub use target::{calc_index, calc_offset_len, Target}; #[cfg(not(feature = "no_index"))]
pub use target::calc_offset_len;
pub use target::{calc_index, Target};
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
mod unchecked { mod unchecked {

View File

@ -258,7 +258,7 @@ impl Engine {
rhs_val = self.get_interned_string(value).into(); rhs_val = self.get_interned_string(value).into();
} }
let _new_val = &mut Some((rhs_val, op_info)); let _new_val = Some((rhs_val, op_info));
// Must be either `var[index] op= val` or `var.prop op= val` // Must be either `var[index] op= val` or `var.prop op= val`
match lhs { match lhs {

View File

@ -13,8 +13,8 @@ use std::{
/// Negative starting positions count from the end. /// Negative starting positions count from the end.
/// ///
/// Values going over bounds are limited to the actual length. /// Values going over bounds are limited to the actual length.
#[cfg(not(feature = "no_index"))]
#[inline] #[inline]
#[allow(dead_code)]
pub fn calc_offset_len(length: usize, start: crate::INT, len: crate::INT) -> (usize, usize) { pub fn calc_offset_len(length: usize, start: crate::INT, len: crate::INT) -> (usize, usize) {
let start = if start < 0 { let start = if start < 0 {
let abs_start = start.unsigned_abs(); let abs_start = start.unsigned_abs();

View File

@ -4,11 +4,12 @@ use super::call::FnCallArgs;
use super::native::FnBuiltin; use super::native::FnBuiltin;
use crate::tokenizer::{Token, Token::*}; use crate::tokenizer::{Token, Token::*};
use crate::{ use crate::{
Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, NativeCallContext, RhaiResult, INT, Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, NativeCallContext, RhaiResult,
SmartString, INT,
}; };
use std::any::TypeId;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{any::TypeId, fmt::Write};
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
use crate::FLOAT; use crate::FLOAT;
@ -241,7 +242,9 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
let x = args[0].as_char().expect(BUILTIN); let x = args[0].as_char().expect(BUILTIN);
let y = args[1].as_char().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN);
let result = format!("{x}{y}"); let mut result = SmartString::new_const();
result.push(x);
result.push(y);
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
_ctx.engine() _ctx.engine()
@ -399,7 +402,10 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
Plus => Some(|_ctx, args| { Plus => Some(|_ctx, args| {
let x = args[0].as_char().expect(BUILTIN); let x = args[0].as_char().expect(BUILTIN);
let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN); let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
let result = format!("{x}{y}");
let mut result = SmartString::new_const();
result.push(x);
result.push_str(y);
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
_ctx.engine() _ctx.engine()
@ -679,7 +685,12 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
PlusAssign => Some(|_, args| { PlusAssign => Some(|_, args| {
let y = args[1].as_char().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN);
let x = &mut *args[0].write_lock::<Dynamic>().expect(BUILTIN); let x = &mut *args[0].write_lock::<Dynamic>().expect(BUILTIN);
Ok((*x = format!("{x}{y}").into()).into())
let mut buf = SmartString::new_const();
write!(&mut buf, "{y}").unwrap();
buf.push(y);
Ok((*x = buf.into()).into())
}), }),
_ => None, _ => None,
}; };

View File

@ -247,8 +247,8 @@ pub mod array_functions {
let mut arr_len = array.len(); let mut arr_len = array.len();
let mut arr = Dynamic::from_array(mem::take(array)); let mut arr = Dynamic::from_array(mem::take(array));
let (mut a1, mut m1, mut s1) = crate::Engine::calc_data_sizes(&arr, true); let (mut a1, mut m1, mut s1) = arr.calc_data_sizes(true);
let (a2, m2, s2) = crate::Engine::calc_data_sizes(&item, true); let (a2, m2, s2) = item.calc_data_sizes(true);
{ {
let mut guard = arr.write_lock::<Array>().unwrap(); let mut guard = arr.write_lock::<Array>().unwrap();

View File

@ -269,9 +269,13 @@ fn collect_fn_metadata(
.iter_script_fn() .iter_script_fn()
.filter(|(s, a, n, p, f)| filter(*s, *a, n, *p, f)) .filter(|(s, a, n, p, f)| filter(*s, *a, n, *p, f))
.for_each(|(.., f)| list.push(make_metadata(dict, namespace.into(), f).into())); .for_each(|(.., f)| list.push(make_metadata(dict, namespace.into(), f).into()));
for (ns, m) in module.iter_sub_modules() { for (name, m) in module.iter_sub_modules() {
let ns = format!( use std::fmt::Write;
"{namespace}{}{ns}",
let mut ns = crate::SmartString::new_const();
write!(
&mut ns,
"{namespace}{}{name}",
crate::tokenizer::Token::DoubleColon.literal_syntax() crate::tokenizer::Token::DoubleColon.literal_syntax()
); );
scan_module(dict, list, &ns, &**m, filter); scan_module(dict, list, &ns, &**m, filter);

View File

@ -1,7 +1,7 @@
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, FnPtr, INT}; use crate::{def_package, FnPtr, SmartString, INT};
use std::any::TypeId; use std::any::TypeId;
use std::fmt::{Binary, LowerHex, Octal}; use std::fmt::{Binary, LowerHex, Octal, Write};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -69,7 +69,9 @@ mod print_debug_functions {
/// Convert the value of the `item` into a string in debug format. /// Convert the value of the `item` into a string in debug format.
#[rhai_fn(name = "to_debug", pure)] #[rhai_fn(name = "to_debug", pure)]
pub fn to_debug_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString { pub fn to_debug_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
ctx.engine().map_type_name(&format!("{item:?}")).into() let mut buf = SmartString::new_const();
write!(&mut buf, "{item:?}").unwrap();
ctx.engine().map_type_name(&buf).into()
} }
/// Return the empty string. /// Return the empty string.
@ -86,7 +88,9 @@ mod print_debug_functions {
/// Convert the string into debug format. /// Convert the string into debug format.
#[rhai_fn(name = "debug", name = "to_debug", pure)] #[rhai_fn(name = "debug", name = "to_debug", pure)]
pub fn debug_string(string: &mut ImmutableString) -> ImmutableString { pub fn debug_string(string: &mut ImmutableString) -> ImmutableString {
format!("{string:?}").into() let mut buf = SmartString::new_const();
write!(&mut buf, "{string:?}").unwrap();
buf.into()
} }
/// Return the character into a string. /// Return the character into a string.
@ -97,7 +101,9 @@ mod print_debug_functions {
/// Convert the string into debug format. /// Convert the string into debug format.
#[rhai_fn(name = "debug", name = "to_debug")] #[rhai_fn(name = "debug", name = "to_debug")]
pub fn debug_char(character: char) -> ImmutableString { pub fn debug_char(character: char) -> ImmutableString {
format!("{character:?}").into() let mut buf = SmartString::new_const();
buf.push(character);
buf.into()
} }
/// Convert the function pointer into a string in debug format. /// Convert the function pointer into a string in debug format.
@ -114,7 +120,9 @@ mod print_debug_functions {
/// Convert the boolean value into a string in debug format. /// Convert the boolean value into a string in debug format.
#[rhai_fn(name = "debug", name = "to_debug")] #[rhai_fn(name = "debug", name = "to_debug")]
pub fn debug_bool(value: bool) -> ImmutableString { pub fn debug_bool(value: bool) -> ImmutableString {
format!("{value:?}").into() let mut buf = SmartString::new_const();
write!(&mut buf, "{value:?}").unwrap();
buf.into()
} }
/// Return the empty string. /// Return the empty string.
@ -147,14 +155,18 @@ mod print_debug_functions {
#[rhai_fn(name = "debug", name = "to_debug")] #[rhai_fn(name = "debug", name = "to_debug")]
pub fn debug_f64(number: f64) -> ImmutableString { pub fn debug_f64(number: f64) -> ImmutableString {
let number = crate::types::FloatWrapper::new(number); let number = crate::types::FloatWrapper::new(number);
format!("{number:?}").into() let mut buf = SmartString::new_const();
write!(&mut buf, "{number:?}").unwrap();
buf.into()
} }
/// Convert the value of `number` into a string. /// Convert the value of `number` into a string.
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[rhai_fn(name = "debug", name = "to_debug")] #[rhai_fn(name = "debug", name = "to_debug")]
pub fn debug_f32(number: f32) -> ImmutableString { pub fn debug_f32(number: f32) -> ImmutableString {
let number = crate::types::FloatWrapper::new(number); let number = crate::types::FloatWrapper::new(number);
format!("{number:?}").into() let mut buf = SmartString::new_const();
write!(&mut buf, "{number:?}").unwrap();
buf.into()
} }
/// Convert the array into a string. /// Convert the array into a string.
@ -217,13 +229,19 @@ mod print_debug_functions {
#[export_module] #[export_module]
mod number_formatting { mod number_formatting {
fn to_hex<T: LowerHex>(value: T) -> ImmutableString { fn to_hex<T: LowerHex>(value: T) -> ImmutableString {
format!("{value:x}").into() let mut buf = SmartString::new_const();
write!(&mut buf, "{value:x}").unwrap();
buf.into()
} }
fn to_octal<T: Octal>(value: T) -> ImmutableString { fn to_octal<T: Octal>(value: T) -> ImmutableString {
format!("{value:o}").into() let mut buf = SmartString::new_const();
write!(&mut buf, "{value:o}").unwrap();
buf.into()
} }
fn to_binary<T: Binary>(value: T) -> ImmutableString { fn to_binary<T: Binary>(value: T) -> ImmutableString {
format!("{value:b}").into() let mut buf = SmartString::new_const();
write!(&mut buf, "{value:b}").unwrap();
buf.into()
} }
/// Convert the `value` into a string in hex format. /// Convert the `value` into a string in hex format.

View File

@ -33,7 +33,9 @@ mod string_functions {
if s.is_empty() { if s.is_empty() {
string.clone() string.clone()
} else { } else {
format!("{string}{s}").into() let mut buf = SmartString::from(string.as_str());
buf.push_str(&s);
buf.into()
} }
} }
#[rhai_fn(name = "+=", name = "append")] #[rhai_fn(name = "+=", name = "append")]
@ -41,7 +43,9 @@ mod string_functions {
let s = print_with_func(FUNC_TO_STRING, &ctx, &mut item); let s = print_with_func(FUNC_TO_STRING, &ctx, &mut item);
if !s.is_empty() { if !s.is_empty() {
*string = format!("{string}{s}").into(); let mut buf = SmartString::from(string.as_str());
buf.push_str(&s);
*string = buf.into();
} }
} }
#[rhai_fn(name = "+", pure)] #[rhai_fn(name = "+", pure)]
@ -74,7 +78,10 @@ mod string_functions {
} }
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]
pub fn add_prepend_char(character: char, string: &str) -> ImmutableString { pub fn add_prepend_char(character: char, string: &str) -> ImmutableString {
format!("{character}{string}").into() let mut buf = SmartString::new_const();
buf.push(character);
buf.push_str(string);
buf.into()
} }
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]

View File

@ -333,8 +333,12 @@ impl ParseSettings {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline] #[inline]
#[must_use] #[must_use]
pub fn make_anonymous_fn(hash: u64) -> String { pub fn make_anonymous_fn(hash: u64) -> Identifier {
format!("{}{:016x}", crate::engine::FN_ANONYMOUS, hash) use std::fmt::Write;
let mut buf = Identifier::new_const();
write!(&mut buf, "{}{:016x}", crate::engine::FN_ANONYMOUS, hash).unwrap();
buf
} }
/// Is this function an anonymous function? /// Is this function an anonymous function?
@ -387,7 +391,7 @@ impl Expr {
}; };
Err( Err(
PERR::MismatchedType("a boolean expression".to_string(), type_name.to_string()) PERR::MismatchedType("a boolean expression".into(), type_name.into())
.into_err(self.start_position()), .into_err(self.start_position()),
) )
} }
@ -405,7 +409,7 @@ impl Expr {
}; };
Err( Err(
PERR::MismatchedType("an iterable value".to_string(), type_name.to_string()) PERR::MismatchedType("an iterable value".into(), type_name.into())
.into_err(self.start_position()), .into_err(self.start_position()),
) )
} }
@ -426,8 +430,8 @@ fn ensure_not_statement_expr(
fn ensure_not_assignment(input: &mut TokenStream) -> ParseResult<()> { fn ensure_not_assignment(input: &mut TokenStream) -> ParseResult<()> {
match input.peek().expect(NEVER_ENDS) { match input.peek().expect(NEVER_ENDS) {
(Token::Equals, pos) => Err(LexError::ImproperSymbol( (Token::Equals, pos) => Err(LexError::ImproperSymbol(
"=".to_string(), "=".into(),
"Possibly a typo of '=='?".to_string(), "Possibly a typo of '=='?".into(),
) )
.into_err(*pos)), .into_err(*pos)),
_ => Ok(()), _ => Ok(()),
@ -438,15 +442,15 @@ fn ensure_not_assignment(input: &mut TokenStream) -> ParseResult<()> {
/// ///
/// # Panics /// # Panics
/// ///
/// Panics if the next token is not the expected one. /// Panics if the next token is not the expected one, or either tokens is not a literal symbol.
fn eat_token(input: &mut TokenStream, expected_token: Token) -> Position { fn eat_token(input: &mut TokenStream, expected_token: Token) -> Position {
let (t, pos) = input.next().expect(NEVER_ENDS); let (t, pos) = input.next().expect(NEVER_ENDS);
if t != expected_token { if t != expected_token {
unreachable!( unreachable!(
"{} expected but gets {} at {}", "{} expected but gets {} at {}",
expected_token.syntax(), expected_token.literal_syntax(),
t.syntax(), t.literal_syntax(),
pos pos
); );
} }
@ -588,7 +592,7 @@ impl Engine {
&& !self.global_sub_modules.contains_key(root) && !self.global_sub_modules.contains_key(root)
{ {
return Err( return Err(
PERR::ModuleUndefined(root.to_string()).into_err(namespace.position()) PERR::ModuleUndefined(root.into()).into_err(namespace.position())
); );
} }
@ -655,8 +659,9 @@ impl Engine {
&& !state.global_imports.iter().any(|m| m.as_str() == root) && !state.global_imports.iter().any(|m| m.as_str() == root)
&& !self.global_sub_modules.contains_key(root) && !self.global_sub_modules.contains_key(root)
{ {
return Err(PERR::ModuleUndefined(root.to_string()) return Err(
.into_err(namespace.position())); PERR::ModuleUndefined(root.into()).into_err(namespace.position())
);
} }
namespace.set_index(index); namespace.set_index(index);
@ -901,7 +906,7 @@ impl Engine {
if self.max_array_size() > 0 && array.len() >= self.max_array_size() { if self.max_array_size() > 0 && array.len() >= self.max_array_size() {
return Err(PERR::LiteralTooLarge( return Err(PERR::LiteralTooLarge(
"Size of array literal".to_string(), "Size of array literal".into(),
self.max_array_size(), self.max_array_size(),
) )
.into_err(input.peek().expect(NEVER_ENDS).1)); .into_err(input.peek().expect(NEVER_ENDS).1));
@ -1037,7 +1042,7 @@ impl Engine {
if self.max_map_size() > 0 && map.len() >= self.max_map_size() { if self.max_map_size() > 0 && map.len() >= self.max_map_size() {
return Err(PERR::LiteralTooLarge( return Err(PERR::LiteralTooLarge(
"Number of properties in object map literal".to_string(), "Number of properties in object map literal".into(),
self.max_map_size(), self.max_map_size(),
) )
.into_err(input.peek().expect(NEVER_ENDS).1)); .into_err(input.peek().expect(NEVER_ENDS).1));
@ -1185,7 +1190,7 @@ impl Engine {
(.., pos) => { (.., pos) => {
return Err(PERR::MissingToken( return Err(PERR::MissingToken(
Token::DoubleArrow.into(), Token::DoubleArrow.into(),
"in this switch case".to_string(), "in this switch case".into(),
) )
.into_err(pos)) .into_err(pos))
} }
@ -1213,7 +1218,7 @@ impl Engine {
} else { } else {
for expr in case_expr_list { for expr in case_expr_list {
let value = expr.get_literal_value().ok_or_else(|| { let value = expr.get_literal_value().ok_or_else(|| {
PERR::ExprExpected("a literal".to_string()).into_err(expr.start_position()) PERR::ExprExpected("a literal".into()).into_err(expr.start_position())
})?; })?;
let mut range_value: Option<RangeCase> = None; let mut range_value: Option<RangeCase> = None;
@ -1315,9 +1320,7 @@ impl Engine {
let root_expr = match token { let root_expr = match token {
_ if !(state.expr_filter)(token) => { _ if !(state.expr_filter)(token) => {
return Err( return Err(LexError::UnexpectedInput(token.to_string()).into_err(settings.pos))
LexError::UnexpectedInput(token.syntax().to_string()).into_err(settings.pos)
)
} }
Token::EOF => return Err(PERR::UnexpectedEOF.into_err(settings.pos)), Token::EOF => return Err(PERR::UnexpectedEOF.into_err(settings.pos)),
@ -1674,11 +1677,7 @@ impl Engine {
token => unreachable!("Token::LexError expected but gets {:?}", token), token => unreachable!("Token::LexError expected but gets {:?}", token),
}, },
_ => { _ => return Err(LexError::UnexpectedInput(token.to_string()).into_err(settings.pos)),
return Err(
LexError::UnexpectedInput(token.syntax().to_string()).into_err(settings.pos)
)
}
}; };
if !(state.expr_filter)(&input.peek().expect(NEVER_ENDS).0) { if !(state.expr_filter)(&input.peek().expect(NEVER_ENDS).0) {
@ -1716,12 +1715,11 @@ impl Engine {
(Expr::Variable(x, ..), Token::Bang) if !x.1.is_empty() => { (Expr::Variable(x, ..), Token::Bang) if !x.1.is_empty() => {
return match input.peek().expect(NEVER_ENDS) { return match input.peek().expect(NEVER_ENDS) {
(Token::LeftParen | Token::Unit, ..) => { (Token::LeftParen | Token::Unit, ..) => {
Err(LexError::UnexpectedInput(Token::Bang.syntax().to_string()) Err(LexError::UnexpectedInput(Token::Bang.into()).into_err(tail_pos))
.into_err(tail_pos))
} }
_ => Err(LexError::ImproperSymbol( _ => Err(LexError::ImproperSymbol(
"!".to_string(), "!".into(),
"'!' cannot be used to call module functions".to_string(), "'!' cannot be used to call module functions".into(),
) )
.into_err(tail_pos)), .into_err(tail_pos)),
}; };
@ -1732,7 +1730,7 @@ impl Engine {
(Token::LeftParen | Token::Unit, ..) => (), (Token::LeftParen | Token::Unit, ..) => (),
(_, pos) => { (_, pos) => {
return Err(PERR::MissingToken( return Err(PERR::MissingToken(
Token::LeftParen.syntax().into(), Token::LeftParen.into(),
"to start arguments list of function call".into(), "to start arguments list of function call".into(),
) )
.into_err(*pos)) .into_err(*pos))
@ -1821,11 +1819,9 @@ impl Engine {
Self::make_dot_expr(state, expr, rhs, ASTFlags::NONE, op_flags, tail_pos)? Self::make_dot_expr(state, expr, rhs, ASTFlags::NONE, op_flags, tail_pos)?
} }
// Unknown postfix operator // Unknown postfix operator
(expr, token) => unreachable!( (expr, token) => {
"unknown postfix operator '{}' for {:?}", unreachable!("unknown postfix operator '{}' for {:?}", token, expr)
token.syntax(), }
expr
),
} }
} }
@ -1863,7 +1859,7 @@ impl Engine {
&& !self.global_sub_modules.contains_key(root) && !self.global_sub_modules.contains_key(root)
{ {
return Err( return Err(
PERR::ModuleUndefined(root.to_string()).into_err(namespace.position()) PERR::ModuleUndefined(root.into()).into_err(namespace.position())
); );
} }
@ -1890,7 +1886,7 @@ impl Engine {
let (token, token_pos) = input.peek().expect(NEVER_ENDS); let (token, token_pos) = input.peek().expect(NEVER_ENDS);
if !(state.expr_filter)(token) { if !(state.expr_filter)(token) {
return Err(LexError::UnexpectedInput(token.syntax().to_string()).into_err(*token_pos)); return Err(LexError::UnexpectedInput(token.to_string()).into_err(*token_pos));
} }
let mut settings = settings; let mut settings = settings;
@ -2092,8 +2088,8 @@ impl Engine {
} }
// ??? && ??? = rhs, ??? || ??? = rhs, xxx ?? xxx = rhs // ??? && ??? = rhs, ??? || ??? = rhs, xxx ?? xxx = rhs
Expr::And(..) | Expr::Or(..) | Expr::Coalesce(..) => Err(LexError::ImproperSymbol( Expr::And(..) | Expr::Or(..) | Expr::Coalesce(..) => Err(LexError::ImproperSymbol(
"=".to_string(), "=".into(),
"Possibly a typo of '=='?".to_string(), "Possibly a typo of '=='?".into(),
) )
.into_err(op_pos)), .into_err(op_pos)),
// expr = rhs // expr = rhs
@ -2362,7 +2358,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let op = op_token.syntax(); let op = op_token.to_string();
let hash = calc_fn_hash(None, &op, 2); let hash = calc_fn_hash(None, &op, 2);
let is_valid_script_function = is_valid_function_name(&op); let is_valid_script_function = is_valid_function_name(&op);
let operator_token = if is_valid_script_function { let operator_token = if is_valid_script_function {
@ -2379,7 +2375,7 @@ impl Engine {
let mut op_base = FnCallExpr { let mut op_base = FnCallExpr {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
namespace: Default::default(), namespace: Default::default(),
name: state.get_interned_string(op.as_ref()), name: state.get_interned_string(&op),
hashes: FnCallHashes::from_native(hash), hashes: FnCallHashes::from_native(hash),
args, args,
op_token: operator_token, op_token: operator_token,
@ -2492,7 +2488,7 @@ impl Engine {
settings.pos = *fwd_pos; settings.pos = *fwd_pos;
let settings = settings.level_up(); let settings = settings.level_up();
required_token = match parse_func(&segments, &*fwd_token.syntax(), &mut user_state) { required_token = match parse_func(&segments, &fwd_token.to_string(), &mut user_state) {
Ok(Some(seg)) Ok(Some(seg))
if seg.starts_with(CUSTOM_SYNTAX_MARKER_SYNTAX_VARIANT) if seg.starts_with(CUSTOM_SYNTAX_MARKER_SYNTAX_VARIANT)
&& seg.len() > CUSTOM_SYNTAX_MARKER_SYNTAX_VARIANT.len() => && seg.len() > CUSTOM_SYNTAX_MARKER_SYNTAX_VARIANT.len() =>
@ -2551,8 +2547,7 @@ impl Engine {
} }
(.., pos) => { (.., pos) => {
return Err( return Err(
PERR::MissingSymbol("Expecting 'true' or 'false'".to_string()) PERR::MissingSymbol("Expecting 'true' or 'false'".into()).into_err(pos)
.into_err(pos),
) )
} }
}, },
@ -2564,8 +2559,7 @@ impl Engine {
} }
(.., pos) => { (.., pos) => {
return Err( return Err(
PERR::MissingSymbol("Expecting an integer number".to_string()) PERR::MissingSymbol("Expecting an integer number".into()).into_err(pos)
.into_err(pos),
) )
} }
}, },
@ -2577,10 +2571,10 @@ impl Engine {
tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_FLOAT)); tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_FLOAT));
} }
(.., pos) => { (.., pos) => {
return Err(PERR::MissingSymbol( return Err(
"Expecting a floating-point number".to_string(), PERR::MissingSymbol("Expecting a floating-point number".into())
.into_err(pos),
) )
.into_err(pos))
} }
}, },
CUSTOM_SYNTAX_MARKER_STRING => match input.next().expect(NEVER_ENDS) { CUSTOM_SYNTAX_MARKER_STRING => match input.next().expect(NEVER_ENDS) {
@ -2591,20 +2585,26 @@ impl Engine {
tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_STRING)); tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_STRING));
} }
(.., pos) => { (.., pos) => {
return Err( return Err(PERR::MissingSymbol("Expecting a string".into()).into_err(pos))
PERR::MissingSymbol("Expecting a string".to_string()).into_err(pos)
)
} }
}, },
s => match input.next().expect(NEVER_ENDS) { s => match input.next().expect(NEVER_ENDS) {
(Token::LexError(err), pos) => return Err(err.into_err(pos)), (Token::LexError(err), pos) => return Err(err.into_err(pos)),
(t, ..) if &*t.syntax() == s => { (Token::Identifier(t), ..)
| (Token::Reserved(t), ..)
| (Token::Custom(t), ..)
if *t == s =>
{
segments.push(required_token.clone());
tokens.push(required_token.clone().into());
}
(t, ..) if t.is_literal() && t.literal_syntax() == s => {
segments.push(required_token.clone()); segments.push(required_token.clone());
tokens.push(required_token.clone().into()); tokens.push(required_token.clone().into());
} }
(.., pos) => { (.., pos) => {
return Err(PERR::MissingToken( return Err(PERR::MissingToken(
s.to_string(), s.into(),
format!("for '{}' expression", segments[0]), format!("for '{}' expression", segments[0]),
) )
.into_err(pos)) .into_err(pos))
@ -2805,9 +2805,7 @@ impl Engine {
let (counter_name, counter_pos) = parse_var_name(input)?; let (counter_name, counter_pos) = parse_var_name(input)?;
if counter_name == name { if counter_name == name {
return Err( return Err(PERR::DuplicatedVariable(counter_name.into()).into_err(counter_pos));
PERR::DuplicatedVariable(counter_name.to_string()).into_err(counter_pos)
);
} }
let (has_close_paren, pos) = match_token(input, Token::RightParen); let (has_close_paren, pos) = match_token(input, Token::RightParen);
@ -2893,7 +2891,7 @@ impl Engine {
let (name, pos) = parse_var_name(input)?; let (name, pos) = parse_var_name(input)?;
if !self.allow_shadowing() && state.stack.iter().any(|(v, ..)| v == name) { if !self.allow_shadowing() && state.stack.iter().any(|(v, ..)| v == name) {
return Err(PERR::VariableExists(name.to_string()).into_err(pos)); return Err(PERR::VariableExists(name.into()).into_err(pos));
} }
if let Some(ref filter) = self.def_var_filter { if let Some(ref filter) = self.def_var_filter {
@ -2914,10 +2912,10 @@ impl Engine {
match filter(false, info, context) { match filter(false, info, context) {
Ok(true) => (), Ok(true) => (),
Ok(false) => return Err(PERR::ForbiddenVariable(name.to_string()).into_err(pos)), Ok(false) => return Err(PERR::ForbiddenVariable(name.into()).into_err(pos)),
Err(err) => match *err { Err(err) => match *err {
EvalAltResult::ErrorParsing(e, pos) => return Err(e.into_err(pos)), EvalAltResult::ErrorParsing(e, pos) => return Err(e.into_err(pos)),
_ => return Err(PERR::ForbiddenVariable(name.to_string()).into_err(pos)), _ => return Err(PERR::ForbiddenVariable(name.into()).into_err(pos)),
}, },
} }
} }
@ -3361,7 +3359,7 @@ impl Engine {
(.., pos) => Err(PERR::MissingToken( (.., pos) => Err(PERR::MissingToken(
Token::Fn.into(), Token::Fn.into(),
format!("following '{}'", Token::Private.syntax()), format!("following '{}'", Token::Private),
) )
.into_err(pos)), .into_err(pos)),
} }
@ -3564,7 +3562,7 @@ impl Engine {
eat_token(input, Token::Unit); eat_token(input, Token::Unit);
true true
} }
(.., pos) => return Err(PERR::FnMissingParams(name.to_string()).into_err(*pos)), (.., pos) => return Err(PERR::FnMissingParams(name.into()).into_err(*pos)),
}; };
let mut params = StaticVec::<(ImmutableString, _)>::new_const(); let mut params = StaticVec::<(ImmutableString, _)>::new_const();
@ -3577,8 +3575,9 @@ impl Engine {
(Token::RightParen, ..) => break, (Token::RightParen, ..) => break,
(Token::Identifier(s), pos) => { (Token::Identifier(s), pos) => {
if params.iter().any(|(p, _)| p.as_str() == &*s) { if params.iter().any(|(p, _)| p.as_str() == &*s) {
return Err(PERR::FnDuplicatedParam(name.to_string(), s.to_string()) return Err(
.into_err(pos)); PERR::FnDuplicatedParam(name.into(), s.to_string()).into_err(pos)
);
} }
let s = state.get_interned_string(*s); let s = state.get_interned_string(*s);
state.stack.push(s.clone(), ()); state.stack.push(s.clone(), ());
@ -3611,7 +3610,7 @@ impl Engine {
settings.is_breakable = false; settings.is_breakable = false;
self.parse_block(input, state, lib, settings.level_up())? self.parse_block(input, state, lib, settings.level_up())?
} }
(.., pos) => return Err(PERR::FnMissingBody(name.to_string()).into_err(*pos)), (.., pos) => return Err(PERR::FnMissingBody(name.into()).into_err(*pos)),
} }
.into(); .into();
@ -3851,9 +3850,7 @@ impl Engine {
match input.peek().expect(NEVER_ENDS) { match input.peek().expect(NEVER_ENDS) {
(Token::EOF, ..) => (), (Token::EOF, ..) => (),
// Return error if the expression doesn't end // Return error if the expression doesn't end
(token, pos) => { (token, pos) => return Err(LexError::UnexpectedInput(token.to_string()).into_err(*pos)),
return Err(LexError::UnexpectedInput(token.syntax().to_string()).into_err(*pos))
}
} }
let mut statements = StmtBlockContainer::new_const(); let mut statements = StmtBlockContainer::new_const();

View File

@ -9,7 +9,6 @@ use crate::{Engine, Identifier, LexError, SmartString, StaticVec, INT, UNSIGNED_
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{ use std::{
borrow::Cow,
cell::RefCell, cell::RefCell,
char, fmt, char, fmt,
iter::{FusedIterator, Peekable}, iter::{FusedIterator, Peekable},
@ -591,12 +590,62 @@ pub enum Token {
impl fmt::Display for Token { impl fmt::Display for Token {
#[inline(always)] #[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.syntax()) use Token::*;
match self {
IntegerConstant(i) => write!(f, "{i}"),
#[cfg(not(feature = "no_float"))]
FloatConstant(v) => write!(f, "{v}"),
#[cfg(feature = "decimal")]
DecimalConstant(d) => write!(f, "{d}"),
StringConstant(s) => write!(f, r#""{s}""#),
InterpolatedString(..) => f.write_str("string"),
CharConstant(c) => write!(f, "{c}"),
Identifier(s) => f.write_str(s),
Reserved(s) => f.write_str(s),
#[cfg(not(feature = "no_custom_syntax"))]
Custom(s) => f.write_str(s),
LexError(err) => write!(f, "{err}"),
Comment(s) => f.write_str(s),
EOF => f.write_str("{EOF}"),
token => f.write_str(token.literal_syntax()),
}
} }
} }
impl Token { impl Token {
/// Is the token a literal symbol?
#[must_use]
pub const fn is_literal(&self) -> bool {
use Token::*;
match self {
IntegerConstant(..) => false,
#[cfg(not(feature = "no_float"))]
FloatConstant(..) => false,
#[cfg(feature = "decimal")]
DecimalConstant(..) => false,
StringConstant(..)
| InterpolatedString(..)
| CharConstant(..)
| Identifier(..)
| Reserved(..) => false,
#[cfg(not(feature = "no_custom_syntax"))]
Custom(..) => false,
LexError(..) | Comment(..) => false,
EOF => false,
_ => true,
}
}
/// Get the literal syntax of the token. /// Get the literal syntax of the token.
///
/// # Panics
///
/// Panics if the token is not a literal symbol.
#[must_use] #[must_use]
pub const fn literal_syntax(&self) -> &'static str { pub const fn literal_syntax(&self) -> &'static str {
use Token::*; use Token::*;
@ -690,34 +739,7 @@ impl Token {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
As => "as", As => "as",
_ => "ERROR: NOT A KEYWORD", _ => panic!("token is not a literal symbol"),
}
}
/// Get the syntax of the token.
#[must_use]
pub fn syntax(&self) -> Cow<'static, str> {
use Token::*;
match self {
IntegerConstant(i) => i.to_string().into(),
#[cfg(not(feature = "no_float"))]
FloatConstant(f) => f.to_string().into(),
#[cfg(feature = "decimal")]
DecimalConstant(d) => d.to_string().into(),
StringConstant(s) => format!("\"{s}\"").into(),
InterpolatedString(..) => "string".into(),
CharConstant(c) => c.to_string().into(),
Identifier(s) => s.to_string().into(),
Reserved(s) => s.to_string().into(),
#[cfg(not(feature = "no_custom_syntax"))]
Custom(s) => s.to_string().into(),
LexError(err) => err.to_string().into(),
Comment(s) => s.to_string().into(),
EOF => "{EOF}".into(),
token => token.literal_syntax().into(),
} }
} }
@ -1127,7 +1149,7 @@ impl Token {
impl From<Token> for String { impl From<Token> for String {
#[inline(always)] #[inline(always)]
fn from(token: Token) -> Self { fn from(token: Token) -> Self {
token.syntax().into() token.to_string()
} }
} }
@ -2399,7 +2421,7 @@ impl<'a> Iterator for TokenIterator<'a> {
Some((Token::Reserved(s), pos)) => (match Some((Token::Reserved(s), pos)) => (match
(s.as_str(), (s.as_str(),
#[cfg(not(feature = "no_custom_syntax"))] #[cfg(not(feature = "no_custom_syntax"))]
(!self.engine.custom_keywords.is_empty() && self.engine.custom_keywords.contains_key(&*s)), self.engine.custom_keywords.contains_key(&*s),
#[cfg(feature = "no_custom_syntax")] #[cfg(feature = "no_custom_syntax")]
false false
) )
@ -2436,7 +2458,7 @@ impl<'a> Iterator for TokenIterator<'a> {
#[cfg(feature = "no_custom_syntax")] #[cfg(feature = "no_custom_syntax")]
(.., true) => unreachable!("no custom operators"), (.., true) => unreachable!("no custom operators"),
// Reserved keyword that is not custom and disabled. // Reserved keyword that is not custom and disabled.
(token, false) if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token) => { (token, false) if self.engine.disabled_symbols.contains(token) => {
let msg = format!("reserved {} '{token}' is disabled", if is_valid_identifier(token) { "keyword"} else {"symbol"}); let msg = format!("reserved {} '{token}' is disabled", if is_valid_identifier(token) { "keyword"} else {"symbol"});
Token::LexError(LERR::ImproperSymbol(s.to_string(), msg).into()) Token::LexError(LERR::ImproperSymbol(s.to_string(), msg).into())
}, },
@ -2445,13 +2467,13 @@ impl<'a> Iterator for TokenIterator<'a> {
}, pos), }, pos),
// Custom keyword // Custom keyword
#[cfg(not(feature = "no_custom_syntax"))] #[cfg(not(feature = "no_custom_syntax"))]
Some((Token::Identifier(s), pos)) if !self.engine.custom_keywords.is_empty() && self.engine.custom_keywords.contains_key(&*s) => { Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.contains_key(&*s) => {
(Token::Custom(s), pos) (Token::Custom(s), pos)
} }
// Custom keyword/symbol - must be disabled // Custom keyword/symbol - must be disabled
#[cfg(not(feature = "no_custom_syntax"))] #[cfg(not(feature = "no_custom_syntax"))]
Some((token, pos)) if !self.engine.custom_keywords.is_empty() && self.engine.custom_keywords.contains_key(token.literal_syntax()) => { Some((token, pos)) if token.is_literal() && self.engine.custom_keywords.contains_key(token.literal_syntax()) => {
if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token.literal_syntax()) { if self.engine.disabled_symbols.contains(token.literal_syntax()) {
// Disabled standard keyword/symbol // Disabled standard keyword/symbol
(Token::Custom(Box::new(token.literal_syntax().into())), pos) (Token::Custom(Box::new(token.literal_syntax().into())), pos)
} else { } else {
@ -2460,7 +2482,7 @@ impl<'a> Iterator for TokenIterator<'a> {
} }
} }
// Disabled symbol // Disabled symbol
Some((token, pos)) if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token.literal_syntax()) => { Some((token, pos)) if token.is_literal() && self.engine.disabled_symbols.contains(token.literal_syntax()) => {
(Token::Reserved(Box::new(token.literal_syntax().into())), pos) (Token::Reserved(Box::new(token.literal_syntax().into())), pos)
} }
// Normal symbol // Normal symbol