1grammar Psl;
2
3@parser::members
4{
5	public void printPosition(String name, Token tok)
6	{
7		System.out.printf("%s: pos %d, len %d%n",
8				name, tok.getCharPositionInLine(), tok.getText().length());
9	}
10
11
12	/**
13	 * Checks whether a set of digit groups and commas construct
14	 * a valid command-number.
15	 *
16	 * @param digits
17	 *		The groups of digits, each group in a separate item.
18	 * @param commas
19	 *		The commas found separating the digit groups.
20	 *
21	 * There should be one more digit group than commas.
22	 * There should be no internal white space.
23	 *
24	 * @returns true (valid), false (invalid)
25	 */
26
27	public boolean isValidCommaNumber(List<Token> digits, List<Token> commas)
28	{
29		Token[]	aDigits = new Token[0];
30		Token[]	aCommas = new Token[0];
31		int		j;
32
33		aDigits = digits.toArray(aDigits);
34		aCommas = commas.toArray(aCommas);
35		if (aDigits.length != aCommas.length + 1)
36		{
37			return false;
38		}
39		for (j = 0; j < aCommas.length; ++j)
40		{
41			int	p1, p2, p3;
42			p1 = aDigits[j].getCharPositionInLine()
43					+ aDigits[j].getText().length();
44			p2 = aCommas[j].getCharPositionInLine();
45			p3 = aDigits[j + 1].getCharPositionInLine();
46			if (p1 != p2 || (p2 + 1) != p3)
47			{
48				return false;
49			}
50		}
51		return true;
52	}
53
54
55	/**
56	 * Checks whether a the pieces of a floating-point number
57	 * construct a valid number.
58	 *
59	 * @param whole
60	 *		The whole part of the number.  Can be null.
61	 * @param period
62	 *		The decimal point.
63	 * @param fraction
64	 *		The fraction part of the number.  Can be null.
65	 *
66	 * At least one of the whole or fraction must be present.
67	 * The decimal point is required.
68	 *
69	 * @returns true (valid), false (invalid)
70	 */
71
72	public boolean isValidFloatingConstant(
73		Token whole,
74		Token period,
75		Token fraction
76	)
77	{
78		boolean		foundDigits = false;
79		int			column;
80
81		if (whole != null)
82		{
83			foundDigits = true;
84			column = whole.getCharPositionInLine()
85					+ whole.getText().length();
86			if (column != period.getCharPositionInLine())
87			{
88				return false;
89			}
90		}
91		if (fraction != null)
92		{
93			foundDigits = true;
94			column = period.getCharPositionInLine() + 1;
95			if (column != fraction.getCharPositionInLine())
96			{
97				return false;
98			}
99		}
100		return foundDigits;
101	}
102}
103
104translation_unit
105	:	numeric_range
106		EOF
107	;
108
109pattern
110	:	numeric_range
111	;
112
113numeric_range
114	:	EURO_NUMBER
115		PAREN_LEFT
116		numeric_endpoint
117		TILDE
118		numeric_endpoint
119		PAREN_RIGHT
120	|	NUMBER
121		PAREN_LEFT
122		numeric_endpoint
123		TILDE
124		numeric_endpoint
125		PAREN_RIGHT
126	;
127
128numeric_endpoint
129	:	( PLUS | MINUS )? integer_constant
130	|	( PLUS | MINUS )? floating_constant
131	|	( PLUS | MINUS )? comma_number
132	;
133
134	/* Floating-point numbers and comma numbers are valid only
135	 * as numeric endpoints in number() or euro_number().  Otherwise,
136	 * the pieces should be parsed as separate lexical tokens, such as
137	 *
138	 *	integer_constant '.' integer_constant
139	 *
140	 * Because of parser lookahead and the subtle interactions between
141	 * the parser and the lexer, changing lexical modes from the parser
142	 * is not safe.  The code below checks the constraints for floating
143	 * numbers, forbidding internal white space.
144	 */
145
146floating_constant
147	:	comma_number PERIOD fraction=DIGIT_SEQUENCE?
148		{
149			isValidFloatingConstant($comma_number.stop, $PERIOD, $fraction)
150		}?<fail = {
151			"COMMA:A floating-point constant cannot have internal white space"
152		}>
153
154	/*|	whole=DIGIT_SEQUENCE PERIOD fraction=DIGIT_SEQUENCE?
155		{
156			isValidFloatingConstant($whole, $PERIOD, $fraction)
157		}?/* <fail = {
158			"DIG:A floating-point constant cannot have internal white space"
159		}>*/
160
161	|	PERIOD fraction=DIGIT_SEQUENCE
162		{
163			isValidFloatingConstant(null, $PERIOD, $fraction)
164		}?<fail = {
165			"DEC:A floating-point constant cannot have internal white space"
166		}>
167	;
168
169comma_number
170	:	digits+=DIGIT_SEQUENCE ( commas+=COMMA digits+=DIGIT_SEQUENCE )+
171		{
172			isValidCommaNumber($digits, $commas)
173		}?<fail = {
174			"A comma-number cannot have internal white space"
175		}>
176	;
177
178term_expression
179	:	term
180	|	RETURN
181		(
182			PAREN_LEFT
183			( integer_constant | ALL )
184			PAREN_RIGHT
185		)?
186		term
187	;
188
189term
190	:	pattern
191	|	PAREN_LEFT term_expression PAREN_RIGHT
192	;
193
194integer_constant
195	:	DIGIT_SEQUENCE
196	|	INTEGER_CONSTANT
197	|	BINARY_CONSTANT
198	|	DECIMAL_CONSTANT
199	|	HEXADECIMAL_CONSTANT
200	|	OCTAL_CONSTANT
201	;
202
203// LEXER
204
205/* Letter fragments
206 */
207
208fragment A: [Aa] ;
209fragment B: [BB] ;
210fragment C: [Cc] ;
211fragment D: [Dd] ;
212fragment E: [Ee] ;
213fragment F: [Ff] ;
214fragment G: [Gg] ;
215fragment H: [Hh] ;
216fragment I: [Ii] ;
217fragment J: [Jj] ;
218fragment K: [Kk] ;
219fragment L: [Ll] ;
220fragment M: [Mm] ;
221fragment N: [Nn] ;
222fragment O: [Oo] ;
223fragment P: [Pp] ;
224fragment Q: [Qq] ;
225fragment R: [Rr] ;
226fragment S: [Ss] ;
227fragment T: [Tt] ;
228fragment U: [Uu] ;
229fragment V: [Vv] ;
230fragment W: [Ww] ;
231fragment X: [Xx] ;
232fragment Y: [Yy] ;
233fragment Z: [Zz] ;
234
235
236WHITESPACE_IN_LINE
237	:	[ \t]+
238		-> skip
239	;
240
241NEWLINE
242	:	'\r'? '\n'
243		-> skip
244	;
245
246WHITESPACE_ALL
247	:	[ \n\r\t]+
248		-> skip
249	;
250
251
252	/* A sequence of decimal digits is useful on its own,
253	 * to avoid the base-prefixes (0b, 0x, ...) that an
254	 * INTEGER_CONTANT would allow.
255	 * Need to define before INTEGER_CONSTANT to make sure
256	 * DIGIT_SEQUENCE is recognized before INTEGER_CONSTANT.
257	 */
258
259DIGIT_SEQUENCE
260	:	[0-9]+
261	;
262
263INTEGER_CONSTANT
264	:	BINARY_CONSTANT
265	|	DECIMAL_CONSTANT
266	|	HEXADECIMAL_CONSTANT
267	|	OCTAL_CONSTANT
268	;
269
270BINARY_CONSTANT
271	:	'0' [Bb] [0-1]+
272	;
273
274DECIMAL_CONSTANT
275	:	( '0' [Dd] )? [0-9]+
276	;
277
278HEXADECIMAL_CONSTANT
279	:	'0' [HhXx] [0-9a-fA-F]+
280	;
281
282OCTAL_CONSTANT
283	:	'0' [Oo] [0-7]+
284	;
285
286/*	keywords
287 */
288
289ALL
290	:	A L L
291	;
292
293EURO_NUMBER
294	:	E U R O '_' N U M B E R
295	;
296
297
298NUMBER
299	:	N U M B E R
300	;
301
302RETURN
303	:	R E T U R N
304	;
305
306IDENTIFIER
307	:	[A-Za-z][A-Za-z0-9_]*
308	;
309
310
311/* The single-character tokens.
312 */
313
314COMMA
315	:	','
316	;
317
318MINUS
319	:	'-'
320	;
321
322PAREN_LEFT
323	:	'('
324	;
325
326PAREN_RIGHT
327	:	')'
328	;
329
330PERIOD
331	:	'.'
332	;
333
334PLUS
335	:	'+'
336	;
337
338TILDE
339	:	'~'
340	;
341
342	/* This rule must be last (or nearly last) to avoid
343	 * matching individual characters for other rules.
344	 */
345
346ANY_CHAR_BUT_NEWLINE
347	:	~[\n\r]
348	;
349