csml_interpreter/parser/
step_checksum.rs

1use crate::data::{ast::*, tokens::*};
2use crate::error_format::*;
3use crate::interpreter::variable_handler::interval::interval_from_expr;
4use crate::parser::parse_comments::comment;
5
6use nom::{
7    bytes::complete::take_while1,
8    error::{ContextError, ParseError},
9    multi::fold_many0,
10    sequence::preceded,
11    Err, IResult, InputTake,
12};
13
14////////////////////////////////////////////////////////////////////////////////
15// PRIVATE FUNCTIONS
16////////////////////////////////////////////////////////////////////////////////
17
18fn get_text<'a, E>(s: Span<'a>) -> IResult<Span<'a>, &'a str, E>
19where
20    E: ParseError<Span<'a>> + ContextError<Span<'a>>,
21{
22    let (rest, string) = take_while1(|c: char| !WHITE_SPACE.contains(c))(s)?;
23
24    Ok((rest, (*string.fragment())))
25}
26
27fn clean_text<'a, E>(s: Span<'a>) -> IResult<Span<'a>, String, E>
28where
29    E: ParseError<Span<'a>> + ContextError<Span<'a>>,
30{
31    let (span, vec) = fold_many0(preceded(comment, get_text), Vec::new, |mut acc, item| {
32        acc.push(item);
33        acc
34    })(s)?;
35
36    let s: String = vec.into_iter().collect();
37    Ok((span, s))
38}
39
40fn get_step_offset(
41    name: &str,
42    offsets: &[(String, usize)],
43) -> ((String, usize), Option<(String, usize)>) {
44    let mut step_info = None;
45    let mut index = 0;
46
47    for (i, (step_name, offset)) in offsets.iter().enumerate() {
48        if step_name == name {
49            step_info = Some((step_name.to_owned(), offset.to_owned()));
50            index = i;
51            break;
52        }
53    }
54
55    match step_info {
56        Some(step_info) => {
57            if offsets.len() > index + 1 {
58                let next_step = offsets[index + 1].clone();
59                (step_info, Some(next_step))
60            } else {
61                (step_info, None)
62            }
63        }
64        None => unreachable!(),
65    }
66}
67
68fn get_skip_offset(skip_offsets: &[usize], offset: usize) -> Option<usize> {
69    for skip_offset in skip_offsets.iter() {
70        if *skip_offset > offset {
71            return Some(*skip_offset);
72        }
73    }
74    None
75}
76
77fn get_next_offset(
78    offset: usize,
79    next_step: Option<(String, usize)>,
80    skip_offsets: &[usize],
81) -> Option<usize> {
82    match next_step {
83        Some((_, next_step_offset)) => match get_skip_offset(skip_offsets, offset) {
84            Some(skip_offset) => {
85                if skip_offset > next_step_offset {
86                    Some(skip_offset)
87                } else {
88                    Some(next_step_offset)
89                }
90            }
91            None => Some(next_step_offset),
92        },
93        None => get_skip_offset(skip_offsets, offset),
94    }
95}
96
97fn get_offsets(ast: &Flow) -> (Vec<(String, usize)>, Vec<usize>) {
98    let mut offsets = vec![];
99    let mut skip_offsets = vec![];
100
101    for (instruction_type, block) in ast.flow_instructions.iter() {
102        match instruction_type {
103            InstructionScope::StepScope(name) | InstructionScope::Constant(name) => {
104                let interval = interval_from_expr(block);
105                offsets.push((name.to_owned(), interval.offset))
106            }
107            InstructionScope::FunctionScope { .. }
108            | InstructionScope::ImportScope(_)
109            | InstructionScope::InsertStep(_) => {
110                let interval = interval_from_expr(block);
111                skip_offsets.push(interval.offset)
112            }
113            InstructionScope::DuplicateInstruction(..) => {}
114        }
115    }
116    offsets.sort_by(|(_, a), (_, b)| a.cmp(b));
117    skip_offsets.sort_by(|a, b| a.cmp(b));
118
119    (offsets, skip_offsets)
120}
121
122////////////////////////////////////////////////////////////////////////////////
123// PUBLIC FUNCTIONS
124////////////////////////////////////////////////////////////////////////////////
125
126pub fn get_step<'a>(step_name: &'a str, flow: &'a str, ast: &'a Flow) -> String {
127    let (offsets, skip_offsets) = get_offsets(ast);
128    let span = Span::new(flow);
129
130    let ((_, offset), next_step) = get_step_offset(step_name, &offsets);
131    let (new, _) = span.take_split(offset);
132    match get_next_offset(offset, next_step, &skip_offsets) {
133        Some(skip_offset) => {
134            let (_, old) = new.take_split(skip_offset - offset);
135            old.fragment().to_string()
136        }
137        None => match clean_text::<CustomError<Span<'a>>>(new) {
138            Ok((_s, string)) => string,
139            Err(e) => match e {
140                Err::Error(_) | Err::Failure(_) => unreachable!(),
141                Err::Incomplete(_err) => unreachable!(),
142            },
143        },
144    }
145}