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