mcfunction_debugger/generator/
partition.rs

1// Mcfunction-Debugger is a debugger for Minecraft's *.mcfunction files that does not require any
2// Minecraft mods.
3//
4// © Copyright (C) 2021-2024 Adrodoc <adrodoc55@googlemail.com> & Skagaros <skagaros@gmail.com>
5//
6// This file is part of Mcfunction-Debugger.
7//
8// Mcfunction-Debugger is free software: you can redistribute it and/or modify it under the terms of
9// the GNU General Public License as published by the Free Software Foundation, either version 3 of
10// the License, or (at your option) any later version.
11//
12// Mcfunction-Debugger is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License along with Mcfunction-Debugger.
17// If not, see <http://www.gnu.org/licenses/>.
18
19use crate::generator::parser::{
20    command::{argument::minecraft::MinecraftEntityAnchor, resource_location::ResourceLocation},
21    Line, SelectorValue,
22};
23use std::{collections::BTreeMap, fmt::Display, str::FromStr};
24
25pub(crate) struct Partition<'l> {
26    pub(crate) start: Position,
27    pub(crate) end: Position,
28    pub(crate) regular_lines: &'l [(usize, String, Line)],
29    pub(crate) terminator: Terminator<'l>,
30}
31
32pub(crate) enum Terminator<'l> {
33    ConfigurableBreakpoint {
34        position_in_line: SuspensionPositionInLine,
35    },
36    FunctionCall {
37        column_index: usize,
38        line: &'l str,
39        name: &'l ResourceLocation,
40        anchor: &'l Option<MinecraftEntityAnchor>,
41        selectors: &'l BTreeMap<usize, SelectorValue>,
42    },
43    Return,
44}
45
46pub(crate) fn partition<'l>(lines: &'l [(usize, String, Line)]) -> Vec<Partition<'l>> {
47    let mut partitions = Vec::new();
48    let mut end = Position {
49        line_number: 1,
50        position_in_line: PositionInLine::Entry,
51    };
52
53    // TODO: Can we remove line_number from the triple?
54    for (line_index, (_line_number, line, command)) in lines.iter().enumerate() {
55        let line_number = line_index + 1;
56        if let Line::Empty | Line::Comment = command {
57            continue;
58        }
59        let start = end;
60        end = Position {
61            line_number,
62            position_in_line: PositionInLine::Breakpoint,
63        };
64        let include_start_line = start.position_in_line == PositionInLine::Entry
65            || start.position_in_line == PositionInLine::Breakpoint;
66        let start_regular_line_index = start.line_number - if include_start_line { 1 } else { 0 };
67        partitions.push(Partition {
68            start,
69            end,
70            regular_lines: &lines[start_regular_line_index..line_index],
71            terminator: Terminator::ConfigurableBreakpoint {
72                position_in_line: SuspensionPositionInLine::Breakpoint,
73            },
74        });
75
76        if let Line::FunctionCall {
77            column_index,
78            name,
79            anchor,
80            selectors,
81            ..
82        } = command
83        {
84            let start = end;
85            end = Position {
86                line_number,
87                position_in_line: PositionInLine::Function,
88            };
89            partitions.push(Partition {
90                start,
91                end,
92                regular_lines: &[],
93                terminator: Terminator::FunctionCall {
94                    column_index: *column_index,
95                    line,
96                    name,
97                    anchor,
98                    selectors,
99                },
100            });
101        }
102    }
103    if end.position_in_line == PositionInLine::Entry {
104        let start = end;
105        end = Position {
106            line_number: start.line_number,
107            position_in_line: PositionInLine::Breakpoint,
108        };
109        partitions.push(Partition {
110            start,
111            end,
112            regular_lines: &[],
113            terminator: Terminator::ConfigurableBreakpoint {
114                position_in_line: SuspensionPositionInLine::Breakpoint,
115            },
116        })
117    }
118
119    if end.position_in_line == PositionInLine::Function {
120        let start = end;
121        let last_line = start.line_number >= lines.len();
122        let line_number = start.line_number + if last_line { 0 } else { 1 };
123        let position_in_line = if last_line {
124            SuspensionPositionInLine::AfterFunction
125        } else {
126            SuspensionPositionInLine::Breakpoint
127        };
128        end = Position {
129            line_number,
130            position_in_line: position_in_line.into(),
131        };
132        partitions.push(Partition {
133            start,
134            end,
135            regular_lines: &[],
136            terminator: Terminator::ConfigurableBreakpoint { position_in_line },
137        });
138    }
139
140    let start = end;
141    end = Position {
142        line_number: lines.len(),
143        position_in_line: PositionInLine::Return,
144    };
145    let start_regular_line_index = start.line_number
146        - if start.position_in_line == PositionInLine::Breakpoint {
147            1
148        } else {
149            0
150        };
151    partitions.push(Partition {
152        start,
153        end,
154        regular_lines: &lines[start_regular_line_index..lines.len()],
155        terminator: Terminator::Return,
156    });
157    partitions
158}
159
160#[derive(Clone, Copy, Debug, Eq, PartialEq)]
161pub(crate) struct Position {
162    pub(crate) line_number: usize,
163    pub(crate) position_in_line: PositionInLine,
164}
165impl FromStr for Position {
166    type Err = ();
167
168    fn from_str(s: &str) -> Result<Self, Self::Err> {
169        fn from_str_inner(s: &str) -> Option<Position> {
170            let (line_number, position_in_line) = s.split_once('_')?;
171            let line_number = line_number.parse().ok()?;
172            let position_in_line = position_in_line.parse().ok()?;
173            Some(Position {
174                line_number,
175                position_in_line,
176            })
177        }
178        from_str_inner(s).ok_or(())
179    }
180}
181impl Display for Position {
182    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183        write!(f, "{}_{}", self.line_number, self.position_in_line)
184    }
185}
186#[derive(Clone, Copy, Debug, Eq, PartialEq)]
187pub(crate) enum PositionInLine {
188    Entry,
189    Breakpoint,
190    Function,
191    AfterFunction,
192    Return,
193}
194impl FromStr for PositionInLine {
195    type Err = ();
196
197    fn from_str(s: &str) -> Result<Self, Self::Err> {
198        match s {
199            "entry" => Ok(PositionInLine::Entry),
200            "breakpoint" => Ok(PositionInLine::Breakpoint),
201            "function" => Ok(PositionInLine::Function),
202            "after_function" => Ok(PositionInLine::AfterFunction),
203            "return" => Ok(PositionInLine::Return),
204            _ => Err(()),
205        }
206    }
207}
208impl Display for PositionInLine {
209    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210        match self {
211            PositionInLine::Entry => write!(f, "entry"),
212            PositionInLine::Breakpoint => write!(f, "breakpoint"),
213            PositionInLine::Function => write!(f, "function"),
214            PositionInLine::AfterFunction => write!(f, "after_function"),
215            PositionInLine::Return => write!(f, "return"),
216        }
217    }
218}
219
220#[derive(Clone, Copy, Debug, Eq, PartialEq)]
221pub enum SuspensionPositionInLine {
222    Breakpoint,
223    AfterFunction,
224}
225impl From<SuspensionPositionInLine> for PositionInLine {
226    fn from(value: SuspensionPositionInLine) -> Self {
227        match value {
228            SuspensionPositionInLine::Breakpoint => PositionInLine::Breakpoint,
229            SuspensionPositionInLine::AfterFunction => PositionInLine::AfterFunction,
230        }
231    }
232}
233impl FromStr for SuspensionPositionInLine {
234    type Err = ();
235
236    fn from_str(s: &str) -> Result<Self, Self::Err> {
237        match s {
238            "breakpoint" => Ok(SuspensionPositionInLine::Breakpoint),
239            "after_function" => Ok(SuspensionPositionInLine::AfterFunction),
240            _ => Err(()),
241        }
242    }
243}
244impl Display for SuspensionPositionInLine {
245    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
246        match self {
247            SuspensionPositionInLine::Breakpoint => write!(f, "breakpoint"),
248            SuspensionPositionInLine::AfterFunction => write!(f, "after_function"),
249        }
250    }
251}