Package tikzparser

Source Code of tikzparser.TikzParser

package tikzparser;

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import main.Debug;

import tikzmodel.KeyValue;
import tikzmodel.TeXDimension;
import tikzmodel.TeXUnit;
import tikzmodel.TikzCircle;
import tikzmodel.TikzCoordinate;
import tikzmodel.TikzFigure;
import tikzmodel.TikzLine;
import tikzmodel.TikzOperator;
import tikzmodel.TikzParameters;
import tikzmodel.TikzPath;
import tikzmodel.TikzPathType;
import tikzmodel.TikzPicture;
import tikzmodel.TikzRectangle;


/**
* This is a TikZ-parser that creates various syntactical data structures from
* a given TikZ source code.
*
* @author Florian Noack
*
*/
public class TikzParser implements Debug {
 
  // Floating-point fields
  public static final String  FP_REGEX;
  public static final Pattern  FP_PATTERN;
 
  // TikZ-path separator
  public static final Pattern  PATH_SEPARATOR_PATTERN;
 
  static {
    // Floating-point fields
    FP_REGEX  =  "[+-]?"    // Optional sign character
            "[\\d]*"  // Optional before floating-point
            "[.]?"    // Optional floating-point
            "\\d+";      // Digits
    FP_PATTERN  = Pattern.compile(FP_REGEX);
   
   
    // TikZ-path separator pattern
    PATH_SEPARATOR_PATTERN
      = Pattern.compile(new Character(TikzPath.PATH_SEPARATOR).toString());
  }
 

  /**
   * Offset during the parsing routine
   */
  private int    offset;
 
 
  /**
   * Code to be parsed
   */
  private char[]  code;
 
 
  /*
   * Path chain variables
   */
  String          operatorToken  = null;
  TikzOperator      lastOperator  = null;
  SyntaxTikzCoordinate  origin      = null;
  SyntaxTikzCoordinate  lastOrigin    = null;
 
 

  /**
   * Returns a SyntaxTikzPicture from a specified TikZ source code.
   *
   * @param code
   *   TikZ source code to be parsed.
   *
   * @return
   *   parsed picture.
   */
  public SyntaxTikzPicture parseCode(String code) {
    offset    = -1;
    this.code  = code.toCharArray();
   
    p("parseCode: #"+code+"#");
   
    TikzPicture tp = new TikzPicture();
    SyntaxTikzPicture stxPicture = new SyntaxTikzPicture(tp);
   
    /*
     * Read while new path triggers are detected
     */
    boolean  running    = true;
    int    firstSave  = 0;
   
    while(running) {
      offset = code.indexOf(TikzPath.PATH_TRIGGER, ++offset);

      // No more path triggers found
      if(offset == -1) {
        stxPicture.addToken(Util.substring(this.code, firstSave, this.code.length-1));
        running = false;
       
      } else {
        // Try to decode a path and save it
        try {
          int pathStart = offset;
          SyntaxTikzPath path = parsePath();
          p("parseCode: new path added");
          stxPicture.addToken(Util.substring(this.code, firstSave, pathStart-1));
          stxPicture.addToken(path);
          firstSave = offset;
          --offset;
        } catch (TikzParserException e) {
          p(e.getMessage());
          //e.printStackTrace();
        }
      }
    }
   
    return stxPicture;
  }


  /**
   *
   * @return
   * @throws TikzParserException
   */
  private SyntaxTikzPath parsePath() throws TikzParserException {
    p("parsePath()");
   
    /*
     * Locals
     */
    TikzPath path = null;
    ArrayList<TikzFigure>  figures  = new ArrayList<TikzFigure>();
    ArrayList<Object>    tokens  = new ArrayList<Object>();
   
   
    /*
     * Check path trigger \
     */
    if(skipChar() != TikzPath.PATH_TRIGGER) {
      throw new TikzParserException(TikzPath.PATH_TRIGGER +" expected");
    }
   
   
    /*
     * Read path type (i.e. draw)
     */
    TikzPathType pt = readPathType();
    tokens.add(pt);
    p("PathType gelesen");
    tokens.add(readWhitespaces());
    p("whitespaces nach PathType gelesen");


    /*
     * Read parameters (i.e. [color=blue,fill=white])
     */
    TikzParameters pathParams = null;
    try {
      pathParams = parseParameters();
      tokens.add(pathParams);
    } catch(TikzParserException tpe) {}
    p("TikzParameters gelesen");
    tokens.add(readWhitespaces());
   
   
    /*
     * Read first coordinate
     */
    origin = parseCoordinate();
    tokens.add(origin);
   
   
    /*
     * Read path chain (i.e. rectangle (2,4) -- (1,2))
     */
    parsePathChain(tokens,figures);

   
    /*
     * Construct path and return it
     */
    path = new TikzPath(pt, pathParams, figures);
    SyntaxTikzPath stp = new SyntaxTikzPath(path,tokens);
    return stp;
  }
 
 
  /**
   *
   */
  private void parsePathChain(ArrayList<Object> tokens,
                ArrayList<TikzFigure> figures) throws TikzParserException {
    p("readPathChain()");
   
    boolean running = true;
   
    while(running) {
      tokens.add(readWhitespaces());

      /*
       * Check whether this path has ended and stop...
       */
      if(readChar() == TikzPath.PATH_SEPARATOR) {
        running = false;
        lastOperator = null;
        offset++;
       
      /*
       * ...or go on reading path commands
       */
      } else {

        /*
         * Read PATH OPERATOR like --, rectangle or circle
         *
         * QUICK AND DIRTY -- REWRITE THIS!
         */
        p("reading operatorToken");
       
        if(skipChar()=='-' && readChar()=='-') {
          operatorToken = "--";
          offset++;
        } else {
          offset--;
          operatorToken = readWord();
        }
       
        /*
         * Try to convert read PATH OPERATOR
         */
        try {
          lastOperator = TikzOperator.getValue(operatorToken.toUpperCase());
          tokens.add(lastOperator);
          tokens.add(readWhitespaces());
        } catch(IllegalArgumentException iae) {
          throw new TikzParserException("expected TikzOperator but found: " +operatorToken);
        }
       
        /*
         * Neues Objekt erzeugen?
         */
        if(lastOperator != null) {
          lastOrigin  = origin;
         
          switch(lastOperator) {
         
            case LINETO:
              origin = parseCoordinate();     
              tokens.add(origin);
              TikzLine line =  new TikzLine(
                lastOrigin.getRepresented(),
                origin.getRepresented()
              );
              p("parsePathChain: added: " +line.toString());
              figures.add(line);
              break;
             
           
            case RECTANGLE:
              origin = parseCoordinate();     
              tokens.add(origin);
              TikzRectangle rect = new TikzRectangle(
                lastOrigin.getRepresented(),
                origin.getRepresented()
              );
              p("parsePathChain: added: " +rect.toString());
              figures.add(rect);
              break;
             
           
            case CIRCLE:
              SyntaxTeXDimension circleDim = parseDimension();
              tokens.add(circleDim);
              TikzCircle circle =  new TikzCircle(
                lastOrigin.getRepresented(),
                circleDim.getRepresented()
              );
              p("parsePathChain: added: " +circle.toString());
              figures.add(circle);
              lastOperator = null;
             
              // Neue Koordinate?
              tokens.add(readWhitespaces());
              if(readChar() == TikzCoordinate.PREFIX) {
                origin = parseCoordinate();
              }
             
              break;
             
           
            default:
              throw new TikzParserException("unknown TikzOperator: " +operatorToken);
          }
        }
       
        // Skip whitespaces
        tokens.add(readWhitespaces());
      }     
    } // while
  }
 
 
  /**
   *
   * @return
   * @throws TikzParserException
   */
  private TikzPathType readPathType() throws TikzParserException {
    TikzPathType pt = null;
    String stringType = null;
    try {
      stringType = readWord();
      p("stringType gelesen");
      pt = TikzPathType.valueOf(stringType.toUpperCase());
     
    } catch(IllegalArgumentException iae) {
      throw new TikzParserException("unknown TikzPathType detected: #" +stringType + "#");
    }
   
    return pt;
  }
 
 
  /**
   *
   * @return
   * @throws TikzParserException
   */
  private SyntaxTikzCoordinate parseCoordinate() throws TikzParserException {
    int start = offset;
   
   
    p("reading coordinate");
   
    // Skip TikzCoordinate.PREFIX
    if(skipChar() != TikzCoordinate.PREFIX) {
      throw new TikzParserException("coordinate must start with " +TikzCoordinate.PREFIX);
    }
   
    // Skip whitespaces
    readWhitespaces();
   
    // Read x-Dimension
    SyntaxTeXDimension sdx = parseDimension();
   
    // Skip whitespaces
    readWhitespaces();
   
    // Skip Coordinate.SEPARATOR
    if(skipChar() != TikzCoordinate.SEPARATOR) { 
      throw new TikzParserException(TikzCoordinate.SEPARATOR +" expected");
    }
   
    // Skip whitespaces
    readWhitespaces();
   
    // Read y-Dimension
    SyntaxTeXDimension sdy = parseDimension();
   
    // Skip whitespaces
    readWhitespaces();
   
    // Skip Coordinate.SUFFIX
    if(skipChar() != TikzCoordinate.SUFFIX) {
      throw new TikzParserException(TikzCoordinate.SUFFIX +" expected");
    }
   
    /*
     * Build object.
     */
    TikzCoordinate c = new TikzCoordinate(sdx.getRepresented(), sdy.getRepresented());
    SyntaxTikzCoordinate sc = new SyntaxTikzCoordinate(c, Util.substring(code,start,offset-1));
   
    return sc;
  }
 
 
  /**
   *
   * @return
   * @throws TikzParserException
   */
  private SyntaxTeXDimension parseDimension() throws TikzParserException {
    int start = offset;
    boolean bracketed = false;
   
    if(readChar() == TeXDimension.PREFIX) {
      bracketed = true;
      offset++;
      readWhitespaces();
    }
   
    p("parseDimension: reading numeral");
   
    // Read numeral
    String numeral = readNumeral();
    if(numeral.trim().length() == 0) {
      throw new TikzParserException("SyntaxDimension doesn't contain a valid numeral");
    }
   
    // Skip whitespaces
    readWhitespaces();
   
    p("reading unit");
   
    // Read unit
    TeXUnit unit = TeXUnit.NONE;
    if(Character.isLetter(readChar())) {
      String strUnit = readWord();
      p(strUnit);
      unit = TeXUnit.getValue(strUnit.toUpperCase());
    }
   
    if(bracketed) {
      readWhitespaces();
     
      if(skipChar() != TeXDimension.SUFFIX) {
        throw new TikzParserException("dimension is bracketed, but doesn't end with "
                        +TeXDimension.SUFFIX);
      }
    }
   
   
    // Build object
    TeXDimension d = new TeXDimension(Float.parseFloat(numeral), unit);
    d.setBracketed(bracketed);
    SyntaxTeXDimension sd = new SyntaxTeXDimension(d, Util.substring(code,start,offset-1));
   
    return sd;
  }
 
 
  /**
   *
   * @return
   * @throws TikzParserException
   */
  private TikzParameters parseParameters() throws TikzParserException {
    //int start = offset;
    TikzParameters tp = new TikzParameters();
   
    // Make sure the first character is the parameter prefix
    if(readChar() != TikzParameters.PARAMETERS_PREFIX) {
      throw new TikzParserException"parameters must start with "
                      + TikzParameters.PARAMETERS_PREFIX);
    }
   
    // Go ahead with next char
    offset++;
   
    // Then read all key-values-pairs
    String params
      = readWord(TikzParameters.PARAMETERS_SUFFIX);
   
    String[] keyvals
      = params.split(String.valueOf(TikzParameters.PARAMETERS_SEPARATOR));
   
    for(String keyval : keyvals) {
      /*
       * TODO
       * Need SyntaxTikzParameters instead of TikzParameters
       * so the SyntaxKeyValue can be stored.
       */
      tp.addKeyValue(parseKeyValue(keyval).getRepresented());
    }
   
    // Make sure the last character is the parameter suffix
    if(skipChar() != TikzParameters.PARAMETERS_SUFFIX) {
      throw new TikzParserException"parameters must end with "
                      + TikzParameters.PARAMETERS_SUFFIX);
    }
   
    return tp;
  }
 
 
  /**
   *
   * @param keyval
   * @return
   * @throws TikzParserException
   */
  private SyntaxKeyValue parseKeyValue(String keyval) throws TikzParserException {
    String[] tokens = keyval.split(KeyValue.KEY_VALUE_SEPARATOR);
   
    KeyValue<String,String> kv = null;
   
    switch(tokens.length) {
      case 1:
        kv = new KeyValue<String,String>(tokens[0], null);
        break;
       
      case 2:
        kv = new KeyValue<String,String>(tokens[0], tokens[1]);
        break;
       
      default:
        throw new TikzParserException("no valid key-value-string detected");
    }
   
    return new SyntaxKeyValue(kv, keyval);
  }
 
 
 
  /**
   * Returns a String of whitespaces until the next non-whitespace-chracter
   * ist found.
   *
   * @return
   *   String of whitespaces.
   */
  private String readWhitespaces() throws TikzParserException {
    String  ret    = "";
    int    start  = offset;
   
    // Parse whitespaces.
    while(Character.isWhitespace(skipChar()));
   
    if(offset != start+1) {
      ret = Util.substring(code, start, offset-2);
    }
   
    offset--;
    return ret;
  }
 
 
 
  /**
   * Returns the next consecutive char sequence till the given delimiters.
   *
   * @return
   *  Found char sequence.
   */
  private String readWord(char delimiter) throws TikzParserException {
    int start  = offset;
   
    // Parse letters.
    boolean walk = true;
    do {
      char c  = skipChar();
      walk  = (c != delimiter);
    } while(walk);
   
    offset--;  // Move pointer back on the delimiter
   
    return Util.substring(code, start, offset-1);
  }
 
 
 
  /**
   * Returns the next consecutive char sequence.
   *
   * @return
   *  Found char sequence.
   */
  private String readWord() throws TikzParserException {
    int start  = offset;
   
    // Parse letters.
    while(Character.isLetter(skipChar()));
    offset--;
   
    return Util.substring(code, start, offset-1);
  }
 
 
  /**
   * Returns the next found numeral as a String that matches the
   * following Regex:
   *
   * FP_REGEX =  "[+-]?"    +  // Optional sign character
   *        "[\\d]*"  +  // Optional before floating-point
   *        "[.]?"    +  // Optional floating-point
   *        "\\d+";      // Digits
   *
   * @return
   *   Next found numeral group
   */
  private String readNumeral() throws TikzParserException {
    String numeral = "0";
   
    Matcher m = FP_PATTERN.matcher(
      Util.substring(code, offset, code.length-1)
      //new String(code, offset, code.length-offset)
    );
   
    if(m.find()) {
      numeral = m.group();
    } else {
      throw new TikzParserException("no numeral found");
    }
    offset += numeral.length();
   
    return numeral;
  }
 
 
 
  private char skipChar() throws TikzParserException {
    try {
      return code[offset++];
    } catch(NullPointerException npe) {
      throw new TikzParserException("code sequence ended unexpectedly");
    }
  }
 

  private char readChar() throws TikzParserException {
    try {
      return code[offset];
    } catch(NullPointerException npe) {
      throw new TikzParserException("code sequence ended unexpectedly");
    }
  }
 
 
 
  /*
   * BELOW: Debug methods
   */
 
  public void debug() {
    SyntaxTikzPicture p
      = parseCode("\\begin{tikzpicture}[bla=blie,bsa] \\draw[color=blue] (1,2) rectangle (3,4); \\end{tikzpicture}");
    ArrayList<Object> tokens = p.getTokens();
    ArrayList<Object> tokens2 = ((SyntaxTikzPath) tokens.get(1)).tokens;
    for(Object o : tokens2) {
      System.out.println(o);
    }
   
    System.out.println(p.toString());
  }
 
  public final void p(String s) {
    if(DEBUG_PARSER) {
      try {
        System.out.println("TikzParser.p: " +offset+" "+code[offset]+" " +s);
      } catch(NullPointerException npe) {
        System.out.println("TikzParser.p: " +offset+" " +s);
      } catch(ArrayIndexOutOfBoundsException aioobe) {
        System.out.println("TikzParser.p: " +offset+" " +s);
      }
    }
  }
 
  /*
  public static void main(String[] args) {
    TikzParser p = new TikzParser();
    p.debug();
  }
  */
 
TOP

Related Classes of tikzparser.TikzParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.