From 65e70187297d28067c1ca4ca9d877a8424c589dd Mon Sep 17 00:00:00 2001 From: Alexis Merelo Date: Wed, 20 Oct 2021 12:47:34 -0500 Subject: [PATCH 1/2] add while key word to csml --- .../CSML/examples/hello_world.csml | 7 +- csml_interpreter/src/data/ast.rs | 1 + csml_interpreter/src/data/tokens.rs | 7 +- csml_interpreter/src/interpreter.rs | 12 +++- .../src/interpreter/ast_interpreter.rs | 2 + .../ast_interpreter/if_statement.rs | 2 +- .../interpreter/ast_interpreter/while_loop.rs | 68 +++++++++++++++++++ .../src/interpreter/function_scope.rs | 5 +- .../interpreter/variable_handler/interval.rs | 1 + csml_interpreter/src/linter/linter.rs | 5 ++ csml_interpreter/src/parser.rs | 1 + csml_interpreter/src/parser/parse_actions.rs | 2 + .../src/parser/parse_while_loop.rs | 42 ++++++++++++ csml_interpreter/src/parser/state_context.rs | 4 ++ 14 files changed, 152 insertions(+), 7 deletions(-) create mode 100644 csml_interpreter/src/interpreter/ast_interpreter/while_loop.rs create mode 100644 csml_interpreter/src/parser/parse_while_loop.rs diff --git a/csml_interpreter/CSML/examples/hello_world.csml b/csml_interpreter/CSML/examples/hello_world.csml index 342051db..f3a1cc6e 100644 --- a/csml_interpreter/CSML/examples/hello_world.csml +++ b/csml_interpreter/CSML/examples/hello_world.csml @@ -1,5 +1,10 @@ start: - say "Hello World 😆" + // say "Hello World 😆" + do var = 4 + while (var > 1) { + say var + do var = var - 1 + } goto end diff --git a/csml_interpreter/src/data/ast.rs b/csml_interpreter/src/data/ast.rs index b40c11e1..b16759d4 100644 --- a/csml_interpreter/src/data/ast.rs +++ b/csml_interpreter/src/data/ast.rs @@ -245,6 +245,7 @@ pub enum Expr { range: Interval, }, ForEachExpr(Identifier, Option, Box, Block, Interval), + WhileExpr(Box, Block, Interval), ComplexLiteral(Vec, Interval), MapExpr { object: HashMap, diff --git a/csml_interpreter/src/data/tokens.rs b/csml_interpreter/src/data/tokens.rs index 51b5dd0c..7b260b9e 100644 --- a/csml_interpreter/src/data/tokens.rs +++ b/csml_interpreter/src/data/tokens.rs @@ -50,6 +50,7 @@ pub const L2_BRACE: &str = "{{"; pub const R2_BRACE: &str = "}}"; pub const FOREACH: &str = "foreach"; +pub const WHILE: &str = "while"; pub const IF: &str = "if"; pub const ELSE: &str = "else"; @@ -109,18 +110,18 @@ pub const TYPES: &[&str] = &[ ]; pub const RESERVED: &[&str] = &[ - FOREACH, IF, ELSE, IMPORT, AS, IN, DO, FROM, EVENT, FLOW, FILE, STEP, SAY, USE, HOLD, GOTO, + FOREACH, WHILE, IF, ELSE, IMPORT, AS, IN, DO, FROM, EVENT, FLOW, FILE, STEP, SAY, USE, HOLD, GOTO, MATCH, _METADATA, _MEMORY, _ENV, DEFAULT, REMEMBER, FORGET, TRUE, FALSE, NULL, BREAK, COMPONENT, ]; pub const UTILISATION_RESERVED: &[&str] = &[ - FOREACH, IF, ELSE, IMPORT, AS, DO, FLOW, STEP, SAY, USE, HOLD, GOTO, MATCH, REMEMBER, FORGET, + FOREACH, WHILE, IF, ELSE, IMPORT, AS, DO, FLOW, STEP, SAY, USE, HOLD, GOTO, MATCH, REMEMBER, FORGET, BREAK, COMPONENT, ]; pub const ASSIGNATION_RESERVED: &[&str] = &[ - FOREACH, IF, ELSE, IMPORT, AS, DO, EVENT, FLOW, STEP, SAY, USE, HOLD, GOTO, MATCH, REMEMBER, + FOREACH, WHILE , IF, ELSE, IMPORT, AS, DO, EVENT, FLOW, STEP, SAY, USE, HOLD, GOTO, MATCH, REMEMBER, FORGET, _METADATA, _MEMORY, _ENV, TRUE, FALSE, NULL, BREAK, COMPONENT, ]; diff --git a/csml_interpreter/src/interpreter.rs b/csml_interpreter/src/interpreter.rs index f918d30a..2c2d029f 100644 --- a/csml_interpreter/src/interpreter.rs +++ b/csml_interpreter/src/interpreter.rs @@ -12,7 +12,7 @@ use crate::data::position::Position; use crate::data::{ast::*, Data, Hold, IndexInfo, Literal, MessageData, MSG}; use crate::error_format::*; use crate::interpreter::{ - ast_interpreter::{for_loop, match_actions, solve_if_statement}, + ast_interpreter::{for_loop, while_loop, match_actions, solve_if_statement}, variable_handler::{expr_to_literal, interval::interval_from_expr}, }; use crate::parser::ExitCondition; @@ -124,6 +124,16 @@ pub fn interpret_scope( &sender, )? } + Expr::WhileExpr(expr, block, range) => { + message_data = while_loop( + expr, + block, + range, + message_data, + data, + &sender, + )? + } e => { return Err(gen_error_info( Position::new(interval_from_expr(e), &data.context.flow), diff --git a/csml_interpreter/src/interpreter/ast_interpreter.rs b/csml_interpreter/src/interpreter/ast_interpreter.rs index 3c40af22..b4619cb9 100644 --- a/csml_interpreter/src/interpreter/ast_interpreter.rs +++ b/csml_interpreter/src/interpreter/ast_interpreter.rs @@ -1,7 +1,9 @@ mod actions; mod for_loop; +mod while_loop; mod if_statement; pub use actions::match_actions; pub use for_loop::for_loop; +pub use while_loop::while_loop; pub use if_statement::{evaluate_condition, solve_if_statement}; diff --git a/csml_interpreter/src/interpreter/ast_interpreter/if_statement.rs b/csml_interpreter/src/interpreter/ast_interpreter/if_statement.rs index 945079ba..1746d05a 100644 --- a/csml_interpreter/src/interpreter/ast_interpreter/if_statement.rs +++ b/csml_interpreter/src/interpreter/ast_interpreter/if_statement.rs @@ -24,7 +24,7 @@ fn valid_literal(result: Result) -> bool { } //TODO: add warning when comparing some objects -fn valid_condition( +pub fn valid_condition( expr: &Expr, data: &mut Data, msg_data: &mut MessageData, diff --git a/csml_interpreter/src/interpreter/ast_interpreter/while_loop.rs b/csml_interpreter/src/interpreter/ast_interpreter/while_loop.rs new file mode 100644 index 00000000..8908ef27 --- /dev/null +++ b/csml_interpreter/src/interpreter/ast_interpreter/while_loop.rs @@ -0,0 +1,68 @@ +use crate::data::{ + ast::*, + // hold::{ + // hold_index_end_loop, hold_index_start_loop, hold_loop_decrs_index, hold_loop_incrs_index, + // }, + // primitive::tools::get_array, + Data, MessageData, MSG, +}; +use crate::error_format::*; +use crate::interpreter::{ + ast_interpreter::if_statement::valid_condition, + interpret_scope +}; +use crate::parser::ExitCondition; +use std::sync::mpsc; + +//////////////////////////////////////////////////////////////////////////////// +// PUBLIC FUNCTION +//////////////////////////////////////////////////////////////////////////////// + +pub fn while_loop( + cond: &Expr, + block: &Block, + _range_interval: &Interval, + mut msg_data: MessageData, + data: &mut Data, + sender: &Option>, +) -> Result { + // let literal = expr_to_literal(expr, false, None, data, &mut msg_data, sender)?; + + // TODO: hold + // let mut array = get_array(literal, &data.context.flow, ERROR_FOREACH.to_owned())?; + // let mut skip_value = 0; + // let array = hold_index_start_loop(data, &mut array, &mut skip_value); + + while valid_condition(cond, data, &mut msg_data, sender) { + // data.step_vars + // .insert(ident.ident.to_owned(), elem.to_owned()); + // if let Some(index) = index { + // data.step_vars.insert( + // index.ident.to_owned(), + // PrimitiveInt::get_literal(for_loop_index as i64, elem.interval.to_owned()), + // ); + // }; + + // hold_loop_incrs_index(data, for_loop_index + skip_value); + msg_data = msg_data + interpret_scope(block, data, sender)?; + // hold_loop_decrs_index(data); + + + match msg_data.exit_condition { + Some(ExitCondition::Break) => { + msg_data.exit_condition = None; + break; + } + Some(ExitCondition::Continue) => msg_data.exit_condition = None, + Some(_) => break, + None => {} + } + } + + // hold_index_end_loop(data); + // data.step_vars.remove(&ident.ident); + // if let Some(index) = index { + // data.step_vars.remove(&index.ident); + // }; + Ok(msg_data) +} diff --git a/csml_interpreter/src/interpreter/function_scope.rs b/csml_interpreter/src/interpreter/function_scope.rs index 380553a8..ba362580 100644 --- a/csml_interpreter/src/interpreter/function_scope.rs +++ b/csml_interpreter/src/interpreter/function_scope.rs @@ -3,7 +3,7 @@ use crate::data::position::Position; use crate::data::{ast::*, primitive::PrimitiveNull, Data, Literal, MessageData, MSG}; use crate::error_format::*; use crate::interpreter::{ - ast_interpreter::{for_loop, match_actions, solve_if_statement}, + ast_interpreter::{for_loop, while_loop, match_actions, solve_if_statement}, variable_handler::{expr_to_literal, interval::interval_from_expr}, }; use crate::parser::ExitCondition; @@ -36,6 +36,9 @@ fn interpret_function_scope( Expr::ForEachExpr(ident, i, expr, block, range) => { message_data = for_loop(ident, i, expr, block, range, message_data, data, sender)? } + Expr::WhileExpr(expr, block, range) => { + message_data = while_loop(expr, block, range, message_data, data, sender)? + } e => { return Err(gen_error_info( Position::new(interval_from_expr(e), &data.context.flow), diff --git a/csml_interpreter/src/interpreter/variable_handler/interval.rs b/csml_interpreter/src/interpreter/variable_handler/interval.rs index 2d2a1913..67313d8a 100644 --- a/csml_interpreter/src/interpreter/variable_handler/interval.rs +++ b/csml_interpreter/src/interpreter/variable_handler/interval.rs @@ -16,6 +16,7 @@ pub fn interval_from_expr(expr: &Expr) -> Interval { Expr::InfixExpr(_i, expr, _e) => interval_from_expr(expr), // RangeInterval ? Expr::PathExpr { literal, .. } => interval_from_expr(literal), Expr::ForEachExpr(_, _, _, _, range_interval) => *range_interval, + Expr::WhileExpr(_, _, range_interval) => *range_interval, Expr::IdentExpr(ident) => ident.interval.to_owned(), Expr::LitExpr { literal, .. } => literal.interval.to_owned(), Expr::IfExpr(ifstmt) => interval_from_if_stmt(ifstmt), diff --git a/csml_interpreter/src/linter/linter.rs b/csml_interpreter/src/linter/linter.rs index 592f1de3..e67bf75f 100644 --- a/csml_interpreter/src/linter/linter.rs +++ b/csml_interpreter/src/linter/linter.rs @@ -398,6 +398,11 @@ fn validate_scope( validate_scope(block, state, linter_info, step_breakers); state.exit_loop(); } + Expr::WhileExpr(_expr, block, _range) => { + state.enter_loop(); + validate_scope(block, state, linter_info, step_breakers); + state.exit_loop(); + } _ => {} } } diff --git a/csml_interpreter/src/parser.rs b/csml_interpreter/src/parser.rs index e13c9d88..ec862e98 100644 --- a/csml_interpreter/src/parser.rs +++ b/csml_interpreter/src/parser.rs @@ -5,6 +5,7 @@ pub mod parse_built_in; pub mod parse_closure; pub mod parse_comments; pub mod parse_foreach; +pub mod parse_while_loop; pub mod parse_functions; pub mod parse_goto; pub mod parse_idents; diff --git a/csml_interpreter/src/parser/parse_actions.rs b/csml_interpreter/src/parser/parse_actions.rs index 307d4d98..c28154a5 100644 --- a/csml_interpreter/src/parser/parse_actions.rs +++ b/csml_interpreter/src/parser/parse_actions.rs @@ -6,6 +6,7 @@ use crate::parser::{ operator::parse_operator, parse_comments::comment, parse_foreach::parse_foreach, + parse_while_loop::parse_while, parse_goto::parse_goto, parse_previous::parse_previous, parse_idents::{parse_idents_assignation, parse_idents_usage}, @@ -323,6 +324,7 @@ where parse_debug, parse_if, parse_foreach, + parse_while, // only accessible inside foreach or if scopes parse_break, parse_continue, diff --git a/csml_interpreter/src/parser/parse_while_loop.rs b/csml_interpreter/src/parser/parse_while_loop.rs new file mode 100644 index 00000000..f826d1e6 --- /dev/null +++ b/csml_interpreter/src/parser/parse_while_loop.rs @@ -0,0 +1,42 @@ +use crate::data::{ + ast::{Expr}, + tokens::{Span, WHILE, L_PAREN, R_PAREN}, +}; +use crate::parser::operator::parse_operator; +use crate::parser::{ + parse_comments::comment, + parse_scope::parse_scope, + tools::{get_interval}, +}; +use nom::{ + bytes::complete::tag, + combinator::{cut}, + error::ParseError, + sequence::preceded, + *, +}; + +//////////////////////////////////////////////////////////////////////////////// +// PUBLIC FUNCTION +//////////////////////////////////////////////////////////////////////////////// + +pub fn parse_while<'a, E>(s: Span<'a>) -> IResult, Expr, E> +where + E: ParseError>, +{ + let (s, _) = preceded(comment, tag(WHILE))(s)?; + let (s, mut interval) = get_interval(s)?; + + let (s, _) = cut(preceded(comment, tag(L_PAREN)))(s)?; + let (s, expr) = cut(parse_operator)(s)?; + let (s, _) = cut(preceded(comment, tag(R_PAREN)))(s)?; + + let (s, block) = parse_scope(s)?; + let (s, end) = get_interval(s)?; + interval.add_end(end); + + Ok(( + s, + Expr::WhileExpr(Box::new(expr), block, interval), + )) +} diff --git a/csml_interpreter/src/parser/state_context.rs b/csml_interpreter/src/parser/state_context.rs index 0639c6b7..67396c4c 100644 --- a/csml_interpreter/src/parser/state_context.rs +++ b/csml_interpreter/src/parser/state_context.rs @@ -63,6 +63,10 @@ pub fn count_commands(command: &mut Expr, index: &mut usize, info: &mut Instruct info.index = *index; count_scope_commands(block, index) } + Expr::WhileExpr(_expr, block, _range) => { + info.index = *index; + count_scope_commands(block, index) + } _ => {} } From bd1ba192ca7ea6ce92951e55e68eb3e3cfee6d6e Mon Sep 17 00:00:00 2001 From: Alexis Merelo Date: Wed, 20 Oct 2021 13:22:21 -0500 Subject: [PATCH 2/2] add test for while_loops --- .../CSML/basic_test/while_loops.csml | 9 +++++ .../CSML/examples/hello_world.csml | 7 +--- csml_interpreter/tests/while_loop.rs | 36 +++++++++++++++++++ 3 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 csml_interpreter/CSML/basic_test/while_loops.csml create mode 100644 csml_interpreter/tests/while_loop.rs diff --git a/csml_interpreter/CSML/basic_test/while_loops.csml b/csml_interpreter/CSML/basic_test/while_loops.csml new file mode 100644 index 00000000..9cf73ad3 --- /dev/null +++ b/csml_interpreter/CSML/basic_test/while_loops.csml @@ -0,0 +1,9 @@ +start: + do var = 0 + while (var < 5) { + say var + do var = var + 1 + } + + goto end + diff --git a/csml_interpreter/CSML/examples/hello_world.csml b/csml_interpreter/CSML/examples/hello_world.csml index f3a1cc6e..342051db 100644 --- a/csml_interpreter/CSML/examples/hello_world.csml +++ b/csml_interpreter/CSML/examples/hello_world.csml @@ -1,10 +1,5 @@ start: - // say "Hello World 😆" - do var = 4 - while (var > 1) { - say var - do var = var - 1 - } + say "Hello World 😆" goto end diff --git a/csml_interpreter/tests/while_loop.rs b/csml_interpreter/tests/while_loop.rs new file mode 100644 index 00000000..aab4270e --- /dev/null +++ b/csml_interpreter/tests/while_loop.rs @@ -0,0 +1,36 @@ +mod support; + +use csml_interpreter::data::context::Context; +use csml_interpreter::data::event::Event; +use std::collections::HashMap; + +use crate::support::tools::format_message; +use crate::support::tools::message_to_json_value; + +use serde_json::Value; + +#[test] +fn ok_while_loop() { + let data = + r#" + { + "messages":[ + {"content":{ "text": "0" },"content_type":"text"}, + {"content":{ "text": "1" },"content_type":"text"}, + {"content":{ "text": "2" },"content_type":"text"}, + {"content":{ "text": "3" },"content_type":"text"}, + {"content":{ "text": "4" },"content_type":"text"} + ],"memories":[] + } + "#; + let msg = format_message( + Event::new("payload", "", serde_json::json!({})), + Context::new(HashMap::new(), HashMap::new(), None, None, "start", "flow"), + "CSML/basic_test/while_loops.csml", + ); + + let v1: Value = message_to_json_value(msg); + let v2: Value = serde_json::from_str(data).unwrap(); + + assert_eq!(v1, v2) +} \ No newline at end of file