| package c_parser; |
| |
| use strict; |
| |
| use options qw($options); |
| use output qw($output); |
| |
| sub _update_c_position { |
| local $_ = shift; |
| my $refline = shift; |
| my $refcolumn = shift; |
| |
| my $line = $$refline; |
| my $column = $$refcolumn; |
| |
| while($_) { |
| if(s/^[^\n\t\'\"]*//s) { |
| $column += length($&); |
| } |
| |
| if(s/^\'//) { |
| $column++; |
| while(/^./ && !s/^\'//) { |
| s/^([^\'\\]*)//s; |
| $column += length($1); |
| if(s/^\\//) { |
| $column++; |
| if(s/^(.)//s) { |
| $column += length($1); |
| if($1 eq "0") { |
| s/^(\d{0,3})//s; |
| $column += length($1); |
| } |
| } |
| } |
| } |
| $column++; |
| } elsif(s/^\"//) { |
| $column++; |
| while(/^./ && !s/^\"//) { |
| s/^([^\"\\]*)//s; |
| $column += length($1); |
| if(s/^\\//) { |
| $column++; |
| if(s/^(.)//s) { |
| $column += length($1); |
| if($1 eq "0") { |
| s/^(\d{0,3})//s; |
| $column += length($1); |
| } |
| } |
| } |
| } |
| $column++; |
| } elsif(s/^\n//) { |
| $line++; |
| $column = 0; |
| } elsif(s/^\t//) { |
| $column = $column + 8 - $column % 8; |
| } |
| } |
| |
| $$refline = $line; |
| $$refcolumn = $column; |
| } |
| |
| sub parse_c { |
| my $pattern = shift; |
| my $refcurrent = shift; |
| my $refline = shift; |
| my $refcolumn = shift; |
| |
| local $_ = $$refcurrent; |
| my $line = $$refline; |
| my $column = $$refcolumn; |
| |
| if(s/$pattern//) { |
| _update_c_position($&, \$line, \$column); |
| } else { |
| return 0; |
| } |
| |
| $$refcurrent = $_; |
| $$refline = $line; |
| $$refcolumn = $column; |
| |
| return 1; |
| } |
| |
| sub parse_c_until_one_of { |
| my $characters = shift; |
| my $refcurrent = shift; |
| my $refline = shift; |
| my $refcolumn = shift; |
| my $match = shift; |
| |
| local $_ = $$refcurrent; |
| my $line = $$refline; |
| my $column = $$refcolumn; |
| |
| if(!defined($line) || !defined($column)) { |
| $output->write("error: \$characters = '$characters' \$_ = '$_'\n"); |
| exit 1; |
| } |
| |
| if(!defined($match)) { |
| my $blackhole; |
| $match = \$blackhole; |
| } |
| |
| $$match = ""; |
| while(/^[^$characters]/s) { |
| my $submatch = ""; |
| |
| if(s/^[^$characters\n\t\'\"]*//s) { |
| $submatch .= $&; |
| } |
| |
| if(s/^\'//) { |
| $submatch .= "\'"; |
| while(/^./ && !s/^\'//) { |
| s/^([^\'\\]*)//s; |
| $submatch .= $1; |
| if(s/^\\//) { |
| $submatch .= "\\"; |
| if(s/^(.)//s) { |
| $submatch .= $1; |
| if($1 eq "0") { |
| s/^(\d{0,3})//s; |
| $submatch .= $1; |
| } |
| } |
| } |
| } |
| $submatch .= "\'"; |
| |
| $$match .= $submatch; |
| $column += length($submatch); |
| } elsif(s/^\"//) { |
| $submatch .= "\""; |
| while(/^./ && !s/^\"//) { |
| s/^([^\"\\]*)//s; |
| $submatch .= $1; |
| if(s/^\\//) { |
| $submatch .= "\\"; |
| if(s/^(.)//s) { |
| $submatch .= $1; |
| if($1 eq "0") { |
| s/^(\d{0,3})//s; |
| $submatch .= $1; |
| } |
| } |
| } |
| } |
| $submatch .= "\""; |
| |
| $$match .= $submatch; |
| $column += length($submatch); |
| } elsif(s/^\n//) { |
| $submatch .= "\n"; |
| |
| $$match .= $submatch; |
| $line++; |
| $column = 0; |
| } elsif(s/^\t//) { |
| $submatch .= "\t"; |
| |
| $$match .= $submatch; |
| $column = $column + 8 - $column % 8; |
| } else { |
| $$match .= $submatch; |
| $column += length($submatch); |
| } |
| } |
| |
| $$refcurrent = $_; |
| $$refline = $line; |
| $$refcolumn = $column; |
| return 1; |
| } |
| |
| sub parse_c_block { |
| my $refcurrent = shift; |
| my $refline = shift; |
| my $refcolumn = shift; |
| my $refstatements = shift; |
| my $refstatements_line = shift; |
| my $refstatements_column = shift; |
| |
| local $_ = $$refcurrent; |
| my $line = $$refline; |
| my $column = $$refcolumn; |
| |
| my $statements; |
| if(s/^\{//) { |
| $column++; |
| $statements = ""; |
| } else { |
| return 0; |
| } |
| |
| parse_c_until_one_of("\\S", \$_, \$line, \$column); |
| |
| my $statements_line = $line; |
| my $statements_column = $column; |
| |
| my $plevel = 1; |
| while($plevel > 0) { |
| my $match; |
| parse_c_until_one_of("\\{\\}", \$_, \$line, \$column, \$match); |
| |
| $column++; |
| |
| $statements .= $match; |
| if(s/^\}//) { |
| $plevel--; |
| if($plevel > 0) { |
| $statements .= "}"; |
| } |
| } elsif(s/^\{//) { |
| $plevel++; |
| $statements .= "{"; |
| } else { |
| return 0; |
| } |
| } |
| |
| $$refcurrent = $_; |
| $$refline = $line; |
| $$refcolumn = $column; |
| $$refstatements = $statements; |
| $$refstatements_line = $statements_line; |
| $$refstatements_column = $statements_column; |
| |
| return 1; |
| } |
| |
| sub parse_c_expression { |
| my $refcurrent = shift; |
| my $refline = shift; |
| my $refcolumn = shift; |
| my $found_function_call_callback = shift; |
| |
| my $line = $$refline; |
| my $column = $$refcolumn; |
| |
| local $_ = $$refcurrent; |
| |
| parse_c_until_one_of("\\S", \$_, \$line, \$column); |
| |
| if(s/^(.*?)(\w+)(\s*)\(//s) { |
| my $begin_line = $line; |
| my $begin_column = $column + length($1) + 1; |
| |
| $line = $begin_line; |
| $column = $begin_column + length("$2$3") - 1; |
| |
| my $name = $2; |
| |
| $_ = "($'"; |
| |
| # $output->write("$name: $line.$column: '$_'\n"); |
| |
| my @arguments; |
| my @argument_lines; |
| my @argument_columns; |
| if(!parse_c_tuple(\$_, \$line, \$column, \@arguments, \@argument_lines, \@argument_columns)) { |
| return 0; |
| } |
| |
| if($name =~ /^sizeof$/) { |
| # Nothing |
| } else { |
| &$found_function_call_callback($begin_line, $begin_column, $line, $column, |
| $name, \@arguments); |
| } |
| |
| while(defined(my $argument = shift @arguments) && |
| defined(my $argument_line = shift @argument_lines) && |
| defined(my $argument_column = shift @argument_columns)) |
| { |
| parse_c_expression(\$argument, \$argument_line, \$argument_column, $found_function_call_callback); |
| } |
| } elsif(s/^return//) { |
| $column += length($&); |
| parse_c_until_one_of("\\S", \$_, \$line, \$column); |
| if(!parse_c_expression(\$_, \$line, \$column, $found_function_call_callback)) { |
| return 0; |
| } |
| } else { |
| return 0; |
| } |
| |
| _update_c_position($_, \$line, \$column); |
| |
| $$refcurrent = $_; |
| $$refline = $line; |
| $$refcolumn = $column; |
| |
| return 1; |
| } |
| |
| sub parse_c_statement { |
| my $refcurrent = shift; |
| my $refline = shift; |
| my $refcolumn = shift; |
| my $found_function_call_callback = shift; |
| |
| my $line = $$refline; |
| my $column = $$refcolumn; |
| |
| local $_ = $$refcurrent; |
| |
| parse_c_until_one_of("\\S", \$_, \$line, \$column); |
| |
| if(s/^(?:case\s+)?(\w+)\s*://) { |
| $column += length($&); |
| parse_c_until_one_of("\\S", \$_, \$line, \$column); |
| } |
| |
| # $output->write("$line.$column: '$_'\n"); |
| |
| if(/^$/) { |
| # Nothing |
| } elsif(/^\{/) { |
| my $statements; |
| my $statements_line; |
| my $statements_column; |
| if(!parse_c_block(\$_, \$line, \$column, \$statements, \$statements_line, \$statements_column)) { |
| return 0; |
| } |
| if(!parse_c_statements(\$statements, \$statements_line, \$statements_column, $found_function_call_callback)) { |
| return 0; |
| } |
| } elsif(/^(for|if|switch|while)(\s*)\(/) { |
| $column += length("$1$2"); |
| my $name = $1; |
| |
| $_ = "($'"; |
| |
| my @arguments; |
| my @argument_lines; |
| my @argument_columns; |
| if(!parse_c_tuple(\$_, \$line, \$column, \@arguments, \@argument_lines, \@argument_columns)) { |
| return 0; |
| } |
| |
| parse_c_until_one_of("\\S", \$_, \$line, \$column); |
| if(!parse_c_statement(\$_, \$line, \$column, $found_function_call_callback)) { |
| return 0; |
| } |
| parse_c_until_one_of("\\S", \$_, \$line, \$column); |
| |
| while(defined(my $argument = shift @arguments) && |
| defined(my $argument_line = shift @argument_lines) && |
| defined(my $argument_column = shift @argument_columns)) |
| { |
| parse_c_expression(\$argument, \$argument_line, \$argument_column, $found_function_call_callback); |
| } |
| } elsif(s/^else//) { |
| $column += length($&); |
| if(!parse_c_statement(\$_, \$line, \$column, $found_function_call_callback)) { |
| return 0; |
| } |
| } elsif(parse_c_expression(\$_, \$line, \$column, $found_function_call_callback)) { |
| # Nothing |
| } else { |
| # $output->write("error '$_'\n"); |
| # exit 1; |
| } |
| |
| _update_c_position($_, \$line, \$column); |
| |
| $$refcurrent = $_; |
| $$refline = $line; |
| $$refcolumn = $column; |
| |
| return 1; |
| } |
| |
| sub parse_c_statements { |
| my $refcurrent = shift; |
| my $refline = shift; |
| my $refcolumn = shift; |
| my $found_function_call_callback = shift; |
| |
| my $line = $$refline; |
| my $column = $$refcolumn; |
| |
| local $_ = $$refcurrent; |
| |
| parse_c_until_one_of("\\S", \$_, \$line, \$column); |
| my $statement = ""; |
| my $statement_line = $line; |
| my $statement_column = $column; |
| |
| my $blevel = 1; |
| my $plevel = 1; |
| while($plevel > 0 || $blevel > 0) { |
| my $match; |
| parse_c_until_one_of("\\(\\)\\[\\]\\{\\};", \$_, \$line, \$column, \$match); |
| |
| # $output->write("'$match' '$_'\n"); |
| |
| $column++; |
| $statement .= $match; |
| if(s/^[\(\[]//) { |
| $plevel++; |
| $statement .= $&; |
| } elsif(s/^[\)\]]//) { |
| $plevel--; |
| if($plevel <= 0) { |
| $output->write("error $plevel: '$statement' '$match' '$_'\n"); |
| exit 1; |
| } |
| $statement .= $&; |
| } elsif(s/^\{//) { |
| $blevel++; |
| $statement .= $&; |
| } elsif(s/^\}//) { |
| $blevel--; |
| $statement .= $&; |
| if($blevel == 1) { |
| if(!parse_c_statement(\$statement, \$statement_line, \$statement_column, $found_function_call_callback)) { |
| return 0; |
| } |
| parse_c_until_one_of("\\S", \$_, \$line, \$column); |
| $statement = ""; |
| $statement_line = $line; |
| $statement_column = $column; |
| } |
| } elsif(s/^;//) { |
| if($plevel == 1 && $blevel == 1) { |
| if(!parse_c_statement(\$statement, \$statement_line, \$statement_column, $found_function_call_callback)) { |
| return 0; |
| } |
| |
| parse_c_until_one_of("\\S", \$_, \$line, \$column); |
| $statement = ""; |
| $statement_line = $line; |
| $statement_column = $column; |
| } else { |
| $statement .= $&; |
| } |
| } elsif(/^\s*$/ && $statement =~ /^\s*$/ && $match =~ /^\s*$/) { |
| $plevel = 0; |
| $blevel = 0; |
| } else { |
| $output->write("error $plevel: '$statement' '$match' '$_'\n"); |
| exit 1; |
| } |
| } |
| |
| _update_c_position($_, \$line, \$column); |
| |
| $$refcurrent = $_; |
| $$refline = $line; |
| $$refcolumn = $column; |
| |
| return 1; |
| } |
| |
| sub parse_c_tuple { |
| my $refcurrent = shift; |
| my $refline = shift; |
| my $refcolumn = shift; |
| |
| # FIXME: Should not write directly |
| my $items = shift; |
| my $item_lines = shift; |
| my $item_columns = shift; |
| |
| local $_ = $$refcurrent; |
| |
| my $line = $$refline; |
| my $column = $$refcolumn; |
| |
| my $item; |
| if(s/^\(//) { |
| $column++; |
| $item = ""; |
| } else { |
| return 0; |
| } |
| |
| my $item_line = $line; |
| my $item_column = $column + 1; |
| |
| my $plevel = 1; |
| while($plevel > 0) { |
| my $match; |
| parse_c_until_one_of("\\(,\\)", \$_, \$line, \$column, \$match); |
| |
| $column++; |
| |
| $item .= $match; |
| if(s/^\)//) { |
| $plevel--; |
| if($plevel == 0) { |
| push @$item_lines, $item_line; |
| push @$item_columns, $item_column; |
| push @$items, $item; |
| $item = ""; |
| } else { |
| $item .= ")"; |
| } |
| } elsif(s/^\(//) { |
| $plevel++; |
| $item .= "("; |
| } elsif(s/^,//) { |
| if($plevel == 1) { |
| push @$item_lines, $item_line; |
| push @$item_columns, $item_column; |
| push @$items, $item; |
| parse_c_until_one_of("\\S", \$_, \$line, \$column); |
| $item_line = $line; |
| $item_column = $column + 1; |
| $item = ""; |
| } else { |
| $item .= ","; |
| } |
| } else { |
| return 0; |
| } |
| } |
| |
| $$refcurrent = $_; |
| $$refline = $line; |
| $$refcolumn = $column; |
| |
| return 1; |
| } |
| |
| 1; |