Add no_position feature.

This commit is contained in:
Stephen Chung 2021-04-22 23:02:25 +08:00
parent fe37edd123
commit a54fbb32ff
12 changed files with 189 additions and 61 deletions

View File

@ -21,6 +21,7 @@ jobs:
- "--features metadata,serde,internals" - "--features metadata,serde,internals"
- "--features unchecked" - "--features unchecked"
- "--features sync" - "--features sync"
- "--features no_position"
- "--features no_optimize" - "--features no_optimize"
- "--features no_float" - "--features no_float"
- "--features f32_float,serde,metadata,internals" - "--features f32_float,serde,metadata,internals"
@ -34,8 +35,8 @@ jobs:
- "--features no_module" - "--features no_module"
- "--features no_closure" - "--features no_closure"
- "--features unicode-xid-ident" - "--features unicode-xid-ident"
- "--features sync,no_function,no_float,no_optimize,no_module,no_closure,metadata,serde,unchecked" - "--features sync,no_function,no_float,no_position,no_optimize,no_module,no_closure,metadata,serde,unchecked"
- "--features no_function,no_float,no_index,no_object,no_optimize,no_module,no_closure,unchecked" - "--features no_function,no_float,no_position,no_index,no_object,no_optimize,no_module,no_closure,unchecked"
toolchain: [stable] toolchain: [stable]
experimental: [false] experimental: [false]
include: include:

View File

@ -26,6 +26,7 @@ New features
------------ ------------
* A module called `global` is automatically created to hold global-level constants, which can then be accessed from functions. * A module called `global` is automatically created to hold global-level constants, which can then be accessed from functions.
* A new feature `no_position` is added to turn off position tracking during parsing to squeeze out the last drop of performance.
Version 0.20.0 Version 0.20.0

View File

@ -23,9 +23,10 @@ smartstring = { version = "0.2.6", default_features = false }
rhai_codegen = { version = "0.3.4", path = "codegen", default_features = false } rhai_codegen = { version = "0.3.4", path = "codegen", default_features = false }
[features] [features]
default = ["smartstring/std", "ahash/std", "num-traits/std"] # remove 'smartstring/std' when smartstring is updated to support no-std default = ["no_position", "smartstring/std", "ahash/std", "num-traits/std"] # remove 'smartstring/std' when smartstring is updated to support no-std
unchecked = [] # unchecked arithmetic unchecked = [] # unchecked arithmetic
sync = [] # restrict to only types that implement Send + Sync sync = [] # restrict to only types that implement Send + Sync
no_position = [] # do not track position in the parser
no_optimize = [] # no script optimizer no_optimize = [] # no script optimizer
no_float = [] # no floating-point no_float = [] # no floating-point
f32_float = [] # set FLOAT=f32 f32_float = [] # set FLOAT=f32

View File

@ -780,7 +780,12 @@ pub struct Ident {
impl fmt::Debug for Ident { impl fmt::Debug for Ident {
#[inline(always)] #[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?} @ {:?}", self.name, self.pos) #[cfg(not(feature = "no_position"))]
write!(f, "{:?} @ {:?}", self.name, self.pos)?;
#[cfg(feature = "no_position")]
write!(f, "{:?}", self.name)?;
Ok(())
} }
} }
@ -1687,31 +1692,59 @@ impl Default for Expr {
impl fmt::Debug for Expr { impl fmt::Debug for Expr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
#[cfg(not(feature = "no_position"))]
Self::DynamicConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos), Self::DynamicConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos),
#[cfg(not(feature = "no_position"))]
Self::BoolConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos), Self::BoolConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos),
#[cfg(not(feature = "no_position"))]
Self::IntegerConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos), Self::IntegerConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[cfg(not(feature = "no_position"))]
Self::FloatConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos), Self::FloatConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos),
#[cfg(not(feature = "no_position"))]
Self::CharConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos), Self::CharConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos),
#[cfg(not(feature = "no_position"))]
Self::StringConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos), Self::StringConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos),
#[cfg(not(feature = "no_position"))]
Self::Unit(pos) => write!(f, "() @ {:?}", pos), Self::Unit(pos) => write!(f, "() @ {:?}", pos),
#[cfg(feature = "no_position")]
Self::DynamicConstant(value, _) => write!(f, "{:?}", value),
#[cfg(feature = "no_position")]
Self::BoolConstant(value, _) => write!(f, "{:?}", value),
#[cfg(feature = "no_position")]
Self::IntegerConstant(value, _) => write!(f, "{:?}", value),
#[cfg(not(feature = "no_float"))]
#[cfg(feature = "no_position")]
Self::FloatConstant(value, _) => write!(f, "{:?}", value),
#[cfg(feature = "no_position")]
Self::CharConstant(value, _) => write!(f, "{:?}", value),
#[cfg(feature = "no_position")]
Self::StringConstant(value, _) => write!(f, "{:?}", value),
#[cfg(feature = "no_position")]
Self::Unit(_) => f.write_str("()"),
Self::InterpolatedString(x) => { Self::InterpolatedString(x) => {
f.write_str("InterpolatedString")?; f.write_str("InterpolatedString")?;
f.debug_list().entries(x.iter()).finish() f.debug_list().entries(x.iter()).finish()
} }
Self::Array(x, pos) => { Self::Array(x, _pos) => {
f.write_str("Array")?; f.write_str("Array")?;
f.debug_list().entries(x.iter()).finish()?; f.debug_list().entries(x.iter()).finish()?;
write!(f, " @ {:?}", pos) #[cfg(not(feature = "no_position"))]
write!(f, " @ {:?}", _pos)?;
Ok(())
} }
Self::Map(x, pos) => { Self::Map(x, _pos) => {
f.write_str("Map")?; f.write_str("Map")?;
f.debug_map() f.debug_map()
.entries(x.0.iter().map(|(k, v)| (k, v))) .entries(x.0.iter().map(|(k, v)| (k, v)))
.finish()?; .finish()?;
write!(f, " @ {:?}", pos) #[cfg(not(feature = "no_position"))]
write!(f, " @ {:?}", _pos)?;
Ok(())
} }
Self::Variable(i, pos, x) => { Self::Variable(i, _pos, x) => {
f.write_str("Variable(")?; f.write_str("Variable(")?;
match x.1 { match x.1 {
Some((_, ref namespace)) => write!(f, "{}", namespace)?, Some((_, ref namespace)) => write!(f, "{}", namespace)?,
@ -1722,15 +1755,23 @@ impl fmt::Debug for Expr {
Some(n) => write!(f, ", {}", n)?, Some(n) => write!(f, ", {}", n)?,
_ => (), _ => (),
} }
write!(f, ") @ {:?}", pos) f.write_str(")")?;
#[cfg(not(feature = "no_position"))]
write!(f, " @ {:?}", _pos)?;
Ok(())
} }
#[cfg(not(feature = "no_position"))]
Self::Property(x) => write!(f, "Property({:?} @ {:?})", x.2.name, x.2.pos), Self::Property(x) => write!(f, "Property({:?} @ {:?})", x.2.name, x.2.pos),
#[cfg(feature = "no_position")]
Self::Property(x) => write!(f, "Property({:?})", x.2.name),
Self::Stmt(x) => { Self::Stmt(x) => {
f.write_str("Stmt")?; f.write_str("Stmt")?;
f.debug_list().entries(x.0.iter()).finish()?; f.debug_list().entries(x.0.iter()).finish()?;
write!(f, " @ {:?}", x.1) #[cfg(not(feature = "no_position"))]
write!(f, " @ {:?}", x.1)?;
Ok(())
} }
Self::FnCall(x, pos) => { Self::FnCall(x, _pos) => {
let mut ff = f.debug_struct("FnCall"); let mut ff = f.debug_struct("FnCall");
if let Some(ref ns) = x.namespace { if let Some(ref ns) = x.namespace {
ff.field("namespace", ns); ff.field("namespace", ns);
@ -1745,9 +1786,11 @@ impl fmt::Debug for Expr {
ff.field("capture", &x.capture); ff.field("capture", &x.capture);
} }
ff.finish()?; ff.finish()?;
write!(f, " @ {:?}", pos) #[cfg(not(feature = "no_position"))]
write!(f, " @ {:?}", _pos)?;
Ok(())
} }
Self::Dot(x, pos) | Self::Index(x, pos) | Self::And(x, pos) | Self::Or(x, pos) => { Self::Dot(x, _pos) | Self::Index(x, _pos) | Self::And(x, _pos) | Self::Or(x, _pos) => {
let op_name = match self { let op_name = match self {
Self::Dot(_, _) => "Dot", Self::Dot(_, _) => "Dot",
Self::Index(_, _) => "Index", Self::Index(_, _) => "Index",
@ -1760,11 +1803,15 @@ impl fmt::Debug for Expr {
.field("lhs", &x.lhs) .field("lhs", &x.lhs)
.field("rhs", &x.rhs) .field("rhs", &x.rhs)
.finish()?; .finish()?;
write!(f, " @ {:?}", pos) #[cfg(not(feature = "no_position"))]
write!(f, " @ {:?}", _pos)?;
Ok(())
} }
Self::Custom(x, pos) => { Self::Custom(x, _pos) => {
f.debug_tuple("Custom").field(x).finish()?; f.debug_tuple("Custom").field(x).finish()?;
write!(f, " @ {:?}", pos) #[cfg(not(feature = "no_position"))]
write!(f, " @ {:?}", _pos)?;
Ok(())
} }
} }
} }
@ -2058,6 +2105,7 @@ mod tests {
assert_eq!(size_of::<Dynamic>(), 16); assert_eq!(size_of::<Dynamic>(), 16);
assert_eq!(size_of::<Option<Dynamic>>(), 16); assert_eq!(size_of::<Option<Dynamic>>(), 16);
#[cfg(not(feature = "no_position"))]
assert_eq!(size_of::<Position>(), 4); assert_eq!(size_of::<Position>(), 4);
assert_eq!(size_of::<ast::Expr>(), 16); assert_eq!(size_of::<ast::Expr>(), 16);
assert_eq!(size_of::<Option<ast::Expr>>(), 16); assert_eq!(size_of::<Option<ast::Expr>>(), 16);
@ -2066,7 +2114,10 @@ mod tests {
assert_eq!(size_of::<FnPtr>(), 96); assert_eq!(size_of::<FnPtr>(), 96);
assert_eq!(size_of::<Scope>(), 288); assert_eq!(size_of::<Scope>(), 288);
assert_eq!(size_of::<LexError>(), 56); assert_eq!(size_of::<LexError>(), 56);
assert_eq!(size_of::<ParseError>(), 16); assert_eq!(
size_of::<ParseError>(),
if cfg!(feature = "no_position") { 8 } else { 16 }
);
assert_eq!(size_of::<EvalAltResult>(), 72); assert_eq!(size_of::<EvalAltResult>(), 72);
} }
} }

View File

@ -1,7 +1,7 @@
Rhai Tools Rhai Tools
========== ==========
Tools written in Rhai. Tools for running Rhai scripts.
How to Run How to Run

View File

@ -839,6 +839,8 @@ fn default_debug(_s: &str, _source: Option<&str>, _pos: Position) {
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
if let Some(source) = _source { if let Some(source) = _source {
println!("{} @ {:?} | {}", source, _pos, _s); println!("{} @ {:?} | {}", source, _pos, _s);
} else if _pos.is_none() {
println!("{}", _s);
} else { } else {
println!("{:?} | {}", _pos, _s); println!("{:?} | {}", _pos, _s);
} }

View File

@ -1415,20 +1415,20 @@ fn make_assignment_stmt<'a>(
rhs: Expr, rhs: Expr,
op_pos: Position, op_pos: Position,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
fn check_lvalue(expr: &Expr, parent_is_dot: bool) -> Position { fn check_lvalue(expr: &Expr, parent_is_dot: bool) -> Option<Position> {
match expr { match expr {
Expr::Index(x, _) | Expr::Dot(x, _) if parent_is_dot => match x.lhs { Expr::Index(x, _) | Expr::Dot(x, _) if parent_is_dot => match x.lhs {
Expr::Property(_) => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))), Expr::Property(_) => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))),
ref e => e.position(), ref e => Some(e.position()),
}, },
Expr::Index(x, _) | Expr::Dot(x, _) => match x.lhs { Expr::Index(x, _) | Expr::Dot(x, _) => match x.lhs {
Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"), Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"),
_ => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))), _ => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))),
}, },
Expr::Property(_) if parent_is_dot => Position::NONE, Expr::Property(_) if parent_is_dot => None,
Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"), Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"),
e if parent_is_dot => e.position(), e if parent_is_dot => Some(e.position()),
_ => Position::NONE, _ => None,
} }
} }
@ -1464,7 +1464,7 @@ fn make_assignment_stmt<'a>(
// xxx[???]... = rhs, xxx.prop... = rhs // xxx[???]... = rhs, xxx.prop... = rhs
Expr::Index(x, _) | Expr::Dot(x, _) => { Expr::Index(x, _) | Expr::Dot(x, _) => {
match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _))) { match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _))) {
Position::NONE => match &x.lhs { None => match &x.lhs {
// var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs // var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs
Expr::Variable(None, _, x) if x.0.is_none() => { Expr::Variable(None, _, x) if x.0.is_none() => {
Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos)) Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos))
@ -1488,7 +1488,7 @@ fn make_assignment_stmt<'a>(
Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(expr.position())) Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(expr.position()))
} }
}, },
pos => Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(pos)), Some(pos) => Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(pos)),
} }
} }
// ??? && ??? = rhs, ??? || ??? = rhs // ??? && ??? = rhs, ??? || ??? = rhs

View File

@ -57,16 +57,28 @@ pub type TokenStream<'a> = Peekable<TokenIterator<'a>>;
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)] #[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub struct Position { pub struct Position {
/// Line number - 0 = none /// Line number - 0 = none
#[cfg(not(feature = "no_position"))]
line: u16, line: u16,
/// Character position - 0 = BOL /// Character position - 0 = BOL
#[cfg(not(feature = "no_position"))]
pos: u16, pos: u16,
} }
impl Position { impl Position {
/// A [`Position`] representing no position. /// A [`Position`] representing no position.
pub const NONE: Self = Self { line: 0, pos: 0 }; pub const NONE: Self = Self {
#[cfg(not(feature = "no_position"))]
line: 0,
#[cfg(not(feature = "no_position"))]
pos: 0,
};
/// A [`Position`] representing the first position. /// A [`Position`] representing the first position.
pub const START: Self = Self { line: 1, pos: 0 }; pub const START: Self = Self {
#[cfg(not(feature = "no_position"))]
line: 1,
#[cfg(not(feature = "no_position"))]
pos: 0,
};
/// Create a new [`Position`]. /// Create a new [`Position`].
/// ///
@ -77,12 +89,14 @@ impl Position {
/// ///
/// Panics if `line` is zero. /// Panics if `line` is zero.
#[inline(always)] #[inline(always)]
pub fn new(line: u16, position: u16) -> Self { pub fn new(line: u16, _position: u16) -> Self {
assert!(line != 0, "line cannot be zero"); assert!(line != 0, "line cannot be zero");
Self { Self {
#[cfg(not(feature = "no_position"))]
line, line,
pos: position, #[cfg(not(feature = "no_position"))]
pos: _position,
} }
} }
/// Get the line number (1-based), or [`None`] if there is no position. /// Get the line number (1-based), or [`None`] if there is no position.
@ -91,26 +105,40 @@ impl Position {
if self.is_none() { if self.is_none() {
None None
} else { } else {
Some(self.line as usize) #[cfg(not(feature = "no_position"))]
return Some(self.line as usize);
#[cfg(feature = "no_position")]
unreachable!();
} }
} }
/// Get the character position (1-based), or [`None`] if at beginning of a line. /// Get the character position (1-based), or [`None`] if at beginning of a line.
#[inline(always)] #[inline(always)]
pub fn position(self) -> Option<usize> { pub fn position(self) -> Option<usize> {
if self.is_none() || self.pos == 0 { if self.is_none() {
None None
} else { } else {
Some(self.pos as usize) #[cfg(not(feature = "no_position"))]
return if self.pos == 0 {
None
} else {
Some(self.pos as usize)
};
#[cfg(feature = "no_position")]
unreachable!();
} }
} }
/// Advance by one character position. /// Advance by one character position.
#[inline(always)] #[inline(always)]
pub(crate) fn advance(&mut self) { pub(crate) fn advance(&mut self) {
assert!(!self.is_none(), "cannot advance Position::none"); #[cfg(not(feature = "no_position"))]
{
assert!(!self.is_none(), "cannot advance Position::none");
// Advance up to maximum position // Advance up to maximum position
if self.pos < u16::MAX { if self.pos < u16::MAX {
self.pos += 1; self.pos += 1;
}
} }
} }
/// Go backwards by one character position. /// Go backwards by one character position.
@ -120,30 +148,42 @@ impl Position {
/// Panics if already at beginning of a line - cannot rewind to a previous line. /// Panics if already at beginning of a line - cannot rewind to a previous line.
#[inline(always)] #[inline(always)]
pub(crate) fn rewind(&mut self) { pub(crate) fn rewind(&mut self) {
assert!(!self.is_none(), "cannot rewind Position::none"); #[cfg(not(feature = "no_position"))]
assert!(self.pos > 0, "cannot rewind at position 0"); {
self.pos -= 1; assert!(!self.is_none(), "cannot rewind Position::none");
assert!(self.pos > 0, "cannot rewind at position 0");
self.pos -= 1;
}
} }
/// Advance to the next line. /// Advance to the next line.
#[inline(always)] #[inline(always)]
pub(crate) fn new_line(&mut self) { pub(crate) fn new_line(&mut self) {
assert!(!self.is_none(), "cannot advance Position::none"); #[cfg(not(feature = "no_position"))]
{
assert!(!self.is_none(), "cannot advance Position::none");
// Advance up to maximum position // Advance up to maximum position
if self.line < u16::MAX { if self.line < u16::MAX {
self.line += 1; self.line += 1;
self.pos = 0; self.pos = 0;
}
} }
} }
/// Is this [`Position`] at the beginning of a line? /// Is this [`Position`] at the beginning of a line?
#[inline(always)] #[inline(always)]
pub fn is_beginning_of_line(self) -> bool { pub fn is_beginning_of_line(self) -> bool {
self.pos == 0 && !self.is_none() #[cfg(not(feature = "no_position"))]
return self.pos == 0 && !self.is_none();
#[cfg(feature = "no_position")]
return false;
} }
/// Is there no [`Position`]? /// Is there no [`Position`]?
#[inline(always)] #[inline(always)]
pub fn is_none(self) -> bool { pub fn is_none(self) -> bool {
self == Self::NONE #[cfg(not(feature = "no_position"))]
return self == Self::NONE;
#[cfg(feature = "no_position")]
return true;
} }
} }
@ -158,17 +198,27 @@ impl fmt::Display for Position {
#[inline(always)] #[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_none() { if self.is_none() {
write!(f, "none") write!(f, "none")?;
} else { } else {
write!(f, "line {}, position {}", self.line, self.pos) #[cfg(not(feature = "no_position"))]
write!(f, "line {}, position {}", self.line, self.pos)?;
#[cfg(feature = "no_position")]
unreachable!();
} }
Ok(())
} }
} }
impl fmt::Debug for Position { impl fmt::Debug for Position {
#[inline(always)] #[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.line, self.pos) #[cfg(not(feature = "no_position"))]
write!(f, "{}:{}", self.line, self.pos)?;
#[cfg(feature = "no_position")]
f.write_str("none")?;
Ok(())
} }
} }
@ -179,14 +229,17 @@ impl Add for Position {
if rhs.is_none() { if rhs.is_none() {
self self
} else { } else {
Self { #[cfg(not(feature = "no_position"))]
return Self {
line: self.line + rhs.line - 1, line: self.line + rhs.line - 1,
pos: if rhs.is_beginning_of_line() { pos: if rhs.is_beginning_of_line() {
self.pos self.pos
} else { } else {
self.pos + rhs.pos - 1 self.pos + rhs.pos - 1
}, },
} };
#[cfg(feature = "no_position")]
unreachable!();
} }
} }
} }
@ -908,8 +961,9 @@ pub fn parse_string_literal(
let mut escape = String::with_capacity(12); let mut escape = String::with_capacity(12);
let start = *pos; let start = *pos;
let mut skip_whitespace_until = 0;
let mut interpolated = false; let mut interpolated = false;
#[cfg(not(feature = "no_position"))]
let mut skip_whitespace_until = 0;
state.is_within_text_terminated_by = Some(termination_char); state.is_within_text_terminated_by = Some(termination_char);
@ -1044,7 +1098,11 @@ pub fn parse_string_literal(
assert_eq!(escape, "\\", "unexpected escape {} at end of line", escape); assert_eq!(escape, "\\", "unexpected escape {} at end of line", escape);
escape.clear(); escape.clear();
pos.new_line(); pos.new_line();
skip_whitespace_until = start.position().unwrap() + 1;
#[cfg(not(feature = "no_position"))]
{
skip_whitespace_until = start.position().unwrap() + 1;
}
} }
// Unterminated string // Unterminated string
@ -1062,13 +1120,18 @@ pub fn parse_string_literal(
} }
// Whitespace to skip // Whitespace to skip
#[cfg(not(feature = "no_position"))]
_ if next_char.is_whitespace() && pos.position().unwrap() < skip_whitespace_until => {} _ if next_char.is_whitespace() && pos.position().unwrap() < skip_whitespace_until => {}
// All other characters // All other characters
_ => { _ => {
escape.clear(); escape.clear();
result.push(next_char); result.push(next_char);
skip_whitespace_until = 0;
#[cfg(not(feature = "no_position"))]
{
skip_whitespace_until = 0;
}
} }
} }
} }
@ -1241,7 +1304,7 @@ fn get_next_token_inner(
); );
} }
let mut negated_pos = Position::NONE; let mut negated: Option<Position> = None;
while let Some(c) = stream.get_next() { while let Some(c) = stream.get_next() {
pos.advance(); pos.advance();
@ -1350,7 +1413,7 @@ fn get_next_token_inner(
} }
} }
let num_pos = if !negated_pos.is_none() { let num_pos = if let Some(negated_pos) = negated {
result.insert(0, '-'); result.insert(0, '-');
negated_pos negated_pos
} else { } else {
@ -1511,7 +1574,7 @@ fn get_next_token_inner(
('+', _) if !state.non_unary => return Some((Token::UnaryPlus, start_pos)), ('+', _) if !state.non_unary => return Some((Token::UnaryPlus, start_pos)),
('+', _) => return Some((Token::Plus, start_pos)), ('+', _) => return Some((Token::Plus, start_pos)),
('-', '0'..='9') if !state.non_unary => negated_pos = start_pos, ('-', '0'..='9') if !state.non_unary => negated = Some(start_pos),
('-', '0'..='9') => return Some((Token::Minus, start_pos)), ('-', '0'..='9') => return Some((Token::Minus, start_pos)),
('-', '=') => { ('-', '=') => {
eat_next(stream, pos); eat_next(stream, pos);

View File

@ -206,7 +206,7 @@ fn test_map_json() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine.parse_json(" 123", true).expect_err("should error"), *engine.parse_json(" 123", true).expect_err("should error"),
EvalAltResult::ErrorParsing(ParseErrorType::MissingToken(token, _), pos) EvalAltResult::ErrorParsing(ParseErrorType::MissingToken(token, _), pos)
if token == "{" && pos.position() == Some(4) if token == "{"
)); ));
Ok(()) Ok(())

View File

@ -49,6 +49,7 @@ fn test_optimizer_run() -> Result<(), Box<EvalAltResult>> {
} }
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_position"))]
#[test] #[test]
fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> { fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new(); let mut engine = Engine::new();

View File

@ -60,7 +60,11 @@ fn test_print_debug() -> Result<(), Box<EvalAltResult>> {
assert_eq!(logbook.read().unwrap()[0], "entry: 42"); assert_eq!(logbook.read().unwrap()[0], "entry: 42");
assert_eq!( assert_eq!(
logbook.read().unwrap()[1], logbook.read().unwrap()[1],
r#"DEBUG of world at 1:19: "hello!""# if cfg!(not(feature = "no_position")) {
r#"DEBUG of world at 1:19: "hello!""#
} else {
r#"DEBUG of world at none: "hello!""#
}
); );
for entry in logbook.read().unwrap().iter() { for entry in logbook.read().unwrap().iter() {

View File

@ -14,7 +14,11 @@ fn test_string() -> Result<(), Box<EvalAltResult>> {
); );
assert_eq!( assert_eq!(
engine.eval::<String>(" \"Test string: \\u2764\\\n hello, world!\"")?, engine.eval::<String>(" \"Test string: \\u2764\\\n hello, world!\"")?,
"Test string: ❤ hello, world!" if cfg!(not(feature = "no_position")) {
"Test string: ❤ hello, world!"
} else {
"Test string: ❤ hello, world!"
}
); );
assert_eq!( assert_eq!(
engine.eval::<String>(" `Test string: \\u2764\nhello,\\nworld!`")?, engine.eval::<String>(" `Test string: \\u2764\nhello,\\nworld!`")?,