Package dtool.sourcegen

Source Code of dtool.sourcegen.TemplatedSourceProcessorParser$Argument

package dtool.sourcegen;

import static dtool.util.NewUtils.emptyToNull;
import static melnorme.utilbox.core.Assert.AssertNamespace.assertFail;
import static melnorme.utilbox.core.Assert.AssertNamespace.assertNotNull;
import static melnorme.utilbox.core.Assert.AssertNamespace.assertTrue;

import java.util.ArrayList;

import melnorme.utilbox.misc.StringUtil;
import dtool.tests.utils.SimpleParser;


public class TemplatedSourceProcessorParser {
 
  @SuppressWarnings("serial")
  public static class TemplatedSourceException extends Exception {
    public int errorOffset;
    public TemplatedSourceException(int errorOffset) {
      this.errorOffset = errorOffset;
    }
  }
 
  protected String kMARKER;
  protected String[] kMARKER_array;

  protected ArrayList<TspElement> parseSplitCase(String source, String keyMarker) throws TemplatedSourceException {
    this.kMARKER = keyMarker;
    this.kMARKER_array = new String[]{ keyMarker };
    ArrayList<TspElement> sourceElements = parseSource(source);
    return sourceElements;
  }
 
  protected ArrayList<TspElement> parseSource(String unprocessedSource) throws TemplatedSourceException {
    ArrayList<TspElement> unprocessedSourceElements = new ArrayList<TspElement>();
   
    SimpleParser parser = new SimpleParser(unprocessedSource);
    while(true) {
      TspElement tspElem = parseElement(parser);
      if(tspElem == null) {
        break;
      }
      unprocessedSourceElements.add(tspElem);
    }
    return unprocessedSourceElements;
  }
 
  protected static abstract class TspElement {
    public static String DEFAULT_TYPE = "DEFAULT";
    public String getElementType() { return DEFAULT_TYPE; };
    public String getSource() { return null; };
  }
 
  public static class TspStringElement extends TspElement {
    public static final String RAW_TEXT = "TextElement";
    public final String producedText;
    public final String elemType;
   
    protected TspStringElement(String source) {
      this(source, RAW_TEXT);
    }
    protected TspStringElement(String producedText, String elemType) {
      this.producedText = assertNotNull(producedText);
      this.elemType = elemType;
    }
    @Override
    public String getElementType() {
      return elemType;
    }
    @Override
    public String getSource() {
      return producedText;
    }
   
    @Override
    public String toString() {
      return "STRING【" + producedText + "】";
    }
  }
 
  protected TspElement parseElement(SimpleParser parser) throws TemplatedSourceException {
    return parseElementWithCustomStarts(parser, kMARKER_array);
  }
 
  protected TspElement parseElementWithCustomStarts(SimpleParser parser, String... tokenStarts)
    throws TemplatedSourceException {
    if(parser.lookaheadIsEOF()) {
      return null;
    }
   
    int alt = parser.consumeUntilAny(tokenStarts);
    final String string = parser.getLastConsumedString();
   
    if(!string.isEmpty()) {
      return new TspStringElement(string);
    }
   
    String tokenStart = tokenStarts[alt];
    parser.consume(tokenStart);
   
    if(!tokenStart.equals(kMARKER)) {
      return new TspStringElement(parser.getLastConsumedString(), tokenStart);
    }
   
    if(parser.tryConsume(kMARKER)) {
      return new TspStringElement(kMARKER);
    }
    for (String escapableTokenStart : tokenStarts) {
      if(parser.tryConsume(escapableTokenStart)) {
        return new TspStringElement(escapableTokenStart);
      }
    }
    if(parser.lookAhead() == '{' || parser.lookAhead() == '@') {
      return parseExpansionCommand(parser);
    } else if(parser.lookAhead() == '?') {
      return parseIfElseExpansionCommand(parser);
    } else if(Character.isJavaIdentifierStart(parser.lookAhead())) {
      return parseMetadataElement(parser);
    } if(parser.lookAhead() == ':') {
      return parseCommandElement(parser);
    }
   
    reportError(parser.getSourcePosition());
    return null;
  }
 
  protected final void reportError(final int offset) throws TemplatedSourceException {
    handleParserError(new TemplatedSourceException(offset));
  }
 
  protected final void checkError(boolean condition, SimpleParser parser) throws TemplatedSourceException {
    if(condition) {
      reportError(parser.getSourcePosition());
    }
  }
 
  protected void handleParserError(TemplatedSourceException tse) throws TemplatedSourceException {
    throw tse;
  }
 
  public static class TspExpansionElement extends TspElement {
    public final String expansionId;
    public final String pairedExpansionId;
    public final ArrayList<Argument> arguments;
    public final boolean dontOuputSource;
    public final boolean defineOnly;
    public final boolean anonymousExpansion;
   
    public TspExpansionElement(String expansionId, String pairedExpansionId, ArrayList<Argument> arguments,
      boolean anonymousExpansion, boolean dontOuputSource) {
      this.expansionId = expansionId;
      this.pairedExpansionId = pairedExpansionId;
      this.arguments = arguments;
      this.anonymousExpansion = anonymousExpansion;
      this.dontOuputSource = dontOuputSource;
      this.defineOnly = dontOuputSource;
      if(defineOnly) {
        assertTrue(expansionId != null && arguments != null);
      }
    }
   
    public boolean isDefinition() {
      return expansionId != null && arguments != null;
    }
   
    @Override
    public String toString() {
      return "EXPANSION"+(anonymousExpansion?"^":"")+(dontOuputSource?"!":"")+
        "["+ StringUtil.nullAsEmpty(expansionId)+ "]" +
        (pairedExpansionId == null ? "" : "("+pairedExpansionId+")")+
        (arguments == null ? "" : "{"+StringUtil.collToString(arguments, "♦")+"}")
        ;
    }
  }
 
  @SuppressWarnings("serial")
  protected class Argument extends ArrayList<TspElement> {
    @Override
    public String toString() {
      return "["+StringUtil.collToString(this, "")+"]";
    }
  }
 
  protected TspElement parseExpansionCommand(SimpleParser parser) throws TemplatedSourceException {
    assertTrue(parser.lookAhead() == '{' || parser.lookAhead() == '@');
   
    String expansionId = null;
    boolean defineOnly = false;
    boolean anonymousExpansion = false;
   
    if(parser.tryConsume("@")) {
      if(parser.tryConsume("^")) {
        anonymousExpansion = true;
      }
      expansionId = emptyToNull(parser.consumeAlphaNumericUS(false));
     
      if(parser.tryConsume("!")) {
        checkError(expansionId == null, parser); // TODO: No test case for this
        defineOnly = true;
      }
    }
    if(anonymousExpansion) {
      checkError(expansionId == null, parser);
      checkError(defineOnly, parser);
    }
   
    ArrayList<Argument> arguments = null;
    int alt = parser.tryConsume(OPEN_DELIMS);
    if(alt != -1) {
      String closeDelim = CLOSE_DELIMS[alt];
      arguments = parseArgumentList(parser, closeDelim);
    }
    checkError(defineOnly && arguments == null, parser);
    checkError(anonymousExpansion && arguments != null, parser);
   
    String pairedExpansionId = null;
    if(parser.tryConsume("(")) {
      checkError(anonymousExpansion, parser);
      pairedExpansionId = consumeDelimitedId(parser, ")");
    }
   
    // This is just an optional separator, useful when expansion only has an id, like: #@EXP•BLA
    parser.tryConsume("•");
   
    checkError(expansionId == null && arguments == null, parser);
    return new TspExpansionElement(expansionId, pairedExpansionId, arguments, anonymousExpansion, defineOnly);
  }
 
  protected String consumeDelimitedId(SimpleParser parser, String closeDelim) throws TemplatedSourceException {
    String pairedExpansionId = emptyToNull(parser.consumeAlphaNumericUS(false));
    checkError(pairedExpansionId == null, parser);
    checkError(parser.tryConsume(closeDelim) == false, parser);
    return pairedExpansionId;
  }
 
  protected ArrayList<Argument> parseArgumentList(SimpleParser parser, String closeDelim)
    throws TemplatedSourceException {
    String argSep = closeDelim.equals("}") ? "," : "●";
    return parseArgumentList(parser, argSep, closeDelim, false);
  }
 
  protected ArrayList<Argument> parseArgumentList(
    SimpleParser parser, final String argSeparator, final String listEnd, boolean eofTerminates
  ) throws TemplatedSourceException {
    assertNotNull(listEnd);
    assertTrue(!eofTerminates || argSeparator == null);
   
    final String listEndPrefix = "¤";
    final String argStart = "►";
    final String[] tokenStarts;
    if(eofTerminates) {
      tokenStarts = (argSeparator != null ?
          new String[] { argStart, listEnd, kMARKER } :
          new String[] { listEnd, kMARKER });
    } else {
      tokenStarts = argSeparator != null ?
        new String[] { argStart, argSeparator, listEndPrefix, listEnd, kMARKER } :
        new String[] { listEndPrefix, listEnd, kMARKER };
    }
   
    boolean ignoreLastArg = false;
    boolean argumentStartFound = false;
   
    ArrayList<Argument> arguments = new ArrayList<Argument>();
    Argument argument = new Argument();
    while(true) {
      TspElement element = parseElementWithCustomStarts(parser, tokenStarts);
     
      if(element != null && element.getElementType() == listEnd) {
        break;
      }
      checkError(ignoreLastArg, parser);
     
      if(element == null) {
        checkError(!eofTerminates, parser);
        break;
      }
      if(element.getElementType() == argStart) {
        checkError(argumentStartFound || !argumentIsWhiteSpaceOnly(argument), parser);
        argumentStartFound = true;
       
        argument = new Argument();
      } else if(element.getElementType() == listEndPrefix) {
        checkError(argumentStartFound || !argumentIsWhiteSpaceOnly(argument), parser);
        ignoreLastArg = true;
        argument = null;
      } else if(element.getElementType() == argSeparator) {
        arguments.add(argument);
        argumentStartFound = false;
       
        argument = new Argument();
      } else {
        argument.add(element);
      }
    }
   
    if(ignoreLastArg) {
      assertTrue(argument == null);
    } else {
      arguments.add(assertNotNull(argument));
    }
    return arguments;
  }
 
  protected boolean argumentIsWhiteSpaceOnly(Argument argument) {
    if(argument.size() != 0) {
      if(argument.size() == 1) {
        String argSource = argument.get(0).getSource();
        return(argSource != null && argSource.trim().isEmpty());
      }
      return false;
    }
    return true;
  }
 
  public static class TspMetadataElement extends TspElement {
    public final String tag;
    public final String value;
    public final Argument childElements;
    public final boolean outputSource;
   
    public TspMetadataElement(String tag, String value, Argument childElements, boolean outputSource) {
      this.tag = tag;
      this.value = value;
      this.childElements = childElements;
      this.outputSource = outputSource;
    }
  }
 
  public static final String[] OPEN_DELIMS  = {"{","«","〈","《","「","『","【","〔","〖","〚" };
  public static final String[] CLOSE_DELIMS = {"}","»","〉","》","」","』","】","〕","〗","〛"} ;
 
  protected TspMetadataElement parseMetadataElement(SimpleParser parser) throws TemplatedSourceException {
    String name = parser.consumeAlphaNumericUS(false);
    assertTrue(!name.isEmpty());
   
    String value = parser.tryConsume("(") ? consumeDelimitedString(parser, ")", false) : null;
   
    Argument sourceValue = null;
    boolean colonSyntaxConsumed = false;
   
    if(value == null && parser.lookAhead() == ':') {
      String id = SimpleParser.readAlphaNumericUS(parser.getSource(), parser.getSourcePosition()+1);
      if(id.length() != 0) {
        value = id;
        parser.consumeAmount(id.length() + 1);
        colonSyntaxConsumed = true;
      }
    }
   
    boolean outputSource = parser.tryConsume("¤") == false;
   
    int alt = parser.tryConsume(OPEN_DELIMS);
    if(alt != -1) {
      String closeDelim = CLOSE_DELIMS[alt];
      sourceValue = parseArgument(parser, closeDelim, false);
    } else if(colonSyntaxConsumed == false && parser.tryConsume(":")) {
      //checkError(parser.tryConsumeNewlineRule() == false, parser);
      parser.tryConsumeNewlineRule();
      sourceValue = parseArgument(parser, "#:END:", true);
      outputSource = false;
    }
    checkError(outputSource == false && sourceValue == null, parser);
   
    return new TspMetadataElement(name, value, sourceValue, outputSource);
  }
 
  protected Argument parseArgument(SimpleParser parser, String listEnd, boolean eofTerminates)
    throws TemplatedSourceException {
    ArrayList<Argument> argumentList = parseArgumentList(parser, null, listEnd, eofTerminates);
    assertTrue(argumentList.size() == 1);
    return argumentList.get(0);
  }
 
  protected String consumeDelimitedString(SimpleParser parser, String closeSep, boolean eofTerminates)
    throws TemplatedSourceException {
    StringBuilder value = new StringBuilder();
   
    final String[] alts = new String[]{closeSep, kMARKER};
    while(true) {
      int alt = parser.consumeUntilAny(alts);
      value.append(parser.getLastConsumedString());
     
      if(parser.lookaheadIsEOF()) {
        checkError(!eofTerminates, parser); // Unterminated
        break;
      } else if(alt == 0) {
        parser.consume(closeSep);
        break;
      } else if(alt == 1) {
        parser.consume(kMARKER);
        if(parser.tryConsume("#")) {
          value.append("#");
        } else if(parser.tryConsume(closeSep)) {
          value.append(closeSep);
        } else {
          reportError(parser.getSourcePosition()); // Invalid Escape
        }
      } else {
        assertFail();
      }
    }
    return value.toString();
  }
 
  protected static class TspCommandElement extends TspElement {
   
    public static final String DISCARD_CASE = "DISCARD_CASE";
   
    public final String name;
   
    public TspCommandElement(String name) {
      this.name = name;
    }
  }
 
  protected TspCommandElement parseCommandElement(SimpleParser parser) throws TemplatedSourceException {
    parser.consume(":");
    String name = parser.consumeAlphaNumericUS(false);
    checkError(name.isEmpty(), parser);
    checkError(!name.equals(TspCommandElement.DISCARD_CASE), parser);
   
    return new TspCommandElement(name);
  }
 
  protected TspIfElseExpansionElement parseIfElseExpansionCommand(SimpleParser parser)
    throws TemplatedSourceException {
    parser.consume("?");
   
    String mdConditionId = emptyToNull(parser.consumeAlphaNumericUS(false));
    checkError(mdConditionId == null, parser);
   
    boolean invert = parser.tryConsume("!");
   
    ArrayList<Argument> arguments = null;
    int alt = parser.tryConsume(OPEN_DELIMS);
    if(alt == -1) {
      reportError(parser.getSourcePosition());
    } else {
      arguments = parseArgumentList(parser, CLOSE_DELIMS[alt]);
    }
   
    checkError(arguments.size() > 2, parser);
   
    Argument argElse = arguments.size() == 1 ? null: arguments.get(1);
    return new TspIfElseExpansionElement(mdConditionId, invert, arguments.get(0), argElse);
  }
 
  public static class TspIfElseExpansionElement extends TspElement {
    public final String mdConditionId;
    public final boolean invert;
    public final Argument argThen;
    public final Argument argElse;
   
    public TspIfElseExpansionElement(String mdConditionId, boolean invert, Argument argThen, Argument argElse) {
      this.mdConditionId = mdConditionId;
      this.invert = invert;
      this.argThen = argThen;
      this.argElse = argElse;
    }
   
    @Override
    public String toString() {
      return "IF?【"+ StringUtil.nullAsEmpty(mdConditionId)+"{"+argThen+","+argElse+"}"+"】";
    }
  }
 
}
TOP

Related Classes of dtool.sourcegen.TemplatedSourceProcessorParser$Argument

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.