1#!/usr/bin/perl -w
2
3use strict;
4use Data::Dumper;
5
6our %enums;
7
8# Usage: abi-check C-file Ocaml-file
9# Writes out a BUILD_BUG_ON() list to be included back into C.
10#
11# Ocaml-file should be the .ml file.  The ocaml compiler will check
12# that any declarations in a .mli correspond.  We check the .ml
13# rather than the .mli in case there are private types in future.
14
15@ARGV == 2 or die;
16our ($c, $o) = @ARGV;
17
18open C_FILE, "<", $c or die $!;
19
20our $cline = -1;
21our $ei;
22
23# Parse the C file looking for calls to:
24#   c_bitmap_to_ocaml_list()
25#   ocaml_list_to_c_bitmap()
26#
27# followed by anotations of the following form:
28#   /* ! OType OPrefix Mangle */
29#   /* ! CPrefix CFinal CFinalHow */
30# or, for subsequent invocations for the same OType, just
31#   /* ! OType */
32#
33# The function definitions use /* ! */ which simply skips that instance.
34while (<C_FILE>) {
35    if ($cline == -1) {
36        if (m/c_bitmap_to_ocaml_list|ocaml_list_to_c_bitmap/) {
37            $cline = 0;
38            $ei = { };
39        }
40    } else {
41        $cline++;
42        m{^\s+/\* \s+ ! \s+ (.*?) \s* \*/\s*$}x or
43            die "at line $cline of annotation, did not expect $_ ?";
44        my @vals = split /\s+/, $1;
45        if ($cline == 1 && !@vals) {
46            $cline = -1;
47        } elsif ($cline == 1 && @vals == 1) {
48            my ($otype) = @vals;
49            die "reference to undefined OType $otype" unless $enums{$otype};
50            $cline = -1;
51        } elsif ($cline == 1 && @vals == 3) {
52            $ei->{$_} = shift @vals foreach qw(OType OPrefix Mangle);
53        } elsif ($cline == 2 && @vals == 3) {
54            $ei->{$_} = shift @vals foreach qw(CPrefix CFinal CFinalHow);
55            die "redefining OType $ei->{OType}" if $enums{ $ei->{OType} };
56            $enums{ $ei->{OType} } = $ei;
57            $cline = -1;
58        } else {
59            die "$_ ?";
60        }
61    }
62}
63
64sub expect ($$) {
65    printf "BUILD_BUG_ON( %-30s != %-10s );\n", @_ or die $!;
66}
67
68open OCAML_FILE, "<", $o or die $!;
69my $cval;
70$ei = undef;
71my $bitnum = 0;
72while (<OCAML_FILE>) {
73    if ($ei) {
74        if (m{^\s+ \| \s* $ei->{OPrefix} (\w+) \s*$}x) {
75            $cval = $1;
76            if ($ei->{Mangle} eq 'lc') {
77                $cval = lc $cval;
78            } elsif ($ei->{Mangle} eq 'none') {
79            } else {
80                die;
81            }
82            $cval = $ei->{CPrefix}.$cval;
83            expect($cval, "(1u << $bitnum)");
84            $bitnum++;
85        } elsif (m/^\w|\{/) {
86            if ($ei->{CFinalHow} eq 'max') {
87                expect($ei->{CFinal}, "(1u << ".($bitnum-1).")");
88            } elsif ($ei->{CFinalHow} eq 'all') {
89                expect($ei->{CFinal}, "(1u << $bitnum)-1u");
90            } else {
91                die Dumper($ei)." ?";
92            }
93            $ei->{Checked} = 1;
94            $ei = undef;
95        } elsif (!m{\S}) {
96        } else {
97            die "$_ ?";
98        }
99    }
100    if (!$ei) {
101        if (m{^type \s+ (\w+) \s* \= \s* $}x && $enums{$1}) {
102            print "// found ocaml type $1 at $o:$.\n" or die $!;
103            $ei = $enums{$1};
104            $cval = '';
105            $bitnum = 0;
106        }
107    }
108}
109
110foreach $ei (values %enums) {
111    next if $ei->{Checked};
112    die "did not find ocaml type definition for $ei->{OType} in $o";
113}
114
115close STDOUT or die $!;
116