1pub mod analyzer;
2pub mod builder;
3pub mod iter;
4pub mod macros;
5pub mod rnode;
6pub mod transform;
7
8use crate::runtime::action::ActionName;
9use crate::runtime::args::transform::{to_dec_rt_args, to_rt_args};
10
11use crate::runtime::rtree::rnode::{DecoratorType, RNode, RNodeId};
12use crate::runtime::rtree::transform::{StackItem, Transformer};
13use crate::runtime::{RtOk, RtResult, RuntimeError};
14use crate::tree::parser::ast::call::Call;
15
16use crate::runtime::rtree::analyzer::RtTreeAnalyzer;
17use crate::runtime::rtree::iter::RtTreeBfsIter;
18use crate::tree::project::imports::ImportMap;
19use crate::tree::project::{FileName, Project};
20use crate::tree::{cerr, TreeError};
21use std::collections::{HashMap, HashSet, VecDeque};
22use std::path::PathBuf;
23use crate::converter::Converter;
24use crate::converter::to_nav::ToRosNavConverter;
25use log::debug;
26
27pub struct RuntimeTreeStarter {
30 pub tree: RuntimeTree,
31 pub std_actions: HashSet<(ActionName, FileName)>,
35 pub actions: HashSet<ActionName>,
36}
37
38#[derive(Default, Debug, PartialEq)]
40pub struct RuntimeTree {
41 pub root: RNodeId,
42 pub nodes: HashMap<RNodeId, RNode>,
43}
44
45impl RuntimeTree {
46 pub fn iter(&self) -> RtTreeBfsIter<'_> {
48 RtTreeBfsIter {
49 queue: VecDeque::from(vec![self.root]),
50 tree: &self,
51 }
52 }
53 pub fn analyze(&self) -> RtTreeAnalyzer<'_> {
94 RtTreeAnalyzer::new(self)
95 }
96 pub fn build(project: Project) -> Result<RuntimeTreeStarter, TreeError> {
98 let (file, name) = &project.main;
99 let root = project.find_root(name, file)?;
100 let mut builder = Transformer::default();
101 let mut r_tree = RuntimeTree::default();
102 let mut std_actions = HashSet::new();
103 let mut actions = HashSet::new();
104
105 let root_id = builder.next();
106 builder.add_chain_root(root_id);
107
108 let children = builder.push_vec(root.calls.clone(), root_id, file.clone());
109 let root_node = RNode::root(root.name.to_string(), file.clone(), children);
110 r_tree.root = root_id;
111 r_tree.nodes.insert(root_id, root_node);
112
113 while let Some(item) = builder.pop() {
114 let StackItem {
115 id,
116 call,
117 parent_id,
118 file_name,
119 } = item;
120
121 let curr_file = &project.find_file(file_name.as_str())?;
122 let import_map = ImportMap::build(curr_file)?;
123 match call {
124 Call::Lambda(tpe, calls) => {
126 debug!(target:"tree[construct]", "found lambda {tpe}: id {id} and parent {parent_id}");
127 let children = builder.push_vec(calls, id, file_name.clone());
128 builder.add_chain_lambda(id, parent_id);
129 r_tree
130 .nodes
131 .insert(id, RNode::lambda(tpe.try_into()?, children));
132 }
133 Call::HoInvocation(key) => {
139 debug!(target:"tree[construct]", "found ho invocation with id {id} in parent {parent_id}");
140 let (p_id, _parent_args, _parent_params) =
141 builder.get_chain_skip_lambda(&parent_id)?.get_tree();
142 let call = builder.find_ho_call(&parent_id, &key)?;
143 if call.is_lambda() || call.is_decorator() {
144 builder.push_front(id, call, p_id, file_name.clone());
145 } else {
146 let k = call
147 .key()
148 .ok_or(cerr(format!("the call {:?} does not have a name. Therefore, it is no possible to invoke it by name.", call)))?;
149
150 builder.push_front(
151 id,
152 Call::invocation(&k, call.arguments()),
153 p_id,
154 file_name.clone(),
155 );
156 }
157 }
158 Call::Decorator(tpe, decor_args, call) => {
160 debug!(target:"tree[construct]", "found decorator {tpe}, id {id} in parent {parent_id}");
161 let (_, parent_args, parent_params) =
162 builder.get_chain_skip_lambda(&parent_id)?.get_tree();
163 builder.add_chain(id, parent_id, parent_args.clone(), parent_params.clone());
164 let child = builder.push(*call, id, file.clone());
165 let d_tpe: DecoratorType = tpe.try_into()?;
166 let rt_args = to_dec_rt_args(&d_tpe, decor_args, parent_args, parent_params)?;
167 r_tree
168 .nodes
169 .insert(id, RNode::decorator(d_tpe, rt_args, child));
170 }
171 Call::Invocation(name, args) => {
174 debug!(target:"tree[construct]", "found invocation , id {id} in parent {parent_id}");
175 let (_, parent_args, parent_params) = builder
176 .get_chain_skip_lambda(&parent_id)
177 .map(|e| e.get_tree())
178 .unwrap_or_default();
179 match curr_file.definitions.get(&name) {
180 Some(tree) => {
181 let (rt_args, upd_args) = to_rt_args(
182 name.as_str(),
183 args.clone(),
184 tree.params.clone(),
185 parent_args,
186 parent_params,
187 )?;
188 builder.add_chain(id, parent_id, upd_args, tree.params.clone());
189 if tree.tpe.is_action() {
190 r_tree.nodes.insert(id, RNode::action(name, curr_file.name.clone(), rt_args));
191 actions.insert(tree.name.clone());
192 } else {
193 let children =
194 builder.push_vec(tree.calls.clone(), id, file_name.clone());
195 r_tree.nodes.insert(
196 id,
197 RNode::flow(tree.tpe.try_into()?, name, curr_file.name.clone(), rt_args, children),
198 );
199 }
200 }
201 None => {
202 debug!(target:"tree[construct]", "found import from another file, id {id} in parent {parent_id}");
203 let (tree, file) = import_map.find(&name, &project)?;
204 if file.contains("::") {
205 std_actions.insert((tree.name.clone(), file.clone()));
206 }
207 let (rt_args, upd_args) = to_rt_args(
208 name.as_str(),
209 args.clone(),
210 tree.params.clone(),
211 parent_args,
212 parent_params,
213 )?;
214 builder.add_chain(id, parent_id, upd_args, tree.params.clone());
215 let children =
216 builder.push_vec(tree.calls.clone(), id, file_name.clone());
217
218 if tree.name != name {
219 if tree.tpe.is_action() {
220 actions.insert(tree.name.clone());
221 r_tree.nodes.insert(
222 id,
223 RNode::action_alias(tree.name.clone(), file.clone(), name, rt_args),
224 );
225 } else {
226 r_tree.nodes.insert(
227 id,
228 RNode::flow_alias(
229 tree.tpe.try_into()?,
230 tree.name.clone(),
231 file.clone(),
232 name,
233 rt_args,
234 children,
235 ),
236 );
237 }
238 } else if tree.tpe.is_action() {
239 r_tree
240 .nodes
241 .insert(id, RNode::action(name.clone(), file.clone(), rt_args));
242 actions.insert(name);
243 } else {
244 r_tree.nodes.insert(
245 id,
246 RNode::flow(tree.tpe.try_into()?, name, file.clone(), rt_args, children),
247 );
248 };
249 }
250 }
251 }
252 }
253 }
254
255 Ok(RuntimeTreeStarter {
256 tree: r_tree,
257 std_actions,
258 actions,
259 })
260 }
261 pub fn node(&self, id: &RNodeId) -> RtResult<&RNode> {
263 self.nodes.get(id).ok_or(RuntimeError::uex(format!(
264 "the node {id} is not found in the rt tree"
265 )))
266 }
267
268 pub fn max_id(&self) -> RNodeId {
270 self.nodes.keys().max().cloned().unwrap_or_default()
271 }
272
273 pub fn to_ros_nav(&self, xml: PathBuf) -> RtOk {
275 ToRosNavConverter::new(&self, xml).convert()
276 }
277}
278
279#[cfg(test)]
280mod tests {
281 use crate::runtime::args::{RtArgs, RtArgument, RtValue};
282 use crate::runtime::rtree::rnode::FlowType::{Fallback, Root, Sequence};
283 use crate::runtime::rtree::rnode::RNode::{Flow, Leaf};
284 use crate::runtime::rtree::rnode::RNodeName::{Lambda, Name};
285 use crate::runtime::rtree::RuntimeTree;
286 use crate::tree::project::Project;
287 use std::collections::{HashSet};
288 use itertools::Itertools;
289 use crate::tests::turn_on_logs;
290
291
292 #[test]
293 fn smoke() {
294 let project = Project::build_from_text(
295 r#"
296 import "std::actions"
297 impl action();
298 root main fallback{
299 sequence {
300 action()
301 success()
302 }
303 }
304 "#
305 .to_string(),
306 )
307 .unwrap();
308
309 let st_tree = RuntimeTree::build(project).unwrap();
310
311 assert_eq!(
312 st_tree.std_actions,
313 HashSet::from_iter(vec![("success".to_string(), "std::actions".to_string())])
314 );
315 assert_eq!(
316 st_tree.actions,
317 HashSet::from_iter(vec!["action".to_string(), "success".to_string()])
318 );
319 assert_eq!(st_tree.tree.nodes.len(), 5);
320 assert_eq!(st_tree.tree.root, 1);
321 assert_eq!(st_tree.tree.max_id(), 5);
322 let items: Vec<_> = st_tree.tree.nodes.iter().sorted_by_key(|e| e.0).collect();
323 assert_eq!(
324 items,
325 vec![
326 (
327 &1usize,
328 &Flow(Root, Name("main".to_string(), "_".to_string()), RtArgs(vec![]), vec![2])
329 ),
330 (&2usize, &Flow(Fallback, Lambda, RtArgs(vec![]), vec![3])),
331 (&3usize, &Flow(Sequence, Lambda, RtArgs(vec![]), vec![4, 5])),
332 (&4usize, &Leaf(Name("action".to_string(), "_".to_string()), RtArgs(vec![]))),
333 (&5usize, &Leaf(Name("success".to_string(), "std::actions".to_string()), RtArgs(vec![]))),
334 ]
335 );
336 }
337
338 #[test]
339 fn decorator_lambda() {
340 let project = Project::build_from_text(
341 r#"
342 impl action();
343 root main f(t = retry(1) action())
344 sequence f(t:tree) t(..)
345 "#
346 .to_string(),
347 )
348 .unwrap();
349
350 let st_tree = RuntimeTree::build(project).unwrap().tree;
351
352 assert_eq!(st_tree.nodes.len(), 4)
353 }
354
355 #[test]
356 fn params() {
357 let project = Project::build_from_text(
358 r#"
359 impl consumer(arg:any);
360
361 root main test(1)
362
363 sequence test(a:any){
364 test2(a)
365 }
366
367 sequence test2(a1:num){
368 consumer(a1)
369 }
370 "#
371 .to_string(),
372 )
373 .unwrap();
374
375 let st_tree = RuntimeTree::build(project).unwrap().tree;
376
377 let items: Vec<_> = st_tree.nodes.iter().sorted_by_key(|e| e.0).collect();
378 assert_eq!(
379 items,
380 vec![
381 (
382 &1usize,
383 &Flow(
384 Root,
385 Name("main".to_string(), "_".to_string()),
386 RtArgs(vec![]),
387 vec![2],
388 )
389 ),
390 (
391 &2usize,
392 &Flow(
393 Sequence,
394 Name("test".to_string(), "_".to_string()),
395 RtArgs(vec![RtArgument::new(
396 "a".to_string(),
397 RtValue::int(1))]),
398 vec![3],
399 )
400 ),
401 (
402 &3usize,
403 &Flow(
404 Sequence,
405 Name("test2".to_string(), "_".to_string()),
406 RtArgs(vec![RtArgument::new(
407 "a1".to_string(),
408 RtValue::int(1))]),
409 vec![4],
410 )
411 ),
412 (
413 &4usize,
414 &Leaf(
415 Name("consumer".to_string(), "_".to_string()),
416 RtArgs(vec![RtArgument::new(
417 "arg".to_string(),
418 RtValue::int(1))]),
419 )
420 ),
421 ]
422 );
423 }
424
425 #[test]
426 fn params2() {
427 let project = Project::build_from_text(
428 r#"
429 impl consumer(arg0:any,arg:any);
430 root main test("-b-",1)
431
432 sequence test(b:any,a:any){
433 test2(a,b)
434 }
435
436 sequence test2(b1:num,a1:any){
437 consumer(b1,a1)
438 }
439 "#
440 .to_string(),
441 )
442 .unwrap();
443
444 let st_tree = RuntimeTree::build(project).unwrap().tree;
445
446 let item = st_tree.nodes.iter().find(|(id, _)| **id == 4).unwrap().1.args();
447 assert_eq!(
448 item,
449 RtArgs(vec![
450 RtArgument::new("arg0".to_string(), RtValue::int(1)),
451 RtArgument::new("arg".to_string(), RtValue::str("-b-".to_string())),
452 ])
453 );
454 }
455
456}