001/* 002 * Copyright (c) 2012-2018 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others 003 * 004 * This program and the accompanying materials are made available under the 005 * terms of the Eclipse Public License 2.0 which is available at 006 * http://www.eclipse.org/legal/epl-2.0. 007 * 008 * SPDX-License-Identifier: EPL-2.0 009 */ 010package org.eclipse.golo.compiler.parser; 011 012import static gololang.Messages.message; 013 014/** 015 * This exception is thrown when parse errors are encountered. 016 * You can explicitly create objects of this exception type by 017 * calling the method generateParseException in the generated 018 * parser. 019 * 020 * You can modify this class to customize your error reporting 021 * mechanisms so long as you retain the public fields. 022 */ 023public class ParseException extends Exception { 024 025 /** 026 * The version identifier for this Serializable class. 027 * Increment only if the <i>serialized</i> form of the 028 * class changes. 029 */ 030 private static final long serialVersionUID = 1L; 031 032 /** 033 * The end of line string for this machine. 034 */ 035 protected static String EOL = System.getProperty("line.separator", "\n"); 036 037 /** 038 * This constructor is used by the method "generateParseException" 039 * in the generated parser. Calling this constructor generates 040 * a new object of this type with the fields "currentToken", 041 * "expectedTokenSequences", and "tokenImage" set. 042 */ 043 public ParseException(Token currentTokenVal, 044 int[][] expectedTokenSequencesVal, 045 String[] tokenImageVal 046 ) { 047 super(initialise(currentTokenVal, expectedTokenSequencesVal, tokenImageVal)); 048 currentToken = currentTokenVal; 049 expectedTokenSequences = expectedTokenSequencesVal; 050 tokenImage = tokenImageVal; 051 } 052 053 /** 054 * The following constructors are for use by you for whatever 055 * purpose you can think of. Constructing the exception in this 056 * manner makes the exception behave in the normal way - i.e., as 057 * documented in the class "Throwable". The fields "errorToken", 058 * "expectedTokenSequences", and "tokenImage" do not contain 059 * relevant information. The JavaCC generated code does not use 060 * these constructors. 061 */ 062 063 public ParseException() { 064 super(); 065 } 066 067 /** 068 * Constructor with message. 069 */ 070 public ParseException(String message) { 071 super(message); 072 } 073 074 075 /** 076 * This is the last token that has been consumed successfully. If 077 * this object has been created due to a parse error, the token 078 * followng this token will (therefore) be the first error token. 079 */ 080 public Token currentToken; 081 082 /** 083 * Each entry in this array is an array of integers. Each array 084 * of integers represents a sequence of tokens (by their ordinal 085 * values) that is expected at this point of the parse. 086 */ 087 public int[][] expectedTokenSequences; 088 089 /** 090 * This is a reference to the "tokenImage" array of the generated 091 * parser within which the parse error occurred. This array is 092 * defined in the generated ...Constants interface. 093 */ 094 public String[] tokenImage; 095 096 /** 097 * It uses "currentToken" and "expectedTokenSequences" to generate a parse 098 * error message and returns it. If this object has been created 099 * due to a parse error, and you do not catch it (it gets thrown 100 * from the parser) the correct error message 101 * gets displayed. 102 */ 103 private static String initialise(Token currentToken, 104 int[][] expectedTokenSequences, 105 String[] tokenImage) { 106 107 StringBuffer expected = new StringBuffer(); 108 int maxSize = 0; 109 for (int[] expectedTokenSequence : expectedTokenSequences) { 110 if (maxSize < expectedTokenSequence.length) { 111 maxSize = expectedTokenSequence.length; 112 } 113 for (int j = 0; j < expectedTokenSequence.length; j++) { 114 expected.append(tokenImage[expectedTokenSequence[j]]).append(' '); 115 } 116 if (expectedTokenSequence[expectedTokenSequence.length - 1] != 0) { 117 expected.append("..."); 118 } 119 expected.append(EOL).append(" "); 120 } 121 StringBuilder retval = new StringBuilder(message("unexpected_token")); 122 Token tok = currentToken.next; 123 for (int i = 0; i < maxSize; i++) { 124 if (i != 0) { 125 retval.append(" "); 126 } 127 if (tok.kind == 0) { 128 retval.append(tokenImage[0]); 129 break; 130 } 131 retval.append(" ").append(tokenImage[tok.kind]); 132 retval.append(" `").append(addEscapes(tok.image)).append("` "); 133 tok = tok.next; 134 } 135 retval.append(message("source_position", currentToken.next.beginLine, currentToken.next.beginColumn)); 136 137 return retval.toString(); 138 } 139 140 /** 141 * Used to convert raw characters to their escaped version 142 * when these raw version cannot be used as part of an ASCII 143 * string literal. 144 */ 145 static String addEscapes(String str) { 146 StringBuffer retval = new StringBuffer(); 147 char ch; 148 for (int i = 0; i < str.length(); i++) { 149 switch (str.charAt(i)) { 150 case '\b': 151 retval.append("\\b"); 152 continue; 153 case '\t': 154 retval.append("\\t"); 155 continue; 156 case '\n': 157 retval.append("\\n"); 158 continue; 159 case '\f': 160 retval.append("\\f"); 161 continue; 162 case '\r': 163 retval.append("\\r"); 164 continue; 165 case '\"': 166 retval.append("\\\""); 167 continue; 168 case '\'': 169 retval.append("\\\'"); 170 continue; 171 case '\\': 172 retval.append("\\\\"); 173 continue; 174 default: 175 if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { 176 String s = "0000" + Integer.toString(ch, 16); 177 retval.append("\\u" + s.substring(s.length() - 4, s.length())); 178 } else { 179 retval.append(ch); 180 } 181 continue; 182 } 183 } 184 return retval.toString(); 185 } 186 187}