- Begun implementation of a C statements parser.
- More reorganizations and fixes.

diff --git a/tools/winapi/c_parser.pm b/tools/winapi/c_parser.pm
new file mode 100644
index 0000000..957723a
--- /dev/null
+++ b/tools/winapi/c_parser.pm
@@ -0,0 +1,534 @@
+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;