From 5779703575e0b13a5cb31f481c45085549833233 Mon Sep 17 00:00:00 2001 From: Arkerthan <arkerthan@gmail.com> Date: Tue, 12 Mar 2019 23:07:10 +0100 Subject: [PATCH] twine tags fully work --- SanityCheck.jar | Bin 15389 -> 20708 bytes devTools/javaSanityCheck/htmlTags | 6 +- .../src/DisallowedTagException.java | 8 + devTools/javaSanityCheck/src/Main.java | 110 ++++--- devTools/javaSanityCheck/src/SyntaxError.java | 2 +- .../javaSanityCheck/src/{tag => }/Tag.java | 6 +- .../javaSanityCheck/src/TagSearchTree.java | 42 ++- .../src/element/AngleBracketElement.java | 275 ++++++++++++++---- .../src/element/CommentElement.java | 66 +++++ .../javaSanityCheck/src/element/Element.java | 9 +- .../src/element/KnownElement.java | 31 ++ .../src/element/KnownHtmlElement.java | 40 +-- .../src/element/KnownLogicElement.java | 117 ++++++++ .../src/element/KnownTwineElement.java | 41 +++ devTools/javaSanityCheck/src/tag/HtmlTag.java | 7 - devTools/javaSanityCheck/twineTags | 114 +------- 16 files changed, 616 insertions(+), 258 deletions(-) create mode 100644 devTools/javaSanityCheck/src/DisallowedTagException.java rename devTools/javaSanityCheck/src/{tag => }/Tag.java (52%) create mode 100644 devTools/javaSanityCheck/src/element/CommentElement.java create mode 100644 devTools/javaSanityCheck/src/element/KnownElement.java create mode 100644 devTools/javaSanityCheck/src/element/KnownLogicElement.java create mode 100644 devTools/javaSanityCheck/src/element/KnownTwineElement.java delete mode 100644 devTools/javaSanityCheck/src/tag/HtmlTag.java diff --git a/SanityCheck.jar b/SanityCheck.jar index ae0827bf18a9d723f76089c40656eceb4da08e8c..bebdf1da3e6277f966e8fff78ea96d884fe27b0b 100644 GIT binary patch delta 16212 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 delta 11157 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 diff --git a/devTools/javaSanityCheck/htmlTags b/devTools/javaSanityCheck/htmlTags index 608a30a5ff6..751b084ff58 100644 --- a/devTools/javaSanityCheck/htmlTags +++ b/devTools/javaSanityCheck/htmlTags @@ -1,5 +1,5 @@ #Allowed HTML tags -#Effectively everything that is allowed in one of these statements like this: +#Effectively everything that is allowed in a these statements like this: #<tag> or <tag ???> #when ;1 is specified it expects a matching closing tag like this: </tag> #Do not add Twine Tags here. @@ -25,11 +25,11 @@ h1;1 h2;1 h3;1 h4;1 -hr;1 +hr;0 html;1 i;1 img;1 -input;1 +input;0 li;1 option;1 script;1 diff --git a/devTools/javaSanityCheck/src/DisallowedTagException.java b/devTools/javaSanityCheck/src/DisallowedTagException.java new file mode 100644 index 00000000000..f4cc75e736b --- /dev/null +++ b/devTools/javaSanityCheck/src/DisallowedTagException.java @@ -0,0 +1,8 @@ +package org.arkerthan.sanityCheck; + +public class DisallowedTagException extends RuntimeException { + + public DisallowedTagException(String tag) { + super(tag); + } +} diff --git a/devTools/javaSanityCheck/src/Main.java b/devTools/javaSanityCheck/src/Main.java index 6a7aee137ca..eea3a4ff611 100644 --- a/devTools/javaSanityCheck/src/Main.java +++ b/devTools/javaSanityCheck/src/Main.java @@ -1,11 +1,9 @@ package org.arkerthan.sanityCheck; import org.arkerthan.sanityCheck.element.AngleBracketElement; -import org.arkerthan.sanityCheck.element.AtElement; +import org.arkerthan.sanityCheck.element.CommentElement; import org.arkerthan.sanityCheck.element.Element; -import org.arkerthan.sanityCheck.element.KnownHtmlElement; -import org.arkerthan.sanityCheck.tag.HtmlTag; -import org.arkerthan.sanityCheck.tag.Tag; +import org.arkerthan.sanityCheck.element.KnownElement; import java.io.*; import java.nio.charset.Charset; @@ -16,7 +14,7 @@ import java.util.*; public class Main { - public static TagSearchTree<HtmlTag> htmlTags; + public static TagSearchTree<Tag> htmlTags, twineTags; private static String currentFile; private static int currentLine, currentPosition; private static Stack<Element> stack; @@ -27,6 +25,7 @@ public class Main { //setup setupExclude(); setupHtmlTags(); + setupTwineTags(); Path workingDir = Paths.get("").toAbsolutePath(); //actual sanityCheck @@ -42,7 +41,7 @@ public class Main { /** * Goes through the whole directory including subdirectories and runs - * sanitycheck() on all .tw files + * sanityCheck() on all .tw files * * @param dir to be checked */ @@ -61,7 +60,9 @@ public class Main { } } } catch (NullPointerException e) { - System.err.println("Couldn't find directory " + dir.getPath()); + e.printStackTrace(); + System.err.println("Couldn't find directory " + currentFile); + System.exit(-1); } } @@ -96,23 +97,23 @@ public class Main { } /** - * sets up the alphabetical search tree for fast access later + * sets up the alphabetical search tree for fast access of HTML tags later */ private static void setupHtmlTags() { //load HTML tags into a list - List<Tag> htmlTagsList = new LinkedList<>(); + List<Tag> TagsList = new LinkedList<>(); try { Files.lines(new File("devTools/javaSanityCheck/htmlTags").toPath()).map(String::trim) .filter(s -> !s.startsWith("#")) - .forEach(s -> htmlTagsList.add(parseTag(s))); + .forEach(s -> TagsList.add(parseTag(s))); } catch (IOException e) { System.err.println("Couldn't read devTools/javaSanityCheck/htmlTags"); } //turn List into alphabetical search tree try { - htmlTags = new TagSearchTree(htmlTagsList); + htmlTags = new TagSearchTree(TagsList); } catch (ArrayIndexOutOfBoundsException e) { System.err.println("Illegal Character in devTools/javaSanityCheck/htmlTags"); System.exit(-1); @@ -120,18 +121,42 @@ public class Main { } /** - * Turns a string into a HtmlTag + * sets up the alphabetical search tree for fast access of twine tags later + */ + private static void setupTwineTags() { + //load twine tags into a list + List<Tag> TagsList = new LinkedList<>(); + try { + + Files.lines(new File("devTools/javaSanityCheck/twineTags").toPath()).map(String::trim) + .filter(s -> !s.startsWith("#")) + .forEach(s -> TagsList.add(parseTag(s))); + } catch (IOException e) { + System.err.println("Couldn't read devTools/javaSanityCheck/twineTags"); + } + + //turn List into alphabetical search tree + try { + twineTags = new TagSearchTree(TagsList); + } catch (ArrayIndexOutOfBoundsException e) { + System.err.println("Illegal Character in devTools/javaSanityCheck/twineTags"); + System.exit(-1); + } + } + + /** + * Turns a string into a Tag * ";1" at the end of the String indicates that the tag needs to be closed later * * @param s tag as String - * @return tag as HtmlTag + * @return tag as Tag */ - private static HtmlTag parseTag(String s) { + private static Tag parseTag(String s) { String[] st = s.split(";"); if (st.length > 1 && st[1].equals("1")) { - return new HtmlTag(st[0], false); + return new Tag(st[0], false); } - return new HtmlTag(st[0], true); + return new Tag(st[0], true); } /** @@ -225,45 +250,64 @@ public class Main { if (change == 2) { //remove the topmost element from stack since it is complete stack.pop(); + return; } //3 means the Element is complete and part of a two tag system if (change == 3) { //remove the topmost element from stack since it is complete - KnownHtmlElement k = stack.pop().getKnownElement(); - if (k.isOpening()) { + KnownElement k = stack.pop().getKnownElement(); + /*if (k.isOpening()) { stack.push(k); - } else if (stack.empty()) { - addError(new SyntaxError("Closed HTML tag \"" + k.getStatement() + "\" without having any open tags.", -1)); - } else if (stack.peek() instanceof KnownHtmlElement) { - KnownHtmlElement kFirst = (KnownHtmlElement) stack.peek(); - if (!kFirst.isMatchingElement(k)) { - addError(new SyntaxError("Opening HTML tag \"" + kFirst.getStatement() + - "\" does not match closing tag \"" + k.getStatement() + "\".", -1)); + } else */ + if (k.isClosing()) { + if (stack.empty()) { + addError(new SyntaxError("Closed tag " + k.getShortDescription() + " without having any open tags.", -2)); + } else if (stack.peek() instanceof KnownElement) { + KnownElement kFirst = (KnownElement) stack.pop(); + if (!kFirst.isMatchingElement(k)) { + addError(new SyntaxError("Opening tag " + kFirst.getShortDescription() + + " does not match closing tag " + k.getShortDescription() + ".", -2)); + } + //stack.pop(); + } else { + addError(new SyntaxError("Closing tag " + k.getShortDescription() + " inside " + + "another tag: " + stack.peek().getShortDescription(), -2, true)); } - stack.pop(); - } else { - addError(new SyntaxError("Closing HTML tag \"" + k.getStatement() + "\" inside " + - "another tag: " + stack.peek().getShortDescription(), -1, true)); } + if (k.isOpening()) { + stack.push(k); + } + return; + } + if (change == 4) { + stack.pop(); + } else { + return; } - return; } } //innermost element was uninterested, trying to find matching element switch (c) { - case '@': - stack.push(new AtElement(currentLine, currentPosition)); - break; + //case '@': + // stack.push(new AtElement(currentLine, currentPosition)); + //break; case '<': stack.push(new AngleBracketElement(currentLine, currentPosition)); break; + //case '>': + //addError(new SyntaxError("Dangling \">\", current innermost: " + (stack.empty() ? "null" : stack.peek().getShortDescription()), -2)); + //break; + case '/': + stack.push(new CommentElement(currentLine, currentPosition)); + break; } } /** * add an error to the error list + * * @param e new error */ private static void addError(SyntaxError e) { diff --git a/devTools/javaSanityCheck/src/SyntaxError.java b/devTools/javaSanityCheck/src/SyntaxError.java index 90d9db7333c..150f2bd449b 100644 --- a/devTools/javaSanityCheck/src/SyntaxError.java +++ b/devTools/javaSanityCheck/src/SyntaxError.java @@ -4,7 +4,7 @@ public class SyntaxError extends Exception { private String file; private int line, position; private String description; - private int change; //see Element for values; -1 means not thrown + private int change; //see Element for values; -2 means not thrown private boolean warning = false; public SyntaxError(String description, int change) { diff --git a/devTools/javaSanityCheck/src/tag/Tag.java b/devTools/javaSanityCheck/src/Tag.java similarity index 52% rename from devTools/javaSanityCheck/src/tag/Tag.java rename to devTools/javaSanityCheck/src/Tag.java index 386292b48eb..8a13ee4f7a1 100644 --- a/devTools/javaSanityCheck/src/tag/Tag.java +++ b/devTools/javaSanityCheck/src/Tag.java @@ -1,10 +1,10 @@ -package org.arkerthan.sanityCheck.tag; +package org.arkerthan.sanityCheck; -public abstract class Tag { +public class Tag { public final String tag; public final boolean single; - protected Tag(String tag, boolean single) { + public Tag(String tag, boolean single) { this.tag = tag; this.single = single; } diff --git a/devTools/javaSanityCheck/src/TagSearchTree.java b/devTools/javaSanityCheck/src/TagSearchTree.java index 567f33f35d7..dde49df879a 100644 --- a/devTools/javaSanityCheck/src/TagSearchTree.java +++ b/devTools/javaSanityCheck/src/TagSearchTree.java @@ -1,32 +1,52 @@ package org.arkerthan.sanityCheck; -import org.arkerthan.sanityCheck.tag.Tag; - import java.util.List; +/** + * Tag SearchTree stores Tags in an alphabetical search tree. + * Once created the search tree can't be changed anymore. + * + * @param <E> Tag class to be stored + */ public class TagSearchTree<E extends Tag> { private static final int SIZE = 128; + private final TagSearchTree<E>[] branches; private E element = null; - private TagSearchTree<E>[] branches; private String path; + /** + * creates a new empty TagSearchTree + */ private TagSearchTree() { branches = new TagSearchTree[SIZE]; } + /** + * Creates a new filled TagSearchTree + * + * @param list Tags to be inserted + */ public TagSearchTree(List<E> list) { this(); - for (E e : - list) { + for (E e : list) { this.add(e, 0); } } + /** + * adds a new Tag to the TagSearchTree + * + * @param e Tag to be stored + * @param index index of relevant char for adding in tag + */ private void add(E e, int index) { - path = e.tag.substring(0,index); + //set the path to here + path = e.tag.substring(0, index); + //checks if tag has to be stored here or further down if (e.tag.length() == index) { element = e; } else { + //store tag in correct branch char c = e.tag.charAt(index); if (branches[c] == null) { branches[c] = new TagSearchTree<>(); @@ -35,15 +55,25 @@ public class TagSearchTree<E extends Tag> { } } + /** + * @param c character of branch needed + * @return branch or null if branch doesn't exist + */ public TagSearchTree<E> getBranch(char c) { if (c >= SIZE) return null; return branches[c]; } + /** + * @return stored Tag, null if empty + */ public E getElement() { return element; } + /** + * @return path inside full tree to get to this Branch + */ public String getPath() { return path; } diff --git a/devTools/javaSanityCheck/src/element/AngleBracketElement.java b/devTools/javaSanityCheck/src/element/AngleBracketElement.java index 35877dec434..e4819ba37ea 100644 --- a/devTools/javaSanityCheck/src/element/AngleBracketElement.java +++ b/devTools/javaSanityCheck/src/element/AngleBracketElement.java @@ -1,28 +1,39 @@ package org.arkerthan.sanityCheck.element; -import org.arkerthan.sanityCheck.Main; -import org.arkerthan.sanityCheck.SyntaxError; -import org.arkerthan.sanityCheck.TagSearchTree; -import org.arkerthan.sanityCheck.UnknownStateException; -import org.arkerthan.sanityCheck.tag.HtmlTag; +import org.arkerthan.sanityCheck.*; + +import java.util.Arrays; +import java.util.List; public class AngleBracketElement extends Element { + private static final List<String> logicTags = Arrays.asList("if", "elseif", "else", "switch", "case", "default"); private int state = 0; /* - -1 - </ 0 - initial: < + TWINE 1 - << - 2 - <<abc - 3 - <<abc> - - 4 - trying to complete HTML tag: <tag ???> - -4 - trying to complete HTML tag: </tag> - 5 - waiting for > + -1 - <</ + 2 - trying to complete twine tag: <<tag ???>> + -2 - trying to complete twine tag: <</tag>> + 3 - waiting for >> + -3 - expecting > from 3 + 4 - waiting for >> with KnownElement + -4 - expecting > from 4 + 5 - expecting >> -5 - expecting > - 6 - waiting for > with KnownHtmlElement + 6 - expecting > with KnownElement opening; comparison? + -6 - expecting > with KnownElement closing + + HTML + -9 - </ + 10 - trying to complete HTML tag: <tag ???> + -10 - trying to complete HTML tag: </tag> + 11 - waiting for > + -11 - expecting > + 12 - waiting for > with KnownElement */ - private TagSearchTree<HtmlTag> htmlTree; + private TagSearchTree<Tag> tree; public AngleBracketElement(int line, int pos) { super(line, pos); @@ -39,7 +50,7 @@ public class AngleBracketElement extends Element { case '>': throw new SyntaxError("Empty Statement?", 2); case '/': - state = -1; + state = -9; return 1; case ' ':// assume comparison case '=':// " @@ -48,63 +59,118 @@ public class AngleBracketElement extends Element { return 2; default: try { - state = 4; - htmlTree = Main.htmlTags; + state = 10; + tree = Main.htmlTags; return handleOpeningHTML(c); } catch (SyntaxError e) { state = 1; throw new SyntaxError("Opening \"<\" missing, found " + c + " [debug:initialCase]", 1); } } - case -1: - if (c == '>') { - throw new SyntaxError("Empty Statement?", 2, true); - } - state = -4; - htmlTree = Main.htmlTags; - return handleClosingHTML(c); case 1: if (c == '<') { throw new SyntaxError("Too many \"<\".", 1); } else if (c == '>') { state = 3; throw new SyntaxError("Empty Statement?", 1); - } else { - state = 2; + } else if (c == '/') { + state = -1; return 1; } + state = 2; + tree = Main.twineTags; + return handleOpeningTwine(c); + case -1: + if (c == '>') { + throw new SyntaxError("Empty Statement?", 2, true); + } + state = -2; + tree = Main.twineTags; + return handleClosingTwine(c); + case 2: - //TODO finding special Twine statements: IF, ELSE... + return handleOpeningTwine(c); + case -2: + return handleClosingTwine(c); + case 3: if (c == '>') { - state = 3; + state = -3; return 1; } break; - case 3: + case -3: if (c == '>') { return 2; } else if (c == ' ' || c == '=') { // assuming comparison - state = 2; + state = 3; + return 1; } else { - throw new SyntaxError("Closing \">\" missing [1]", 2); + throw new SyntaxError("Closing \">\" missing, opened [" + line + ":" + pos + "]", 2); + } + case 4: + if (c == '>') { + state = -4; + return 1; } break; + case -4: + if (c == '>') { + return 3; + } else if (c == ' ' || c == '=') { // assuming comparison + state = 4; + return 1; + } else { + throw new SyntaxError("Closing \">\" missing, opened [" + line + ":" + pos + "]", 2); + } + case 5: + if (c == '>') { + state = -5; + return 1; + } else { + throw new SyntaxError("Closing \">\" missing, opened [" + line + ":" + pos + "]", 2); + } + case -5: + if (c == '>') { + return 2; + } + throw new SyntaxError("Closing \">\" missing, opened [" + line + ":" + pos + "]", 2); + case 6: + if (c == '>') { + return 3; + } else if (c == ' ' || c == '=') { + state = 3; + return 1; + } else { + throw new SyntaxError("Closing \">\" missing, opened [" + line + ":" + pos + "]", 3); + } + case -6: + if (c == '>') { + return 3; + } + throw new SyntaxError("Closing \">\" missing, opened [" + line + ":" + pos + "]", 3); - case 4: + case -9: + if (c == '>') { + throw new SyntaxError("Empty Statement?", 2, true); + } + state = -10; + tree = Main.htmlTags; + return handleClosingHTML(c); + case 10: return handleOpeningHTML(c); - case -4: + case -10: return handleClosingHTML(c); - case 5: + case 11: if (c == '>') return 2; if (c == '@') //@ inside HTML tags is allowed return 1; break; - case -5: + case -11: if (c == '>') return 2; throw new SyntaxError("Closing \">\" missing [2]", 2); - case 6: + case 12: if (c == '>') return 3; if (c == '@') //@ inside HTML tags is allowed @@ -118,31 +184,31 @@ public class AngleBracketElement extends Element { private int handleOpeningHTML(char c) throws SyntaxError { if (c == ' ') { - state = 5; - if (htmlTree.getElement() == null) { + state = 11; + if (tree.getElement() == null) { throw new SyntaxError("Unknown HTML tag", 1); } - if (!htmlTree.getElement().single) { - k = new KnownHtmlElement(line, pos, true, htmlTree.getElement().tag); - state = 6; + if (!tree.getElement().single) { + k = new KnownHtmlElement(line, pos, true, tree.getElement().tag); + state = 12; return 1; } return 1; } if (c == '>') { - if (htmlTree.getElement() == null) { + if (tree.getElement() == null) { throw new SyntaxError("Unknown HTML tag", 2); } - if (!htmlTree.getElement().single) { - k = new KnownHtmlElement(line, pos, true, htmlTree.getElement().tag); + if (!tree.getElement().single) { + k = new KnownHtmlElement(line, pos, true, tree.getElement().tag); return 3; } return 2; } - htmlTree = htmlTree.getBranch(c); - if (htmlTree == null) { - state = 5; + tree = tree.getBranch(c); + if (tree == null) { + state = 11; throw new SyntaxError("Unknown HTML tag or closing \">\" missing, found " + c, 1); } @@ -151,29 +217,104 @@ public class AngleBracketElement extends Element { private int handleClosingHTML(char c) throws SyntaxError { if (c == '>') { - if (htmlTree.getElement() == null) { + if (tree.getElement() == null) { throw new SyntaxError("Unknown HTML tag", 2); } - if (htmlTree.getElement().single) { - throw new SyntaxError("Single HTML tag used as closing Tag: " + htmlTree.getElement().tag, 2); + if (tree.getElement().single) { + throw new SyntaxError("Single HTML tag used as closing Tag: " + tree.getElement().tag, 2); } - k = new KnownHtmlElement(line, pos, false, htmlTree.getElement().tag); + k = new KnownHtmlElement(line, pos, false, tree.getElement().tag); return 3; } - htmlTree = htmlTree.getBranch(c); - if (htmlTree == null) { - state = -5; + tree = tree.getBranch(c); + if (tree == null) { + state = -11; throw new SyntaxError("Unknown HTML tag or closing \">\" missing, found " + c, 1); } return 1; } + + private int handleOpeningTwine(char c) throws SyntaxError { + if (c == ' ') { + state = 3; + if (tree.getElement() == null) { + //assuming not listed means widget until better solution + return 1; + //throw new SyntaxError("Unknown Twine tag or closing \">>\" missing, found " + tree.getPath(), 1); + } + if (!tree.getElement().single) { + if (logicTags.contains(tree.getElement().tag)) { + k = new KnownLogicElement(line, pos, tree.getElement().tag, false); + } else { + k = new KnownTwineElement(line, pos, true, tree.getElement().tag); + } + state = 4; + return 1; + } + return 1; + } + if (c == '>') { + state = -5; + if (tree.getElement() == null) { + //assuming not listed means widget until better solution + //throw new SyntaxError("Unknown Twine tag or closing \">>\" missing, found " + tree.getPath(), 1); + return 1; + } + if (!tree.getElement().single) { + if (logicTags.contains(tree.getElement().tag)) { + k = new KnownLogicElement(line, pos, tree.getElement().tag, false); + } else { + k = new KnownTwineElement(line, pos, true, tree.getElement().tag); + } + state = 6; + return 1; + } + return 2; + } + + tree = tree.getBranch(c); + if (tree == null) { + //assuming not listed means widget until better solution + state = 3; + //throw new SyntaxError("Unknown Twine tag or closing \">>\" missing, found " + c, 1); + } + + return 1; + } + + private int handleClosingTwine(char c) throws SyntaxError { + if (c == '>') { + if (tree.getElement() == null) { + throw new SyntaxError("Unknown Twine tag", 2); + } + if (tree.getElement().single) { + throw new SyntaxError("Single Twine tag used as closing Tag: " + tree.getElement().tag, 2); + } + if (logicTags.contains(tree.getElement().tag)) { + k = new KnownLogicElement(line, pos, tree.getElement().tag, true); + } else { + k = new KnownTwineElement(line, pos, false, tree.getElement().tag); + } + state = -6; + return 1; + } + + tree = tree.getBranch(c); + if (tree == null) { + state = 3; + throw new SyntaxError("Unknown Twine closing tag or closing \">>\" missing, found " + c, 1); + } + + return 1; + } + @Override public String getShortDescription() { StringBuilder builder = new StringBuilder(); - builder.append(line).append(":").append(pos).append(" "); + builder.append('[').append(line).append(":").append(pos).append("] "); switch (state) { case 0: builder.append("<"); @@ -182,31 +323,37 @@ public class AngleBracketElement extends Element { builder.append("<<"); break; case -1: - builder.append("</"); + builder.append("<</"); break; case 2: - builder.append("<<???"); + builder.append("<<").append(tree.getPath()); + break; + case -2: + builder.append("<</").append(tree.getPath()); break; case 3: - builder.append("<<???>"); + builder.append("<<??? ???"); break; case 4: - builder.append("<").append(htmlTree.getPath()); + builder.append("<<?").append(tree.getPath()).append(" ???"); + break; + case -3: + builder.append("<<??? ???>"); break; case -4: - builder.append("</").append(htmlTree.getPath()); + builder.append("<<?").append(tree.getPath()).append(" ???>"); break; case 5: - builder.append("<").append(htmlTree.getPath()).append(" ???"); + builder.append("<").append(tree.getPath()).append(" ???"); break; case -5: - builder.append("</").append(htmlTree.getPath()); + builder.append("</").append(tree.getPath()); break; case 6: - builder.append("<").append(htmlTree.getPath()).append(" ???"); + builder.append("<").append(tree.getPath()).append(" ???"); break; default: - throw new UnknownStateException(state); + //throw new UnknownStateException(state); } return builder.toString(); } diff --git a/devTools/javaSanityCheck/src/element/CommentElement.java b/devTools/javaSanityCheck/src/element/CommentElement.java new file mode 100644 index 00000000000..3a3cc12aff9 --- /dev/null +++ b/devTools/javaSanityCheck/src/element/CommentElement.java @@ -0,0 +1,66 @@ +package org.arkerthan.sanityCheck.element; + +import org.arkerthan.sanityCheck.SyntaxError; +import org.arkerthan.sanityCheck.UnknownStateException; + +public class CommentElement extends Element { + int state = 0; + /* + 0 - / + 1 - /*??? + 2 - /*???* + 3 - /%??? + 4 - /%???% + */ + + public CommentElement(int line, int pos) { + super(line, pos); + } + + @Override + public int handleChar(char c) throws SyntaxError { + switch (state) { + case 0: + if (c == '*') { + state = 1; + } else if (c == '%') { + state = 3; + } else if (c == '>') { + throw new SyntaxError("XHTML style closure", 4, true); + } else { + return 4; + } + break; + case 1: + if (c == '*') { + state = 2; + } + break; + case 2: + if (c == '/') { + return 2; + } + state = 1; + break; + case 3: + if (c == '%') { + state = 4; + } + break; + case 4: + if (c == '/') { + return 2; + } + state = 3; + break; + default: + throw new UnknownStateException(state); + } + return 1; + } + + @Override + public String getShortDescription() { + return null; + } +} diff --git a/devTools/javaSanityCheck/src/element/Element.java b/devTools/javaSanityCheck/src/element/Element.java index 53934268618..b0f99fa904f 100644 --- a/devTools/javaSanityCheck/src/element/Element.java +++ b/devTools/javaSanityCheck/src/element/Element.java @@ -3,13 +3,12 @@ package org.arkerthan.sanityCheck.element; import org.arkerthan.sanityCheck.SyntaxError; public abstract class Element { - protected KnownHtmlElement k; + protected KnownElement k; protected int line, pos; /** - * * @param line Line the instance was created - * @param pos Position in line the instance was created + * @param pos Position in line the instance was created */ protected Element(int line, int pos) { this.line = line; @@ -22,6 +21,7 @@ public abstract class Element { * 1 - the Element changed state * 2 - the Element is finished * 3 - the Element is finished and a KnownHtmlElement was generated + * 4 - the Element is finished and the char is still open for use * * @param c * @return @@ -29,12 +29,11 @@ public abstract class Element { */ public abstract int handleChar(char c) throws SyntaxError; - public KnownHtmlElement getKnownElement() { + public KnownElement getKnownElement() { return k; } /** - * * @return a short description usually based on state and position of the Element */ public abstract String getShortDescription(); diff --git a/devTools/javaSanityCheck/src/element/KnownElement.java b/devTools/javaSanityCheck/src/element/KnownElement.java new file mode 100644 index 00000000000..305be2fe652 --- /dev/null +++ b/devTools/javaSanityCheck/src/element/KnownElement.java @@ -0,0 +1,31 @@ +package org.arkerthan.sanityCheck.element; + +import org.arkerthan.sanityCheck.SyntaxError; + +public abstract class KnownElement extends Element { + + public KnownElement(int line, int pos) { + super(line, pos); + } + + /** + * @return true, if it needs another Known Element to close it. + */ + public abstract boolean isOpening(); + + /** + * @return true if it closes another Element. + */ + public abstract boolean isClosing(); + + /** + * @param k Element to be checked + * @return true if given Element closes Element + */ + public abstract boolean isMatchingElement(KnownElement k); + + @Override + public int handleChar(char c) throws SyntaxError { + return 0; + } +} diff --git a/devTools/javaSanityCheck/src/element/KnownHtmlElement.java b/devTools/javaSanityCheck/src/element/KnownHtmlElement.java index b3b13673378..d086a74bc54 100644 --- a/devTools/javaSanityCheck/src/element/KnownHtmlElement.java +++ b/devTools/javaSanityCheck/src/element/KnownHtmlElement.java @@ -1,6 +1,6 @@ package org.arkerthan.sanityCheck.element; -public class KnownHtmlElement extends Element { +public class KnownHtmlElement extends KnownElement { private boolean opening; private String statement; @@ -11,43 +11,31 @@ public class KnownHtmlElement extends Element { this.statement = statement; } - @Override - public int handleChar(char c) { - return 0; - } - @Override public String getShortDescription() { StringBuilder builder = new StringBuilder(); - builder.append(line).append(":").append(pos).append(" <"); - if(!opening){ + builder.append('[').append(line).append(":").append(pos).append("] <"); + if (!opening) { builder.append("/"); } - return builder.append(statement).toString(); + return builder.append(statement).append(">").toString(); } - /** - * @return true, if it needs another Known Element to close it, false if it closes another element. - */ + @Override public boolean isOpening() { return opening; } - /** - * @param k Element to be checked - * @return true if given Element closes Element - */ - public boolean isMatchingElement(KnownHtmlElement k) { - return k.statement.equals(this.statement); + @Override + public boolean isClosing() { + return !opening; } - /** - * Returns, if isOpening is true, the needed statement for closing, - * otherwise the statement that generated it. - * - * @return statement - */ - public String getStatement() { - return statement; + @Override + public boolean isMatchingElement(KnownElement k) { + if (k instanceof KnownHtmlElement) { + return ((KnownHtmlElement) k).statement.equals(this.statement); + } + return false; } } diff --git a/devTools/javaSanityCheck/src/element/KnownLogicElement.java b/devTools/javaSanityCheck/src/element/KnownLogicElement.java new file mode 100644 index 00000000000..502abde93a2 --- /dev/null +++ b/devTools/javaSanityCheck/src/element/KnownLogicElement.java @@ -0,0 +1,117 @@ +package org.arkerthan.sanityCheck.element; + +import org.arkerthan.sanityCheck.DisallowedTagException; +import org.arkerthan.sanityCheck.UnknownStateException; + +import java.util.Arrays; +import java.util.List; + +public class KnownLogicElement extends KnownElement { + private static final List<String> allowedTags = Arrays.asList("if", "elseif", "else"); + private final int state; + private boolean last; + /* + 0 - if + 1 - elseif + 2 - else + 3 - switch + 4 - case + 5 - default + */ + + public KnownLogicElement(int line, int pos, String tag, boolean last) { + this(line, pos, tag); + this.last = last; + } + + public KnownLogicElement(int line, int pos, String tag) { + super(line, pos); + switch (tag) { + case "if": + state = 0; + break; + case "elseif": + state = 1; + break; + case "else": + state = 2; + break; + case "switch": + state = 3; + break; + case "case": + state = 4; + break; + case "default": + state = 5; + break; + default: + throw new DisallowedTagException(tag); + } + last = false; + } + + @Override + public boolean isOpening() { + return !last; + } + + @Override + public boolean isClosing() { + return (state != 0 && state != 3) || last; + } + + @Override + public boolean isMatchingElement(KnownElement k) { + if (!(k instanceof KnownLogicElement)) { + return false; + } + KnownLogicElement l = (KnownLogicElement) k; + switch (state) { + case 0: + case 1: + return l.state == 1 || l.state == 2 || (l.state == 0 && l.last); + case 2: + return l.state == 0 && l.last; + case 3: + case 4: + return l.state == 3 || l.state == 4; + case 5: + return l.state == 3 && l.last; + default: + throw new UnknownStateException(state); + } + } + + @Override + public String getShortDescription() { + StringBuilder builder = new StringBuilder(); + builder.append("[").append(line).append(":").append(pos).append("] <<"); + if (last) { + builder.append('/'); + } + switch (state) { + case 0: + builder.append("if"); + break; + case 1: + builder.append("elseif"); + break; + case 2: + builder.append("else"); + break; + case 3: + builder.append("switch"); + break; + case 4: + builder.append("case"); + break; + case 5: + builder.append("default"); + break; + default: + throw new UnknownStateException(state); + } + return builder.append(">>").toString(); + } +} diff --git a/devTools/javaSanityCheck/src/element/KnownTwineElement.java b/devTools/javaSanityCheck/src/element/KnownTwineElement.java new file mode 100644 index 00000000000..24003fd00be --- /dev/null +++ b/devTools/javaSanityCheck/src/element/KnownTwineElement.java @@ -0,0 +1,41 @@ +package org.arkerthan.sanityCheck.element; + +public class KnownTwineElement extends KnownElement { + + private boolean opening; + private String statement; + + public KnownTwineElement(int line, int pos, boolean opening, String statement) { + super(line, pos); + this.opening = opening; + this.statement = statement; + } + + @Override + public String getShortDescription() { + StringBuilder builder = new StringBuilder(); + builder.append("[").append(line).append(":").append(pos).append("] <<"); + if (!opening) { + builder.append("/"); + } + return builder.append(statement).append(">>").toString(); + } + + @Override + public boolean isOpening() { + return opening; + } + + @Override + public boolean isClosing() { + return !opening; + } + + @Override + public boolean isMatchingElement(KnownElement k) { + if (k instanceof KnownTwineElement) { + return ((KnownTwineElement) k).statement.equals(this.statement); + } + return false; + } +} diff --git a/devTools/javaSanityCheck/src/tag/HtmlTag.java b/devTools/javaSanityCheck/src/tag/HtmlTag.java deleted file mode 100644 index 70bff2c5a32..00000000000 --- a/devTools/javaSanityCheck/src/tag/HtmlTag.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.arkerthan.sanityCheck.tag; - -public class HtmlTag extends Tag { - public HtmlTag(String tag, boolean single) { - super(tag, single); - } -} diff --git a/devTools/javaSanityCheck/twineTags b/devTools/javaSanityCheck/twineTags index d8df5639a52..e1dea71621b 100644 --- a/devTools/javaSanityCheck/twineTags +++ b/devTools/javaSanityCheck/twineTags @@ -7,123 +7,17 @@ #Do not add HTML Tags here. #Characters outside of ASCII scope are not supported. # -#widgets -amputeeDescription;0 -assignmentFilter;0 -resetAssignmentFilter;0 -accentDescription;0 -collarDescription;0 -AnusDescription;0 -armsTatDescription;0 -BoobsDescription;0 -boobBrandDescription;0 -BellyInflationDescription;0 -BellyImplantDescription;0 -BellyDescription;0 -ButtDescription;0 -showallAssignmentFilter;0 -boobsShapeDescription;0 -boobsExtraDescription;0 -boobsTatDescription;0 -shouldersDescription;0 -nipplesDescription;0 -crotchDescription;0 -listOfSlavesWithParent;0 -redisplayFamily;0 -parentName;0 -listOfSlavesWithSameParent;0 -nurseryAssignmentFilter;0 -dickDescription;0 -vaginaDescription;0 -SlaveInteractImpreg;0 -SlaveInteractFertility;0 -SlaveInteractSexOption;0 -SlaveInteractAnalSexOption;0 -SlaveInteractGropeOption;0 -SlaveInteractDickGropeOption;0 -SlaveInteractAnalGropeOption;0 -SlaveInteractDrugs;0 -nipplesPiercingDescription;0 -areolaeDescription;0 -nailsDescription;0 -backTatDescription;0 -pregnancyDescription;0 -ClothingDescription;0 -InscripDesc;0 -clothingCorsetDescription;0 -SlaveArt;0 -SlaveSort;0 -shouldersTatDescription;0 -earDescription;0 -upperFaceDescription;0 -HairDescription;0 -earPiercingDescription;0 -nosePiercingDescription;0 -eyebrowPiercingDescription;0 -customTatDescription;0 -faceDescription;0 -mouthDescription;0 -setLocalPronouns;0 -eyeDescription;0 -brandDescription;0 -Family;0 -chastityPiercingDescription;0 -footwearDescription;0 -HairClothingDescription;0 -FlowerDesc;0 -ImageDesc;0 -waistDescription;0 -heightImplantDescription;0 -BodyguardWeapon;0 -CorsetPiercingDescription;0 -heelDescription;0 -FarmyardStatistics;0 -skinDescription;0 -Master;0 -setPlayerPronouns;0 -farmyardAssignmentFilter;0 -setAssistantPronouns;0 -OptionAbbreviateDevotion;0 -OptionAbbreviateDiet;0 -OptionAbbreviateDrugs;0 -OptionAbbreviateHormoneBalance;0 -OptionAbbreviateGenitalia;0 -OptionAbbreviatePhysicals;0 -OptionAbbreviateOrigins;0 -OptionAbbreviateRulesets;0 -OptionAbbreviateSidebar;0 -OptionAbbreviateMissing;0 -OptionAbbreviateSkills;0 -OptionAbbreviateMental;0 -OptionAbbreviateNationality;0 -OptionAbbreviateClothes;0 -OptionAbbreviateRace;0 -OptionAbbreviateRules;0 -OptionAbbreviateHealth;0 -OptionLineSeparations;0 -OptionSortBy;0 -OptionRulesAssistantMain;0 -OptionDisplayAssignments;0 -OptionSortOrder;0 -OptionSortMain;0 -OptionSummaryStats;0 -s;0 -c;0 -ss;0 -S;0 -x;0 -z;0 -say;0 -Sh;0 -# #twine tags capture;1 continue;0 for;1 +#does foreach really exist? +foreach;1 goto;0 htag;1 include;0 link;1 +nobr;1 print;0 replace;1 run;0 @@ -134,7 +28,7 @@ textbox;0 timed;1 unset;0 widget;1 -=; +=;0 # # Twine logic ### DO NOT TOUCH ### if;1 -- GitLab