use vb6core::error::{VBError, VBResult};
use vb6parse::parsers::cst::CstNode;
use vb6parse::parsers::SyntaxKind;
use vb6runtime::VBVariant;
use super::super::program;
use crate::error::RunResult;
use crate::interpreter::Interpreter;
impl Interpreter {
pub(crate) fn exec_assignment(&mut self, node: &CstNode) -> RunResult<()> {
let significant: Vec<&CstNode> = node.significant_children().collect();
let lhs = significant
.iter()
.find(|c| {
matches!(
c.kind(),
SyntaxKind::IdentifierExpression
| SyntaxKind::CallExpression
| SyntaxKind::MemberAccessExpression
)
})
.copied()
.ok_or_else(|| self.error_here(VBError::invalid_procedure_call(), None))?;
let rhs = significant
.last()
.copied()
.ok_or_else(|| self.error_here(VBError::invalid_procedure_call(), None))?;
let value = self.eval_expr(rhs)?;
self.assign(lhs, value)
}
pub(crate) fn assign(&mut self, lhs: &CstNode, value: VBVariant) -> RunResult<()> {
match lhs.kind() {
SyntaxKind::IdentifierExpression => {
let name = program::identifier_name(lhs);
self.assign_to_name(&name, value);
Ok(())
}
SyntaxKind::CallExpression => {
let significant: Vec<&CstNode> = lhs.significant_children().collect();
let name = significant
.iter()
.find(|c| program::is_identifier_like(c))
.map(|c| c.text().trim().to_string())
.unwrap_or_default();
let argument_list = significant
.iter()
.find(|c| c.kind() == SyntaxKind::ArgumentList);
let args = match argument_list {
Some(list) => self.eval_args(list)?,
None => Vec::new(),
};
let indices: Vec<i32> = args
.iter()
.map(|arg| arg.as_i32())
.collect::<VBResult<_>>()?;
let existing = self
.lookup(&name)
.cloned()
.ok_or_else(|| self.error_here(VBError::subscript_out_of_range(), None))?;
if let VBVariant::Array(mut array) = existing {
array.set(&indices, value)?;
self.set_variable(&name, VBVariant::Array(array));
Ok(())
} else {
Err(self.error_here(VBError::type_mismatch(), None))
}
}
SyntaxKind::MemberAccessExpression => Err(self.unsupported(lhs, "member assignment")),
_ => Err(self.error_here(VBError::invalid_procedure_call(), None)),
}
}
fn assign_to_name(&mut self, name: &str, value: VBVariant) {
if let Some(frame) = self.frames.last() {
if frame.is_function && name.to_lowercase() == frame.name.to_lowercase() {
if let Some(frame) = self.frames.last_mut() {
frame.return_value = Some(value);
}
return;
}
}
self.set_variable(name, value);
}
pub(crate) fn exec_set_statement(&mut self, node: &CstNode) -> RunResult<()> {
let significant: Vec<&CstNode> = node.significant_children().collect();
let eq_index = significant
.iter()
.position(|c| c.kind() == SyntaxKind::EqualityOperator)
.ok_or_else(|| self.error_here(VBError::invalid_procedure_call(), None))?;
let target = significant
.iter()
.find(|c| c.kind() == SyntaxKind::IdentifierExpression)
.ok_or_else(|| self.error_here(VBError::invalid_procedure_call(), None))?;
let name = program::identifier_name(target);
let rhs = significant
.iter()
.skip(eq_index + 1)
.find(|c| !matches!(c.kind(), SyntaxKind::Whitespace | SyntaxKind::Newline))
.ok_or_else(|| self.error_here(VBError::invalid_procedure_call(), None))?;
let value = self.eval_expr(rhs)?;
self.assign_to_name(&name, value);
Ok(())
}
pub(crate) fn declare_in(&mut self, name: &str, value: VBVariant) {
if self.frames.is_empty() {
self.globals.declare(name, value);
} else if let Some(frame) = self.frames.last_mut() {
frame.locals.declare(name, value);
}
}
pub(crate) fn set_variable(&mut self, name: &str, value: VBVariant) {
if !self.frames.is_empty() {
if let Some(frame) = self.frames.last_mut() {
if frame.locals.set(name, value.clone()) {
return;
}
}
}
if self.globals.set(name, value.clone()) {
return;
}
self.declare_in(name, value);
}
}