summaryrefslogtreecommitdiff
path: root/base/src
diff options
context:
space:
mode:
authorBenjamin J. Culkin <bjculkin@mix.wvu.edu>2018-09-16 21:12:35 -0300
committerBenjamin J. Culkin <bjculkin@mix.wvu.edu>2018-09-16 21:12:35 -0300
commit2f96f49c2d2c8679841c790e9dd7d9f1b6f3fed1 (patch)
tree23cfb315743bbb5c1c8fa749e75aba8a54e3a2f7 /base/src
parent92c56c5918a0858aad32cc9ee0fb3eee99ebd007 (diff)
Large update
A large update, this contains much debugging of the CL FORMAT routines, as well as a few other minor changes.
Diffstat (limited to 'base/src')
-rw-r--r--base/src/main/java/bjc/utils/esodata/SingleTape.java7
-rw-r--r--base/src/main/java/bjc/utils/ioutils/format/CLFormatter.java208
-rw-r--r--base/src/main/java/bjc/utils/ioutils/format/CLModifiers.java35
-rw-r--r--base/src/main/java/bjc/utils/ioutils/format/CLParameters.java57
-rw-r--r--base/src/main/java/bjc/utils/ioutils/format/directives/AestheticDirective.java1
-rw-r--r--base/src/main/java/bjc/utils/ioutils/format/directives/CaseDirective.java117
-rw-r--r--base/src/main/java/bjc/utils/ioutils/format/directives/ConditionalDirective.java134
-rw-r--r--base/src/main/java/bjc/utils/ioutils/format/directives/Directive.java32
-rw-r--r--base/src/main/java/bjc/utils/ioutils/format/directives/EscapeDirective.java15
-rw-r--r--base/src/main/java/bjc/utils/ioutils/format/directives/GeneralNumberDirective.java6
-rw-r--r--base/src/main/java/bjc/utils/ioutils/format/directives/IterationDirective.java42
-rw-r--r--base/src/main/java/bjc/utils/ioutils/format/directives/NumberDirective.java10
-rw-r--r--base/src/main/java/bjc/utils/ioutils/format/directives/RecursiveDirective.java49
-rw-r--r--base/src/main/java/bjc/utils/ioutils/format/directives/TabulateDirective.java2
-rw-r--r--base/src/main/java/bjc/utils/math/NumberUtils.java22
-rw-r--r--base/src/test/java/bjc/utils/test/ioutils/CLFormatterTest.java92
16 files changed, 631 insertions, 198 deletions
diff --git a/base/src/main/java/bjc/utils/esodata/SingleTape.java b/base/src/main/java/bjc/utils/esodata/SingleTape.java
index d57bc7f..be9d64b 100644
--- a/base/src/main/java/bjc/utils/esodata/SingleTape.java
+++ b/base/src/main/java/bjc/utils/esodata/SingleTape.java
@@ -79,6 +79,8 @@ public class SingleTape<T> implements Tape<T> {
@Override
public T item() {
+ if (pos < 0 || pos >= backing.size()) return null;
+
return backing.get(pos);
}
@@ -150,13 +152,12 @@ public class SingleTape<T> implements Tape<T> {
@Override
public boolean right(final int amt) {
- if(pos + amt >= backing.size() - 1) {
+ if(pos + amt > backing.size()) {
if(autoExtend) {
while(pos + amt >= backing.size() - 1) {
backing.add(null);
}
- } else
- return false;
+ } else return false;
}
pos += amt;
diff --git a/base/src/main/java/bjc/utils/ioutils/format/CLFormatter.java b/base/src/main/java/bjc/utils/ioutils/format/CLFormatter.java
index 1605a75..9e01ca6 100644
--- a/base/src/main/java/bjc/utils/ioutils/format/CLFormatter.java
+++ b/base/src/main/java/bjc/utils/ioutils/format/CLFormatter.java
@@ -24,33 +24,33 @@ import static bjc.utils.misc.PropertyDB.getRegex;
*
*/
public class CLFormatter {
- private static final String prefixParam = getRegex("clFormatPrefix");
-
- private static final String formatMod = getRegex("clFormatModifier");
-
- private static final String prefixList = applyFormat("delimSeparatedList", prefixParam, ",");
-
+ private static final String prefixParam = getRegex("clFormatPrefix");
+ private static final String formatMod = getRegex("clFormatModifier");
private static final String directiveName = getRegex("clFormatName");
- private static final String formatDirective = applyFormat("clFormatDirective", prefixList, formatMod,
- directiveName);
+ private static final String prefixList = applyFormat("delimSeparatedList", prefixParam, ",");
+ private static final String formatDirective = applyFormat("clFormatDirective", prefixList, formatMod, directiveName);
+
private static final Pattern pFormatDirective = Pattern.compile(formatDirective);
private static Map<String, Directive> builtinDirectives;
-
- private Map<String, Directive> extraDirectives;
+ private Map<String, Directive> extraDirectives;
static {
builtinDirectives = new HashMap<>();
builtinDirectives.put("A", new AestheticDirective());
+ // @NOTE 9/6/18
+ //
+ // This is just an alias, not the actual S directive
+ builtinDirectives.put("S", new AestheticDirective());
builtinDirectives.put("C", new CharacterDirective());
- builtinDirectives.put("B", new NumberDirective(-1, 2));
- builtinDirectives.put("O", new NumberDirective(-1, 8));
- builtinDirectives.put("D", new NumberDirective(-1, 10));
- builtinDirectives.put("X", new NumberDirective(-1, 16));
+ builtinDirectives.put("B", new NumberDirective(-1, 2, 'B'));
+ builtinDirectives.put("O", new NumberDirective(-1, 8, 'O'));
+ builtinDirectives.put("D", new NumberDirective(-1, 10, 'D'));
+ builtinDirectives.put("X", new NumberDirective(-1, 16, 'X'));
builtinDirectives.put("R", new RadixDirective());
@@ -58,13 +58,15 @@ public class CLFormatter {
builtinDirectives.put("%", new LiteralDirective("\n", '%'));
builtinDirectives.put("|", new LiteralDirective("\f", '|'));
- builtinDirectives.put("~", new LiteralDirective("~", '~'));
+ builtinDirectives.put("~", new LiteralDirective("~", '~'));
+ builtinDirectives.put("?", new RecursiveDirective());
builtinDirectives.put("*", new GotoDirective());
builtinDirectives.put("^", new EscapeDirective());
builtinDirectives.put("[", new ConditionalDirective());
builtinDirectives.put("{", new IterationDirective());
+ builtinDirectives.put("(", new CaseDirective());
builtinDirectives.put("T", new TabulateDirective());
}
@@ -103,7 +105,7 @@ public class CLFormatter {
/* Put the parameters where we can easily handle them. */
Tape<Object> tParams = new SingleTape<>(params);
- doFormatString(format, rw, tParams);
+ doFormatString(format, rw, tParams, true);
return rw.toString();
}
@@ -123,7 +125,7 @@ public class CLFormatter {
/* Put the parameters where we can easily handle them. */
Tape<Object> tParams = new SingleTape<>(params);
- doFormatString(format, rw, tParams);
+ doFormatString(format, rw, tParams, true);
return rw.toString();
}
@@ -141,7 +143,7 @@ public class CLFormatter {
/* Put the parameters where we can easily handle them. */
Tape<Object> tParams = new SingleTape<>(params);
- doFormatString(format, rw, tParams);
+ doFormatString(format, rw, tParams, true);
}
/**
@@ -159,7 +161,7 @@ public class CLFormatter {
/* Put the parameters where we can easily handle them. */
Tape<Object> tParams = new SingleTape<>(params);
- doFormatString(format, rw, tParams);
+ doFormatString(format, rw, tParams, true);
}
/**
@@ -175,7 +177,7 @@ public class CLFormatter {
* @param tParams
* The parameters to use.
*/
- public void doFormatString(String format, ReportWriter rw, Tape<Object> tParams) throws IOException {
+ public void doFormatString(String format, ReportWriter rw, Tape<Object> tParams, boolean isToplevel) throws IOException {
Matcher dirMatcher = pFormatDirective.matcher(format);
// We need this StringBuffer to use appendReplacement and stuff
@@ -185,90 +187,96 @@ public class CLFormatter {
// ourselves.
StringBuffer sb = new StringBuffer();
- while(dirMatcher.find()) {
- dirMatcher.appendReplacement(sb, "");
- rw.writeBuffer(sb);
-
- String dirName = dirMatcher.group("name");
- String dirFunc = dirMatcher.group("funcname");
- String dirMods = dirMatcher.group("modifiers");
- String dirParams = dirMatcher.group("params");
-
- if(dirMods == null) dirMods = "";
- if(dirParams == null) dirParams = "";
-
- String[] splitPars = dirParams.split("(?<!'),");
- CLParameters arrParams = CLParameters.fromDirective(splitPars, tParams);
-
- CLModifiers mods = CLModifiers.fromString(dirMods);
-
- Object item = tParams.item();
- if(dirName == null && dirFunc != null) {
- /*
- * @TODO implement user-called functions.
- */
- continue;
- }
-
- if(extraDirectives.containsKey(dirName)) {
- extraDirectives.get(dirName).format(rw, item, mods, arrParams, tParams, dirMatcher,
- this);
-
- continue;
+ boolean doTail = true;
+ try {
+ while(dirMatcher.find()) {
+ dirMatcher.appendReplacement(sb, "");
+ rw.writeBuffer(sb);
+
+ String dirName = dirMatcher.group("name");
+ String dirFunc = dirMatcher.group("funcname");
+ String dirMods = dirMatcher.group("modifiers");
+ String dirParams = dirMatcher.group("params");
+
+ if(dirMods == null) dirMods = "";
+ if(dirParams == null) dirParams = "";
+
+ CLParameters arrParams = CLParameters.fromDirective(dirParams, tParams);
+
+ CLModifiers mods = CLModifiers.fromString(dirMods);
+
+ Object item = tParams.item();
+
+ if(dirName == null && dirFunc != null) {
+ /*
+ * @TODO implement user-called functions.
+ */
+ continue;
+ }
+
+ if(extraDirectives.containsKey(dirName)) {
+ extraDirectives.get(dirName).format(rw, item, mods, arrParams, tParams, dirMatcher,
+ this);
+
+ continue;
+ }
+
+ if(builtinDirectives.containsKey(dirName)) {
+ // System.err.printf("Executing directive %s (%s) (%d to %d) from string %s\n", dirName, dirMatcher.group(), dirMatcher.start(), dirMatcher.end(), format);
+
+ builtinDirectives.get(dirName).format(rw,
+ item, mods, arrParams, tParams, dirMatcher, this);
+
+ continue;
+ }
+
+ if(dirName == null) dirName = "<null>";
+
+ switch(dirName) {
+ case "]":
+ throw new IllegalArgumentException("Found conditional-end outside of conditional.");
+ case ";":
+ throw new IllegalArgumentException(
+ "Found seperator outside of block.");
+ case "}":
+ throw new IllegalArgumentException("Found iteration-end outside of iteration");
+ case "<":
+ case ">":
+ throw new IllegalArgumentException("Layout-control directives aren't implemented yet.");
+ case "F":
+ case "E":
+ case "G":
+ case "$":
+ /* @TODO implement floating point directives. */
+ throw new IllegalArgumentException("Floating-point directives aren't implemented yet.");
+ case "W":
+ /*
+ * @TODO figure out if we want to implement
+ * someting for these directives instead of
+ * punting.
+ */
+ throw new IllegalArgumentException("S and W aren't implemented. Use A instead");
+ case "P":
+ throw new IllegalArgumentException("These directives aren't implemented yet");
+ case ")":
+ throw new IllegalArgumentException("Case-conversion end outside of case conversion");
+ case "\n":
+ /*
+ * Ignored newline.
+ */
+ break;
+ default:
+ String msg = String.format("Unknown format directive '%s'", dirName);
+ throw new IllegalArgumentException(msg);
+ }
}
+ } catch (EscapeException eex) {
+ if (!isToplevel) throw eex;
- if(builtinDirectives.containsKey(dirName)) {
- builtinDirectives.get(dirName).format(rw, item, mods, arrParams, tParams, dirMatcher,
- this);
-
- continue;
- }
-
- if(dirName == null) dirName = "<null>";
-
- switch(dirName) {
- case "]":
- throw new IllegalArgumentException("Found conditional-end outside of conditional.");
- case ";":
- throw new IllegalArgumentException(
- "Found seperator outside of block.");
- case "}":
- throw new IllegalArgumentException("Found iteration-end outside of iteration");
- case "<":
- case ">":
- throw new IllegalArgumentException("Layout-control directives aren't implemented yet.");
- case "F":
- case "E":
- case "G":
- case "$":
- /* @TODO implement floating point directives. */
- throw new IllegalArgumentException("Floating-point directives aren't implemented yet.");
- case "S":
- case "W":
- /*
- * @TODO figure out if we want to implement
- * someting for these directives instead of
- * punting.
- */
- throw new IllegalArgumentException("S and W aren't implemented. Use A instead");
- case "?":
- case "(":
- case "P":
- throw new IllegalArgumentException("These directives aren't implemented yet");
- case ")":
- throw new IllegalArgumentException("Case-conversion end outside of case conversion");
- case "\n":
- /*
- * Ignored newline.
- */
- break;
- default:
- String msg = String.format("Unknown format directive '%s'", dirName);
- throw new UnknownFormatConversionException(msg);
- }
+ doTail = false;
}
- dirMatcher.appendTail(sb);
+ if (doTail) dirMatcher.appendTail(sb);
rw.writeBuffer(sb);
}
}
diff --git a/base/src/main/java/bjc/utils/ioutils/format/CLModifiers.java b/base/src/main/java/bjc/utils/ioutils/format/CLModifiers.java
index 0625ff8..68127b6 100644
--- a/base/src/main/java/bjc/utils/ioutils/format/CLModifiers.java
+++ b/base/src/main/java/bjc/utils/ioutils/format/CLModifiers.java
@@ -15,6 +15,14 @@ public class CLModifiers {
* Whether the colon mod is on.
*/
public final boolean colonMod;
+ /**
+ * Whether the dollar mod is on.
+ */
+ public final boolean dollarMod;
+ /**
+ * Whether the star mod is on.
+ */
+ public final boolean starMod;
/**
* Create a new set of CL modifiers.
@@ -23,10 +31,14 @@ public class CLModifiers {
* The state of the at mod.
* @param colon
* The state of the colon mod.
+ * @param dollar
+ * The state of the dollar mod.
*/
- public CLModifiers(boolean at, boolean colon) {
- atMod = at;
- colonMod = colon;
+ public CLModifiers(boolean at, boolean colon, boolean dollar, boolean star) {
+ atMod = at;
+ colonMod = colon;
+ dollarMod = dollar;
+ starMod = star;
}
/**
@@ -37,13 +49,18 @@ public class CLModifiers {
* @return A set of modifiers matching the string.
*/
public static CLModifiers fromString(String modString) {
- boolean atMod = false;
- boolean colonMod = false;
+ boolean atMod = false;
+ boolean colonMod = false;
+ boolean dollarMod = false;
+ boolean starMod = false;
+
if(modString != null) {
- atMod = modString.contains("@");
- colonMod = modString.contains(":");
+ atMod = modString.contains("@");
+ colonMod = modString.contains(":");
+ dollarMod = modString.contains("$");
+ starMod = modString.contains("*");
}
- return new CLModifiers(atMod, colonMod);
+ return new CLModifiers(atMod, colonMod, dollarMod, starMod);
}
-} \ No newline at end of file
+}
diff --git a/base/src/main/java/bjc/utils/ioutils/format/CLParameters.java b/base/src/main/java/bjc/utils/ioutils/format/CLParameters.java
index 2588446..bde7a7d 100644
--- a/base/src/main/java/bjc/utils/ioutils/format/CLParameters.java
+++ b/base/src/main/java/bjc/utils/ioutils/format/CLParameters.java
@@ -44,10 +44,33 @@ public class CLParameters {
*
* @return A set of CL parameters.
*/
- public static CLParameters fromDirective(String[] params, Tape<Object> dirParams) {
+ public static CLParameters fromDirective(String unsplit, Tape<Object> dirParams) {
+ List<String> lParams = new ArrayList<>();
+ StringBuilder currParm = new StringBuilder();
+
+ char prevChar = ' ';
+
+ for (int i = 0; i < unsplit.length(); i++) {
+ char c = unsplit.charAt(i);
+
+ if (c == ',' && prevChar != '\'') {
+ lParams.add(currParm.toString());
+
+ currParm = new StringBuilder();
+ } else {
+ currParm.append(c);
+ }
+
+ prevChar = c;
+ }
+ lParams.add(currParm.toString());
+
List<String> parameters = new ArrayList<>();
- for(String param : params) {
+ if (lParams.size() == 1 && lParams.get(0).equals(""))
+ return new CLParameters(parameters.toArray(new String[0]));
+
+ for(String param : lParams) {
if(param.equalsIgnoreCase("V")) {
Object par = dirParams.item();
boolean succ = dirParams.right();
@@ -73,7 +96,9 @@ public class CLParameters {
throw new IllegalArgumentException(
"Incorrect type of parameter for V inline parameter");
}
- } else if(param.equals("#")) {
+ } else if (param.equals("#")) {
+ parameters.add(Integer.toString(dirParams.size() - dirParams.position()));
+ } else if (param.equals("%")) {
parameters.add(Integer.toString(dirParams.position()));
} else {
parameters.add(param);
@@ -119,9 +144,14 @@ public class CLParameters {
public char getChar(int idx, String paramName, char directive) {
String param = params[idx];
+ if (param.length() == 1) {
+ // Punt in the case we have a slightly malformed issue
+ return param.charAt(0);
+ }
+
if(!param.startsWith("'")) {
throw new IllegalArgumentException(
- String.format("Invalid %s %s to %c directive", paramName, param, directive));
+ String.format("Invalid %s \"%s\" to %c directive", paramName, param, directive));
}
return param.charAt(1);
@@ -166,7 +196,7 @@ public class CLParameters {
try {
return Integer.parseInt(param);
} catch(NumberFormatException nfex) {
- String msg = String.format("Invalid %s %s to %c directive", paramName, param, directive);
+ String msg = String.format("Invalid %s \"%s\" to %c directive", paramName, param, directive);
IllegalArgumentException iaex = new IllegalArgumentException(msg);
iaex.initCause(nfex);
@@ -174,4 +204,21 @@ public class CLParameters {
throw iaex;
}
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("[");
+
+ for (int i = 0; i < params.length; i++) {
+ if (i != 0) sb.append(", ");
+
+ sb.append("\"");
+ sb.append(params[i]);
+ sb.append("\"");
+ }
+
+ sb.append("]");
+
+ return sb.toString();
+ }
}
diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/AestheticDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/AestheticDirective.java
index 4276f40..9bad6d7 100644
--- a/base/src/main/java/bjc/utils/ioutils/format/directives/AestheticDirective.java
+++ b/base/src/main/java/bjc/utils/ioutils/format/directives/AestheticDirective.java
@@ -21,6 +21,7 @@ public class AestheticDirective implements Directive {
@Override
public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters params, Tape<Object> tParams,
Matcher dirMatcher, CLFormatter fmt) throws IOException {
+ // System.err.printf("Aesthetic directive with item \"%s\" and params: %s\n", item, tParams);
CLFormatter.checkItem(item, 'A');
int mincol = 0, colinc = 1, minpad = 0;
diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/CaseDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/CaseDirective.java
new file mode 100644
index 0000000..728bb43
--- /dev/null
+++ b/base/src/main/java/bjc/utils/ioutils/format/directives/CaseDirective.java
@@ -0,0 +1,117 @@
+
+package bjc.utils.ioutils.format.directives;
+
+import bjc.utils.esodata.Tape;
+import bjc.utils.ioutils.format.*;
+import bjc.utils.ioutils.ReportWriter;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.IllegalFormatConversionException;
+import java.util.List;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class CaseDirective implements Directive {
+ private static final Pattern wordPattern = Pattern.compile("(\\w+)(\\b*)");
+
+ @Override
+ public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters params, Tape<Object> tParams,
+ Matcher dirMatcher, CLFormatter fmt) throws IOException {
+ StringBuffer condBody = new StringBuffer();
+
+ int nestLevel = 1;
+
+ while (dirMatcher.find()) {
+ /* Process a list of clauses. */
+ String dirName = dirMatcher.group("name");
+
+ if (dirName != null) {
+ /* Append everything up to this directive. */
+ dirMatcher.appendReplacement(condBody, "");
+
+ if (dirName.equals("(")) {
+ if (nestLevel > 0) {
+ condBody.append(dirMatcher.group());
+ }
+
+ nestLevel += 1;
+ } else if (Directive.isOpening(dirName)) {
+ nestLevel += 1;
+
+ condBody.append(dirMatcher.group());
+ } else if (dirName.equals(")")) {
+ nestLevel = Math.max(0, nestLevel - 1);
+
+ /* End the iteration. */
+ if (nestLevel == 0) break;
+ } else if (Directive.isClosing(dirName)) {
+ nestLevel = Math.max(0, nestLevel - 1);
+ } else {
+ /* Not a special directive. */
+ condBody.append(dirMatcher.group());
+ }
+ }
+ }
+
+ String frmt = condBody.toString();
+
+ ReportWriter nrw = rw.duplicate(new StringWriter());
+
+ fmt.doFormatString(frmt, nrw, tParams, false);
+
+ String strang = nrw.toString();
+
+ if (mods.colonMod && mods.atMod) {
+ strang = strang.toUpperCase();
+ } else if (mods.colonMod) {
+ Matcher mat = wordPattern.matcher(strang);
+
+ StringBuffer sb = new StringBuffer();
+ while(!mat.find()) {
+ mat.appendReplacement(sb, "");
+
+ String word = mat.group(1);
+
+ word = word.substring(0, 1).toUpperCase() + word.substring(1);
+
+ sb.append(word);
+ sb.append(mat.group(2));
+ }
+
+ mat.appendTail(sb);
+
+ strang = sb.toString();
+ } else if (mods.atMod) {
+ Matcher mat = wordPattern.matcher(strang);
+
+ StringBuffer sb = new StringBuffer();
+ boolean doCap = true;
+ while(!mat.find()) {
+ mat.appendReplacement(sb, "");
+
+ String word = mat.group(1);
+
+ if (doCap) {
+ doCap = false;
+
+ word = word.substring(0, 1).toUpperCase() + word.substring(1);
+ }
+
+ sb.append(word);
+ sb.append(mat.group(2));
+ }
+
+ mat.appendTail(sb);
+
+ strang = sb.toString();
+
+ } else {
+ strang = strang.toLowerCase();
+ }
+
+ rw.write(strang);
+ }
+}
diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/ConditionalDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/ConditionalDirective.java
index 71d9e7d..ed0b39b 100644
--- a/base/src/main/java/bjc/utils/ioutils/format/directives/ConditionalDirective.java
+++ b/base/src/main/java/bjc/utils/ioutils/format/directives/ConditionalDirective.java
@@ -8,6 +8,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.IllegalFormatConversionException;
import java.util.List;
+import java.util.logging.Logger;
import java.util.regex.Matcher;
/**
@@ -17,6 +18,7 @@ import java.util.regex.Matcher;
*
*/
public class ConditionalDirective implements Directive {
+ private static Logger LOG = Logger.getLogger(ConditionalDirective.class.getName());
@Override
public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters arrParams,
@@ -24,42 +26,75 @@ public class ConditionalDirective implements Directive {
StringBuffer condBody = new StringBuffer();
List<String> clauses = new ArrayList<>();
- String defClause = null;
+
+ String defClause = null;
boolean isDefault = false;
+ int nestLevel = 1;
+
while (dirMatcher.find()) {
/* Process a list of clauses. */
String dirName = dirMatcher.group("name");
String dirMods = dirMatcher.group("modifiers");
+ //System.err.printf("Found conditional directive %s with %s mods and level %d\n", dirName, dirMods, nestLevel);
if (dirName != null) {
/* Append everything up to this directive. */
dirMatcher.appendReplacement(condBody, "");
- if (dirName.equals("]")) {
- /* End the conditional. */
- String clause = condBody.toString();
- if (isDefault) {
- defClause = clause;
- } else {
+ if (dirName.equals("[")) {
+ if (nestLevel > 0) {
+ condBody.append(dirMatcher.group());
+ }
+ nestLevel += 1;
+ } else if (Directive.isOpening(dirName)) {
+ nestLevel += 1;
+
+ condBody.append(dirMatcher.group());
+ } else if (dirName.equals("]")) {
+ nestLevel = Math.max(0, nestLevel - 1);
+
+ if (nestLevel == 0) {
+ /* End the conditional. */
+ String clause = condBody.toString();
+ // System.err.printf("Found clause \"%s]\"\n", clause);
+ condBody = new StringBuffer();
+
+ if (isDefault) {
+ defClause = clause;
+ }
clauses.add(clause);
+
+ break;
+ } else {
+ /* Not a special directive. */
+ condBody.append(dirMatcher.group());
}
+ } else if (Directive.isClosing(dirName)) {
+ nestLevel = Math.max(0, nestLevel - 1);
- break;
+ condBody.append(dirMatcher.group());
} else if (dirName.equals(";")) {
- /* End the clause. */
- String clause = condBody.toString();
- if (isDefault) {
- defClause = clause;
- } else {
+ if (nestLevel == 1) {
+ /* End the clause. */
+ String clause = condBody.toString();
+ // System.err.printf("Found clause \"%s;\"\n", clause);
+ condBody = new StringBuffer();
+
+ if (isDefault) {
+ defClause = clause;
+ }
clauses.add(clause);
- }
- /*
- * Mark the next clause as the default.
- */
- if (dirMods.contains(":")) {
- isDefault = true;
+ /*
+ * Mark the next clause as the default.
+ */
+ if (dirMods.contains(":")) {
+ isDefault = true;
+ }
+ } else {
+ /* Not a special directive. */
+ condBody.append(dirMatcher.group());
}
} else {
/* Not a special directive. */
@@ -67,18 +102,21 @@ public class ConditionalDirective implements Directive {
}
}
}
+
+ if (mods.starMod && clauses.size() > 0) defClause = clauses.get(0);
- Object par = formatParams.item();
try {
if (mods.colonMod) {
formatParams.right();
- if (par == null) {
- throw new IllegalArgumentException("No parameter provided for [ directive.");
- } else if (!(par instanceof Boolean)) {
- throw new IllegalFormatConversionException('[', par.getClass());
+ boolean res = false;
+ if (item == null) {
+ //throw new IllegalArgumentException("No parameter provided for [ directive.");
+ } else if (!(item instanceof Boolean)) {
+ throw new IllegalFormatConversionException('[', item.getClass());
+ } else {
+ res = (Boolean) item;
}
- boolean res = (Boolean) par;
String frmt;
if (res)
@@ -86,17 +124,21 @@ public class ConditionalDirective implements Directive {
else
frmt = clauses.get(0);
- fmt.doFormatString(frmt, rw, formatParams);
+ fmt.doFormatString(frmt, rw, formatParams, false);
} else if (mods.atMod) {
- if (par == null) {
- throw new IllegalArgumentException("No parameter provided for [ directive.");
- } else if (!(par instanceof Boolean)) {
- throw new IllegalFormatConversionException('[', par.getClass());
+ boolean res = false;
+ if (item == null) {
+ // throw new IllegalArgumentException("No parameter provided for [ directive.");
+ } else if (item instanceof Integer) {
+ if ((Integer)item != 0) res = true;
+ } else if (item instanceof Boolean) {
+ res = (Boolean) item;
+ } else {
+ throw new IllegalFormatConversionException('[', item.getClass());
}
- boolean res = (Boolean) par;
if (res) {
- fmt.doFormatString(clauses.get(0), rw, formatParams);
+ fmt.doFormatString(clauses.get(0), rw, formatParams, false);
} else {
formatParams.right();
}
@@ -105,21 +147,33 @@ public class ConditionalDirective implements Directive {
if (arrParams.length() >= 1) {
res = arrParams.getInt(0, "conditional choice", '[');
} else {
- if (par == null) {
+ if (item == null) {
throw new IllegalArgumentException("No parameter provided for [ directive.");
- } else if (!(par instanceof Number)) {
- throw new IllegalFormatConversionException('[', par.getClass());
+ } else if (!(item instanceof Number)) {
+ throw new IllegalFormatConversionException('[', item.getClass());
}
- res = ((Number) par).intValue();
+ res = ((Number) item).intValue();
formatParams.right();
}
- if (res < 0 || res > clauses.size()) {
- if (defClause != null)
- fmt.doFormatString(defClause, rw, formatParams);
+ if (mods.dollarMod) res -= 1;
+
+ // System.err.printf("Attempting selection of clause %d of %d (%s) (default %s)\n",
+ // res, clauses.size(), clauses, defClause);
+ if (clauses.size() == 0 || res < 0 || res >= clauses.size()) {
+ // System.err.printf("Selecting default clause (res %d, max %d): %s\n", res, clauses.size(), defClause);
+ // int clauseNo = 0;
+ // for (String clause : clauses) {
+ // System.err.printf("... clause %d: %s\n", ++clauseNo, clause);
+ // }
+
+ if (defClause != null) fmt.doFormatString(defClause, rw, formatParams, false);
} else {
- fmt.doFormatString(clauses.get(res), rw, formatParams);
+ String frmt = clauses.get(res);
+
+ // System.out.printf("Selecting clause %d of %d (params %s): %s\n", res, clauses.size(), formatParams, frmt);
+ fmt.doFormatString(frmt, rw, formatParams, false);
}
}
} catch (EscapeException eex) {
diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/Directive.java b/base/src/main/java/bjc/utils/ioutils/format/directives/Directive.java
index ad9c34c..61abfc1 100644
--- a/base/src/main/java/bjc/utils/ioutils/format/directives/Directive.java
+++ b/base/src/main/java/bjc/utils/ioutils/format/directives/Directive.java
@@ -37,4 +37,36 @@ public interface Directive {
*/
public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters arrParams, Tape<Object> tParams,
Matcher dirMatcher, CLFormatter fmt) throws IOException;
+
+ public static boolean isOpening(String str) {
+ return isOpening(str.charAt(0));
+ }
+
+ public static boolean isOpening(char dir) {
+ switch(dir) {
+ case '(':
+ case '<':
+ case '[':
+ case '{':
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public static boolean isClosing(String str) {
+ return isClosing(str.charAt(0));
+ }
+
+ public static boolean isClosing(char dir) {
+ switch(dir) {
+ case ')':
+ case '>':
+ case ']':
+ case '}':
+ return true;
+ default:
+ return false;
+ }
+ }
}
diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/EscapeDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/EscapeDirective.java
index 5badff0..74488ed 100644
--- a/base/src/main/java/bjc/utils/ioutils/format/directives/EscapeDirective.java
+++ b/base/src/main/java/bjc/utils/ioutils/format/directives/EscapeDirective.java
@@ -21,28 +21,35 @@ public class EscapeDirective implements Directive {
Tape<Object> formatParams, Matcher dirMatcher, CLFormatter fmt) {
boolean shouldExit;
+ if (mods.dollarMod) formatParams.right();
+
switch(params.length()) {
case 0:
- shouldExit = formatParams.size() == 0;
+ shouldExit = formatParams.atEnd();
break;
case 1:
int num = params.getInt(0, "condition count", '^');
+
shouldExit = num == 0;
break;
case 2:
- int left = params.getInt(0, "left-hand condition", '^');
+ int left = params.getInt(0, "left-hand condition", '^');
int right = params.getInt(1, "right-hand condition", '^');
+
shouldExit = left == right;
break;
case 3:
default:
- int low = params.getInt(0, "lower-bound condition", '^');
- int mid = params.getInt(1, "interval condition", '^');
+ int low = params.getInt(0, "lower-bound condition", '^');
+ int mid = params.getInt(1, "interval condition", '^');
int high = params.getInt(2, "upper-bound condition", '^');
+
shouldExit = (low <= mid) && (mid <= high);
break;
}
+ if (mods.dollarMod) formatParams.left();
+
/* At negates it. */
if(mods.atMod) shouldExit = !shouldExit;
diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/GeneralNumberDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/GeneralNumberDirective.java
index ccf01d8..3eb741e 100644
--- a/base/src/main/java/bjc/utils/ioutils/format/directives/GeneralNumberDirective.java
+++ b/base/src/main/java/bjc/utils/ioutils/format/directives/GeneralNumberDirective.java
@@ -38,10 +38,10 @@ public abstract class GeneralNumberDirective implements Directive {
*/
int commaInterval = 0;
char commaChar = ',';
- if (params.length() >= (argidx + 3)) {
- commaChar = params.getCharDefault((argidx + 3), "comma character", 'R', ' ');
- }
if (params.length() >= (argidx + 4)) {
+ commaChar = params.getCharDefault((argidx + 3), "comma character", 'R', ',');
+ }
+ if (params.length() >= (argidx + 5)) {
commaInterval = params.getIntDefault((argidx + 4), "comma interval", 'R', 0);
}
diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/IterationDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/IterationDirective.java
index 97e392e..2ce6309 100644
--- a/base/src/main/java/bjc/utils/ioutils/format/directives/IterationDirective.java
+++ b/base/src/main/java/bjc/utils/ioutils/format/directives/IterationDirective.java
@@ -36,18 +36,18 @@ public class IterationDirective implements Directive {
dirMatcher.appendReplacement(condBody, "");
if (dirName.equals("}")) {
- /* End the iteration. */
break;
+ } else {
+ /* Not a special directive. */
+ condBody.append(dirMatcher.group());
}
-
- /* Not a special directive. */
- condBody.append(dirMatcher.group());
}
}
String frmt = condBody.toString();
Object iter = item;
+ // System.err.printf("Iteration format \"%s\" (iter %s)\n", frmt, item);
if (frmt.equals("")) {
/* Grab an argument. */
if (!(item instanceof String)) {
@@ -86,25 +86,29 @@ public class IterationDirective implements Directive {
Tape<Object> nParams = new SingleTape<>(nitr);
try {
- fmt.doFormatString(frmt, rw, nParams);
+ fmt.doFormatString(frmt, rw, nParams, false);
} catch (EscapeException eex) {
if (eex.endIteration) {
- if (tParams.atEnd()) throw eex;
+ if (tParams.atEnd()) {
+ throw eex;
+ }
}
}
- iter = tParams.right();
+ tParams.right();
+ iter = tParams.item();
} while (tParams.position() < tParams.size());
} catch (EscapeException eex) {
}
} else if (mods.atMod) {
try {
- while (tParams.position() < tParams.size()) {
- if (numItr > maxItr) break;
- numItr += 1;
+ while (!tParams.atEnd()) {
+ // System.err.printf("Iterating with format \"%s\"\n", frmt);
+ if (numItr > maxItr) break;
+ numItr += 1;
- fmt.doFormatString(frmt, rw, tParams);
- }
+ fmt.doFormatString(frmt, rw, tParams, false);
+ }
} catch (EscapeException eex) {
if (eex.endIteration)
throw new UnsupportedOperationException("Colon mod not allowed on escape marker without colon mod on iteration");
@@ -133,13 +137,12 @@ public class IterationDirective implements Directive {
Tape<Object> nParams = new SingleTape<>(nitr);
try {
- fmt.doFormatString(frmt, rw, nParams);
+ fmt.doFormatString(frmt, rw, nParams, false);
} catch (EscapeException eex) {
if(eex.endIteration && !itr.hasNext()) throw eex;
}
}
- }
- catch (EscapeException eex) {
+ } catch (EscapeException eex) {
}
} else {
if (!(item instanceof Iterable<?>)) {
@@ -149,16 +152,13 @@ public class IterationDirective implements Directive {
try {
@SuppressWarnings("unchecked")
Iterable<Object> itr = (Iterable<Object>) item;
-
Tape<Object> nParams = new SingleTape<>(itr);
- while (nParams.position() < nParams.size()) {
- if (numItr > maxItr)
- break;
+ while (!nParams.atEnd()) {
+ if (numItr > maxItr) break;
numItr += 1;
- fmt.doFormatString(frmt, rw, nParams);
-
+ fmt.doFormatString(frmt, rw, nParams, false);
}
} catch (EscapeException eex) {
if (eex.endIteration)
diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/NumberDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/NumberDirective.java
index 763665d..88b3e7e 100644
--- a/base/src/main/java/bjc/utils/ioutils/format/directives/NumberDirective.java
+++ b/base/src/main/java/bjc/utils/ioutils/format/directives/NumberDirective.java
@@ -26,21 +26,25 @@ public class NumberDirective extends GeneralNumberDirective {
* @param radix
* The radix of the number to use.
*/
- public NumberDirective(int argidx, int radix) {
+ public NumberDirective(int argidx, int radix, char directive) {
this.argidx = argidx;
this.radix = radix;
+
+ this.directive = directive;
}
private int argidx;
private int radix;
+ private char directive;
+
@Override
public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters params, Tape<Object> tParams,
Matcher dirMatcher, CLFormatter fmt) throws IOException {
- CLFormatter.checkItem(item, 'B');
+ CLFormatter.checkItem(item, directive);
if (!(item instanceof Number)) {
- throw new IllegalFormatConversionException('B', item.getClass());
+ throw new IllegalFormatConversionException(directive, item.getClass());
}
long val = ((Number) item).longValue();
diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/RecursiveDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/RecursiveDirective.java
new file mode 100644
index 0000000..44a25ad
--- /dev/null
+++ b/base/src/main/java/bjc/utils/ioutils/format/directives/RecursiveDirective.java
@@ -0,0 +1,49 @@
+package bjc.utils.ioutils.format.directives;
+
+import bjc.utils.esodata.SingleTape;
+import bjc.utils.esodata.Tape;
+import bjc.utils.ioutils.format.*;
+import bjc.utils.ioutils.ReportWriter;
+import java.util.IllegalFormatConversionException;
+
+import java.io.IOException;
+import java.util.regex.Matcher;
+
+public class RecursiveDirective implements Directive {
+ public void format(ReportWriter rw, Object arg, CLModifiers mods, CLParameters params, Tape<Object> tParams,
+ Matcher dirMatcher, CLFormatter fmt) throws IOException {
+ tParams.right();
+
+ CLFormatter.checkItem(arg, '?');
+
+ if (mods.atMod) {
+ if (!(arg instanceof String))
+ throw new IllegalFormatConversionException('?', arg.getClass());
+
+ try {
+ fmt.doFormatString((String)arg, rw, tParams, true);
+ } catch (EscapeException eex) {
+ if (eex.endIteration)
+ throw new UnsupportedOperationException("Colon mod not allowed on escape marker without colon mod on iteration");
+ }
+ } else {
+ if (tParams.atEnd())
+ throw new IllegalArgumentException("? directive requires two format parameters");
+
+ Object o = tParams.item();
+ tParams.right();
+
+ if (!(o instanceof Iterable))
+ throw new IllegalFormatConversionException('?', o.getClass());
+
+ Iterable<Object> itb = (Iterable<Object>)o;
+ Tape<Object> newParams = new SingleTape<>(itb);
+
+ try {
+ fmt.doFormatString((String)arg, rw, newParams, true);
+ } catch (EscapeException eex) {
+ throw new UnsupportedOperationException("Colon mod not allowed on escape marker without colon mod on iteration");
+ }
+ }
+ }
+}
diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/TabulateDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/TabulateDirective.java
index 7775904..d9136f2 100644
--- a/base/src/main/java/bjc/utils/ioutils/format/directives/TabulateDirective.java
+++ b/base/src/main/java/bjc/utils/ioutils/format/directives/TabulateDirective.java
@@ -14,7 +14,7 @@ public class TabulateDirective implements Directive {
// Unsupported feature.
//
// I can't really make out what this is supposed to do from the
- // documentation, but I suspect that it depends of font glyph
+ // documentation, but I suspect that it depends on font glyph
// size, not character positions
if (mods.colonMod) {
throw new UnsupportedOperationException("Colon mod is not supported for T directive");
diff --git a/base/src/main/java/bjc/utils/math/NumberUtils.java b/base/src/main/java/bjc/utils/math/NumberUtils.java
index 53ab211..5c8ec7e 100644
--- a/base/src/main/java/bjc/utils/math/NumberUtils.java
+++ b/base/src/main/java/bjc/utils/math/NumberUtils.java
@@ -400,7 +400,7 @@ public class NumberUtils {
boolean isNeg = false;
long currVal = val;
if(currVal < 0) {
- isNeg = true;
+ isNeg = true;
currVal *= -1;
}
@@ -416,12 +416,11 @@ public class NumberUtils {
work.append(radixChars[radDigit]);
currVal = currVal / radix;
- if(commaInterval != 0 && valCounter % commaInterval == 0) work.append(commaChar);
+ if(commaInterval != 0 && valCounter % commaInterval == 0 && currVal != 0) work.append(commaChar);
}
}
- if(isNeg)
- work.append("-");
+ if(isNeg) work.append("-");
else if(signed) work.append("+");
work.reverse();
@@ -437,8 +436,21 @@ public class NumberUtils {
StringBuilder pad = new StringBuilder();
if(work.length() < mincols) {
+ int padCount = 0;
for(int i = work.length(); i < mincols; i++) {
- pad.append(padchar);
+ // @NOTE 9/6/18 :CommaPad
+ //
+ // I have no idea if this is the intended
+ // behavior, or if something is wrong with the
+ // example case in the menu
+ // if (commaInterval != 0 && padCount != 0) {
+ // if (Character.isDigit(padchar) && padCount % commaInterval == 0)
+ // pad.append(commaChar);
+ // else
+ pad.append(padchar);
+ // }
+
+ padCount++;
}
}
diff --git a/base/src/test/java/bjc/utils/test/ioutils/CLFormatterTest.java b/base/src/test/java/bjc/utils/test/ioutils/CLFormatterTest.java
index 1e80dcd..08dfe78 100644
--- a/base/src/test/java/bjc/utils/test/ioutils/CLFormatterTest.java
+++ b/base/src/test/java/bjc/utils/test/ioutils/CLFormatterTest.java
@@ -4,10 +4,13 @@ import static org.junit.Assert.*;
import java.io.IOException;
+import java.util.Arrays;
+
import bjc.utils.ioutils.format.CLFormatter;
import org.junit.Test;
+import static java.util.Arrays.asList;
/**
* Tests for CL format strings.
*
@@ -19,19 +22,100 @@ public class CLFormatterTest {
private CLFormatter fmt = new CLFormatter();
@Test
- public void testFormatStringLiteral() {
+ public void testLiteralString() {
+ // Print literal strings exactly
assertEquals("foo", format("foo"));
}
@Test
- public void testFormatStringD() {
+ public void testDecimalPrinting() {
+ // Test decimal printing
assertEquals("5", format("~D", 5));
assertEquals(" 5", format("~3D", 5));
assertEquals("005", format("~3,'0D", 5));
+ assertEquals("6|55|35", format("~,,'|,2:D", 0xFFFF));
+ }
+
+ @Test
+ public void testRadixPrinting() {
+ // Test radix printing
+ assertEquals("1 22", format("~3,,,' ,2:R", 17));
+ }
+
+ @Test
+ public void testBinaryPrinting() {
+ // Test binary printing
+ assertEquals("1101", format("~,,' ,4:B", 13));
+ assertEquals("1 0001", format("~,,' ,4:B", 17));
+ // @NOTE 9/6/18 :CommaPad
+ //
+ // I'm not sure how this is the expected behavior, unless the
+ // comma interval is enforced in the case of a digit padchar.
+ // assertEquals("0000 1101 0000 0101", format("~19,0,' ,4:B", 3333));
+ assertEquals("000001101 0000 0101", format("~19,0,' ,4:B", 3333));
+ }
+
+ @Test
+ public void testConditionalPrinting() {
+ // Test conditional printing
+ assertEquals("print length = 5", format("~@[print level = ~D~]~@[print length = ~D~]", null, 5));
+ }
+
+ @Test
+ public void testIterationPrinting() {
+ assertEquals("The winners are: fred harry jill.", format("The winners are:~{ ~S~}.",
+ asList("fred", "harry", "jill")));
+ assertEquals("Pairs: (1, 1) (2, 2) (3, 3).", format("Pairs:~{ (~S, ~S)~}.",
+ asList(1, 1, 2, 2, 3, 3)));
+
+ assertEquals("Pairs: (1, 1) (2, 2) (3, 3).", format("Pairs:~:{ (~S, ~S)~}.",
+ asList(asList(1, 1), asList(2, 2), asList(3, 3))));
+
+ assertEquals("Pairs: (1, 1) (2, 2) (3, 3).", format("Pairs:~@{ (~S, ~S)~}.",
+ 1, 1, 2, 2, 3, 3));
+
+ assertEquals("Pairs: (1, 1) (2, 2) (3, 3).", format("Pairs:~:@{ (~S, ~S)~}.",
+ asList(1, 1), asList(2, 2), asList(3, 3)));
}
- public void testFormatStringR() {
- assertEquals("3 dogs are here", format("~R dog~:*~[s are~; is~] here", 3, 3 == 1));
+ @Test
+ public void testRecursivePrinting() {
+ assertEquals("<Foo 5> 7", format("~? ~D", "<~A ~D>", asList("Foo", 5), 7));
+ assertEquals("<Foo 5> 7", format("~? ~D", "<~A ~D>", asList("Foo", 5, 14), 7));
+
+ assertEquals("<Foo 5> 7", format("~@? ~D", "<~A ~D>", "Foo", 5, 7));
+ assertEquals("<Foo 5> 14", format("~@? ~D", "<~A ~D>", "Foo", 5, 14, 7));
+ }
+
+ @Test
+ public void testEscapePrinting() {
+ assertEquals("Done.", format("Done.~^ ~D warning.~^ ~D error."));
+ assertEquals("Done. 3 warning.", format("Done.~^ ~D warning.~^ ~D error.", 3));
+ assertEquals("Done. 1 warning. 5 error.", format("Done.~^ ~D warning.~^ ~D error.", 1, 5));
+ }
+
+ @Test
+ public void testCapsPrinting() {
+ assertEquals("XIV xiv", format("~@R ~(~@R~)", 14, 14));
+ }
+
+ @Test
+ public void testListPrinting() {
+ // Test printing a list
+ // String fmtStr = "Items:~#[ none~; ~A~; ~A and ~A~:;~@{~#[~; and~] ~A~^,~}~].";
+ String fmtStr = "Items:~#[ none~; ~A~; ~A and ~A~:;~@{~#*[ ~A,~; and ~A~; ~A~]~}~].";
+
+ assertEquals("Items: none.", format(fmtStr));
+ assertEquals("Items: foo.", format(fmtStr, "foo"));
+ assertEquals("Items: foo and bar.", format(fmtStr, "foo", "bar"));
+ assertEquals("Items: foo, bar and baz.", format(fmtStr, "foo", "bar", "baz"));
+ assertEquals("Items: foo, bar, baz and quux.", format(fmtStr, "foo", "bar", "baz", "quux"));
+ }
+
+ @Test
+ public void testRandomCases() {
+ // Random test cases
+ assertEquals("3 dogs are here", format("~D dog~:[s are~; is~] here", 3, 3 == 1));
}
private String format(String str, Object... params) {