struct_automaton 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "heck"
|
|
7
|
+
version = "0.5.0"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
10
|
+
|
|
11
|
+
[[package]]
|
|
12
|
+
name = "libc"
|
|
13
|
+
version = "0.2.186"
|
|
14
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
15
|
+
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
|
16
|
+
|
|
17
|
+
[[package]]
|
|
18
|
+
name = "once_cell"
|
|
19
|
+
version = "1.21.4"
|
|
20
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
21
|
+
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
22
|
+
|
|
23
|
+
[[package]]
|
|
24
|
+
name = "portable-atomic"
|
|
25
|
+
version = "1.13.1"
|
|
26
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
27
|
+
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
|
|
28
|
+
|
|
29
|
+
[[package]]
|
|
30
|
+
name = "proc-macro2"
|
|
31
|
+
version = "1.0.106"
|
|
32
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
33
|
+
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
|
34
|
+
dependencies = [
|
|
35
|
+
"unicode-ident",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[[package]]
|
|
39
|
+
name = "pyo3"
|
|
40
|
+
version = "0.29.0"
|
|
41
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
42
|
+
checksum = "cd274650b21d4bfc26a0a47587962c1edb425f69287324355cd040c3ea66071c"
|
|
43
|
+
dependencies = [
|
|
44
|
+
"libc",
|
|
45
|
+
"once_cell",
|
|
46
|
+
"portable-atomic",
|
|
47
|
+
"pyo3-build-config",
|
|
48
|
+
"pyo3-ffi",
|
|
49
|
+
"pyo3-macros",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[[package]]
|
|
53
|
+
name = "pyo3-build-config"
|
|
54
|
+
version = "0.29.0"
|
|
55
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
56
|
+
checksum = "c5e2a7d2f0d013342f295c048ad19237add5154a55b1c5a254c0ec93d4109078"
|
|
57
|
+
dependencies = [
|
|
58
|
+
"target-lexicon",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
[[package]]
|
|
62
|
+
name = "pyo3-ffi"
|
|
63
|
+
version = "0.29.0"
|
|
64
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
65
|
+
checksum = "ca85c467da1bbc8d866eea5deff9cf29ea5f7785054a17da36e65bda9c05845b"
|
|
66
|
+
dependencies = [
|
|
67
|
+
"libc",
|
|
68
|
+
"pyo3-build-config",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
[[package]]
|
|
72
|
+
name = "pyo3-macros"
|
|
73
|
+
version = "0.29.0"
|
|
74
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
75
|
+
checksum = "9ac53762fd065daa3194dd09337a38bd793a188100fd1a9304c4ab312d901771"
|
|
76
|
+
dependencies = [
|
|
77
|
+
"proc-macro2",
|
|
78
|
+
"pyo3-macros-backend",
|
|
79
|
+
"quote",
|
|
80
|
+
"syn",
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
[[package]]
|
|
84
|
+
name = "pyo3-macros-backend"
|
|
85
|
+
version = "0.29.0"
|
|
86
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
87
|
+
checksum = "4ca3a1557399783172dc5bf39cfca835157732532cba56b71d2292161e53b362"
|
|
88
|
+
dependencies = [
|
|
89
|
+
"heck",
|
|
90
|
+
"proc-macro2",
|
|
91
|
+
"quote",
|
|
92
|
+
"syn",
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
[[package]]
|
|
96
|
+
name = "quote"
|
|
97
|
+
version = "1.0.46"
|
|
98
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
99
|
+
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
|
|
100
|
+
dependencies = [
|
|
101
|
+
"proc-macro2",
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
[[package]]
|
|
105
|
+
name = "struct_automaton"
|
|
106
|
+
version = "0.1.0"
|
|
107
|
+
dependencies = [
|
|
108
|
+
"pyo3",
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
[[package]]
|
|
112
|
+
name = "syn"
|
|
113
|
+
version = "2.0.118"
|
|
114
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
115
|
+
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
|
116
|
+
dependencies = [
|
|
117
|
+
"proc-macro2",
|
|
118
|
+
"quote",
|
|
119
|
+
"unicode-ident",
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
[[package]]
|
|
123
|
+
name = "target-lexicon"
|
|
124
|
+
version = "0.13.5"
|
|
125
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
126
|
+
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
127
|
+
|
|
128
|
+
[[package]]
|
|
129
|
+
name = "unicode-ident"
|
|
130
|
+
version = "1.0.24"
|
|
131
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
132
|
+
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "struct_automaton"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2024"
|
|
5
|
+
|
|
6
|
+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
7
|
+
[lib]
|
|
8
|
+
name = "struct_automaton"
|
|
9
|
+
crate-type = ["cdylib"]
|
|
10
|
+
|
|
11
|
+
[dependencies]
|
|
12
|
+
pyo3 = "0.29.0"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: struct_automaton
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
7
|
+
Summary: A small example package
|
|
8
|
+
Author-email: Intplex <gabriel08romao0@gmail.com>
|
|
9
|
+
Requires-Python: >=3.8
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["maturin>=1.14,<2.0"]
|
|
3
|
+
build-backend = "maturin"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "struct_automaton"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name="Intplex", email="gabriel08romao0@gmail.com" },
|
|
9
|
+
]
|
|
10
|
+
description = "A small example package"
|
|
11
|
+
requires-python = ">=3.8"
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Programming Language :: Rust",
|
|
14
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
15
|
+
"Programming Language :: Python :: Implementation :: PyPy",
|
|
16
|
+
]
|
|
17
|
+
dynamic = ["version"]
|
|
18
|
+
|
|
19
|
+
[tool.maturin]
|
|
20
|
+
# Força o Maturin a aplicar as regras do auditwheel e gerar tags válidas para o PyPI
|
|
21
|
+
compatibility = "pypi"
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
use pyo3::prelude::*;
|
|
2
|
+
use pyo3::exceptions::{PyValueError, PyTypeError};
|
|
3
|
+
use pyo3::types::{PyAnyMethods, PyList, PyTuple};
|
|
4
|
+
// Importação obrigatória nas novas versões para expor o método de conversão limpa .into_py_any()
|
|
5
|
+
use pyo3::conversion::IntoPyObjectExt;
|
|
6
|
+
use std::collections::{HashMap, HashSet};
|
|
7
|
+
|
|
8
|
+
#[pyclass]
|
|
9
|
+
struct AutomatonStruct {
|
|
10
|
+
states: HashSet<String>,
|
|
11
|
+
alphabet: HashSet<String>,
|
|
12
|
+
data: Py<PyAny>,
|
|
13
|
+
initial: String,
|
|
14
|
+
current_state: String,
|
|
15
|
+
finals: HashSet<String>,
|
|
16
|
+
delta: HashMap<(String, char), String>,
|
|
17
|
+
history: Vec<(String, Py<PyAny>)>,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
#[pymethods]
|
|
21
|
+
impl AutomatonStruct {
|
|
22
|
+
#[new]
|
|
23
|
+
fn new(py: Python<'_>, states: HashSet<String>, alphabet: HashSet<String>, initial: String, finals: HashSet<String>) -> Self {
|
|
24
|
+
AutomatonStruct {
|
|
25
|
+
states,
|
|
26
|
+
alphabet,
|
|
27
|
+
initial: initial.clone(),
|
|
28
|
+
current_state: initial,
|
|
29
|
+
finals,
|
|
30
|
+
delta: HashMap::new(),
|
|
31
|
+
data: py.None(),
|
|
32
|
+
history: Vec::new(),
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
fn add_transition(&mut self, of: String, with: char, for_: String) -> PyResult<()> {
|
|
37
|
+
if !self.states.contains(&of) || !self.states.contains(&for_) {
|
|
38
|
+
return Err(PyValueError::new_err(
|
|
39
|
+
"Erro: Um ou ambos os estados nao pertencem ao automato."
|
|
40
|
+
));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if !self.alphabet.contains(&with.to_string()) {
|
|
44
|
+
return Err(PyValueError::new_err(format!(
|
|
45
|
+
"Erro: O simbolo '{}' nao pertence ao alfabeto do automato.", with
|
|
46
|
+
)));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if self.delta.contains_key(&(of.clone(), with)) {
|
|
50
|
+
return Err(PyValueError::new_err(format!(
|
|
51
|
+
"Erro: O estado '{}' ja possui uma transicao definida para o simbolo '{}'.",
|
|
52
|
+
of, with
|
|
53
|
+
)));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
self.delta.insert((of, with), for_);
|
|
57
|
+
Ok(())
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
fn accept(&self, st: String) -> bool {
|
|
61
|
+
let mut now_state = self.initial.clone();
|
|
62
|
+
|
|
63
|
+
for caracter in st.chars() {
|
|
64
|
+
if let Some(next) = self.delta.get(&(now_state.clone(), caracter)) {
|
|
65
|
+
now_state = next.clone();
|
|
66
|
+
} else {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
self.finals.contains(&now_state)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// --- MÉTODOS DE MANIPULAÇÃO DE DADOS (CONTEXTO) ---
|
|
74
|
+
|
|
75
|
+
fn transform_data(&mut self, py: Python<'_>, data_type_result: String, operation: String, assert_val: &Bound<'_, PyAny>) -> PyResult<()> {
|
|
76
|
+
let current_bound = self.data.bind(py);
|
|
77
|
+
|
|
78
|
+
if operation == "clear" {
|
|
79
|
+
self.data = py.None();
|
|
80
|
+
return Ok(());
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if data_type_result == "str" {
|
|
84
|
+
let mut texto_base = match current_bound.extract::<String>() {
|
|
85
|
+
Ok(t) => t,
|
|
86
|
+
Err(_) => current_bound.to_string(),
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
if operation == "concat" {
|
|
90
|
+
let sufixo = assert_val.extract::<String>()?;
|
|
91
|
+
texto_base.push_str(&sufixo);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// RESOLUÇÃO: O método do PyO3 converte e entrega diretamente o Py<PyAny>
|
|
95
|
+
self.data = texto_base.into_py_any(py)?;
|
|
96
|
+
|
|
97
|
+
} else if data_type_result == "int" {
|
|
98
|
+
let mut numero_base = current_bound.extract::<i64>().unwrap_or(0);
|
|
99
|
+
|
|
100
|
+
if operation == "add" {
|
|
101
|
+
let valor_somar = assert_val.extract::<i64>()?;
|
|
102
|
+
numero_base += valor_somar;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// RESOLUÇÃO: O método do PyO3 converte e entrega diretamente o Py<PyAny>
|
|
106
|
+
self.data = numero_base.into_py_any(py)?;
|
|
107
|
+
} else {
|
|
108
|
+
return Err(PyTypeError::new_err("Tipo de dado nao suportado. Use 'str' ou 'int'."));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
Ok(())
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
fn update_context(&mut self, new_data: &Bound<'_, PyAny>) -> PyResult<()> {
|
|
115
|
+
self.data = new_data.clone().unbind();
|
|
116
|
+
Ok(())
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
fn get_context_type(&self, py: Python<'_>) -> String {
|
|
120
|
+
match self.data.bind(py).get_type().name() {
|
|
121
|
+
Ok(n) => n.to_string(),
|
|
122
|
+
Err(_) => String::from("unknown"),
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
#[getter]
|
|
127
|
+
fn data(&self, py: Python<'_>) -> Py<PyAny> {
|
|
128
|
+
self.data.clone_ref(py)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// --- MÉTODOS DE CONTROLE DO AUTÔMATO ---
|
|
132
|
+
|
|
133
|
+
fn step(&mut self, py: Python<'_>, symbol: char) -> PyResult<bool> {
|
|
134
|
+
self.history.push((self.current_state.clone(), self.data.clone_ref(py)));
|
|
135
|
+
|
|
136
|
+
if let Some(next_state) = self.delta.get(&(self.current_state.clone(), symbol)) {
|
|
137
|
+
self.current_state = next_state.clone();
|
|
138
|
+
Ok(true)
|
|
139
|
+
} else {
|
|
140
|
+
Ok(false)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
fn force_state(&mut self, py: Python<'_>, state_id: String) -> PyResult<()> {
|
|
145
|
+
if !self.states.contains(&state_id) {
|
|
146
|
+
return Err(PyValueError::new_err("O estado destino nao existe no automato."));
|
|
147
|
+
}
|
|
148
|
+
self.history.push((self.current_state.clone(), self.data.clone_ref(py)));
|
|
149
|
+
self.current_state = state_id;
|
|
150
|
+
Ok(())
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
fn undo(&mut self) -> PyResult<bool> {
|
|
154
|
+
if let Some((previous_state, previous_data)) = self.history.pop() {
|
|
155
|
+
self.current_state = previous_state;
|
|
156
|
+
self.data = previous_data;
|
|
157
|
+
Ok(true)
|
|
158
|
+
} else {
|
|
159
|
+
Ok(false)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
fn reset(&mut self, py: Python<'_>) {
|
|
164
|
+
self.current_state = self.initial.clone();
|
|
165
|
+
self.data = py.None();
|
|
166
|
+
self.history.clear();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
fn is_accepted(&self) -> bool {
|
|
170
|
+
self.finals.contains(&self.current_state)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
#[getter]
|
|
174
|
+
fn current_state(&self) -> String {
|
|
175
|
+
self.current_state.clone()
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// --- NOVOS MÉTODOS DE CONSULTA E CONTROLE DO AUTÔMATO ---
|
|
179
|
+
|
|
180
|
+
fn remove_transition(&mut self, of: String, with: char) -> PyResult<()> {
|
|
181
|
+
if self.delta.remove(&(of.clone(), with)).is_none() {
|
|
182
|
+
return Err(PyValueError::new_err(format!(
|
|
183
|
+
"Erro: Nao existe transicao definida para o estado '{}' com o simbolo '{}'.",
|
|
184
|
+
of, with
|
|
185
|
+
)));
|
|
186
|
+
}
|
|
187
|
+
Ok(())
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
fn has_transition(&self, of: String, with: char) -> bool {
|
|
191
|
+
self.delta.contains_key(&(of, with))
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
fn is_final(&self, state: String) -> bool {
|
|
195
|
+
self.finals.contains(&state)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
fn get_states(&self) -> Vec<String> {
|
|
199
|
+
let mut v: Vec<String> = self.states.iter().cloned().collect();
|
|
200
|
+
v.sort();
|
|
201
|
+
v
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
fn get_alphabet(&self) -> Vec<String> {
|
|
205
|
+
let mut v: Vec<String> = self.alphabet.iter().cloned().collect();
|
|
206
|
+
v.sort();
|
|
207
|
+
v
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
fn get_finals(&self) -> Vec<String> {
|
|
211
|
+
let mut v: Vec<String> = self.finals.iter().cloned().collect();
|
|
212
|
+
v.sort();
|
|
213
|
+
v
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
#[getter]
|
|
217
|
+
fn initial_state(&self) -> String {
|
|
218
|
+
self.initial.clone()
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/// Retorna todas as transições como tuplas (origem, símbolo, destino).
|
|
222
|
+
fn get_transitions(&self) -> Vec<(String, char, String)> {
|
|
223
|
+
self.delta
|
|
224
|
+
.iter()
|
|
225
|
+
.map(|((of, sym), to)| (of.clone(), *sym, to.clone()))
|
|
226
|
+
.collect()
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/// Retorna os símbolos que possuem transição definida a partir do estado atual.
|
|
230
|
+
fn available_symbols(&self) -> Vec<char> {
|
|
231
|
+
let mut v: Vec<char> = self
|
|
232
|
+
.delta
|
|
233
|
+
.keys()
|
|
234
|
+
.filter(|(of, _)| *of == self.current_state)
|
|
235
|
+
.map(|(_, sym)| *sym)
|
|
236
|
+
.collect();
|
|
237
|
+
v.sort();
|
|
238
|
+
v
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/// Simula o consumo de uma cadeia a partir do estado atual SEM alterar o estado
|
|
242
|
+
/// do autômato, retornando o caminho de estados percorrido. Falha se, em algum
|
|
243
|
+
/// ponto, não houver transição definida para o símbolo lido.
|
|
244
|
+
fn simulate(&self, input: String) -> PyResult<Vec<String>> {
|
|
245
|
+
let mut path = vec![self.current_state.clone()];
|
|
246
|
+
let mut now_state = self.current_state.clone();
|
|
247
|
+
|
|
248
|
+
for caracter in input.chars() {
|
|
249
|
+
match self.delta.get(&(now_state.clone(), caracter)) {
|
|
250
|
+
Some(next) => {
|
|
251
|
+
now_state = next.clone();
|
|
252
|
+
path.push(now_state.clone());
|
|
253
|
+
}
|
|
254
|
+
None => {
|
|
255
|
+
return Err(PyValueError::new_err(format!(
|
|
256
|
+
"Erro: Nao ha transicao definida a partir do estado '{}' com o simbolo '{}'.",
|
|
257
|
+
now_state, caracter
|
|
258
|
+
)));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
Ok(path)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// --- NOVOS MÉTODOS DE CONTROLE DE DADOS (CONTEXTO) ---
|
|
266
|
+
|
|
267
|
+
/// Substitui o dado de contexto diretamente (alias explícito de update_context).
|
|
268
|
+
fn set_data(&mut self, new_data: &Bound<'_, PyAny>) -> PyResult<()> {
|
|
269
|
+
self.update_context(new_data)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/// Zera o dado de contexto, sem afetar o estado atual do autômato.
|
|
273
|
+
fn clear_data(&mut self, py: Python<'_>) {
|
|
274
|
+
self.data = py.None();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/// Limpa o histórico de (estado, dado) acumulado, sem alterar estado ou dado atuais.
|
|
278
|
+
fn clear_history(&mut self) {
|
|
279
|
+
self.history.clear();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/// Quantidade de entradas atualmente registradas no histórico.
|
|
283
|
+
fn history_len(&self) -> usize {
|
|
284
|
+
self.history.len()
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/// Observa a última entrada do histórico (estado, dado) sem removê-la.
|
|
288
|
+
fn peek_history(&self, py: Python<'_>) -> Option<(String, Py<PyAny>)> {
|
|
289
|
+
self.history
|
|
290
|
+
.last()
|
|
291
|
+
.map(|(state, data)| (state.clone(), data.clone_ref(py)))
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
fn get_history(&self, py: Python<'_>) -> PyResult<Py<PyList>> {
|
|
295
|
+
let list = PyList::empty(py);
|
|
296
|
+
for (state, data) in &self.history {
|
|
297
|
+
// RESOLUÇÃO: Converte a String nativa em objeto genérico do Python e anexa de forma limpa na tupla
|
|
298
|
+
let py_state = state.clone().into_py_any(py)?;
|
|
299
|
+
let py_data = data.clone_ref(py);
|
|
300
|
+
|
|
301
|
+
let tuple = PyTuple::new(py, &[py_state, py_data])?;
|
|
302
|
+
list.append(tuple)?;
|
|
303
|
+
}
|
|
304
|
+
Ok(list.unbind())
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
#[pymodule]
|
|
309
|
+
fn struct_automaton(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
310
|
+
m.add_class::<AutomatonStruct>()?;
|
|
311
|
+
Ok(())
|
|
312
|
+
}
|