Optimize position in variable access.

This commit is contained in:
Stephen Chung 2021-04-05 23:59:15 +08:00
parent 94fc5af285
commit d3cfb3c605
6 changed files with 146 additions and 139 deletions

View File

@ -1574,14 +1574,19 @@ pub enum Expr {
), ),
/// () /// ()
Unit(Position), Unit(Position),
/// Variable access - optional short index, (optional index, optional (hash, modules), variable name) /// Variable access - optional short index, position, (optional index, optional (hash, modules), variable name)
/// ///
/// The short index is [`u8`] which is used when the index is <= 255, which should be the vast /// The short index is [`u8`] which is used when the index is <= 255, which should be the vast
/// majority of cases (unless there are more than 255 variables defined!). /// majority of cases (unless there are more than 255 variables defined!).
/// This is to avoid reading a pointer redirection during each variable access. /// This is to avoid reading a pointer redirection during each variable access.
Variable( Variable(
Option<NonZeroU8>, Option<NonZeroU8>,
Box<(Option<NonZeroUsize>, Option<(u64, NamespaceRef)>, Ident)>, Position,
Box<(
Option<NonZeroUsize>,
Option<(u64, NamespaceRef)>,
Identifier,
)>,
), ),
/// Property access - ((getter, hash), (setter, hash), prop) /// Property access - ((getter, hash), (setter, hash), prop)
Property(Box<((Identifier, u64), (Identifier, u64), Ident)>), Property(Box<((Identifier, u64), (Identifier, u64), Ident)>),
@ -1647,11 +1652,19 @@ impl Expr {
_ => return None, _ => return None,
}) })
} }
/// Is the expression a simple variable access?
#[inline(always)]
pub(crate) fn is_variable_access(&self, non_qualified: bool) -> bool {
match self {
Self::Variable(_, _, x) => !non_qualified || x.1.is_none(),
_ => false,
}
}
/// Return the variable name if the expression a simple variable access. /// Return the variable name if the expression a simple variable access.
#[inline(always)] #[inline(always)]
pub(crate) fn get_variable_access(&self, non_qualified: bool) -> Option<&str> { pub(crate) fn get_variable_name(&self, non_qualified: bool) -> Option<&str> {
match self { match self {
Self::Variable(_, x) if !non_qualified || x.1.is_none() => Some((x.2).name.as_str()), Self::Variable(_, _, x) if !non_qualified || x.1.is_none() => Some(x.2.as_str()),
_ => None, _ => None,
} }
} }
@ -1673,7 +1686,7 @@ impl Expr {
Self::Map(_, pos) => *pos, Self::Map(_, pos) => *pos,
Self::Property(x) => (x.2).pos, Self::Property(x) => (x.2).pos,
Self::Stmt(x) => x.pos, Self::Stmt(x) => x.pos,
Self::Variable(_, x) => (x.2).pos, Self::Variable(_, pos, _) => *pos,
Self::FnCall(_, pos) => *pos, Self::FnCall(_, pos) => *pos,
Self::And(x, _) | Self::Or(x, _) => x.lhs.position(), Self::And(x, _) | Self::Or(x, _) => x.lhs.position(),
@ -1703,7 +1716,7 @@ impl Expr {
Self::FnPointer(_, pos) => *pos = new_pos, Self::FnPointer(_, pos) => *pos = new_pos,
Self::Array(_, pos) => *pos = new_pos, Self::Array(_, pos) => *pos = new_pos,
Self::Map(_, pos) => *pos = new_pos, Self::Map(_, pos) => *pos = new_pos,
Self::Variable(_, x) => (x.2).pos = new_pos, Self::Variable(_, pos, _) => *pos = new_pos,
Self::Property(x) => (x.2).pos = new_pos, Self::Property(x) => (x.2).pos = new_pos,
Self::Stmt(x) => x.pos = new_pos, Self::Stmt(x) => x.pos = new_pos,
Self::FnCall(_, pos) => *pos = new_pos, Self::FnCall(_, pos) => *pos = new_pos,
@ -1731,7 +1744,7 @@ impl Expr {
Self::Stmt(x) => x.statements.iter().all(Stmt::is_pure), Self::Stmt(x) => x.statements.iter().all(Stmt::is_pure),
Self::Variable(_, _) => true, Self::Variable(_, _, _) => true,
_ => self.is_constant(), _ => self.is_constant(),
} }
@ -1801,7 +1814,7 @@ impl Expr {
_ => false, _ => false,
}, },
Self::Variable(_, _) => match token { Self::Variable(_, _, _) => match token {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Token::LeftBracket => true, Token::LeftBracket => true,
Token::LeftParen => true, Token::LeftParen => true,

View File

@ -959,14 +959,14 @@ impl Engine {
expr: &Expr, expr: &Expr,
) -> Result<(Target<'s>, Position), Box<EvalAltResult>> { ) -> Result<(Target<'s>, Position), Box<EvalAltResult>> {
match expr { match expr {
Expr::Variable(Some(_), _) => { Expr::Variable(Some(_), _, _) => {
self.search_scope_only(scope, mods, state, lib, this_ptr, expr) self.search_scope_only(scope, mods, state, lib, this_ptr, expr)
} }
Expr::Variable(None, v) => match v.as_ref() { Expr::Variable(None, var_pos, v) => match v.as_ref() {
// Normal variable access // Normal variable access
(_, None, _) => self.search_scope_only(scope, mods, state, lib, this_ptr, expr), (_, None, _) => self.search_scope_only(scope, mods, state, lib, this_ptr, expr),
// Qualified variable // Qualified variable
(_, Some((hash_var, modules)), Ident { name, pos, .. }) => { (_, Some((hash_var, modules)), var_name) => {
let module = self.search_imports(mods, state, modules).ok_or_else(|| { let module = self.search_imports(mods, state, modules).ok_or_else(|| {
EvalAltResult::ErrorModuleNotFound( EvalAltResult::ErrorModuleNotFound(
modules[0].name.to_string(), modules[0].name.to_string(),
@ -976,17 +976,17 @@ impl Engine {
let target = module.get_qualified_var(*hash_var).map_err(|mut err| { let target = module.get_qualified_var(*hash_var).map_err(|mut err| {
match *err { match *err {
EvalAltResult::ErrorVariableNotFound(ref mut err_name, _) => { EvalAltResult::ErrorVariableNotFound(ref mut err_name, _) => {
*err_name = format!("{}{}", modules, name); *err_name = format!("{}{}", modules, var_name);
} }
_ => (), _ => (),
} }
err.fill_position(*pos) err.fill_position(*var_pos)
})?; })?;
// Module variables are constant // Module variables are constant
let mut target = target.clone(); let mut target = target.clone();
target.set_access_mode(AccessMode::ReadOnly); target.set_access_mode(AccessMode::ReadOnly);
Ok((target.into(), *pos)) Ok((target.into(), *var_pos))
} }
}, },
_ => unreachable!("Expr::Variable expected, but gets {:?}", expr), _ => unreachable!("Expr::Variable expected, but gets {:?}", expr),
@ -1003,28 +1003,21 @@ impl Engine {
this_ptr: &'s mut Option<&mut Dynamic>, this_ptr: &'s mut Option<&mut Dynamic>,
expr: &Expr, expr: &Expr,
) -> Result<(Target<'s>, Position), Box<EvalAltResult>> { ) -> Result<(Target<'s>, Position), Box<EvalAltResult>> {
let (short_index, (index, _, Ident { name, pos, .. })) = match expr { // Make sure that the pointer indirection is taken only when absolutely necessary.
Expr::Variable(i, v) => (i, v.as_ref()),
_ => unreachable!("Expr::Variable expected, but gets {:?}", expr),
};
let (index, var_pos) = match expr {
// Check if the variable is `this` // Check if the variable is `this`
if *name == KEYWORD_THIS { Expr::Variable(None, pos, v) if v.0.is_none() && v.2 == KEYWORD_THIS => {
return if let Some(val) = this_ptr { return if let Some(val) = this_ptr {
Ok(((*val).into(), *pos)) Ok(((*val).into(), *pos))
} else { } else {
EvalAltResult::ErrorUnboundThis(*pos).into() EvalAltResult::ErrorUnboundThis(*pos).into()
};
} }
}
// Check if it is directly indexed _ if state.always_search => (0, expr.position()),
let index = if state.always_search { Expr::Variable(Some(i), pos, _) => (i.get() as usize, *pos),
0 Expr::Variable(None, pos, v) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos),
} else { _ => unreachable!("Expr::Variable expected, but gets {:?}", expr),
short_index.map_or_else(
|| index.map(NonZeroUsize::get).unwrap_or(0),
|x| x.get() as usize,
)
}; };
// Check the variable resolver, if any // Check the variable resolver, if any
@ -1039,10 +1032,11 @@ impl Engine {
level: 0, level: 0,
}; };
if let Some(mut result) = if let Some(mut result) =
resolve_var(name, index, &context).map_err(|err| err.fill_position(*pos))? resolve_var(expr.get_variable_name(true).unwrap(), index, &context)
.map_err(|err| err.fill_position(var_pos))?
{ {
result.set_access_mode(AccessMode::ReadOnly); result.set_access_mode(AccessMode::ReadOnly);
return Ok((result.into(), *pos)); return Ok((result.into(), var_pos));
} }
} }
@ -1050,15 +1044,16 @@ impl Engine {
scope.len() - index scope.len() - index
} else { } else {
// Find the variable in the scope // Find the variable in the scope
let var_name = expr.get_variable_name(true).unwrap();
scope scope
.get_index(name) .get_index(var_name)
.ok_or_else(|| EvalAltResult::ErrorVariableNotFound(name.to_string(), *pos))? .ok_or_else(|| EvalAltResult::ErrorVariableNotFound(var_name.to_string(), var_pos))?
.0 .0
}; };
let val = scope.get_mut_by_index(index); let val = scope.get_mut_by_index(index);
Ok((val.into(), *pos)) Ok((val.into(), var_pos))
} }
/// Chain-evaluate a dot/index chain. /// Chain-evaluate a dot/index chain.
@ -1410,13 +1405,7 @@ impl Engine {
match lhs { match lhs {
// id.??? or id[???] // id.??? or id[???]
Expr::Variable(_, x) => { Expr::Variable(_, var_pos, x) => {
let Ident {
name: var_name,
pos: var_pos,
..
} = &x.2;
self.inc_operations(state, *var_pos)?; self.inc_operations(state, *var_pos)?;
let (target, pos) = let (target, pos) =
@ -1424,8 +1413,7 @@ impl Engine {
// Constants cannot be modified // Constants cannot be modified
if target.as_ref().is_read_only() && new_val.is_some() { if target.as_ref().is_read_only() && new_val.is_some() {
return EvalAltResult::ErrorAssignmentToConstant(var_name.to_string(), pos) return EvalAltResult::ErrorAssignmentToConstant(x.2.to_string(), pos).into();
.into();
} }
let obj_ptr = &mut target.into(); let obj_ptr = &mut target.into();
@ -1688,11 +1676,11 @@ impl Engine {
Expr::CharConstant(x, _) => Ok((*x).into()), Expr::CharConstant(x, _) => Ok((*x).into()),
Expr::FnPointer(x, _) => Ok(FnPtr::new_unchecked(x.clone(), Default::default()).into()), Expr::FnPointer(x, _) => Ok(FnPtr::new_unchecked(x.clone(), Default::default()).into()),
Expr::Variable(None, x) if x.0.is_none() && (x.2).name == KEYWORD_THIS => this_ptr Expr::Variable(None, var_pos, x) if x.0.is_none() && x.2 == KEYWORD_THIS => this_ptr
.as_deref() .as_deref()
.cloned() .cloned()
.ok_or_else(|| EvalAltResult::ErrorUnboundThis((x.2).pos).into()), .ok_or_else(|| EvalAltResult::ErrorUnboundThis(*var_pos).into()),
Expr::Variable(_, _) => self Expr::Variable(_, _, _) => self
.search_namespace(scope, mods, state, lib, this_ptr, expr) .search_namespace(scope, mods, state, lib, this_ptr, expr)
.map(|(val, _)| val.take_or_clone()), .map(|(val, _)| val.take_or_clone()),
@ -2019,7 +2007,7 @@ impl Engine {
.flatten()), .flatten()),
// var op= rhs // var op= rhs
Stmt::Assignment(x, op_pos) if x.0.get_variable_access(false).is_some() => { Stmt::Assignment(x, op_pos) if x.0.is_variable_access(false) => {
let (lhs_expr, op_info, rhs_expr) = x.as_ref(); let (lhs_expr, op_info, rhs_expr) = x.as_ref();
let rhs_val = self let rhs_val = self
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)? .eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
@ -2029,7 +2017,7 @@ impl Engine {
if !lhs_ptr.is_ref() { if !lhs_ptr.is_ref() {
return EvalAltResult::ErrorAssignmentToConstant( return EvalAltResult::ErrorAssignmentToConstant(
lhs_expr.get_variable_access(false).unwrap().to_string(), lhs_expr.get_variable_name(false).unwrap().to_string(),
pos, pos,
) )
.into(); .into();
@ -2040,7 +2028,7 @@ impl Engine {
if lhs_ptr.as_ref().is_read_only() { if lhs_ptr.as_ref().is_read_only() {
// Assignment to constant variable // Assignment to constant variable
EvalAltResult::ErrorAssignmentToConstant( EvalAltResult::ErrorAssignmentToConstant(
lhs_expr.get_variable_access(false).unwrap().to_string(), lhs_expr.get_variable_name(false).unwrap().to_string(),
pos, pos,
) )
.into() .into()
@ -2070,7 +2058,7 @@ impl Engine {
// 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_expr { match lhs_expr {
// name op= rhs (handled above) // name op= rhs (handled above)
Expr::Variable(_, _) => { Expr::Variable(_, _, _) => {
unreachable!("Expr::Variable case should already been handled") unreachable!("Expr::Variable case should already been handled")
} }
// idx_lhs[idx_expr] op= rhs // idx_lhs[idx_expr] op= rhs

View File

@ -1293,10 +1293,7 @@ impl Engine {
// If the first argument is a variable, and there is no curried arguments, // If the first argument is a variable, and there is no curried arguments,
// convert to method-call style in order to leverage potential &mut first argument and // convert to method-call style in order to leverage potential &mut first argument and
// avoid cloning the value // avoid cloning the value
if curry.is_empty() if curry.is_empty() && !args_expr.is_empty() && args_expr[0].is_variable_access(false) {
&& !args_expr.is_empty()
&& args_expr[0].get_variable_access(false).is_some()
{
// func(x, ...) -> x.func(...) // func(x, ...) -> x.func(...)
arg_values = args_expr arg_values = args_expr
.iter() .iter()
@ -1378,7 +1375,7 @@ impl Engine {
// See if the first argument is a variable (not namespace-qualified). // See if the first argument is a variable (not namespace-qualified).
// If so, convert to method-call style in order to leverage potential // If so, convert to method-call style in order to leverage potential
// &mut first argument and avoid cloning the value // &mut first argument and avoid cloning the value
if !args_expr.is_empty() && args_expr[0].get_variable_access(true).is_some() { if !args_expr.is_empty() && args_expr[0].is_variable_access(true) {
// func(x, ...) -> x.func(...) // func(x, ...) -> x.func(...)
arg_values = args_expr arg_values = args_expr
.iter() .iter()

View File

@ -385,7 +385,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
match stmt { match stmt {
// expr op= expr // expr op= expr
Stmt::Assignment(x, _) => match x.0 { Stmt::Assignment(x, _) => match x.0 {
Expr::Variable(_, _) => optimize_expr(&mut x.2, state), Expr::Variable(_, _, _) => optimize_expr(&mut x.2, state),
_ => { _ => {
optimize_expr(&mut x.0, state); optimize_expr(&mut x.0, state);
optimize_expr(&mut x.2, state); optimize_expr(&mut x.2, state);
@ -635,7 +635,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
.unwrap_or_else(|| Expr::Unit(*pos)); .unwrap_or_else(|| Expr::Unit(*pos));
} }
// var.rhs // var.rhs
(Expr::Variable(_, _), rhs) => optimize_expr(rhs, state), (Expr::Variable(_, _, _), rhs) => optimize_expr(rhs, state),
// lhs.rhs // lhs.rhs
(lhs, rhs) => { optimize_expr(lhs, state); optimize_expr(rhs, state); } (lhs, rhs) => { optimize_expr(lhs, state); optimize_expr(rhs, state); }
} }
@ -670,7 +670,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
*expr = Expr::CharConstant(s.chars().nth(*i as usize).unwrap(), *pos); *expr = Expr::CharConstant(s.chars().nth(*i as usize).unwrap(), *pos);
} }
// var[rhs] // var[rhs]
(Expr::Variable(_, _), rhs) => optimize_expr(rhs, state), (Expr::Variable(_, _, _), rhs) => optimize_expr(rhs, state),
// lhs[rhs] // lhs[rhs]
(lhs, rhs) => { optimize_expr(lhs, state); optimize_expr(rhs, state); } (lhs, rhs) => { optimize_expr(lhs, state); optimize_expr(rhs, state); }
}, },
@ -901,12 +901,12 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
} }
// constant-name // constant-name
Expr::Variable(_, x) if x.1.is_none() && state.find_constant(&x.2.name).is_some() => { Expr::Variable(_, pos, x) if x.1.is_none() && state.find_constant(&x.2).is_some() => {
state.set_dirty(); state.set_dirty();
// Replace constant with value // Replace constant with value
let mut result = state.find_constant(&x.2.name).unwrap().clone(); let mut result = state.find_constant(&x.2).unwrap().clone();
result.set_position(x.2.pos); result.set_position(*pos);
*expr = result; *expr = result;
} }

View File

@ -225,17 +225,20 @@ impl Expr {
#[inline(always)] #[inline(always)]
fn into_property(self, state: &mut ParseState) -> Self { fn into_property(self, state: &mut ParseState) -> Self {
match self { match self {
Self::Variable(_, x) if x.1.is_none() => { Self::Variable(_, pos, x) if x.1.is_none() => {
let ident = x.2; let ident = x.2;
let getter = state.get_identifier(crate::engine::make_getter(&ident.name)); let getter = state.get_identifier(crate::engine::make_getter(&ident));
let hash_get = calc_fn_hash(empty(), &getter, 1); let hash_get = calc_fn_hash(empty(), &getter, 1);
let setter = state.get_identifier(crate::engine::make_setter(&ident.name)); let setter = state.get_identifier(crate::engine::make_setter(&ident));
let hash_set = calc_fn_hash(empty(), &setter, 2); let hash_set = calc_fn_hash(empty(), &setter, 2);
Self::Property(Box::new(( Self::Property(Box::new((
(getter, hash_get), (getter, hash_get),
(setter, hash_set), (setter, hash_set),
ident.into(), Ident {
name: state.get_identifier(ident),
pos,
},
))) )))
} }
_ => self, _ => self,
@ -1077,11 +1080,11 @@ fn parse_primary(
// Once the identifier consumed we must enable next variables capturing // Once the identifier consumed we must enable next variables capturing
state.allow_capture = true; state.allow_capture = true;
} }
let var_name_def = Ident { Expr::Variable(
name: state.get_identifier(s), None,
pos: settings.pos, settings.pos,
}; Box::new((None, None, state.get_identifier(s))),
Expr::Variable(None, Box::new((None, None, var_name_def))) )
} }
// Namespace qualification // Namespace qualification
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
@ -1091,19 +1094,15 @@ fn parse_primary(
// Once the identifier consumed we must enable next variables capturing // Once the identifier consumed we must enable next variables capturing
state.allow_capture = true; state.allow_capture = true;
} }
let var_name_def = Ident { Expr::Variable(
name: state.get_identifier(s), None,
pos: settings.pos, settings.pos,
}; Box::new((None, None, state.get_identifier(s))),
Expr::Variable(None, Box::new((None, None, var_name_def))) )
} }
// Normal variable access // Normal variable access
_ => { _ => {
let index = state.access_var(&s, settings.pos); let index = state.access_var(&s, settings.pos);
let var_name_def = Ident {
name: state.get_identifier(s),
pos: settings.pos,
};
let short_index = index.and_then(|x| { let short_index = index.and_then(|x| {
if x.get() <= u8::MAX as usize { if x.get() <= u8::MAX as usize {
NonZeroU8::new(x.get() as u8) NonZeroU8::new(x.get() as u8)
@ -1111,7 +1110,11 @@ fn parse_primary(
None None
} }
}); });
Expr::Variable(short_index, Box::new((index, None, var_name_def))) Expr::Variable(
short_index,
settings.pos,
Box::new((index, None, state.get_identifier(s))),
)
} }
} }
} }
@ -1125,21 +1128,17 @@ fn parse_primary(
match input.peek().unwrap().0 { match input.peek().unwrap().0 {
// Function call is allowed to have reserved keyword // Function call is allowed to have reserved keyword
Token::LeftParen | Token::Bang if is_keyword_function(&s) => { Token::LeftParen | Token::Bang if is_keyword_function(&s) => Expr::Variable(
let var_name_def = Ident { None,
name: state.get_identifier(s), settings.pos,
pos: settings.pos, Box::new((None, None, state.get_identifier(s))),
}; ),
Expr::Variable(None, Box::new((None, None, var_name_def)))
}
// Access to `this` as a variable is OK within a function scope // Access to `this` as a variable is OK within a function scope
_ if s == KEYWORD_THIS && settings.is_function_scope => { _ if s == KEYWORD_THIS && settings.is_function_scope => Expr::Variable(
let var_name_def = Ident { None,
name: state.get_identifier(s), settings.pos,
pos: settings.pos, Box::new((None, None, state.get_identifier(s))),
}; ),
Expr::Variable(None, Box::new((None, None, var_name_def)))
}
// Cannot access to `this` as a variable not in a function scope // Cannot access to `this` as a variable not in a function scope
_ if s == KEYWORD_THIS => { _ if s == KEYWORD_THIS => {
let msg = format!("'{}' can only be used in functions", s); let msg = format!("'{}' can only be used in functions", s);
@ -1175,7 +1174,7 @@ fn parse_primary(
root_expr = match (root_expr, tail_token) { root_expr = match (root_expr, tail_token) {
// Qualified function call with ! // Qualified function call with !
(Expr::Variable(_, x), Token::Bang) if x.1.is_some() => { (Expr::Variable(_, _, x), Token::Bang) if x.1.is_some() => {
return Err(if !match_token(input, Token::LeftParen).0 { return Err(if !match_token(input, Token::LeftParen).0 {
LexError::UnexpectedInput(Token::Bang.syntax().to_string()).into_err(tail_pos) LexError::UnexpectedInput(Token::Bang.syntax().to_string()).into_err(tail_pos)
} else { } else {
@ -1187,7 +1186,7 @@ fn parse_primary(
}); });
} }
// Function call with ! // Function call with !
(Expr::Variable(_, x), Token::Bang) => { (Expr::Variable(_, var_pos, x), Token::Bang) => {
let (matched, pos) = match_token(input, Token::LeftParen); let (matched, pos) = match_token(input, Token::LeftParen);
if !matched { if !matched {
return Err(PERR::MissingToken( return Err(PERR::MissingToken(
@ -1197,22 +1196,26 @@ fn parse_primary(
.into_err(pos)); .into_err(pos));
} }
let (_, namespace, Ident { name, pos, .. }) = *x; let (_, namespace, name) = *x;
settings.pos = pos; settings.pos = var_pos;
let ns = namespace.map(|(_, ns)| ns); let ns = namespace.map(|(_, ns)| ns);
parse_fn_call(input, state, lib, name, true, ns, settings.level_up())? parse_fn_call(input, state, lib, name, true, ns, settings.level_up())?
} }
// Function call // Function call
(Expr::Variable(_, x), Token::LeftParen) => { (Expr::Variable(_, var_pos, x), Token::LeftParen) => {
let (_, namespace, Ident { name, pos, .. }) = *x; let (_, namespace, name) = *x;
settings.pos = pos; settings.pos = var_pos;
let ns = namespace.map(|(_, ns)| ns); let ns = namespace.map(|(_, ns)| ns);
parse_fn_call(input, state, lib, name, false, ns, settings.level_up())? parse_fn_call(input, state, lib, name, false, ns, settings.level_up())?
} }
// module access // module access
(Expr::Variable(_, x), Token::DoubleColon) => match input.next().unwrap() { (Expr::Variable(_, var_pos, x), Token::DoubleColon) => match input.next().unwrap() {
(Token::Identifier(id2), pos2) => { (Token::Identifier(id2), pos2) => {
let (_, mut namespace, var_name_def) = *x; let (_, mut namespace, var_name) = *x;
let var_name_def = Ident {
name: var_name,
pos: var_pos,
};
if let Some((_, ref mut namespace)) = namespace { if let Some((_, ref mut namespace)) = namespace {
namespace.push(var_name_def); namespace.push(var_name_def);
@ -1222,11 +1225,11 @@ fn parse_primary(
namespace = Some((42, ns)); namespace = Some((42, ns));
} }
let var_name_def = Ident { Expr::Variable(
name: state.get_identifier(id2), None,
pos: pos2, pos2,
}; Box::new((None, namespace, state.get_identifier(id2))),
Expr::Variable(None, Box::new((None, namespace, var_name_def))) )
} }
(Token::Reserved(id2), pos2) if is_valid_identifier(id2.chars()) => { (Token::Reserved(id2), pos2) if is_valid_identifier(id2.chars()) => {
return Err(PERR::Reserved(id2).into_err(pos2)); return Err(PERR::Reserved(id2).into_err(pos2));
@ -1269,15 +1272,15 @@ fn parse_primary(
// Cache the hash key for namespace-qualified variables // Cache the hash key for namespace-qualified variables
match &mut root_expr { match &mut root_expr {
Expr::Variable(_, x) if x.1.is_some() => Some(x), Expr::Variable(_, _, x) if x.1.is_some() => Some(x),
Expr::Index(x, _) | Expr::Dot(x, _) => match &mut x.lhs { Expr::Index(x, _) | Expr::Dot(x, _) => match &mut x.lhs {
Expr::Variable(_, x) if x.1.is_some() => Some(x), Expr::Variable(_, _, x) if x.1.is_some() => Some(x),
_ => None, _ => None,
}, },
_ => None, _ => None,
} }
.map(|x| match x.as_mut() { .map(|x| match x.as_mut() {
(_, Some((ref mut hash, ref mut namespace)), Ident { name, .. }) => { (_, Some((ref mut hash, ref mut namespace)), name) => {
*hash = calc_fn_hash(namespace.iter().map(|v| v.name.as_str()), name, 0); *hash = calc_fn_hash(namespace.iter().map(|v| v.name.as_str()), name, 0);
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
@ -1429,12 +1432,12 @@ fn make_assignment_stmt<'a>(
Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position())) Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position()))
} }
// 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))
} }
// var (indexed) = rhs // var (indexed) = rhs
Expr::Variable(i, x) => { Expr::Variable(i, var_pos, x) => {
let (index, _, Ident { name, pos, .. }) = x.as_ref(); let (index, _, name) = x.as_ref();
let index = i.map_or_else(|| index.unwrap().get(), |n| n.get() as usize); let index = i.map_or_else(|| index.unwrap().get(), |n| n.get() as usize);
match state.stack[state.stack.len() - index].1 { match state.stack[state.stack.len() - index].1 {
AccessMode::ReadWrite => { AccessMode::ReadWrite => {
@ -1442,7 +1445,7 @@ fn make_assignment_stmt<'a>(
} }
// Constant values cannot be assigned to // Constant values cannot be assigned to
AccessMode::ReadOnly => { AccessMode::ReadOnly => {
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*pos)) Err(PERR::AssignmentToConstant(name.to_string()).into_err(*var_pos))
} }
} }
} }
@ -1451,12 +1454,12 @@ fn make_assignment_stmt<'a>(
match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _))) { match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _))) {
Position::NONE => match &x.lhs { Position::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))
} }
// var[???] (indexed) = rhs, var.??? (indexed) = rhs // var[???] (indexed) = rhs, var.??? (indexed) = rhs
Expr::Variable(i, x) => { Expr::Variable(i, var_pos, x) => {
let (index, _, Ident { name, pos, .. }) = x.as_ref(); let (index, _, name) = x.as_ref();
let index = i.map_or_else(|| index.unwrap().get(), |n| n.get() as usize); let index = i.map_or_else(|| index.unwrap().get(), |n| n.get() as usize);
match state.stack[state.stack.len() - index].1 { match state.stack[state.stack.len() - index].1 {
AccessMode::ReadWrite => { AccessMode::ReadWrite => {
@ -1464,7 +1467,7 @@ fn make_assignment_stmt<'a>(
} }
// Constant values cannot be assigned to // Constant values cannot be assigned to
AccessMode::ReadOnly => { AccessMode::ReadOnly => {
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*pos)) Err(PERR::AssignmentToConstant(name.to_string()).into_err(*var_pos))
} }
} }
} }
@ -1540,19 +1543,26 @@ fn make_dot_expr(
Expr::Index(x, pos) Expr::Index(x, pos)
} }
// lhs.id // lhs.id
(lhs, Expr::Variable(_, x)) if x.1.is_none() => { (lhs, Expr::Variable(_, var_pos, x)) if x.1.is_none() => {
let ident = x.2; let ident = x.2;
let getter = state.get_identifier(crate::engine::make_getter(&ident.name)); let getter = state.get_identifier(crate::engine::make_getter(&ident));
let hash_get = calc_fn_hash(empty(), &getter, 1); let hash_get = calc_fn_hash(empty(), &getter, 1);
let setter = state.get_identifier(crate::engine::make_setter(&ident.name)); let setter = state.get_identifier(crate::engine::make_setter(&ident));
let hash_set = calc_fn_hash(empty(), &setter, 2); let hash_set = calc_fn_hash(empty(), &setter, 2);
let rhs = Expr::Property(Box::new(((getter, hash_get), (setter, hash_set), ident))); let rhs = Expr::Property(Box::new((
(getter, hash_get),
(setter, hash_set),
Ident {
name: state.get_identifier(ident),
pos: var_pos,
},
)));
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
} }
// lhs.module::id - syntax error // lhs.module::id - syntax error
(_, Expr::Variable(_, x)) if x.1.is_some() => { (_, Expr::Variable(_, _, x)) if x.1.is_some() => {
return Err(PERR::PropertyExpected.into_err(x.1.unwrap().1[0].pos)) return Err(PERR::PropertyExpected.into_err(x.1.unwrap().1[0].pos))
} }
// lhs.prop // lhs.prop
@ -1561,7 +1571,7 @@ fn make_dot_expr(
} }
// lhs.dot_lhs.dot_rhs // lhs.dot_lhs.dot_rhs
(lhs, Expr::Dot(x, pos)) => match x.lhs { (lhs, Expr::Dot(x, pos)) => match x.lhs {
Expr::Variable(_, _) | Expr::Property(_) => { Expr::Variable(_, _, _) | Expr::Property(_) => {
let rhs = Expr::Dot( let rhs = Expr::Dot(
Box::new(BinaryExpr { Box::new(BinaryExpr {
lhs: x.lhs.into_property(state), lhs: x.lhs.into_property(state),
@ -1876,8 +1886,7 @@ fn parse_custom_syntax(
let name = state.get_identifier(s); let name = state.get_identifier(s);
segments.push(name.clone().into()); segments.push(name.clone().into());
tokens.push(state.get_identifier(MARKER_IDENT)); tokens.push(state.get_identifier(MARKER_IDENT));
let var_name_def = Ident { name, pos }; keywords.push(Expr::Variable(None, pos, Box::new((None, None, name))));
keywords.push(Expr::Variable(None, Box::new((None, None, var_name_def))));
} }
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => { (Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
return Err(PERR::Reserved(s).into_err(pos)); return Err(PERR::Reserved(s).into_err(pos));
@ -2829,11 +2838,11 @@ fn make_curry_from_externals(
args.push(fn_expr); args.push(fn_expr);
externals.iter().for_each(|x| { externals.iter().for_each(|x| {
let var_def = Ident { args.push(Expr::Variable(
name: x.clone(), None,
pos: Position::NONE, Position::NONE,
}; Box::new((None, None, x.clone())),
args.push(Expr::Variable(None, Box::new((None, None, var_def)))); ));
}); });
let expr = Expr::FnCall( let expr = Expr::FnCall(

View File

@ -45,7 +45,7 @@ impl Expression<'_> {
/// If this expression is a variable name, return it. Otherwise [`None`]. /// If this expression is a variable name, return it. Otherwise [`None`].
#[inline(always)] #[inline(always)]
pub fn get_variable_name(&self) -> Option<&str> { pub fn get_variable_name(&self) -> Option<&str> {
self.0.get_variable_access(true) self.0.get_variable_name(true)
} }
/// Get the expression. /// Get the expression.
#[inline(always)] #[inline(always)]