5 use vars
qw($VERSION @ISA @EXPORT @EXPORT_OK);
12 use options
qw($options);
13 use output qw($output);
17 ########################################################################
22 my $class = ref($proto) || $proto;
24 bless ($self, $class);
26 my $file = \${$self->{FILE}};
27 my $found_comment = \${$self->{FOUND_COMMENT}};
28 my $found_declaration = \${$self->{FOUND_DECLARATION}};
29 my $create_function = \${$self->{CREATE_FUNCTION}};
30 my $found_function = \${$self->{FOUND_FUNCTION}};
31 my $found_function_call = \${$self->{FOUND_FUNCTION_CALL}};
32 my $found_preprocessor = \${$self->{FOUND_PREPROCESSOR}};
33 my $found_statement = \${$self->{FOUND_STATEMENT}};
34 my $found_variable = \${$self->{FOUND_VARIABLE}};
38 $$found_comment = sub { return 1; };
39 $$found_declaration = sub { return 1; };
40 $$create_function = sub { return new c_function; };
41 $$found_function = sub { return 1; };
42 $$found_function_call = sub { return 1; };
43 $$found_preprocessor = sub { return 1; };
44 $$found_statement = sub { return 1; };
45 $$found_variable = sub { return 1; };
50 ########################################################################
51 # set_found_comment_callback
53 sub set_found_comment_callback {
56 my $found_comment = \${$self->{FOUND_COMMENT}};
58 $$found_comment = shift;
61 ########################################################################
62 # set_found_declaration_callback
64 sub set_found_declaration_callback {
67 my $found_declaration = \${$self->{FOUND_DECLARATION}};
69 $$found_declaration = shift;
72 ########################################################################
73 # set_found_function_callback
75 sub set_found_function_callback {
78 my $found_function = \${$self->{FOUND_FUNCTION}};
80 $$found_function = shift;
83 ########################################################################
84 # set_found_function_call_callback
86 sub set_found_function_call_callback {
89 my $found_function_call = \${$self->{FOUND_FUNCTION_CALL}};
91 $$found_function_call = shift;
94 ########################################################################
95 # set_found_preprocessor_callback
97 sub set_found_preprocessor_callback {
100 my $found_preprocessor = \${$self->{FOUND_PREPROCESSOR}};
102 $$found_preprocessor = shift;
105 ########################################################################
106 # set_found_statement_callback
108 sub set_found_statement_callback {
111 my $found_statement = \${$self->{FOUND_STATEMENT}};
113 $$found_statement = shift;
116 ########################################################################
117 # set_found_variable_callback
119 sub set_found_variable_callback {
122 my $found_variable = \${$self->{FOUND_VARIABLE}};
124 $$found_variable = shift;
127 ########################################################################
134 my $refcurrent = shift;
136 my $refcolumn = shift;
138 my $refmatch = shift;
140 local $_ = $$refcurrent;
141 my $line = $$refline;
142 my $column = $$refcolumn;
145 if(s/^(?:$pattern)//s) {
146 $self->_update_c_position($&, \$line, \$column);
152 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
156 $$refcolumn = $column;
163 ########################################################################
169 my $file = \${$self->{FILE}};
176 my @lines = split(/\n/, $_);
179 $current .= $lines[0] . "\n" || "";
180 $current .= $lines[1] . "\n" || "";
182 if($output->prefix) {
183 $output->write("\n");
186 $output->write("$$file:$line." . ($column + 1) . ": $context: parse error: \\$current");
191 ########################################################################
194 sub _parse_c_output {
202 my @lines = split(/\n/, $_);
205 $current .= $lines[0] . "\n" || "";
206 $current .= $lines[1] . "\n" || "";
208 $output->write("$line." . ($column + 1) . ": $message: \\$current");
211 ########################################################################
212 # _parse_c_until_one_of
214 sub _parse_c_until_one_of {
217 my $characters = shift;
218 my $refcurrent = shift;
220 my $refcolumn = shift;
223 local $_ = $$refcurrent;
224 my $line = $$refline;
225 my $column = $$refcolumn;
227 if(!defined($match)) {
229 $match = \$blackhole;
233 while(/^[^$characters]/s) {
236 if(s/^[^$characters\n\t\'\"]*//s) {
242 while(/^./ && !s/^\'//) {
258 $$match .= $submatch;
259 $column += length($submatch);
262 while(/^./ && !s/^\"//) {
278 $$match .= $submatch;
279 $column += length($submatch);
283 $$match .= $submatch;
289 $$match .= $submatch;
290 $column = $column + 8 - $column % 8;
292 $$match .= $submatch;
293 $column += length($submatch);
299 $$refcolumn = $column;
303 ########################################################################
306 sub _update_c_position {
311 my $refcolumn = shift;
313 my $line = $$refline;
314 my $column = $$refcolumn;
317 if(s/^[^\n\t\'\"]*//s) {
318 $column += length($&);
323 while(/^./ && !s/^\'//) {
325 $column += length($1);
329 $column += length($1);
332 $column += length($1);
340 while(/^./ && !s/^\"//) {
342 $column += length($1);
346 $column += length($1);
349 $column += length($1);
359 $column = $column + 8 - $column % 8;
364 $$refcolumn = $column;
367 ########################################################################
373 my $refcurrent = shift;
375 my $refcolumn = shift;
377 my $refstatements = shift;
378 my $refstatements_line = shift;
379 my $refstatements_column = shift;
381 local $_ = $$refcurrent;
382 my $line = $$refline;
383 my $column = $$refcolumn;
385 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
395 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
397 my $statements_line = $line;
398 my $statements_column = $column;
403 $self->_parse_c_until_one_of("\\{\\}", \$_, \$line, \$column, \$match);
407 $statements .= $match;
423 $$refcolumn = $column;
424 $$refstatements = $statements;
425 $$refstatements_line = $statements_line;
426 $$refstatements_column = $statements_column;
431 ########################################################################
432 # parse_c_declaration
434 sub parse_c_declaration {
437 my $found_declaration = \${$self->{FOUND_DECLARATION}};
438 my $found_function = \${$self->{FOUND_FUNCTION}};
440 my $refcurrent = shift;
442 my $refcolumn = shift;
444 local $_ = $$refcurrent;
445 my $line = $$refline;
446 my $column = $$refcolumn;
448 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
450 my $begin_line = $line;
451 my $begin_column = $column + 1;
453 my $end_line = $begin_line;
454 my $end_column = $begin_column;
455 $self->_update_c_position($_, \$end_line, \$end_column);
457 if(!&$$found_declaration($begin_line, $begin_column, $end_line, $end_column, $_)) {
462 my $function = shift;
465 my $calling_convention = shift;
466 my $return_type = shift;
468 my @arguments = shift;
469 my @argument_lines = shift;
470 my @argument_columns = shift;
475 # $self->_parse_c_output($_, $line, $column, "declaration");
479 } elsif(s/^(?:DEFAULT|DECLARE)_DEBUG_CHANNEL\s*\(\s*(\w+)\s*\)\s*//s) { # FIXME: Wine specific kludge
480 $self->_update_c_position($&, \$line, \$column);
481 } elsif(s/^extern\s*\"(.*?)\"\s*//s) {
482 $self->_update_c_position($&, \$line, \$column);
484 my $declarations_line;
485 my $declarations_column;
486 if(!$self->parse_c_block(\$_, \$line, \$column, \$declarations, \$declarations_line, \$declarations_column)) {
489 if(!$self->parse_c_declarations(\$declarations, \$declarations_line, \$declarations_column)) {
492 } elsif($self->parse_c_function(\$_, \$line, \$column, \$function)) {
493 if(&$$found_function($function))
495 my $statements = $function->statements;
496 my $statements_line = $function->statements_line;
497 my $statements_column = $function->statements_column;
499 if(defined($statements)) {
500 if(!$self->parse_c_statements(\$statements, \$statements_line, \$statements_column)) {
505 } elsif($self->parse_c_typedef(\$_, \$line, \$column)) {
507 } elsif($self->parse_c_variable(\$_, \$line, \$column, \$linkage, \$type, \$name)) {
510 $self->_parse_c_error($_, $line, $column, "declaration");
515 $$refcolumn = $column;
520 ########################################################################
521 # parse_c_declarations
523 sub parse_c_declarations {
526 my $refcurrent = shift;
528 my $refcolumn = shift;
533 ########################################################################
536 sub parse_c_expression {
539 my $refcurrent = shift;
541 my $refcolumn = shift;
543 my $found_function_call = \${$self->{FOUND_FUNCTION_CALL}};
545 local $_ = $$refcurrent;
546 my $line = $$refline;
547 my $column = $$refcolumn;
549 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
551 if(s/^(.*?)(\w+\s*\()/$2/s) {
552 $column += length($1);
554 my $begin_line = $line;
555 my $begin_column = $column + 1;
560 my @argument_columns;
561 if(!$self->parse_c_function_call(\$_, \$line, \$column, \$name, \@arguments, \@argument_lines, \@argument_columns)) {
565 if($name =~ /^sizeof$/ ||
566 &$$found_function_call($begin_line, $begin_column, $line, $column, $name, \@arguments))
568 while(defined(my $argument = shift @arguments) &&
569 defined(my $argument_line = shift @argument_lines) &&
570 defined(my $argument_column = shift @argument_columns))
572 $self->parse_c_expression(\$argument, \$argument_line, \$argument_column);
575 } elsif(s/^return//) {
576 $column += length($&);
577 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
578 if(!$self->parse_c_expression(\$_, \$line, \$column)) {
585 $self->_update_c_position($_, \$line, \$column);
589 $$refcolumn = $column;
594 ########################################################################
600 my $found_comment = \${$self->{FOUND_COMMENT}};
602 my $refcurrent = shift;
604 my $refcolumn = shift;
606 local $_ = $$refcurrent;
607 my $line = $$refline;
608 my $column = $$refcolumn;
610 my $declaration = "";
611 my $declaration_line = $line;
612 my $declaration_column = $column;
614 my $previous_line = 0;
615 my $previous_column = -1;
619 while($plevel > 0 || $blevel > 0) {
621 $self->_parse_c_until_one_of("#/\\(\\)\\[\\]\\{\\};", \$_, \$line, \$column, \$match);
623 if($line == $previous_line && $column == $previous_column) {
624 # $self->_parse_c_error($_, $line, $column, "file: no progress");
626 $previous_line = $line;
627 $previous_column = $column;
629 # $self->_parse_c_output($_, $line, $column, "'$match'");
631 if(!$declaration && $match =~ s/^\s+//s) {
632 $self->_update_c_position($&, \$declaration_line, \$declaration_column);
634 $declaration .= $match;
639 my $preprocessor_line = $line;
640 my $preprocessor_column = $column;
642 my $preprocessor = $&;
643 while(s/^(.*?)\\\s*\n//) {
645 $preprocessor .= "$1\n";
647 if(s/^(.*?)(\/[\*\/].*)?\n//) {
656 if(!$self->parse_c_preprocessor(\$preprocessor, \$preprocessor_line, \$preprocessor_column)) {
661 if(s/^\/\*(.*?)\*\///s) {
662 &$$found_comment($line, $column + 1, "/*$1*/");
663 my @lines = split(/\n/, $1);
665 $blank_lines += $#lines;
667 $column += length($1);
669 } elsif(s/^\/\/(.*?)\n//) {
670 &$$found_comment($line, $column + 1, "//$1");
676 $line += $blank_lines;
677 if($blank_lines > 0) {
682 $declaration_line = $line;
683 $declaration_column = $column;
685 $declaration .= "\n" x $blank_lines;
695 } elsif(s/^[\)\]]//) {
704 if($plevel == 1 && $blevel == 1 && $declaration !~ /^typedef/) {
705 if(!$self->parse_c_declaration(\$declaration, \$declaration_line, \$declaration_column)) {
708 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
710 $declaration_line = $line;
711 $declaration_column = $column;
714 if($plevel == 1 && $blevel == 1) {
715 if($declaration && !$self->parse_c_declaration(\$declaration, \$declaration_line, \$declaration_column)) {
718 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
720 $declaration_line = $line;
721 $declaration_column = $column;
725 } elsif(/^\s*$/ && $declaration =~ /^\s*$/ && $match =~ /^\s*$/) {
729 $self->_parse_c_error($_, $line, $column, "file");
735 $$refcolumn = $column;
740 ########################################################################
743 sub parse_c_function {
746 my $file = \${$self->{FILE}};
747 my $create_function = \${$self->{CREATE_FUNCTION}};
749 my $refcurrent = shift;
751 my $refcolumn = shift;
753 my $reffunction = shift;
755 local $_ = $$refcurrent;
756 my $line = $$refline;
757 my $column = $$refcolumn;
760 my $calling_convention = "";
765 my @argument_columns;
768 my $statements_column;
770 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
772 my $begin_line = $line;
773 my $begin_column = $column + 1;
775 $self->_parse_c("inline", \$_, \$line, \$column);
776 $self->_parse_c("extern|static", \$_, \$line, \$column, \$linkage);
777 $self->_parse_c("inline", \$_, \$line, \$column);
778 if(!$self->parse_c_type(\$_, \$line, \$column, \$return_type)) {
782 $self->_parse_c("__cdecl|__stdcall|CDECL|VFWAPIV|VFWAPI|WINAPIV|WINAPI|CALLBACK",
783 \$_, \$line, \$column, \$calling_convention);
784 if(!$self->_parse_c("\\w+", \$_, \$line, \$column, \$name)) {
787 if(!$self->parse_c_tuple(\$_, \$line, \$column, \@arguments, \@argument_lines, \@argument_columns)) {
790 if($_ && !$self->parse_c_block(\$_, \$line, \$column, \$statements, \$statements_line, \$statements_column)) {
794 my $end_line = $line;
795 my $end_column = $column;
799 $$refcolumn = $column;
801 my $function = &$$create_function;
803 $function->file($$file);
804 $function->begin_line($begin_line);
805 $function->begin_column($begin_column);
806 $function->end_line($end_line);
807 $function->end_column($end_column);
808 $function->linkage($linkage);
809 $function->return_type($return_type);
810 $function->calling_convention($calling_convention);
811 $function->name($name);
812 # if(defined($argument_types)) {
813 # $function->argument_types([@$argument_types]);
815 # if(defined($argument_names)) {
816 # $function->argument_names([@$argument_names]);
818 $function->statements_line($statements_line);
819 $function->statements_column($statements_column);
820 $function->statements($statements);
822 $$reffunction = $function;
827 ########################################################################
828 # parse_c_function_call
830 sub parse_c_function_call {
833 my $refcurrent = shift;
835 my $refcolumn = shift;
838 my $refarguments = shift;
839 my $refargument_lines = shift;
840 my $refargument_columns = shift;
842 local $_ = $$refcurrent;
843 my $line = $$refline;
844 my $column = $$refcolumn;
849 my @argument_columns;
851 if(s/^(\w+)(\s*)\(/\(/s) {
852 $column += length("$1$2");
856 if(!$self->parse_c_tuple(\$_, \$line, \$column, \@arguments, \@argument_lines, \@argument_columns)) {
865 $$refcolumn = $column;
868 @$refarguments = @arguments;
869 @$refargument_lines = @argument_lines;
870 @$refargument_columns = @argument_columns;
875 ########################################################################
876 # parse_c_preprocessor
878 sub parse_c_preprocessor {
881 my $found_preprocessor = \${$self->{FOUND_PREPROCESSOR}};
883 my $refcurrent = shift;
885 my $refcolumn = shift;
887 local $_ = $$refcurrent;
888 my $line = $$refline;
889 my $column = $$refcolumn;
891 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
893 my $begin_line = $line;
894 my $begin_column = $column + 1;
896 if(!&$$found_preprocessor($begin_line, $begin_column, "$_")) {
902 } elsif(/^\#\s*define\s+(.*?)$/s) {
903 $self->_update_c_position($_, \$line, \$column);
904 } elsif(/^\#\s*else/s) {
905 $self->_update_c_position($_, \$line, \$column);
906 } elsif(/^\#\s*endif/s) {
907 $self->_update_c_position($_, \$line, \$column);
908 } elsif(/^\#\s*(?:if|ifdef|ifndef)?\s+(.*?)$/s) {
909 $self->_update_c_position($_, \$line, \$column);
910 } elsif(/^\#\s*include\s+(.*?)$/s) {
911 $self->_update_c_position($_, \$line, \$column);
912 } elsif(/^\#\s*undef\s+(.*?)$/s) {
913 $self->_update_c_position($_, \$line, \$column);
915 $self->_parse_c_error($_, $line, $column, "preprocessor");
920 $$refcolumn = $column;
925 ########################################################################
928 sub parse_c_statement {
931 my $refcurrent = shift;
933 my $refcolumn = shift;
935 my $found_function_call = \${$self->{FOUND_FUNCTION_CALL}};
937 local $_ = $$refcurrent;
938 my $line = $$refline;
939 my $column = $$refcolumn;
941 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
943 if(s/^(?:case\s+)?(\w+)\s*://) {
944 $column += length($&);
945 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
948 # $output->write("$line.$column: '$_'\n");
955 my $statements_column;
956 if(!$self->parse_c_block(\$_, \$line, \$column, \$statements, \$statements_line, \$statements_column)) {
959 if(!$self->parse_c_statements(\$statements, \$statements_line, \$statements_column)) {
962 } elsif(/^(for|if|switch|while)(\s*)\(/) {
963 $column += length("$1$2");
970 my @argument_columns;
971 if(!$self->parse_c_tuple(\$_, \$line, \$column, \@arguments, \@argument_lines, \@argument_columns)) {
975 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
976 if(!$self->parse_c_statement(\$_, \$line, \$column)) {
979 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
981 while(defined(my $argument = shift @arguments) &&
982 defined(my $argument_line = shift @argument_lines) &&
983 defined(my $argument_column = shift @argument_columns))
985 $self->parse_c_expression(\$argument, \$argument_line, \$argument_column);
988 $column += length($&);
989 if(!$self->parse_c_statement(\$_, \$line, \$column)) {
992 } elsif($self->parse_c_expression(\$_, \$line, \$column)) {
995 # $self->_parse_c_error($_, $line, $column, "statement");
998 $self->_update_c_position($_, \$line, \$column);
1002 $$refcolumn = $column;
1007 ########################################################################
1008 # parse_c_statements
1010 sub parse_c_statements {
1013 my $refcurrent = shift;
1014 my $refline = shift;
1015 my $refcolumn = shift;
1017 my $found_function_call = \${$self->{FOUND_FUNCTION_CALL}};
1019 local $_ = $$refcurrent;
1020 my $line = $$refline;
1021 my $column = $$refcolumn;
1023 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
1026 my $statement_line = $line;
1027 my $statement_column = $column;
1031 while($plevel > 0 || $blevel > 0) {
1033 $self->_parse_c_until_one_of("\\(\\)\\[\\]\\{\\};", \$_, \$line, \$column, \$match);
1035 # $output->write("'$match' '$_'\n");
1038 $statement .= $match;
1042 } elsif(s/^[\)\]]//) {
1045 $self->_parse_c_error($_, $line, $column, "statements");
1055 if(!$self->parse_c_statement(\$statement, \$statement_line, \$statement_column)) {
1058 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
1060 $statement_line = $line;
1061 $statement_column = $column;
1064 if($plevel == 1 && $blevel == 1) {
1065 if(!$self->parse_c_statement(\$statement, \$statement_line, \$statement_column)) {
1069 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
1071 $statement_line = $line;
1072 $statement_column = $column;
1076 } elsif(/^\s*$/ && $statement =~ /^\s*$/ && $match =~ /^\s*$/) {
1080 $self->_parse_c_error($_, $line, $column, "statements");
1084 $self->_update_c_position($_, \$line, \$column);
1088 $$refcolumn = $column;
1093 ########################################################################
1099 my $refcurrent = shift;
1100 my $refline = shift;
1101 my $refcolumn = shift;
1103 # FIXME: Should not write directly
1105 my $item_lines = shift;
1106 my $item_columns = shift;
1108 local $_ = $$refcurrent;
1110 my $line = $$refline;
1111 my $column = $$refcolumn;
1121 my $item_line = $line;
1122 my $item_column = $column + 1;
1125 while($plevel > 0) {
1127 $self->_parse_c_until_one_of("\\(,\\)", \$_, \$line, \$column, \$match);
1135 push @$item_lines, $item_line;
1136 push @$item_columns, $item_column;
1137 push @$items, $item;
1147 push @$item_lines, $item_line;
1148 push @$item_columns, $item_column;
1149 push @$items, $item;
1150 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
1152 $item_column = $column + 1;
1164 $$refcolumn = $column;
1169 ########################################################################
1175 my $refcurrent = shift;
1176 my $refline = shift;
1177 my $refcolumn = shift;
1179 my $reftype = shift;
1181 local $_ = $$refcurrent;
1182 my $line = $$refline;
1183 my $column = $$refcolumn;
1187 $self->_parse_c("const", \$_, \$line, \$column);
1192 } elsif($self->_parse_c('ICOM_VTABLE\(.*?\)', \$_, \$line, \$column, \$type)) {
1194 } elsif($self->_parse_c('\w+\s*(\*\s*)*', \$_, \$line, \$column, \$type)) {
1203 $$refcolumn = $column;
1210 ########################################################################
1213 sub parse_c_typedef {
1216 my $refcurrent = shift;
1217 my $refline = shift;
1218 my $refcolumn = shift;
1220 my $reftype = shift;
1222 local $_ = $$refcurrent;
1223 my $line = $$refline;
1224 my $column = $$refcolumn;
1228 if(!$self->_parse_c("typedef", \$_, \$line, \$column)) {
1234 $$refcolumn = $column;
1241 ########################################################################
1244 sub parse_c_variable {
1247 my $found_variable = \${$self->{FOUND_VARIABLE}};
1249 my $refcurrent = shift;
1250 my $refline = shift;
1251 my $refcolumn = shift;
1253 my $reflinkage = shift;
1254 my $reftype = shift;
1255 my $refname = shift;
1257 local $_ = $$refcurrent;
1258 my $line = $$refline;
1259 my $column = $$refcolumn;
1261 $self->_parse_c_until_one_of("\\S", \$_, \$line, \$column);
1263 my $begin_line = $line;
1264 my $begin_column = $column + 1;
1270 $self->_parse_c("extern|static", \$_, \$line, \$column, \$linkage);
1271 if(!$self->parse_c_type(\$_, \$line, \$column, \$type)) { return 0; }
1272 if(!$self->_parse_c("\\w+", \$_, \$line, \$column, \$name)) { return 0; }
1276 $$refcolumn = $column;
1278 $$reflinkage = $linkage;
1282 if(&$$found_variable($begin_line, $begin_column, $linkage, $type, $name))