diff --git a/interpreter/src/cursor.rs b/interpreter/src/cursor.rs index 391777ba80..9f2893fc22 100644 --- a/interpreter/src/cursor.rs +++ b/interpreter/src/cursor.rs @@ -245,6 +245,8 @@ pub struct Cursor<'a> { /// Consts are stored here. pub globals: HashMap, + pub user_values: HashMap, + pub mappings: HashMap>, /// For each struct type, we only need to remember the names of its members, in order. @@ -271,6 +273,7 @@ impl<'a> Cursor<'a> { values: Default::default(), functions: Default::default(), globals: Default::default(), + user_values: Default::default(), mappings: Default::default(), structs: Default::default(), contexts: Default::default(), @@ -310,16 +313,15 @@ impl<'a> Cursor<'a> { } fn lookup(&self, name: Symbol) -> Option { - let Some(context) = self.contexts.last() else { - panic!("lookup requires a context"); + if let Some(context) = self.contexts.last() { + let option_value = + context.names.get(&name).or_else(|| self.globals.get(&GlobalId { program: context.program, name })); + if option_value.is_some() { + return option_value.cloned(); + } }; - let option_value = context.names.get(&name); - if option_value.is_some() { - return option_value.cloned(); - } - - self.globals.get(&GlobalId { program: context.program, name }).cloned() + self.user_values.get(&name).cloned() } pub fn lookup_mapping(&self, program: Option, name: Symbol) -> Option<&HashMap> { @@ -340,6 +342,14 @@ impl<'a> Cursor<'a> { self.functions.get(&GlobalId { program, name }).cloned() } + fn set_variable(&mut self, symbol: Symbol, value: Value) { + if self.contexts.len() > 0 { + self.contexts.set(symbol, value); + } else { + self.user_values.insert(symbol, value); + } + } + /// Execute the whole step of the current Element. /// /// That is, perform a step, and then finish all statements and expressions that have been pushed, @@ -462,7 +472,7 @@ impl<'a> Cursor<'a> { Statement::Assign(assign) if step == 1 => { let value = self.values.pop().unwrap(); let Expression::Identifier(id) = &assign.place else { tc_fail!() }; - self.contexts.set(id.name, value); + self.set_variable(id.name, value); true } Statement::Block(block) => return Ok(self.step_block(block, false, step)), @@ -494,7 +504,7 @@ impl<'a> Cursor<'a> { } Statement::Const(const_) if step == 1 => { let value = self.pop_value()?; - self.contexts.set(const_.place.name, value); + self.set_variable(const_.place.name, value); true } Statement::Definition(definition) if step == 0 => { @@ -504,7 +514,7 @@ impl<'a> Cursor<'a> { Statement::Definition(definition) if step == 1 => { let value = self.pop_value()?; match &definition.place { - Expression::Identifier(id) => self.contexts.set(id.name, value), + Expression::Identifier(id) => self.set_variable(id.name, value), Expression::Tuple(tuple) => { let Value::Tuple(rhs) = value else { tc_fail!(); @@ -513,7 +523,7 @@ impl<'a> Cursor<'a> { let Expression::Identifier(id) = name else { tc_fail!(); }; - self.contexts.set(id.name, val); + self.set_variable(id.name, val); } } _ => tc_fail!(), @@ -542,7 +552,7 @@ impl<'a> Cursor<'a> { true } else { let new_start = start.inc_wrapping(); - self.contexts.set(iteration.variable.name, start); + self.set_variable(iteration.variable.name, start); self.frames.push(Frame { step: 0, element: Element::Block { block: &iteration.block, function_body: false }, @@ -906,15 +916,15 @@ impl<'a> Cursor<'a> { FunctionVariant::Leo(function) => { assert!(function.variant == Variant::AsyncFunction); let len = self.values.len(); - let values_iter = self.values.drain(len - function.input.len()..); + let values: Vec = self.values.drain(len - function.input.len()..).collect(); self.contexts.push( gid.program, self.signer, true, // is_async ); let param_names = function.input.iter().map(|input| input.identifier.name); - for (name, value) in param_names.zip(values_iter) { - self.contexts.set(name, value); + for (name, value) in param_names.zip(values) { + self.set_variable(name, value); } self.frames.last_mut().unwrap().step = 1; self.frames.push(Frame { @@ -995,7 +1005,7 @@ impl<'a> Cursor<'a> { self.contexts.push(function_program, caller, is_async); let param_names = function.input.iter().map(|input| input.identifier.name); for (name, value) in param_names.zip(arguments) { - self.contexts.set(name, value); + self.set_variable(name, value); } self.frames.push(Frame { step: 0, diff --git a/interpreter/src/lib.rs b/interpreter/src/lib.rs index 1afe8671b8..0d150d71e3 100644 --- a/interpreter/src/lib.rs +++ b/interpreter/src/lib.rs @@ -399,11 +399,14 @@ Once a function is running, commands include #step to take one step towards evaluating the current expression or statement; #over to complete evaluating the current expression or statement; #run to finish evaluating -#quit to exit the interpreter. +#quit to quit the interpreter. You can set a breakpoint with #break program_name line_number +When executing Aleo VM code, you can print the value of a register like this: +#print 2 + You may also use one letter abbreviations for these commands, such as #i. You may simply enter expressions or statements on the command line @@ -541,6 +544,7 @@ pub fn interpret( } std::io::stdin().read_line(&mut buffer).expect("read_line"); let action = match buffer.trim() { + "" => continue, "#i" | "#into" => InterpreterAction::Into, "#s" | "#step" => InterpreterAction::Step, "#o" | "#over" => InterpreterAction::Over,