Repository: CSML-by-Clevy/csml-engine (rust)

Issue:
goto previous flow/step
**Is your feature request related to a problem? Please describe.**
On top of #154 it would be useful to be able to go back to the previous place (step/flow), not necessarily resetting whatever was done inbetween (so not true backtracking) but at least be able to retrieve the place where you are coming from.

For instance, it would be interesting to be able to be able to quickly go to some centralized flow/step that is used in a lot of places, and at the end of that, go back to where you came from to continue the regular flow.

**Describe the solution you'd like**
To be discussed (some solution that plays nice with #154)

Pull request:
feat previous flow/ step
Fixes #196 

## Proposed Changes

- add new keyword 'previous' in order to go back to the last step or flow executed


Patch:
diff --git a/csml_engine/CSML/flow.csml b/csml_engine/CSML/flow.csml
index d6675e9a..dae63583 100644
--- a/csml_engine/CSML/flow.csml
+++ b/csml_engine/CSML/flow.csml
@@ -5,14 +5,16 @@ fn add(var1, var2):
     return 56
 
 start:
-    say val
-    remember val = "42"
+    say "start"
+    if (!test) {
+        say "create test"
+        remember test = 1
+    } else {
+        do test = test + 1
+    }
 
-    remember toto = "tataaa"
-    hold
-
-    goto end
-    // goto toto
+    // goto end
+    goto @flow2
 
 toto:
     do vec = [1, 2]
diff --git a/csml_engine/CSML/flow2.csml b/csml_engine/CSML/flow2.csml
index 4c6c6091..2bfcbc25 100644
--- a/csml_engine/CSML/flow2.csml
+++ b/csml_engine/CSML/flow2.csml
@@ -13,5 +13,12 @@ fn pow(var, exp):
 
 start:
   say "flow 2 -> {{test}}"
-  say Button("end", hola = "hola")
-  goto end
+//   say Button("end", hola = "hola")
+  goto toto
+
+toto:
+    hold
+    if (test < 2) {
+        previous flow
+    }
+    goto end
diff --git a/csml_engine/src/db_connectors/state.rs b/csml_engine/src/db_connectors/state.rs
index 9eb4084e..741bc8c8 100644
--- a/csml_engine/src/db_connectors/state.rs
+++ b/csml_engine/src/db_connectors/state.rs
@@ -113,6 +113,7 @@ mod tests {
             step_vars: serde_json::json!({}),
             step_name: "step_name".to_owned(),
             flow_name: "flow_name".to_owned(),
+            previous: None,
         };
 
         let state_hold: serde_json::Value = serde_json::json!({
diff --git a/csml_engine/src/interpreter_actions.rs b/csml_engine/src/interpreter_actions.rs
index cd14fd0b..7f494cd4 100644
--- a/csml_engine/src/interpreter_actions.rs
+++ b/csml_engine/src/interpreter_actions.rs
@@ -69,12 +69,14 @@ pub fn interpret_step(
                 step_vars,
                 step_name,
                 flow_name,
+                previous,
             }) => {
                 let hash = get_current_step_hash(&data.context, bot)?;
                 let state_hold: Value = serde_json::json!({
                     "index": index,
                     "step_vars": step_vars,
-                    "hash": hash
+                    "hash": hash,
+                    "previous": previous
                 });
 
                 set_state_items(
@@ -88,6 +90,7 @@ pub fn interpret_step(
                     step_vars,
                     step_name,
                     flow_name,
+                    previous,
                 });
             }
             MSG::Next { flow, step } => match (flow, step) {
diff --git a/csml_engine/src/lib.rs b/csml_engine/src/lib.rs
index f1e23493..73b05222 100644
--- a/csml_engine/src/lib.rs
+++ b/csml_engine/src/lib.rs
@@ -348,6 +348,7 @@ fn check_for_hold(data: &mut ConversationInfo, bot: &CsmlBot) -> Result<(), Engi
                 step_vars: hold["step_vars"].clone(),
                 step_name: data.context.step.to_owned(),
                 flow_name: data.context.flow.to_owned(),
+                previous: serde_json::from_value(hold["previous"].clone()).unwrap_or(None)
             });
            state::delete_state_key(&data.client, "hold", "position", &mut data.db)?;
         }
diff --git a/csml_interpreter/CSML/examples/hello_world.csml b/csml_interpreter/CSML/examples/hello_world.csml
index 342051db..59150541 100644
--- a/csml_interpreter/CSML/examples/hello_world.csml
+++ b/csml_interpreter/CSML/examples/hello_world.csml
@@ -1,5 +1,28 @@
+// start:
+
+//     say "Hello World 😆"
+
+//     goto end
+
+
+
 start:
 
-    say "Hello World 😆"
+    remember test = 0
+
+    goto toto
+
+
+toto:
+    do test = test + 1
+    // say "in toto test == {{test}}"
+
+    goto tata
+
+tata: 
+    // say "in tata"
+    if (test < 2) {
+        previous step
+    }
 
-    goto end
+    goto end
\ No newline at end of file
diff --git a/csml_interpreter/src/data/ast.rs b/csml_interpreter/src/data/ast.rs
index 423bdaaf..f3ff487e 100644
--- a/csml_interpreter/src/data/ast.rs
+++ b/csml_interpreter/src/data/ast.rs
@@ -160,9 +160,17 @@ pub enum ForgetMemory {
     LIST(Vec<Identifier>)
 }
 
+#[derive(Debug, Clone, Serialize, Deserialize)]
+
+pub enum PreviousType {
+    Step(Interval),
+    Flow(Interval)
+}
+
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub enum ObjectType {
     Goto(GotoType, Interval),
+    Previous(PreviousType, Interval),
     Hold(Interval),
     Say(Box<Expr>),
     Debug(Box<Expr>, Interval),
diff --git a/csml_interpreter/src/data/data.rs b/csml_interpreter/src/data/data.rs
index ebecba1e..4369140f 100644
--- a/csml_interpreter/src/data/data.rs
+++ b/csml_interpreter/src/data/data.rs
@@ -2,11 +2,17 @@ use crate::data::context::Context;
 use crate::data::Event;
 use crate::data::{ast::*, Literal};
 
+use serde::{Serialize, Deserialize};
 use std::collections::HashMap;
 
 ////////////////////////////////////////////////////////////////////////////////
 // DATA STRUCTURES
 ////////////////////////////////////////////////////////////////////////////////
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct PreviousInfo {
+    pub flow: String,
+    pub step_at_flow: (String, String) // step / flow
+}
 
 #[derive(Debug)]
 pub struct Data<'a> {
@@ -22,6 +28,7 @@ pub struct Data<'a> {
     pub step_count: &'a mut i32,
 
     pub step_vars: HashMap<String, Literal>,
+    pub previous_info: PreviousInfo,
     pub custom_component: &'a serde_json::Map<String, serde_json::Value>,
     pub native_component: &'a serde_json::Map<String, serde_json::Value>,
 }
@@ -29,6 +36,24 @@ pub struct Data<'a> {
 ////////////////////////////////////////////////////////////////////////////////
 // STATIC FUNCTIONS
 ////////////////////////////////////////////////////////////////////////////////
+
+impl PreviousInfo {
+    pub fn new(flow: String, step: String) -> Self {
+        Self { 
+            flow: flow.clone(),
+            step_at_flow: (step, flow)
+        }
+    }
+
+    pub fn goto(&mut self, flow: String, step: String) {
+        if self.step_at_flow.1 != flow {
+            self.flow = self.step_at_flow.1.clone();
+        }
+
+        self.step_at_flow = (step, flow);
+    }
+}
+
 impl<'a> Data<'a> {
     pub fn new(
         flows: &'a HashMap<String, Flow>,
@@ -40,6 +65,7 @@ impl<'a> Data<'a> {
         loop_index: usize,
         step_count: &'a mut i32,
         step_vars: HashMap<String, Literal>,
+        previous_info: PreviousInfo,
         custom_component: &'a serde_json::Map<String, serde_json::Value>,
         native_component: &'a serde_json::Map<String, serde_json::Value>,
     ) -> Self {
@@ -53,6 +79,7 @@ impl<'a> Data<'a> {
             loop_index,
             step_count,
             step_vars,
+            previous_info,
             custom_component,
             native_component,
         }
@@ -111,6 +138,7 @@ pub fn init_child_scope<'a>(data: &'a Data, context: &'a mut Context, step_count
         data.loop_index,
         step_count,
         HashMap::new(),
+        data.previous_info.clone(),
         &data.custom_component,
         &data.native_component,
     )
diff --git a/csml_interpreter/src/data/hold.rs b/csml_interpreter/src/data/hold.rs
index 583c8f72..659363af 100644
--- a/csml_interpreter/src/data/hold.rs
+++ b/csml_interpreter/src/data/hold.rs
@@ -1,6 +1,8 @@
 use crate::data::{Data, Literal};
 use serde::{Deserialize, Serialize};
 
+use super::data::PreviousInfo;
+
 ////////////////////////////////////////////////////////////////////////////////
 // DATA STRUCTURE
 ////////////////////////////////////////////////////////////////////////////////
@@ -17,6 +19,7 @@ pub struct Hold {
     pub step_vars: serde_json::Value,
     pub step_name: String,
     pub flow_name: String,
+    pub previous: Option<PreviousInfo>
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -29,12 +32,14 @@ impl Hold {
         step_vars: serde_json::Value,
         step_name: String,
         flow_name: String,
+        previous: Option<PreviousInfo>,
     ) -> Self {
         Self {
             index,
             step_vars,
             step_name,
             flow_name,
+            previous,
         }
     }
 
@@ -47,6 +52,7 @@ impl Hold {
             step_vars: serde_json::json!({}),
             step_name: "".to_owned(),
             flow_name: "".to_owned(),
+            previous: None,
         }
     }
 }
diff --git a/csml_interpreter/src/data/primitive/array.rs b/csml_interpreter/src/data/primitive/array.rs
index c417e46c..70ec9c45 100644
--- a/csml_interpreter/src/data/primitive/array.rs
+++ b/csml_interpreter/src/data/primitive/array.rs
@@ -816,7 +816,6 @@ impl PrimitiveArray {
                     let mut map = HashMap::new();
                     map.insert("arg0".to_owned(), value.to_owned());
                     if closure.args.len() >= 2 {
-                        dbg!(index);
                         map.insert(
                             "arg1".to_owned(), 
                             PrimitiveInt::get_literal(index as i64, interval)
diff --git a/csml_interpreter/src/data/tokens.rs b/csml_interpreter/src/data/tokens.rs
index c20d458b..d468ca45 100644
--- a/csml_interpreter/src/data/tokens.rs
+++ b/csml_interpreter/src/data/tokens.rs
@@ -68,6 +68,7 @@ pub const DEBUG_ACTION: &str = "debug";
 pub const USE: &str = "use";
 pub const HOLD: &str = "hold";
 pub const GOTO: &str = "goto";
+pub const PREVIOUS: &str = "previous";
 pub const MATCH: &str = "match";
 pub const NOT_MATCH: &str = "!match";
 pub const DEFAULT: &str = "default";
diff --git a/csml_interpreter/src/interpreter.rs b/csml_interpreter/src/interpreter.rs
index e611cda7..439394a0 100644
--- a/csml_interpreter/src/interpreter.rs
+++ b/csml_interpreter/src/interpreter.rs
@@ -91,6 +91,7 @@ pub fn interpret_scope(
                     step_vars_to_json(map),
                     data.context.step.clone(),
                     data.context.flow.clone(),
+                    Some(data.previous_info.clone()),
                 );
 
                 message_data.hold = Some(hold.to_owned());
diff --git a/csml_interpreter/src/interpreter/ast_interpreter/actions.rs b/csml_interpreter/src/interpreter/ast_interpreter/actions.rs
index 30bc23cb..fd3d8f2a 100644
--- a/csml_interpreter/src/interpreter/ast_interpreter/actions.rs
+++ b/csml_interpreter/src/interpreter/ast_interpreter/actions.rs
@@ -55,6 +55,7 @@ pub fn match_actions(
     data: &mut Data,
     sender: &Option<mpsc::Sender<MSG>>,
 ) -> Result<MessageData, ErrorInfo> {
+
     match function {
         ObjectType::Say(arg) => {
             let msg = Message::new(expr_to_literal(
@@ -113,6 +114,7 @@ pub fn match_actions(
                 tmp_loop_index,
                 &mut tmp_step_count,
                 tmp_step_vars,
+                data.previous_info.clone(),
                 &tmp_custom_component,
                 &tmp_native_component,
             );
@@ -161,6 +163,9 @@ pub fn match_actions(
                 },
             );
 
+            // previous flow/step
+            data.previous_info.goto(data.context.flow.clone(), data.context.step.clone());
+            // current flow/step
             data.context.step = step.to_string();
             msg_data.exit_condition = Some(ExitCondition::Goto);
 
@@ -181,8 +186,12 @@ pub fn match_actions(
                 },
             );
 
+            // previous flow/step
+            data.previous_info.goto(data.context.flow.clone(), data.context.step.clone());
+            // current flow/step
             data.context.step = "start".to_string();
             data.context.flow = flow.to_string();
+
             msg_data.exit_condition = Some(ExitCondition::Goto);
 
             Ok(msg_data)
@@ -214,8 +223,53 @@ pub fn match_actions(
                 },
             );
 
-            data.context.step = step.to_string();
+            // previous flow/step
+            data.previous_info.goto(data.context.flow.clone(), data.context.step.clone());
+            // current flow/step
             data.context.flow = flow.to_string();
+            data.context.step = step.to_string();
+
+            Ok(msg_data)
+        }
+        ObjectType::Previous(previous_type, _) => {
+            let flow_opt;
+            let mut step_opt = None;
+
+            match previous_type {
+                PreviousType::Flow(_interval) => {
+                    let tmp_f = data.previous_info.flow.clone();
+                    flow_opt = Some(tmp_f.clone());
+
+                    data.previous_info.flow = data.context.flow.clone();
+                    data.previous_info.step_at_flow = (data.context.step.clone(), data.context.flow.clone());
+
+                    data.context.flow = tmp_f;
+                    data.context.step = "start".to_string();
+                },
+                PreviousType::Step(_interval) => {
+                    let (tmp_s, tmp_f) = data.previous_info.step_at_flow.clone();
+                    flow_opt = Some(tmp_f.clone());
+                    step_opt = Some(tmp_s.clone());
+
+                    if data.context.flow != tmp_f {
+                        data.previous_info.flow = tmp_f.clone();
+                    }
+                    data.previous_info.step_at_flow = (data.context.step.clone(), data.context.flow.clone());
+
+                    data.context.flow = tmp_f;
+                    data.context.step = tmp_s;
+                }
+            }
+
+            msg_data.exit_condition = Some(ExitCondition::Goto);
+
+            MSG::send(
+                &sender,
+                MSG::Next {
+                    flow: flow_opt,
+                    step: step_opt,
+                },
+            );
 
             Ok(msg_data)
         }
diff --git a/csml_interpreter/src/interpreter/variable_handler.rs b/csml_interpreter/src/interpreter/variable_handler.rs
index b2d32524..f6d558e4 100644
--- a/csml_interpreter/src/interpreter/variable_handler.rs
+++ b/csml_interpreter/src/interpreter/variable_handler.rs
@@ -540,6 +540,7 @@ pub fn get_var(
                 tmp_loop_index,
                 &mut tmp_step_count, 
                 tmp_step_vars,
+                data.previous_info.clone(),
                 &tmp_custom_component,
                 &tmp_native_component,
             );
diff --git a/csml_interpreter/src/interpreter/variable_handler/interval.rs b/csml_interpreter/src/interpreter/variable_handler/interval.rs
index c2a31c93..2d2a1913 100644
--- a/csml_interpreter/src/interpreter/variable_handler/interval.rs
+++ b/csml_interpreter/src/interpreter/variable_handler/interval.rs
@@ -32,6 +32,7 @@ pub fn interval_from_if_stmt(ifstmt: &IfStatement) -> Interval {
 pub fn interval_from_reserved_fn(reserved_fn: &ObjectType) -> Interval {
     match reserved_fn {
         ObjectType::Goto(_g, interval) => interval.to_owned(),
+        ObjectType::Previous(_p, interval) => interval.to_owned(),
         ObjectType::Use(expr) => interval_from_expr(expr),
         ObjectType::Do(DoType::Update(expr, ..)) => interval_from_expr(expr),
         ObjectType::Do(DoType::Exec(expr)) => interval_from_expr(expr),
diff --git a/csml_interpreter/src/lib.rs b/csml_interpreter/src/lib.rs
index a87b49f1..be79e5a5 100644
--- a/csml_interpreter/src/lib.rs
+++ b/csml_interpreter/src/lib.rs
@@ -4,6 +4,7 @@ pub mod interpreter;
 pub mod linter;
 pub mod parser;
 
+use data::data::PreviousInfo;
 pub use interpreter::components::load_components;
 pub use parser::step_checksum::get_step;
 
@@ -201,6 +202,14 @@ pub fn interpret(
         None => data::primitive::PrimitiveNull::get_literal(Interval::default()),
     };
 
+    let mut previous_info = match &context.hold {
+        Some(hold) => match &hold.previous {
+            Some(previous) => previous.clone(),
+            None => PreviousInfo::new(flow.clone(), step.clone())
+        },
+        None => PreviousInfo::new(flow.clone(), step.clone())
+    };
+
     while msg_data.exit_condition.is_none() {
 
         let ast = match flows.get(&flow) {
@@ -229,11 +238,13 @@ pub fn interpret(
             0,
             &mut step_count,
             step_vars,
+            previous_info.clone(),
             &custom,
             &native,
         );
 
         msg_data = msg_data + execute_step(&step, &ast, &mut data, &sender);
+        previous_info = data.previous_info.clone();
         flow = data.context.flow.to_string();
         step = data.context.step.to_string();
         // add reset loops index
diff --git a/csml_interpreter/src/parser.rs b/csml_interpreter/src/parser.rs
index 005511b1..c7e190e2 100644
--- a/csml_interpreter/src/parser.rs
+++ b/csml_interpreter/src/parser.rs
@@ -7,6 +7,7 @@ pub mod parse_comments;
 pub mod parse_foreach;
 pub mod parse_functions;
 pub mod parse_goto;
+pub mod parse_previous;
 pub mod parse_idents;
 pub mod parse_if;
 pub mod parse_import;
diff --git a/csml_interpreter/src/parser/parse_actions.rs b/csml_interpreter/src/parser/parse_actions.rs
index 0e99a828..307d4d98 100644
--- a/csml_interpreter/src/parser/parse_actions.rs
+++ b/csml_interpreter/src/parser/parse_actions.rs
@@ -7,6 +7,7 @@ use crate::parser::{
     parse_comments::comment,
     parse_foreach::parse_foreach,
     parse_goto::parse_goto,
+    parse_previous::parse_previous,
     parse_idents::{parse_idents_assignation, parse_idents_usage},
     parse_if::parse_if,
     parse_path::parse_path,
@@ -327,6 +328,7 @@ where
         parse_continue,
         // only accessible inside normal scopes
         parse_goto,
+        parse_previous,
         parse_say,
         parse_remember,
         parse_forget,
diff --git a/csml_interpreter/src/parser/parse_previous.rs b/csml_interpreter/src/parser/parse_previous.rs
new file mode 100644
index 00000000..d1b801a1
--- /dev/null
+++ b/csml_interpreter/src/parser/parse_previous.rs
@@ -0,0 +1,41 @@
+use crate::data::{ast::*, tokens::*};
+use crate::parser::{
+    get_interval, parse_comments::comment, 
+    tools::get_string, tools::get_tag
+};
+
+use nom::{error::*, sequence::preceded, *};
+
+fn get_previous<I, E: ParseError<I>>(
+    var: String,
+    interval: Interval
+) -> impl Fn(I) -> IResult<I, PreviousType, E> {
+    move |input: I| {
+        if var == FLOW {
+            Ok((input, PreviousType::Flow(interval)))
+        } else if var == STEP {
+            Ok((input, PreviousType::Step(interval)))
+        } else {
+            Err(Err::Error(E::from_error_kind(input, ErrorKind::Tag)))
+        }
+    }
+}
+
+pub fn parse_previous<'a, E>(s: Span<'a>) -> IResult<Span<'a>, Expr, E>
+where
+    E: ParseError<Span<'a>>,
+{
+    let (s, interval) = preceded(comment, get_interval)(s)?;
+    let (s, name) = get_string(s)?;
+    let (s, ..) = get_tag(name, PREVIOUS)(s)?;
+
+
+    let (s, inter) = preceded(comment, get_interval)(s)?;
+    let (s, name) = get_string(s)?;
+    let (s, previous) = get_previous(name, inter)(s)?;
+
+    Ok((
+        s,
+        Expr::ObjectExpr(ObjectType::Previous(previous, interval)),
+    ))
+}
\ No newline at end of file


Test patch:
diff --git a/csml_interpreter/tests/hold.rs b/csml_interpreter/tests/hold.rs
index f237b277..0aa2f83e 100644
--- a/csml_interpreter/tests/hold.rs
+++ b/csml_interpreter/tests/hold.rs
@@ -61,6 +61,7 @@ fn hold_test_some_0() {
                 serde_json::json!({}),
                 "".to_owned(),
                 "".to_owned(),
+                None
             )),
             "start",
             "flow",
@@ -91,6 +92,7 @@ fn hold_test_some_3() {
                 serde_json::json!({}),
                 "".to_owned(),
                 "".to_owned(),
+                None
             )),
             "start",
             "flow",
@@ -121,6 +123,7 @@ fn hold_test_some_7() {
                 serde_json::json!({}),
                 "".to_owned(),
                 "".to_owned(),
+                None
             )),
             "start",
             "flow",
@@ -151,6 +154,7 @@ fn hold_test_some_8() {
                 serde_json::json!({}),
                 "".to_owned(),
                 "".to_owned(),
+                None
             )),
             "start",
             "flow",
@@ -181,6 +185,7 @@ fn hold_test_some_17() {
                 serde_json::json!({}),
                 "".to_owned(),
                 "".to_owned(),
+                None
             )),
             "start",
             "flow",
@@ -212,6 +217,7 @@ fn hold_test_step_1_ok() {
                 serde_json::json!({}),
                 "".to_owned(),
                 "".to_owned(),
+                None
             )),
             "hold_1_ok",
             "flow",
@@ -242,6 +248,7 @@ fn hold_test_step_2_ok() {
                 serde_json::json!({}),
                 "".to_owned(),
                 "".to_owned(),
+                None
             )),
             "hold_2_ok",
             "flow",
@@ -272,6 +279,7 @@ fn hold_test_step_3_ok() {
                 serde_json::json!({}),
                 "".to_owned(),
                 "".to_owned(),
+                None
             )),
             "hold_3_ok",
             "flow",
@@ -302,6 +310,7 @@ fn hold_test_step_4_ok() {
                 serde_json::json!({}),
                 "".to_owned(),
                 "".to_owned(),
+                None
             )),
             "hold_4_ok",
             "flow",