1
2###
3###  Copyright 2002-2003 University of Illinois Board of Trustees
4###  Copyright 2002-2003 Mark D. Roth
5###  All rights reserved.
6###
7###  grammar.yp - Parse::Yapp grammar for Config::Objective
8###
9###  Mark D. Roth <roth@uiuc.edu>
10###  Campus Information Technologies and Educational Services
11###  University of Illinois at Urbana-Champaign
12###
13
14
15###
16### Header section
17###
18
19%{
20use strict;
21
22###
23### constants for conditional evaluation state
24###
25
26### either we already found a true condition, or the parent conditional
27### block is not being evaluated, so:
28###  1. subsequent %else or %elif clauses are NOT evaluated
29###  2. enclosed statements are NOT evaluated
30use constant CS_NO_EVAL		=> -1;
31
32### have not yet found a true conditional in this block, which means:
33###  1. subsequent %else or %elif clauses are evaluated
34###  2. enclosed statements are NOT evaluated
35use constant CS_FALSE		=> 0;
36
37### immediately preceding condition was true, so:
38###  1. subsequent %else or %elif clauses are NOT evaluated
39###  2. enclosed statements are evaluated
40use constant CS_TRUE		=> 1;
41%}
42
43
44%left AND OR
45%right NOT
46
47
48%%
49
50###
51### Rules section
52###
53
54config	: #empty
55	| config directive
56	;
57
58directive
59	: statement
60	| conditional
61	| include
62	;
63
64include	: INCLUDE string
65	{
66		my ($config) = $_[0]->YYData->{'config'};
67
68		return undef
69			if (@{$config->{'cond_stack'}}
70			    && $config->{'cond_stack'}->[-1] != CS_TRUE);
71
72		$_[2] = $config->{'include_dir'} . '/' . $_[2]
73			if ($_[2] !~ m|^/|);
74
75		$config->parse($_[2]);
76	}
77	;
78
79conditional
80	: cond_if config cond_endif
81	| cond_elif
82	| cond_else
83	;
84
85expression
86	: expr
87	| NOT expression
88	{
89		return (! $_[2]);
90	}
91	| expression OR expression
92	{
93		return ($_[1] || $_[3]);
94	}
95	| expression AND expression
96	{
97		return ($_[1] && $_[3]);
98	}
99	| PAREN_START expression PAREN_END
100	{
101		return $_[2];
102	}
103	;
104
105expr	:
106	{
107		my ($config) = $_[0]->YYData->{'config'};
108		$config->{'in_expr'} = 1;
109	}
110	method_call
111	{
112		my ($config) = $_[0]->YYData->{'config'};
113		$config->{'in_expr'} = 0;
114		return $_[2];
115	}
116	;
117
118cond_if	: IF PAREN_START expression PAREN_END
119	{
120		my ($config) = $_[0]->YYData->{'config'};
121
122		push(@{$config->{'cond_stack'}},
123		     ((@{$config->{'cond_stack'}}
124		       && $config->{'cond_stack'}->[-1] != CS_TRUE)
125		      ? CS_NO_EVAL
126		      : ($_[3]
127			 ? CS_TRUE
128			 : CS_FALSE)));
129	}
130	;
131
132cond_endif
133	: ENDIF
134	{
135		my ($config) = $_[0]->YYData->{'config'};
136
137		die "%endif: not in conditional\n"
138			if (! @{$config->{'cond_stack'}});
139
140		pop(@{$config->{'cond_stack'}});
141	}
142	;
143
144cond_elif
145	: ELIF PAREN_START expression PAREN_END
146	{
147		my ($config) = $_[0]->YYData->{'config'};
148
149		die "%elif: not in conditional\n"
150			if (! @{$config->{'cond_stack'}});
151
152		### all previous conditions were false, so evaluate this one
153		if ($config->{'cond_stack'}->[-1] == CS_FALSE)
154		{
155			$config->{'cond_stack'}->[-1] = ($_[3]
156							 ? CS_TRUE
157							 : CS_FALSE);
158		}
159
160		### the last condition was true, so all subsequent %else
161		### or %elif clauses must be false
162		elsif ($config->{'cond_stack'}->[-1] == CS_TRUE)
163		{
164			$config->{'cond_stack'}->[-1] = CS_NO_EVAL;
165		}
166
167		### if it's CS_NO_EVAL, leave it alone
168	}
169	;
170
171cond_else
172	: ELSE
173	{
174		my ($config) = $_[0]->YYData->{'config'};
175
176		die '%else: not in conditional'
177			if (! @{$config->{'cond_stack'}});
178
179		### all previous conditions were false, so set to true
180		if ($config->{'cond_stack'}->[-1] == CS_FALSE)
181		{
182			$config->{'cond_stack'}->[-1] = CS_TRUE;
183		}
184
185		### the last condition was true, so set to CS_NO_EVAL
186		elsif ($config->{'cond_stack'}->[-1] == CS_TRUE)
187		{
188			$config->{'cond_stack'}->[-1] = CS_NO_EVAL;
189		}
190
191		### if it's CS_NO_EVAL, leave it alone
192	}
193	;
194
195string	: WORD
196	| QSTRING
197	;
198
199value	: string
200	| list
201	| hash
202	;
203
204statement
205	: method_call EOS
206	;
207
208method_name
209	: #empty
210	{
211		my ($config) = $_[0]->YYData->{'config'};
212		return ($config->{'in_expr'} ? 'equals' : 'default');
213	}
214	| METHOD_ARROW WORD
215	{
216		return $_[2];
217	}
218	;
219
220method_args
221	: #empty
222	{
223		my ($config) = $_[0]->YYData->{'config'};
224		push(@{$config->{arg_stack}}, []);
225	}
226	| value
227	{
228		my ($config) = $_[0]->YYData->{'config'};
229		push(@{$config->{arg_stack}}, [ $_[1] ]);
230	}
231	| PAREN_START
232	{
233		my ($config) = $_[0]->YYData->{'config'};
234		push(@{$config->{'list_stack'}}, []);
235	}
236	list_values PAREN_END
237	{
238		my ($config) = $_[0]->YYData->{'config'};
239		push(@{$config->{arg_stack}}, pop(@{$config->{'list_stack'}}));
240	}
241	;
242
243method_call
244	: WORD method_name method_args
245	{
246		my ($config) = $_[0]->YYData->{'config'};
247
248#		print "var='$_[1]' method='$_[2]' value='$_[3]'\n";
249
250		### for conditional expressions, don't bother evaluating
251		### if a previous condition was true
252		return undef
253			if ($config->{'in_expr'}
254			    && @{$config->{'cond_stack'}}
255			    && $config->{'cond_stack'}->[-1] == CS_NO_EVAL);
256
257		### for statements, don't evaluate if we're inside a
258		### false conditional block
259		return undef
260			if (! $config->{'in_expr'}
261			    && @{$config->{'cond_stack'}}
262			    && $config->{'cond_stack'}->[-1] != CS_TRUE);
263
264		return $config->_call_obj_method($_[1], $_[2],
265					@{pop(@{$config->{arg_stack}})});
266	}
267	;
268
269list	: LIST_START
270	{
271		my ($config) = $_[0]->YYData->{'config'};
272		push(@{$config->{'list_stack'}}, []);
273	}
274	list_values LIST_END
275	{
276		my ($config) = $_[0]->YYData->{'config'};
277		return pop(@{$config->{'list_stack'}});
278	}
279	;
280
281list_values
282	: #empty
283	| value
284	{
285		my ($config) = $_[0]->YYData->{'config'};
286		push(@{$config->{'list_stack'}->[-1]}, $_[1]);
287	}
288	| list_values COMMA value
289	{
290		my ($config) = $_[0]->YYData->{'config'};
291		push(@{$config->{'list_stack'}->[-1]}, $_[3]);
292	}
293	;
294
295hash	: HASH_START HASH_END
296	{
297		return {};
298	}
299	| HASH_START
300	{
301		my ($config) = $_[0]->YYData->{'config'};
302
303		push(@{$config->{'hash_stack'}}, {});
304	}
305	hash_values HASH_END
306	{
307		my ($config) = $_[0]->YYData->{'config'};
308
309		return pop(@{$config->{'hash_stack'}});
310	}
311	;
312
313hash_values
314	: hash_values COMMA hash_value
315	| hash_value
316	;
317
318hash_value
319	: string
320	{
321		my ($config) = $_[0]->YYData->{'config'};
322
323#		print "\t'$_[1]' => undef\n";
324		$config->{'hash_stack'}->[-1]->{$_[1]} = undef;
325	}
326	| string HASH_ARROW value
327	{
328		my ($config) = $_[0]->YYData->{'config'};
329
330#		print "\t'$_[1]' => '$_[3]'\n";
331		$config->{'hash_stack'}->[-1]->{$_[1]} = $_[3];
332	}
333	;
334
335%%
336
337###
338### Footer section
339###
340
341
342