Add no_position feature.
This commit is contained in:
parent
fe37edd123
commit
a54fbb32ff
5
.github/workflows/build.yml
vendored
5
.github/workflows/build.yml
vendored
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
81
src/ast.rs
81
src/ast.rs
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
Rhai Tools
|
Rhai Tools
|
||||||
==========
|
==========
|
||||||
|
|
||||||
Tools written in Rhai.
|
Tools for running Rhai scripts.
|
||||||
|
|
||||||
|
|
||||||
How to Run
|
How to Run
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
97
src/token.rs
97
src/token.rs
@ -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,21 +105,34 @@ 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
|
||||||
|
} else {
|
||||||
|
#[cfg(not(feature = "no_position"))]
|
||||||
|
return if self.pos == 0 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(self.pos as usize)
|
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) {
|
||||||
|
#[cfg(not(feature = "no_position"))]
|
||||||
|
{
|
||||||
assert!(!self.is_none(), "cannot advance Position::none");
|
assert!(!self.is_none(), "cannot advance Position::none");
|
||||||
|
|
||||||
// Advance up to maximum position
|
// Advance up to maximum position
|
||||||
@ -113,6 +140,7 @@ impl Position {
|
|||||||
self.pos += 1;
|
self.pos += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/// Go backwards by one character position.
|
/// Go backwards by one character position.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
@ -120,13 +148,18 @@ 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) {
|
||||||
|
#[cfg(not(feature = "no_position"))]
|
||||||
|
{
|
||||||
assert!(!self.is_none(), "cannot rewind Position::none");
|
assert!(!self.is_none(), "cannot rewind Position::none");
|
||||||
assert!(self.pos > 0, "cannot rewind at position 0");
|
assert!(self.pos > 0, "cannot rewind at position 0");
|
||||||
self.pos -= 1;
|
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) {
|
||||||
|
#[cfg(not(feature = "no_position"))]
|
||||||
|
{
|
||||||
assert!(!self.is_none(), "cannot advance Position::none");
|
assert!(!self.is_none(), "cannot advance Position::none");
|
||||||
|
|
||||||
// Advance up to maximum position
|
// Advance up to maximum position
|
||||||
@ -135,15 +168,22 @@ impl Position {
|
|||||||
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,8 +1098,12 @@ 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();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_position"))]
|
||||||
|
{
|
||||||
skip_whitespace_until = start.position().unwrap() + 1;
|
skip_whitespace_until = start.position().unwrap() + 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Unterminated string
|
// Unterminated string
|
||||||
'\n' => {
|
'\n' => {
|
||||||
@ -1062,16 +1120,21 @@ 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);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_position"))]
|
||||||
|
{
|
||||||
skip_whitespace_until = 0;
|
skip_whitespace_until = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(max) = state.max_string_size {
|
if let Some(max) = state.max_string_size {
|
||||||
if result.len() > max.get() {
|
if result.len() > max.get() {
|
||||||
@ -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);
|
||||||
|
@ -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(())
|
||||||
|
@ -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();
|
||||||
|
@ -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],
|
||||||
|
if cfg!(not(feature = "no_position")) {
|
||||||
r#"DEBUG of world at 1:19: "hello!""#
|
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() {
|
||||||
|
@ -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!\"")?,
|
||||||
|
if cfg!(not(feature = "no_position")) {
|
||||||
"Test string: ❤ hello, world!"
|
"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!`")?,
|
||||||
|
Loading…
Reference in New Issue
Block a user