pub mod calls; pub mod keywords; pub mod models; pub mod parser; pub mod queries; pub mod writes; use crate::calls::extract_calls; use crate::models::ParsedSemanticUnit; use crate::parser::extract_procedures; use crate::queries::extract_queries; use crate::writes::extract_writes; pub fn parse_module(source_path: &str, source: &str) -> ParsedSemanticUnit { ParsedSemanticUnit { source_path: source_path.to_string(), procedures: extract_procedures(source), calls: extract_calls(source), queries: extract_queries(source), writes: extract_writes(source), diagnostics: Vec::new(), } } #[cfg(test)] mod tests { use super::parse_module; #[test] fn parses_procedure_calls_query_and_write() { let source = r#" Процедура Проведение() ПроверитьОстатки(); Движения.ОстаткиТоваров.Записать(); КонецПроцедуры Процедура ПроверитьОстатки() Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ Остатки.Номенклатура ИЗ РегистрНакопления.ОстаткиТоваров КАК Остатки"; КонецПроцедуры "#; let unit = parse_module("module.bsl", source); assert_eq!(unit.procedures.len(), 2); assert_eq!(unit.calls.len(), 1); assert_eq!(unit.queries.len(), 1); assert_eq!(unit.writes.len(), 1); assert_eq!(unit.calls[0].caller, "Проведение"); assert_eq!(unit.calls[0].callee, "ПроверитьОстатки"); assert_eq!(unit.writes[0].target, "ОстаткиТоваров"); } #[test] fn parses_english_function_query_call_and_write() { let source = r#" Procedure Posting() CheckStock(); // inline comment Movements.StockBalance.Write(); EndProcedure Function CheckStock() Query = New Query; Query.Text = "SELECT Stock.Item FROM AccumulationRegister.StockBalance AS Stock"; EndFunction "#; let unit = parse_module("module.bsl", source); assert_eq!(unit.procedures.len(), 2); assert!(unit.procedures[1].is_function); assert_eq!(unit.calls.len(), 1); assert_eq!(unit.calls[0].callee, "CheckStock"); assert_eq!(unit.queries.len(), 1); assert_eq!( unit.queries[0].tables, vec!["AccumulationRegister.StockBalance"] ); assert!(!unit.queries[0].query_text.ends_with('"')); assert_eq!(unit.writes.len(), 1); assert_eq!(unit.writes[0].target, "StockBalance"); } #[test] fn parses_mixed_russian_and_english_keywords_in_one_module() { let source = r#" Procedure Posting() ПроверитьОстатки(); Movements.StockBalance.Write(); EndProcedure Процедура ПроверитьОстатки() Query = New Query; Query.Text = "SELECT Stock.Item FROM AccumulationRegister.StockBalance AS Stock"; КонецПроцедуры "#; let unit = parse_module("module.bsl", source); assert_eq!(unit.procedures.len(), 2); assert_eq!(unit.procedures[0].name, "Posting"); assert_eq!(unit.procedures[1].name, "ПроверитьОстатки"); assert_eq!(unit.calls.len(), 1); assert_eq!(unit.calls[0].caller, "Posting"); assert_eq!(unit.calls[0].callee, "ПроверитьОстатки"); assert_eq!(unit.queries.len(), 1); assert_eq!( unit.queries[0].tables, vec!["AccumulationRegister.StockBalance"] ); assert_eq!(unit.writes.len(), 1); assert_eq!(unit.writes[0].target, "StockBalance"); } #[test] fn parses_inline_query_assignment_with_pipe_prefixed_lines() { let source = r#" Процедура ПолучитьТовары() Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ |Товары.Ссылка |ИЗ |Справочник.Номенклатура КАК Товары"; КонецПроцедуры "#; let unit = parse_module("module.bsl", source); assert_eq!(unit.queries.len(), 1); assert_eq!(unit.queries[0].tables, vec!["Справочник.Номенклатура"]); assert!(unit.queries[0].query_text.starts_with("ВЫБРАТЬ")); assert!(!unit.queries[0].query_text.contains("|ИЗ")); } #[test] fn parses_query_from_and_table_on_same_line() { let source = r#" Процедура ПолучитьКонтрагентов() Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ |Контрагенты.Ссылка |ИЗ Справочник.Контрагенты КАК Контрагенты"; КонецПроцедуры "#; let unit = parse_module("module.bsl", source); assert_eq!(unit.queries.len(), 1); assert_eq!(unit.queries[0].tables, vec!["Справочник.Контрагенты"]); } #[test] fn parses_assignment_function_calls() { let source = r#" Процедура Проведение() МожноПроводить = ПроверитьОстатки(); КонецПроцедуры Функция ПроверитьОстатки() Возврат Истина; КонецФункции "#; let unit = parse_module("module.bsl", source); assert_eq!(unit.calls.len(), 1); assert_eq!(unit.calls[0].caller, "Проведение"); assert_eq!(unit.calls[0].callee, "ПроверитьОстатки"); } #[test] fn parses_condition_function_calls() { let source = r#" Процедура Проведение() Если ПроверитьОстатки() Тогда Движения.ОстаткиТоваров.Записать(); КонецЕсли; КонецПроцедуры Функция ПроверитьОстатки() Возврат Истина; КонецФункции "#; let unit = parse_module("module.bsl", source); assert_eq!(unit.calls.len(), 1); assert_eq!(unit.calls[0].caller, "Проведение"); assert_eq!(unit.calls[0].callee, "ПроверитьОстатки"); } #[test] fn parses_join_tables_from_query_text() { let source = r#" Процедура ПолучитьЗаказы() Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ |Заказы.Ссылка, |Контрагенты.Наименование |ИЗ Документ.ЗаказПокупателя КАК Заказы |ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты |ПО Заказы.Контрагент = Контрагенты.Ссылка"; КонецПроцедуры "#; let unit = parse_module("module.bsl", source); assert_eq!( unit.queries[0].tables, vec!["Документ.ЗаказПокупателя", "Справочник.Контрагенты"] ); } #[test] fn parses_object_write_targets_from_create_assignments() { let source = r#" Процедура СоздатьНоменклатуру() Элемент = Справочники.Номенклатура.СоздатьЭлемент(); Элемент.Записать(); КонецПроцедуры Procedure CreateOrder() Order = Documents.CustomerOrder.CreateDocument(); Order.Write(); EndProcedure "#; let unit = parse_module("module.bsl", source); assert_eq!(unit.writes.len(), 2); assert_eq!(unit.writes[0].target, "Справочник.Номенклатура"); assert_eq!(unit.writes[0].write_type, "OBJECT_WRITE"); assert_eq!(unit.writes[1].target, "Документ.CustomerOrder"); assert_eq!(unit.writes[1].write_type, "OBJECT_WRITE"); } #[test] fn parses_recordset_write_targets_from_create_assignments() { let source = r#" Процедура ЗаписатьЦены() Набор = РегистрыСведений.Цены.СоздатьНаборЗаписей(); Набор.Записать(); КонецПроцедуры Procedure WriteBalances() Records = AccumulationRegisters.StockBalance.CreateRecordSet(); Records.Write(); EndProcedure "#; let unit = parse_module("module.bsl", source); assert_eq!(unit.writes.len(), 2); assert_eq!(unit.writes[0].target, "РегистрСведений.Цены"); assert_eq!(unit.writes[0].write_type, "REGISTER_WRITE"); assert_eq!(unit.writes[1].target, "РегистрНакопления.StockBalance"); assert_eq!(unit.writes[1].write_type, "REGISTER_WRITE"); } #[test] fn parses_calls_and_writes_inside_control_flow_blocks() { let source = r#" Процедура Проведение() Для Каждого Строка Из Товары Цикл ПроверитьСтроку(Строка); КонецЦикла; Попытка Движения.ОстаткиТоваров.Записать(); Исключение СообщитьОбОшибке(); КонецПопытки; КонецПроцедуры Процедура ПроверитьСтроку(Строка) КонецПроцедуры Процедура СообщитьОбОшибке() КонецПроцедуры "#; let unit = parse_module("module.bsl", source); assert_eq!(unit.calls.len(), 2); assert_eq!(unit.calls[0].callee, "ПроверитьСтроку"); assert_eq!(unit.calls[1].callee, "СообщитьОбОшибке"); assert_eq!(unit.writes.len(), 1); assert_eq!(unit.writes[0].target, "ОстаткиТоваров"); } }