@@ -79,10 +79,7 @@ fn infer_type_from_pic(pic: &str) -> String {
7979 if pic. starts_with ( 'X' ) || pic. contains ( "X(" ) {
8080 return "string" . to_string ( ) ;
8181 }
82- if pic. contains ( '9' ) {
83- if pic. contains ( 'V' ) || pic. contains ( '.' ) {
84- return "float" . to_string ( ) ;
85- }
82+ if pic. contains ( '9' ) || pic. contains ( '$' ) || pic. contains ( 'Z' ) {
8683 return "numeric" . to_string ( ) ;
8784 }
8885 if pic. contains ( "COMP" ) {
@@ -121,6 +118,31 @@ fn is_cobol_comment_or_blank(line: &str) -> bool {
121118 cobol:: is_cobol_comment_or_blank ( line)
122119}
123120
121+ fn identification_comment_text ( line : & str ) -> Option < String > {
122+ if !is_cobol_comment_or_blank ( line) {
123+ return None ;
124+ }
125+ let t = line. trim ( ) ;
126+ if t. is_empty ( ) || !t. starts_with ( '*' ) {
127+ return None ;
128+ }
129+ let body = t
130+ . trim_start_matches ( '*' )
131+ . trim ( )
132+ . trim_end_matches ( '*' )
133+ . trim ( ) ;
134+ if body. is_empty ( ) {
135+ return None ;
136+ }
137+ if body
138+ . chars ( )
139+ . all ( |c| matches ! ( c, '-' | '=' | '+' | '*' | '.' | '_' | ' ' ) )
140+ {
141+ return None ;
142+ }
143+ Some ( body. to_string ( ) )
144+ }
145+
124146fn has_terminator_period ( s : & str ) -> bool {
125147 cobol:: has_terminator_period ( s)
126148}
@@ -228,8 +250,8 @@ fn parse_identification_division(source: &str) -> (String, String, String, Vec<S
228250 }
229251 } else if pending_program_id {
230252 if is_cobol_comment_or_blank ( line) {
231- if line . trim_start ( ) . starts_with ( '*' ) {
232- comments. push ( line . trim_start_matches ( '*' ) . trim ( ) . to_string ( ) ) ;
253+ if let Some ( text ) = identification_comment_text ( line ) {
254+ comments. push ( text ) ;
233255 }
234256 } else if let Some ( tok) = line. split_whitespace ( ) . next ( ) {
235257 program_name = tok. trim_end_matches ( '.' ) . to_string ( ) ;
@@ -245,8 +267,8 @@ fn parse_identification_division(source: &str) -> (String, String, String, Vec<S
245267 . get ( 1 )
246268 . map ( |m| m. as_str ( ) . trim_end_matches ( '.' ) . to_string ( ) )
247269 . unwrap_or_default ( ) ;
248- } else if line . trim_start ( ) . starts_with ( '*' ) {
249- comments. push ( line . trim_start_matches ( '*' ) . trim ( ) . to_string ( ) ) ;
270+ } else if let Some ( text ) = identification_comment_text ( line ) {
271+ comments. push ( text ) ;
250272 }
251273 }
252274 }
@@ -358,7 +380,7 @@ fn extract_variable_usage(
358380 . iter ( )
359381 . filter ( |op| {
360382 let opu = cobol:: normalize_name ( op) ;
361- ! cobol:: KEYWORDS . contains ( opu . as_str ( ) )
383+ cobol:: is_valid_identifier ( op )
362384 && !cobol:: is_literal ( op)
363385 && !para_name_map. contains_key ( & opu)
364386 } )
@@ -592,6 +614,62 @@ fn record_statement(
592614 }
593615}
594616
617+ fn statement_first_token ( line : & str ) -> String {
618+ cobol:: split_cobol_tokens ( line)
619+ . into_iter ( )
620+ . next ( )
621+ . map ( |s| s. trim_end_matches ( '.' ) . to_uppercase ( ) )
622+ . unwrap_or_default ( )
623+ }
624+
625+ struct StmtCtx < ' a > {
626+ para : & ' a mut Option < Paragraph > ,
627+ paragraph_name : & ' a str ,
628+ section : & ' a Option < ProcedureSection > ,
629+ para_name_map : & ' a HashMap < String , String > ,
630+ call_graph : & ' a mut Vec < CallGraphEntry > ,
631+ }
632+
633+ fn flush_pending_statement ( pending : & mut Option < ( usize , String ) > , ctx : & mut StmtCtx < ' _ > ) {
634+ if let Some ( ( line_no, raw) ) = pending. take ( ) {
635+ if let Some ( ref mut p) = ctx. para {
636+ record_statement (
637+ p,
638+ ctx. paragraph_name ,
639+ ctx. section ,
640+ & raw ,
641+ line_no,
642+ ctx. para_name_map ,
643+ ctx. call_graph ,
644+ ) ;
645+ }
646+ }
647+ }
648+
649+ fn accumulate_statement (
650+ pending : & mut Option < ( usize , String ) > ,
651+ line : & str ,
652+ line_no : usize ,
653+ ctx : & mut StmtCtx < ' _ > ,
654+ ) {
655+ let tok = statement_first_token ( line) ;
656+ if pending. is_some ( ) && cobol:: is_statement_verb ( & tok) {
657+ flush_pending_statement ( pending, ctx) ;
658+ }
659+ if let Some ( ( _, buf) ) = pending. as_mut ( ) {
660+ buf. push ( ' ' ) ;
661+ buf. push_str ( line. trim ( ) ) ;
662+ } else {
663+ * pending = Some ( ( line_no, line. trim ( ) . to_string ( ) ) ) ;
664+ }
665+ if pending
666+ . as_ref ( )
667+ . is_some_and ( |( _, raw) | has_terminator_period ( raw) )
668+ {
669+ flush_pending_statement ( pending, ctx) ;
670+ }
671+ }
672+
595673fn parse_procedure_division_and_call_graph (
596674 source : & str ,
597675 program_name : & str ,
@@ -646,6 +724,7 @@ fn parse_procedure_division_and_call_graph(
646724 let mut control_flow_graph = Vec :: new ( ) ;
647725 let mut all_paragraphs_flat: Vec < Paragraph > = Vec :: new ( ) ;
648726 let mut exec_sql: Option < ( usize , String ) > = None ;
727+ let mut stmt_pending: Option < ( usize , String ) > = None ;
649728 for ( i, line) in source. lines ( ) . enumerate ( ) {
650729 let line = line. trim_end ( ) ;
651730 if consume_procedure_header ( line, & mut in_proc, & mut skip_using) {
@@ -675,6 +754,16 @@ fn parse_procedure_division_and_call_graph(
675754 continue ;
676755 }
677756 if starts_exec_sql ( line) {
757+ flush_pending_statement (
758+ & mut stmt_pending,
759+ & mut StmtCtx {
760+ para : & mut current_paragraph,
761+ paragraph_name : & current_paragraph_name,
762+ section : & current_section,
763+ para_name_map : & para_name_map,
764+ call_graph : & mut call_graph,
765+ } ,
766+ ) ;
678767 if ends_exec ( line) {
679768 ensure_paragraph (
680769 & mut current_paragraph,
@@ -692,6 +781,16 @@ fn parse_procedure_division_and_call_graph(
692781 continue ;
693782 }
694783 if let Some ( sec_caps) = re_section. captures ( line) {
784+ flush_pending_statement (
785+ & mut stmt_pending,
786+ & mut StmtCtx {
787+ para : & mut current_paragraph,
788+ paragraph_name : & current_paragraph_name,
789+ section : & current_section,
790+ para_name_map : & para_name_map,
791+ call_graph : & mut call_graph,
792+ } ,
793+ ) ;
695794 finish_paragraph (
696795 & mut current_paragraph,
697796 & mut current_section,
@@ -723,19 +822,30 @@ fn parse_procedure_division_and_call_graph(
723822 program_name,
724823 i + 1 ,
725824 ) ;
726- if let Some ( ref mut para) = current_paragraph {
727- record_statement (
728- para,
729- & current_paragraph_name,
730- & current_section,
731- line,
732- i + 1 ,
733- & para_name_map,
734- & mut call_graph,
735- ) ;
736- }
825+ accumulate_statement (
826+ & mut stmt_pending,
827+ line,
828+ i + 1 ,
829+ & mut StmtCtx {
830+ para : & mut current_paragraph,
831+ paragraph_name : & current_paragraph_name,
832+ section : & current_section,
833+ para_name_map : & para_name_map,
834+ call_graph : & mut call_graph,
835+ } ,
836+ ) ;
737837 continue ;
738838 }
839+ flush_pending_statement (
840+ & mut stmt_pending,
841+ & mut StmtCtx {
842+ para : & mut current_paragraph,
843+ paragraph_name : & current_paragraph_name,
844+ section : & current_section,
845+ para_name_map : & para_name_map,
846+ call_graph : & mut call_graph,
847+ } ,
848+ ) ;
739849 finish_paragraph (
740850 & mut current_paragraph,
741851 & mut current_section,
@@ -761,20 +871,31 @@ fn parse_procedure_division_and_call_graph(
761871 program_name,
762872 i + 1 ,
763873 ) ;
764- if let Some ( ref mut para) = current_paragraph {
765- record_statement (
766- para,
767- & current_paragraph_name,
768- & current_section,
769- line,
770- i + 1 ,
771- & para_name_map,
772- & mut call_graph,
773- ) ;
774- }
874+ accumulate_statement (
875+ & mut stmt_pending,
876+ line,
877+ i + 1 ,
878+ & mut StmtCtx {
879+ para : & mut current_paragraph,
880+ paragraph_name : & current_paragraph_name,
881+ section : & current_section,
882+ para_name_map : & para_name_map,
883+ call_graph : & mut call_graph,
884+ } ,
885+ ) ;
775886 }
776887 }
777888 }
889+ flush_pending_statement (
890+ & mut stmt_pending,
891+ & mut StmtCtx {
892+ para : & mut current_paragraph,
893+ paragraph_name : & current_paragraph_name,
894+ section : & current_section,
895+ para_name_map : & para_name_map,
896+ call_graph : & mut call_graph,
897+ } ,
898+ ) ;
778899 finish_paragraph (
779900 & mut current_paragraph,
780901 & mut current_section,
0 commit comments