74 lines
2.5 KiB
Rust
74 lines
2.5 KiB
Rust
use crate::keywords::{is_function_start, is_procedure_start};
|
|
use crate::models::{ParsedCall, SourceRange};
|
|
use crate::parser::{extract_name_from_header, routine_ended};
|
|
|
|
pub fn extract_calls(source: &str) -> Vec<ParsedCall> {
|
|
let mut result = Vec::new();
|
|
let mut current_routine: Option<String> = None;
|
|
for (index, line) in source.lines().enumerate() {
|
|
let line_no = index + 1;
|
|
let trimmed = strip_inline_comment(line).trim();
|
|
if is_procedure_start(trimmed) || is_function_start(trimmed) {
|
|
current_routine = Some(extract_name_from_header(trimmed));
|
|
continue;
|
|
}
|
|
if routine_ended(trimmed) {
|
|
current_routine = None;
|
|
continue;
|
|
}
|
|
let Some(caller) = current_routine.as_ref() else {
|
|
continue;
|
|
};
|
|
if let Some(callee) = simple_call_name(trimmed).or_else(|| condition_call_name(trimmed)) {
|
|
result.push(ParsedCall {
|
|
caller: caller.clone(),
|
|
callee,
|
|
source_range: SourceRange {
|
|
line_start: line_no,
|
|
line_end: line_no,
|
|
column_start: 1,
|
|
column_end: line.len() + 1,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
result
|
|
}
|
|
|
|
fn simple_call_name(line: &str) -> Option<String> {
|
|
if !line.ends_with(';') || !line.contains('(') || !line.contains(')') || line.contains('.') {
|
|
return None;
|
|
}
|
|
let call_expr = line.split('=').next_back().unwrap_or(line).trim();
|
|
let name = call_expr.split('(').next()?.trim();
|
|
if name.is_empty() {
|
|
return None;
|
|
}
|
|
Some(name.to_string())
|
|
}
|
|
|
|
fn condition_call_name(line: &str) -> Option<String> {
|
|
let lowered = line.to_lowercase();
|
|
let is_condition = (lowered.starts_with("если ") && lowered.contains(" тогда"))
|
|
|| (lowered.starts_with("if ") && lowered.contains(" then"));
|
|
if !is_condition || line.contains('.') {
|
|
return None;
|
|
}
|
|
let before_then = if let Some(index) = lowered.find(" тогда") {
|
|
&line[..index]
|
|
} else if let Some(index) = lowered.find(" then") {
|
|
&line[..index]
|
|
} else {
|
|
line
|
|
};
|
|
let name_part = before_then.split('(').next()?.split_whitespace().last()?;
|
|
if name_part.eq_ignore_ascii_case("Если") || name_part.eq_ignore_ascii_case("If") {
|
|
return None;
|
|
}
|
|
Some(name_part.to_string())
|
|
}
|
|
|
|
fn strip_inline_comment(line: &str) -> &str {
|
|
line.split("//").next().unwrap_or(line)
|
|
}
|