All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.squarespace.less.parse.Stream Maven / Gradle / Ivy

There is a newer version: 1.7.2
Show newest version
/**
 * Copyright (c) 2014 SQUARESPACE, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.squarespace.less.parse;

import org.apache.commons.lang3.StringEscapeUtils;

import com.squarespace.less.core.CharClass;
import com.squarespace.less.core.CharPattern;
import com.squarespace.less.core.Chars;


/**
 * Wraps a String and provides an interface for interacting with the sequence of characters.
 */
public class Stream {

  protected static final boolean DEBUG = false;

  protected final String raw;

  protected final int length;

  protected int index;

  protected int furthest;

  protected int lineOffset;

  protected int charOffset;

  public Stream(String raw) {
    this.raw = raw;
    this.length = raw.length();
  }

  public int getLineOffset() {
    return lineOffset;
  }

  public int getCharOffset() {
    return charOffset;
  }

  protected void dump() {
    char ch = (index >= length) ? Chars.EOF : raw.charAt(index);
    String esc = StringEscapeUtils.escapeJava(ch + "");
    System.out.printf("Stream: index=%d len=%d line=%d char=%d ch=\"%s\"\n",
          index, length, lineOffset, charOffset, esc);
  }

  public String raw() {
    return raw;
  }

  public int position() {
    return index;
  }

  public Mark mark() {
    Mark pos = new Mark();
    mark(pos);
    return pos;
  }

  /**
   * Mark current position in the stream so that we can restore it later.
   */
  public void mark(Mark mark) {
    mark.index = index;
    mark.lineOffset = lineOffset;
    mark.charOffset = charOffset;
  }

  /**
   * Restore the stream to the marked position.
   */
  public int restore(Mark mark) {
    index = mark.index;
    lineOffset = mark.lineOffset;
    charOffset = mark.charOffset;
    return index;
  }

  /**
   * Return the character under the stream pointer. Does not increment the
   * stream pointer.
   */
  public char peek() {
    return (index >= length) ? Chars.EOF : raw.charAt(index);
  }

  /**
   * Return the character at position 'index + offset'. Does not increment the
   * stream pointer.
   */
  public char peek(int offset) {
    int pos = index + offset;
    return (pos < 0 || pos >= length) ? Chars.EOF : raw.charAt(pos);
  }

  /**
   * Seek ahead in the stream 'offset' characters. Increments the stream pointer and
   * increments the line/offset counters.
   */
  public char seek(int offset) {
    int limit = Math.min(length, index + offset);
    while (index < limit) {
      consume(raw.charAt(index));
      index++;
    }
    furthest = Math.max(index, furthest);
    return peek();
  }

  public char seek1() {
    if (index < length) {
      consume(raw.charAt(index));
      index++;
    }
    furthest = Math.max(index, furthest);
    return peek();
  }

  /**
   * Seek ahead until 'ch' is found. Increments the stream pointer, leaving it pointing at
   * the character just after 'ch', or the end of the string if not matched.
   */
  public void seekTo(char ch) {
    while (index < length) {
      char curr = raw.charAt(index);
      consume(curr);
      index++;
      if (curr == ch) {
        break;
      }
    }
    furthest = Math.max(index, furthest);
  }

  /**
   * If the character under the cursor equals 'ch', consume it and return
   * true; else return false;
   */
  public boolean seekIf(char ch) {
    if (peek() == ch) {
      seek1();
      return true;
    }
    return false;
  }

  /**
   * Consume all whitespace characters, positioning the pointer over the next non-whitespace character.
   * Returns the number of characters skipped.
   */
  public int skipWs() {
    int start = index;
    while (index < length) {
      char curr = raw.charAt(index);
      if (!CharClass.whitespace(curr)) {
        break;
      }
      consume(curr);
      index++;
    }
    // Important not to update 'furthest' pointer when skipping whitespace
    return index - start;
  }

  public int skipEmpty() {
    int start = index;
    while (index < length) {
      char curr = raw.charAt(index);
      if (!CharClass.skippable(curr)) {
        break;
      }
      consume(curr);
      index++;
    }
    // Important not to update 'furthest' pointer when skipping 'empty' chars.
    return index - start;
  }

  /**
   * Searches the stream to find the character pattern, consuming every character
   * it sees along the way.  Searching the string "ABCDEFGH" for the pattern "DE",
   * this would position the cursor over the 'F'.
   *
   * Searching for a pattern P in a string S of length N, this runs O(N).
   */
  public boolean seek(CharPattern table) {
    char[] pattern = table.pattern();
    int[] next = table.next();
    int patternLen = pattern.length;
    int j;
    for (j = 0; index < length && j < patternLen; index++) {
      char ch = raw.charAt(index);
      consume(ch);
      while (j >= 0 && ch != pattern[j]) {
        j = next[j];
      }
      j++;
    }
    furthest = Math.max(index, furthest);
    return (j == patternLen);
  }


  @Override
  public String toString() {
    return "Stream(\"" + StringEscapeUtils.escapeJava(raw.substring(index)) + "\")";
  }

  public String furthest() {
    return raw.substring(Math.min(furthest, length - 1));
  }

  private void consume(char ch) {
    if (ch == Chars.LINE_FEED) {
      lineOffset++;
      charOffset = 0;
    } else {
      charOffset++;
    }
  }

  /**
   * Useful for debugging the parser.
   */
  @SuppressWarnings("unused")
  private void stack() {
    StackTraceElement[] elems = Thread.currentThread().getStackTrace();
    for (int i = 1; i < 5; i++) {
      System.out.println(elems[i].getLineNumber() + " " + elems[i].getFileName() + " " + elems[i].getMethodName());
    }
    System.out.println();
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy