package org.simantics.scl.compiler.internal.parsing.utils; import java.io.IOException; import java.io.Reader; import org.simantics.scl.compiler.errors.Locations; /** * A wrapper to reader that remembers the last characters * read and line numbers. * * @author Hannu Niemistö */ public class MemoReader extends Reader { Reader base; char[] buffer = new char[80]; int absoluteBegin = 0; int begin = 0; int end = 0; int firstLine = 1; int[] lineLocations = new int[8]; int lineLocationsPos; public MemoReader(Reader base) { this.base = base; addLine(0); } private int findLineLocationArrayOffset(int location) { int l = 0; int h = lineLocationsPos; while(l+1 < h) { int c = (l+h)>>1; int loc = lineLocations[c]; if(loc <= location) l = c; else h = c; } return l; } private void addLine(int location) { if(lineLocationsPos == lineLocations.length) { int p = findLineLocationArrayOffset(absoluteBegin); if(p*2 >= lineLocationsPos) System.arraycopy(lineLocations, p, lineLocations, 0, lineLocationsPos-p); else { int[] newLineLocations = new int[lineLocations.length*2]; System.arraycopy(lineLocations, p, newLineLocations, 0, lineLocationsPos-p); lineLocations = newLineLocations; } lineLocationsPos -= p; firstLine += p; } lineLocations[lineLocationsPos++] = location; } public String locationToString(int location) { int lOffset = findLineLocationArrayOffset(location); int row = lOffset + firstLine; int column = location + 1 - lineLocations[lOffset]; return row + ":" + column; } public String locationToString(long location) { if(location == Locations.NO_LOCATION) return ""; else return locationToString(Locations.beginOf(location)) + "-" + locationToString(Locations.endOf(location)) + ": "; } public String locationUnderlining(long coupledLocation) { int beginRow; int beginColumn; int endRow; int endColumn; { int location = Locations.beginOf(coupledLocation); int lOffset = findLineLocationArrayOffset(location); beginRow = lOffset + firstLine; beginColumn = location - lineLocations[lOffset] + 2; } { int location = Locations.endOf(coupledLocation); int lOffset = findLineLocationArrayOffset(location); endRow = lOffset + firstLine; endColumn = location - lineLocations[lOffset] + 2; } StringBuilder b = new StringBuilder(); while(b.length() < beginColumn) b.append(' '); if(beginRow == endRow) { int end = Math.max(endColumn, beginColumn+1); while(b.length() < end) b.append('^'); } else b.append("^^^..."); b.append(" (").append(beginRow).append(':').append(beginColumn).append('-') .append(endRow).append(':').append(endColumn).append(')'); return b.toString(); } private void ensureCapacity(int count) { if(end + count > buffer.length) { if(end - begin + count <= buffer.length) { System.arraycopy(buffer, begin, buffer, 0, end-begin); end -= begin; begin = 0; } else { char[] newBuffer = new char[Math.max(buffer.length*2, end+count)]; System.arraycopy(buffer, begin, newBuffer, begin, end-begin); buffer = newBuffer; } } } @Override public int read(char[] cbuf, int off, int len) throws IOException { int count = base.read(cbuf, off, len); if(count > 0) { for(int i=0;i= 0) { ensureCapacity(1); buffer[end++] = (char)result; if(result == '\n') addLine(end); } return result; } public String extractString(long location) { int requestedBegin = Locations.beginOf(location); int requestedEnd = Locations.endOf(location); if(requestedBegin < absoluteBegin) throw new IllegalArgumentException(); return new String(buffer, begin + requestedBegin - absoluteBegin, requestedEnd-requestedBegin); } public void forgetEverythingBefore(int location) { begin += location-absoluteBegin; absoluteBegin = location; } @Override public void close() throws IOException { base.close(); } public String getLastCommand() { while(true) { try { int c = read(); if(c < 0 || c == '\n') break; } catch (IOException e) { break; } } int p = begin; if(buffer[p] == '\n') ++p; return new String(buffer, p, end-p); } }