--- /dev/null
+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<count;++i)
+ if(cbuf[off+i] == '\n')
+ addLine(end+i+1);
+ ensureCapacity(count);
+ System.arraycopy(cbuf, off, buffer, end, count);
+ end += count;
+ }
+ return count;
+ }
+
+ @Override
+ public int read() throws IOException {
+ int result = base.read();
+ if(result >= 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);
+ }
+
+}