pub mod data;
pub mod error_format;
pub mod interpreter;
pub mod linter;
pub mod parser;
pub use interpreter::components::load_components;
use interpreter::interpret_scope;
use parser::parse_flow;
use data::ast::{Expr, Flow, ImportScope, InstructionScope, Interval};
use data::context::get_hashmap_from_mem;
use data::csml_bot::CsmlBot;
use data::csml_result::CsmlResult;
use data::error_info::ErrorInfo;
use data::event::Event;
use data::message_data::MessageData;
use data::msg::MSG;
use data::warnings::Warnings;
use data::ContextJson;
use data::Data;
use data::Position;
use error_format::*;
use linter::data::Linter;
use linter::linter::lint_flow;
use parser::ExitCondition;
use std::collections::HashMap;
use std::sync::mpsc;
fn execute_step(
step: &str,
flow: &Flow,
mut data: &mut Data,
instruction_index: &Option<usize>,
sender: &Option<mpsc::Sender<MSG>>,
) -> MessageData {
let mut msg_data = match flow
.flow_instructions
.get(&InstructionScope::StepScope(step.to_owned()))
{
Some(Expr::Scope { scope, .. }) => {
Position::set_step(&step);
interpret_scope(scope, &mut data, &instruction_index, &sender)
}
_ => Err(gen_error_info(
Position::new(Interval::new_as_u32(0, 0)),
format!("[{}] {}", step, ERROR_STEP_EXIST),
)),
};
if let Ok(msg_data) = &mut msg_data {
match &mut msg_data.exit_condition {
Some(condition) if *condition == ExitCondition::Goto => {
msg_data.exit_condition = None;
}
Some(_) => (),
None => {
msg_data.exit_condition = Some(ExitCondition::End);
data.context.step = "end".to_string();
MSG::send(
&sender,
MSG::Next {
flow: None,
step: Some("end".to_owned()),
},
);
}
}
}
MessageData::error_to_message(msg_data, sender)
}
pub fn get_steps_from_flow(bot: CsmlBot) -> HashMap<String, Vec<String>> {
let mut result = HashMap::new();
for flow in bot.flows.iter() {
if let Ok(parsed_flow) = parse_flow(&flow.content) {
let mut vec = vec![];
for instruction_type in parsed_flow.flow_instructions.keys() {
if let InstructionScope::StepScope(step_name) = instruction_type {
vec.push(step_name.to_owned());
}
}
result.insert(flow.name.to_owned(), vec);
}
}
Warnings::clear();
Linter::clear();
result
}
fn validate_imports(
bot: &HashMap<String, Flow>,
imports: Vec<ImportScope>,
errors: &mut Vec<ErrorInfo>,
) {
for import in imports.iter() {
match search_function(bot, import) {
Ok(_) => {}
Err(err) => errors.push(err),
}
}
}
fn get_function<'a>(
flow: &'a Flow,
fn_name: &str,
original_name: &Option<String>,
) -> Option<(Vec<String>, Expr, &'a Flow)> {
let name = match original_name {
Some(original_name) => original_name.to_owned(),
None => fn_name.to_owned(),
};
if let (InstructionScope::FunctionScope { name: _, args }, expr) = flow
.flow_instructions
.get_key_value(&InstructionScope::FunctionScope {
name,
args: Vec::new(),
})?
{
return Some((args.to_owned(), expr.to_owned(), flow));
}
None
}
pub fn search_function<'a>(
bot: &'a HashMap<String, Flow>,
import: &ImportScope,
) -> Result<(Vec<String>, Expr, &'a Flow), ErrorInfo> {
match &import.from_flow {
Some(flow_name) => match bot.get(flow_name) {
Some(flow) => {
get_function(flow, &import.name, &import.original_name).ok_or(ErrorInfo {
position: import.position.clone(),
message: format!("function '{}' not found in bot", import.name),
})
}
None => Err(ErrorInfo {
position: import.position.clone(),
message: format!("function '{}' not found in bot", import.name),
}),
},
None => {
for (_name, flow) in bot.iter() {
if let Some(values) = get_function(flow, &import.name, &import.original_name) {
return Ok(values);
}
}
Err(ErrorInfo {
position: import.position.clone(),
message: format!("function '{}' not found in bot", import.name),
})
}
}
}
pub fn validate_bot(bot: CsmlBot) -> CsmlResult {
let mut flows = HashMap::default();
let mut errors = Vec::new();
let mut imports = Vec::new();
for flow in bot.flows.iter() {
Position::set_flow(&flow.name);
Linter::add_flow(&flow.name);
match parse_flow(&flow.content) {
Ok(ast_flow) => {
for (scope, ..) in ast_flow.flow_instructions.iter() {
if let InstructionScope::ImportScope(import_scope) = scope {
imports.push(import_scope.clone());
}
}
flows.insert(flow.name.to_owned(), ast_flow);
}
Err(error) => {
errors.push(error);
}
}
}
let warnings = Warnings::get();
lint_flow(&bot, &mut errors);
validate_imports(&flows, imports, &mut errors);
Warnings::clear();
Linter::clear();
CsmlResult::new(flows, warnings, errors)
}
pub fn interpret(
bot: CsmlBot,
context: ContextJson,
event: Event,
sender: Option<mpsc::Sender<MSG>>,
) -> MessageData {
let mut msg_data = MessageData::default();
let mut context = context.to_literal();
let mut flow = context.flow.to_owned();
let mut step = context.step.to_owned();
let mut step_vars = match &context.hold {
Some(hold) => get_hashmap_from_mem(&hold.step_vars),
None => HashMap::new(),
};
let mut instruction_index = match context.hold {
Some(result) => {
context.hold = None;
Some(result.index)
}
None => None,
};
let native = match bot.native_components {
Some(ref obj) => obj.to_owned(),
None => serde_json::Map::new(),
};
let custom = match bot.custom_components {
Some(serde_json::Value::Object(ref obj)) => obj.to_owned(),
_ => serde_json::Map::new(),
};
let bot = validate_bot(bot);
let flows = match bot.flows {
Some(flows) => flows,
None => HashMap::new(),
};
while msg_data.exit_condition.is_none() {
Position::set_flow(&flow);
let ast = match flows.get(&flow) {
Some(result) => result.to_owned(),
None => {
return MessageData::error_to_message(
Err(ErrorInfo {
position: Position {
flow: flow.clone(),
step: "start".to_owned(),
interval: Interval::default(),
},
message: format!("flow '{}' dose not exist in this bot", flow),
}),
&sender,
);
}
};
let mut data = Data::new(
&flows,
&ast,
&mut context,
&event,
step_vars,
&custom,
&native,
);
msg_data = msg_data + execute_step(&step, &ast, &mut data, &instruction_index, &sender);
flow = data.context.flow.to_string();
step = data.context.step.to_string();
step_vars = HashMap::new();
instruction_index = None;
}
msg_data
}