brokk_bifrost_python/
adapter.rs1use brokk_bifrost_core::analyzer::cognitive_complexity;
9use std::sync::LazyLock;
10
11use crate::declarations::python_is_decorated_function_boundary;
12
13pub const PYTHON_FILE_EXTENSION: &str = "py";
14
15pub static PYTHON_COGNITIVE_CONFIG: LazyLock<cognitive_complexity::Config> =
18 LazyLock::new(|| cognitive_complexity::Config {
19 if_types: &["if_statement"],
20 alternate_if_types: &["elif_clause"],
21 loop_types: &["for_statement", "while_statement"],
22 catch_types: &["except_clause"],
23 conditional_types: &["conditional_expression"],
24 case_types: &["case_clause"],
25 binary_types: &["boolean_operator"],
26 logical_operators: &["and", "or"],
27 named_function_boundary_types: &["function_definition"],
28 anonymous_function_types: &["lambda"],
29 named_function_boundary_predicate: Some(python_is_decorated_function_boundary),
30 ..cognitive_complexity::Config::empty()
31 });
32
33pub fn python_extract_call_receiver(reference: &str) -> Option<String> {
34 brokk_bifrost_core::analyzer::common::extract_call_reference_parts(reference, &["."])
35 .map(|(receiver, _)| receiver.to_string())
36}
37
38pub const PYTHON_LINE_DATA_KINDS: brokk_bifrost_core::analyzer::tree_walk::LineDataKinds =
41 brokk_bifrost_core::analyzer::tree_walk::LineDataKinds {
42 literals: &["string", "concatenated_string", "integer", "float"],
43 interpolations: &["interpolation"],
44 composites: &[
45 ("list", &["[", "]", ","]),
46 ("tuple", &["(", ")", ","]),
47 ("unary_operator", &["-", "+"]),
48 ],
49 };
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54 use std::ops::ControlFlow;
55
56 #[test]
57 fn qs6_string_data_grammar_and_cancellation() {
58 for (literal, kind, interpolations, data) in [
59 ("u'caf\u{e9}'", "string", 0, true),
60 ("b'bytes'", "string", 0, true),
61 ("'one' 'two'", "concatenated_string", 0, true),
62 ("f'plain {{braces}}'", "string", 0, true),
63 ("f'{value:{width}}'", "string", 1, false),
64 ("'prefix' f'{value}'", "concatenated_string", 1, false),
65 ] {
66 let source = format!("value = {literal}\n");
67 let tree = crate::declarations::parse_python_tree(&source).unwrap();
68 let root = tree.root_node();
69 assert!(!root.has_error(), "{}", root.to_sexp());
70 let value = root
71 .named_child(0)
72 .unwrap()
73 .named_child(0)
74 .unwrap()
75 .child_by_field_name("right")
76 .unwrap();
77 assert_eq!(value.kind(), kind);
78 let mut stack = vec![value];
79 let mut count = 0;
80 while let Some(node) = stack.pop() {
81 assert!(!node.is_error() && !node.is_missing());
82 count += usize::from(node.kind() == "interpolation");
83 let mut cursor = node.walk();
84 stack.extend(node.named_children(&mut cursor));
85 }
86 assert_eq!(count, interpolations, "{}", value.to_sexp());
87 let data_subtrees = |should_stop: &mut dyn FnMut() -> bool| {
88 match brokk_bifrost_core::analyzer::tree_walk::maximal_data_subtrees(
89 value,
90 &PYTHON_LINE_DATA_KINDS,
91 |_| true,
92 should_stop,
93 ) {
94 ControlFlow::Continue(subtrees) => ControlFlow::Continue(
95 subtrees
96 .iter()
97 .any(|subtree| subtree.range == value.byte_range()),
98 ),
99 ControlFlow::Break(()) => ControlFlow::Break(()),
100 }
101 };
102 assert_eq!(data_subtrees(&mut || false), ControlFlow::Continue(data));
103 let mut stops = 0;
104 assert_eq!(
105 data_subtrees(&mut || {
106 stops += 1;
107 true
108 }),
109 ControlFlow::Break(())
110 );
111 assert_eq!(stops, 1);
112 }
113 }
114}