1#!/usr/bin/perl
2# src/interfaces/ecpg/preproc/check_rules.pl
3# test parser generator for ecpg
4# call with backend grammar as stdin
5#
6# Copyright (c) 2009-2021, PostgreSQL Global Development Group
7#
8# Written by Michael Meskes <meskes@postgresql.org>
9#            Andy Colson <andy@squeakycode.net>
10#
11# Placed under the same license as PostgreSQL.
12#
13# Command line:  [-v] [path only to ecpg.addons] [full filename of gram.y]
14#  -v enables verbose mode... show's some stats... thought it might be interesting
15#
16# This script loads rule names from gram.y and sets $found{rule} = 1 for each.
17# Then it checks to make sure each rule in ecpg.addons was found in gram.y
18
19use strict;
20use warnings;
21no warnings 'uninitialized';
22
23my $verbose = 0;
24if ($ARGV[0] eq '-v')
25{
26	$verbose = shift;
27}
28my $path   = shift || '.';
29my $parser = shift || '../../../backend/parser/gram.y';
30
31my $filename = $path . "/ecpg.addons";
32if ($verbose)
33{
34	print "parser: $parser\n";
35	print "addons: $filename\n";
36}
37
38my %replace_line = (
39	'ExecuteStmtEXECUTEnameexecute_param_clause' =>
40	  'EXECUTE prepared_name execute_param_clause execute_rest',
41
42	'ExecuteStmtCREATEOptTempTABLEcreate_as_targetASEXECUTEnameexecute_param_clauseopt_with_data'
43	  => 'CREATE OptTemp TABLE create_as_target AS EXECUTE prepared_name execute_param_clause opt_with_data execute_rest',
44
45	'ExecuteStmtCREATEOptTempTABLEIF_PNOTEXISTScreate_as_targetASEXECUTEnameexecute_param_clauseopt_with_data'
46	  => 'CREATE OptTemp TABLE IF_P NOT EXISTS create_as_target AS EXECUTE prepared_name execute_param_clause opt_with_data execute_rest',
47
48	'PrepareStmtPREPAREnameprep_type_clauseASPreparableStmt' =>
49	  'PREPARE prepared_name prep_type_clause AS PreparableStmt');
50
51my $block        = '';
52my $yaccmode     = 0;
53my $in_rule      = 0;
54my $brace_indent = 0;
55my (@arr, %found);
56my $comment     = 0;
57my $non_term_id = '';
58my $cc          = 0;
59
60open my $parser_fh, '<', $parser or die $!;
61while (<$parser_fh>)
62{
63	if (/^%%/)
64	{
65		$yaccmode++;
66	}
67
68	if ($yaccmode != 1)
69	{
70		next;
71	}
72
73	chomp;    # strip record separator
74
75	next if ($_ eq '');
76
77	# Make sure any braces are split
78	s/{/ { /g;
79	s/}/ } /g;
80
81	# Any comments are split
82	s|\/\*| /* |g;
83	s|\*\/| */ |g;
84
85	# Now split the line into individual fields
86	my $n = (@arr = split(' '));
87
88	# Go through each field in turn
89	for (my $fieldIndexer = 0; $fieldIndexer < $n; $fieldIndexer++)
90	{
91		if ($arr[$fieldIndexer] eq '*/' && $comment)
92		{
93			$comment = 0;
94			next;
95		}
96		elsif ($comment)
97		{
98			next;
99		}
100		elsif ($arr[$fieldIndexer] eq '/*')
101		{
102
103			# start of a multiline comment
104			$comment = 1;
105			next;
106		}
107		elsif ($arr[$fieldIndexer] eq '//')
108		{
109			next;
110		}
111		elsif ($arr[$fieldIndexer] eq '}')
112		{
113			$brace_indent--;
114			next;
115		}
116		elsif ($arr[$fieldIndexer] eq '{')
117		{
118			$brace_indent++;
119			next;
120		}
121
122		if ($brace_indent > 0)
123		{
124			next;
125		}
126
127		if ($arr[$fieldIndexer] eq ';' || $arr[$fieldIndexer] eq '|')
128		{
129			$block = $non_term_id . $block;
130			if ($replace_line{$block})
131			{
132				$block = $non_term_id . $replace_line{$block};
133				$block =~ tr/ |//d;
134			}
135			$found{$block} = 1;
136			$cc++;
137			$block = '';
138			$in_rule = 0 if $arr[$fieldIndexer] eq ';';
139		}
140		elsif (($arr[$fieldIndexer] =~ '[A-Za-z0-9]+:')
141			|| $arr[ $fieldIndexer + 1 ] eq ':')
142		{
143			die "unterminated rule at grammar line $.\n"
144			  if $in_rule;
145			$in_rule     = 1;
146			$non_term_id = $arr[$fieldIndexer];
147			$non_term_id =~ tr/://d;
148		}
149		else
150		{
151			$block = $block . $arr[$fieldIndexer];
152		}
153	}
154}
155
156die "unterminated rule at end of grammar\n"
157  if $in_rule;
158
159close $parser_fh;
160if ($verbose)
161{
162	print "$cc rules loaded\n";
163}
164
165my $ret = 0;
166$cc = 0;
167
168open my $ecpg_fh, '<', $filename or die $!;
169while (<$ecpg_fh>)
170{
171	if (!/^ECPG:/)
172	{
173		next;
174	}
175
176	my @Fld = split(' ', $_, 3);
177	$cc++;
178	if (not exists $found{ $Fld[1] })
179	{
180		print $Fld[1], " is not used for building parser!\n";
181		$ret = 1;
182	}
183}
184close $ecpg_fh;
185
186if ($verbose)
187{
188	print "$cc rules checked\n";
189}
190
191exit $ret;
192