3 # Check that the keyword lists in gram.y and kwlist.h are sane.
4 # Usage: check_keywords.pl gram.y kwlist.h
6 # src/backend/parser/check_keywords.pl
7 # Copyright (c) 2009-2019, PostgreSQL Global Development Group
12 my $gram_filename = $ARGV[0];
13 my $kwlist_filename = $ARGV[1];
24 $, = ' '; # set output field separator
25 $\ = "\n"; # set output record separator
27 my %keyword_categories;
28 $keyword_categories{'unreserved_keyword'} = 'UNRESERVED_KEYWORD';
29 $keyword_categories{'col_name_keyword'} = 'COL_NAME_KEYWORD';
30 $keyword_categories{'type_func_name_keyword'} = 'TYPE_FUNC_NAME_KEYWORD';
31 $keyword_categories{'reserved_keyword'} = 'RESERVED_KEYWORD';
33 open(my $gram, '<', $gram_filename) || die("Could not open : $gram_filename");
40 line: while (my $S = <$gram>)
42 chomp $S; # strip record separator
46 # Make sure any braces are split
47 $s = '{', $S =~ s/$s/ { /g;
48 $s = '}', $S =~ s/$s/ } /g;
50 # Any comments are split
51 $s = '[/][*]', $S =~ s#$s# /* #g;
52 $s = '[*][/]', $S =~ s#$s# */ #g;
57 # Is this the beginning of a keyword list?
58 foreach my $k (keys %keyword_categories)
69 # Now split the line into individual fields
70 my $n = (@arr = split(' ', $S));
72 # Ok, we're in a keyword list. Go through each field in turn
73 for (my $fieldIndexer = 0; $fieldIndexer < $n; $fieldIndexer++)
75 if ($arr[$fieldIndexer] eq '*/' && $comment)
84 elsif ($arr[$fieldIndexer] eq '/*')
87 # start of a multiline comment
91 elsif ($arr[$fieldIndexer] eq '//')
96 if ($arr[$fieldIndexer] eq ';')
104 if ($arr[$fieldIndexer] eq '|')
109 # Put this keyword into the right list
110 push @{ $keywords{$kcat} }, $arr[$fieldIndexer];
115 # Check that each keyword list is in alphabetical order (just for neatnik-ism)
116 my ($prevkword, $bare_kword);
117 foreach my $kcat (keys %keyword_categories)
121 foreach my $kword (@{ $keywords{$kcat} })
124 # Some keyword have a _P suffix. Remove it for the comparison.
125 $bare_kword = $kword;
126 $bare_kword =~ s/_P$//;
127 if ($bare_kword le $prevkword)
130 "'$bare_kword' after '$prevkword' in $kcat list is misplaced";
132 $prevkword = $bare_kword;
136 # Transform the keyword lists into hashes.
137 # kwhashes is a hash of hashes, keyed by keyword category id,
138 # e.g. UNRESERVED_KEYWORD.
139 # Each inner hash is keyed by keyword id, e.g. ABORT_P, with a dummy value.
141 while (my ($kcat, $kcat_id) = each(%keyword_categories))
143 @arr = @{ $keywords{$kcat} };
146 foreach my $item (@arr) { $hash->{$item} = 1; }
148 $kwhashes{$kcat_id} = $hash;
151 # Now read in kwlist.h
153 open(my $kwlist, '<', $kwlist_filename)
154 || die("Could not open : $kwlist_filename");
156 my $prevkwstring = '';
159 kwlist_line: while (<$kwlist>)
163 if ($line =~ /^PG_KEYWORD\(\"(.*)\", (.*), (.*)\)/)
169 # Check that the list is in alphabetical order (critical!)
170 if ($kwstring le $prevkwstring)
173 "'$kwstring' after '$prevkwstring' in kwlist.h is misplaced";
175 $prevkwstring = $kwstring;
177 # Check that the keyword string is valid: all lower-case ASCII chars
178 if ($kwstring !~ /^[a-z_]+$/)
181 "'$kwstring' is not a valid keyword string, must be all lower-case ASCII chars";
184 # Check that the keyword name is valid: all upper-case ASCII chars
185 if ($kwname !~ /^[A-Z_]+$/)
188 "'$kwname' is not a valid keyword name, must be all upper-case ASCII chars";
191 # Check that the keyword string matches keyword name
192 $bare_kwname = $kwname;
193 $bare_kwname =~ s/_P$//;
194 if ($bare_kwname ne uc($kwstring))
197 "keyword name '$kwname' doesn't match keyword string '$kwstring'";
200 # Check that the keyword is present in the grammar
201 %kwhash = %{ $kwhashes{$kwcat_id} };
205 error "Unknown keyword category: $kwcat_id";
209 if (!($kwhash{$kwname}))
211 error "'$kwname' not present in $kwcat_id section of gram.y";
216 # Remove it from the hash, so that we can
217 # complain at the end if there's keywords left
218 # that were not found in kwlist.h
219 delete $kwhashes{$kwcat_id}->{$kwname};
226 # Check that we've paired up all keywords from gram.y with lines in kwlist.h
227 while (my ($kwcat, $kwcat_id) = each(%keyword_categories))
229 %kwhash = %{ $kwhashes{$kwcat_id} };
231 for my $kw (keys %kwhash)
233 error "'$kw' found in gram.y $kwcat category, but not in kwlist.h";