package bluej.stride.framedjava.slots;
import static bluej.stride.framedjava.slots.Operator.Precedence.DOT;
import static bluej.stride.framedjava.slots.Operator.Precedence.HIGH;
import static bluej.stride.framedjava.slots.Operator.Precedence.LOW;
import static bluej.stride.framedjava.slots.Operator.Precedence.MEDIUM;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import bluej.stride.framedjava.slots.Operator.Precedence;
public class TestExpressionSlot
{
@Rule
public TestRule runOnFXThreadRule = new TestRule() {
boolean initialised = false;
@Override public Statement apply(Statement base, Description d)
{
if (!initialised)
{
new JFXPanel();
initialised = true;
}
return new Statement() {
@Override public void evaluate() throws Throwable
{
CompletableFuture<Throwable> thrown = new CompletableFuture<>();
Platform.runLater(() -> {
try {
base.evaluate();
thrown.complete(null);
} catch( Throwable throwable ) {
thrown.complete(throwable);
}
});
Throwable t = thrown.get();
if (t != null)
throw t;
}
};
}
};
private static class CompareWrapper
{
private String s;
public CompareWrapper(String s)
{
this.s = s;
}
@Override public boolean equals(Object o)
{
if (o instanceof CompareWrapper) return s.equals(((CompareWrapper)o).s); else{ return false;
}
}
@Override public String toString()
{
return s;
}
}
private void testInsert(String insertion, String result)
{
InfixExpression e = StructuredSlot.testingModification(token -> new InfixExpression(null, null, token));
CaretPos p = e.testingInsert(insertion, '\0');
assertEquals(insertion + " -> " + result, new CompareWrapper(result), new CompareWrapper(e.testingGetState(p)));
for (int i = 0; i <= insertion.length(); i++)
{
CaretPos caretPos = e.stringPosToCaretPos(i, false);
if (caretPos != null)
assertEquals("String pos -> caret pos -> string pos", i, e.caretPosToStringPos(caretPos, false));
}
String noPos = result.replace("$", "");
for (int split = 1; split < insertion.length(); split++)
{
e = StructuredSlot.testingModification(token -> new InfixExpression(null, null, token));
e.testingInsert(e.testingInsert(insertion.substring(0, split), '\0'), insertion.substring(split));
assertEquals(insertion + " -> " + noPos, new CompareWrapper(noPos), new CompareWrapper(e.testingGetState(null)));
}
}
private void testMultiInsert(String multiInsertion, String firstResult, String secondResult)
{
InfixExpression e = StructuredSlot.testingModification(token -> new InfixExpression(null, null, token));
int startNest = multiInsertion.indexOf('{');
int endNest = multiInsertion.indexOf('}', startNest);
if (startNest == -1 || endNest == -1)
throw new IllegalStateException();
String before = multiInsertion.substring(0, startNest);
String nest = multiInsertion.substring(startNest + 1, endNest);
String after = multiInsertion.substring(endNest + 1);
CaretPos p = e.testingInsert(before + "$" + after, '$');
assertEquals(multiInsertion + " -> " + firstResult, new CompareWrapper(firstResult), new CompareWrapper(e.testingGetState(p)));
p = e.testingInsert(p, nest);
assertEquals(multiInsertion + " -> " + secondResult, new CompareWrapper(secondResult), new CompareWrapper(e.testingGetState(p)));
}
private void testInsertExisting(String start, String insertion, String result)
{
InfixExpression e = StructuredSlot.testingModification(token -> new InfixExpression(null, null, token));
CaretPos p = e.testingInsert(start, '$');
p = e.testingInsert(p, insertion);
assertEquals(start + " then \"" + insertion + "\" -> " + result, new CompareWrapper(result), new CompareWrapper(e.testingGetState(p)));
}
private void testBackspace(String insertionInclBackspace, String result)
{
testBackspace(insertionInclBackspace, result, true, true);
}
private void testBackspace(String insertionInclBackspace, String result, boolean testBackspace, boolean testDelete)
{
if (testBackspace)
{
InfixExpression e = StructuredSlot.testingModification(token -> new InfixExpression(null, null, token));
CaretPos p = e.testingInsert(insertionInclBackspace, '\b');
e.positionCaret(p);
p = e.testingBackspace(p);
assertEquals(insertionInclBackspace.replace("\b", "\\b") + " -> " + result, new CompareWrapper(result), new CompareWrapper(e.testingGetState(p)));
}
int index = insertionInclBackspace.indexOf('\b');
if (index > 0 && testDelete)
{
String before = insertionInclBackspace.substring(0, index);
String after = insertionInclBackspace.substring(index + 1);
after = before.substring(before.length() - 1) + after;
before = before.substring(0, before.length() - 1);
String joined = before + "\b" + after;
InfixExpression e = StructuredSlot.testingModification(token -> new InfixExpression(null, null, token));
CaretPos p = e.testingInsert(joined, '\b');
e.positionCaret(p);
p = e.testingDelete(p);
assertEquals(joined.replace("\b", "\\DEL") + " -> " + result, new CompareWrapper(result), new CompareWrapper(e.testingGetState(p)));
}
}
private void testDeleteSelection(String src, String result)
{
InfixExpression e = StructuredSlot.testingModification(token -> new InfixExpression(null, null, token));
int startNest = src.indexOf('{');
int endNest = src.indexOf('}', startNest);
if (startNest == -1 || endNest == -1)
throw new IllegalStateException();
String before = src.substring(0, startNest);
String nest = src.substring(startNest + 1, endNest);
String after = src.substring(endNest + 1);
CaretPos start = e.testingInsert(before, '\0');
CaretPos end = e.testingInsert(start, nest);
e.testingInsert(end, after);
CaretPos p = e.testingDeleteSelection(start, end);
assertEquals(src + " -> " + result, new CompareWrapper(result), new CompareWrapper(e.testingGetState(p)));
}
private void testSelectionInsert(char c, String src, String result)
{
InfixExpression e = StructuredSlot.testingModification(token -> new InfixExpression(null, null, token));
int startNest = src.indexOf('{');
int endNest = src.indexOf('}', startNest);
if (startNest == -1 || endNest == -1)
throw new IllegalStateException();
String before = src.substring(0, startNest);
String nest = src.substring(startNest + 1, endNest);
String after = src.substring(endNest + 1);
CaretPos start = e.testingInsert(before, '\0');
CaretPos end = e.testingInsert(start, nest);
e.testingInsert(end, after);
CaretPos p = e.testingInsertWithSelection(start, end, c);
assertEquals(src + " -> " + result, new CompareWrapper(result), new CompareWrapper(e.testingGetState(p)));
}
private static class CPM
{
private int stringPos;
private CaretPos caretPos;
public CPM(int stringPos, CaretPos caretPos)
{
this.stringPos = stringPos;
this.caretPos = caretPos;
}
@Override
public String toString()
{
return "CPM [stringPos=" + stringPos + ", caretPos=" + caretPos
+ "]";
}
}
private void testCaretPosMap(String src, String result, CPM... cpms)
{
testCaretPosMap(src, result, null, cpms);
}
private void testCaretPosMap(String src, String result, String java, CPM... cpms)
{
InfixExpression e = StructuredSlot.testingModification(token -> new InfixExpression(null, null, token));
CaretPos p = e.testingInsert(src, '\0');
assertEquals(src + " -> " + result, new CompareWrapper(result), new CompareWrapper(e.testingGetState(p).replace("$", "")));
for (CPM cpm : cpms)
{
assertEquals("Caret to String " + cpm.toString() + " for " + src, cpm.stringPos, e.caretPosToStringPos(cpm.caretPos, java != null));
assertEquals("String to Caret " + cpm.toString() + " for " + src, cpm.caretPos, e.stringPosToCaretPos(cpm.stringPos, java != null));
}
if (java != null)
{
assertEquals("To Java, from: " + src, java, e.getJavaCode());
}
}
@Test
public void testOperators()
{
testInsert("aa", "{aa$}");
testInsert("a+b", "{a}+{b$}");
testInsert("a+b-c", "{a}+{b}-{c$}");
testInsert("1++1", "{1}+{+1$}");
testInsert("1<2&&3<=4&&5==6+8", "{1}<{2}&&{3}<={4}&&{5}=={6}+{8$}");
testInsert("false!=!false", "{false}!={}!{false$}");
testInsert("false!=!!!false", "{false}!={}!{}!{}!{false$}");
testInsert("5==-6", "{5}=={-6$}");
testInsert("5==--6", "{5}=={}-{-6$}");
testInsert("5==----6", "{5}=={}-{}-{}-{-6$}");
testInsert("a.b", "{a}.{b$}");
testInsert("a..b", "{a}..{b$}");
testInsert("y-1", "{y}-{1$}");
testInsert("getY()*1", "{getY}_({})_{}*{1$}");
testInsert("getY()-1", "{getY}_({})_{}-{1$}");
testInsert("getY()+-1", "{getY}_({})_{}+{-1$}");
testInsert("s.length()..10", "{s}.{length}_({})_{}..{10$}");
testInsert("s.length()", "{s}.{length}_({})_{$}");
testInsert("s.length().", "{s}.{length}_({})_{}.{$}");
testInsert("s.length()..", "{s}.{length}_({})_{}..{$}");
testInsert("s.length()..1", "{s}.{length}_({})_{}..{1$}");
}
@Test
public void testNew()
{
testInsert("newton", "{newton$}");
testInsert("new ton", "{}new {ton$}");
}
@Test
public void testStrings()
{
testInsert("\"hello\"", "{}_\"hello\"_{$}");
testInsert("\"hello", "{}_\"hello$\"_{}");
testInsert("\"hello\"+\"world\"", "{}_\"hello\"_{}+{}_\"world\"_{$}");
testInsert("\"hello\"+\"world\"+(5*6)", "{}_\"hello\"_{}+{}_\"world\"_{}+{}_({5}*{6})_{$}");
testInsert("\"\\\"\"", "{}_\"\\\"\"_{$}");
testInsert("\"\\\'\"", "{}_\"\\\'\"_{$}");
testInsert("\"'\"", "{}_\"'\"_{$}");
testMultiInsert("abc{\"}def", "{abc$def}", "{abc}_\"$\"_{def}");
testMultiInsert("abc{\"}", "{abc$}", "{abc}_\"$\"_{}");
testMultiInsert("{\"}def", "{$def}", "{}_\"$\"_{def}");
testMultiInsert("abc{\"}.def", "{abc$}.{def}", "{abc}_\"$\"_{}.{def}");
testMultiInsert("abc{\"}*def", "{abc$}*{def}", "{abc}_\"$\"_{}*{def}");
testMultiInsert("abc{\"}def()", "{abc$def}_({})_{}", "{abc}_\"$\"_{def}_({})_{}");
testMultiInsert("abc{\"}()", "{abc$}_({})_{}", "{abc}_\"$\"_{}_({})_{}");
testInsertExisting("$\"b\"", "\"a", "{}_\"a$\"_{}_\"b\"_{}");
testInsertExisting("$\"b\"", "\"a\"", "{}_\"a\"_{$}_\"b\"_{}");
testInsertExisting("\"a\"$", "\"b", "{}_\"a\"_{}_\"b$\"_{}");
testInsert("'a'", "{}_'a'_{$}");
testInsert("'a", "{}_'a$'_{}");
testInsert("'\\''", "{}_'\\''_{$}");
testInsert("'\\\"'", "{}_'\\\"'_{$}");
testInsert("'\"'", "{}_'\"'_{$}");
testInsert("c == '\\\\' || c == '\"' || c == '\\''",
"{c}=={}_\'\\\\\'_{}||{c}=={}_'\"'_{}||{c}=={}_'\\''_{$}");
testBackspace("\"a\bb\"", "{}_\"$b\"_{}");
testBackspace("\"\bab\"", "{$ab}");
testBackspace("\"ab\b\"", "{}_\"a$\"_{}");
testBackspace("\"ab\"\b", "{ab$}");
}
@Test
public void testBrackets()
{
testInsert("a+(b-c)", "{a}+{}_({b}-{c})_{$}");
testInsert("a+(b-(c*d))", "{a}+{}_({b}-{}_({c}*{d})_{})_{$}");
testInsert("(a+b", "{}_({a}+{b$})_{}");
testInsert("(((", "{}_({}_({}_({$})_{})_{})_{}");
testInsert("((()", "{}_({}_({}_({})_{$})_{})_{}");
testInsert("((())", "{}_({}_({}_({})_{})_{$})_{}");
testInsert("((()))", "{}_({}_({}_({})_{})_{})_{$}");
testInsert("(a+(b*c)+d)", "{}_({a}+{}_({b}*{c})_{}+{d})_{$}");
testMultiInsert("({(MyWorld)}getWorld()).getWidth()",
"{}_({$getWorld}_({})_{})_{}.{getWidth}_({})_{}",
"{}_({}_({MyWorld})_{$getWorld}_({})_{})_{}.{getWidth}_({})_{}");
testInsert("a(bc)d", "{a}_({bc})_{d$}");
}
@Test
public void testBackspace()
{
testBackspace("\bxyz", "{$xyz}");
testBackspace("x\byz", "{$yz}");
testBackspace("xy\bz", "{x$z}");
testBackspace("xyz\b", "{xy$}");
testBackspace("xy\b+ab", "{x$}+{ab}");
testBackspace("xy+\bab", "{xy$ab}");
testBackspace("xy+a\bb", "{xy}+{$b}");
testBackspace("new t\bon", "{}new {$on}");
testBackspace("new \bton", "{new$ton}", true, false);
testBackspace("n\bew ton", "{$ewton}", false, true);
testBackspace("move(\b)", "{move$}");
testBackspace("(\b)", "{$}");
}
@Test
public void testFloating()
{
testInsert("1.0", "{1.0$}");
testInsert("10.20", "{10.20$}");
testInsert("a.0", "{a}.{0$}");
testInsert("1.a", "{1.a$}");
testInsert("x1.a", "{x1}.{a$}");
testInsert("+1", "{+1$}");
testInsert("+1.0", "{+1.0$}");
testInsert("+1.0e5", "{+1.0e5$}");
testInsert("+1.0e", "{+1.0e$}");
testInsert("+1.0e+5", "{+1.0e+5$}");
testInsert("+1.0e+5+6", "{+1.0e+5}+{6$}");
testInsert("+1.0p+5", "{+1.0p}+{5$}");
testInsert("3+1", "{3}+{1$}");
testInsert("3+1.0", "{3}+{1.0$}");
testInsert("3+1.0e5", "{3}+{1.0e5$}");
testInsert("3+1.0e+5", "{3}+{1.0e+5$}");
testInsert("3+1.0e+5+6", "{3}+{1.0e+5}+{6$}");
testInsert("3+1.0p+5", "{3}+{1.0p}+{5$}");
testInsert("+1+2+3", "{+1}+{2}+{3$}");
testInsert("+1++2", "{+1}+{+2$}");
testInsert("+1++2+3", "{+1}+{+2}+{3$}");
testInsert("+1++2++3", "{+1}+{+2}+{+3$}");
testInsert("++1++2++3", "{}+{+1}+{+2}+{+3$}");
testMultiInsert("+{1}", "{}+{$}", "{+1$}");
testMultiInsert("+{+1}", "{}+{$}", "{}+{+1$}");
testMultiInsert("1++{2}", "{1}+{}+{$}", "{1}+{+2$}");
testInsert("1e6", "{1e6$}");
testInsert("1e-6", "{1e-6$}");
testInsert("10e20", "{10e20$}");
testInsert("10e+20", "{10e+20$}");
testInsert("10e-20", "{10e-20$}");
testInsert("1.0.3", "{1.0}.{3$}");
testInsert("1.0.3.4", "{1.0}.{3.4$}");
testInsert("1.0.x3.4", "{1.0}.{x3}.{4$}");
testBackspace("+\b1.0e-5", "{$1.0e-5}", false, true);
testBackspace("+1\b.0e-5", "{}+{$}.{0e-5}", true, false);
testBackspace("+1.\b0e-5", "{+1$0e-5}");
testBackspace("+1.0\be-5", "{+1.$e-5}");
testBackspace("+1.0e\b-5", "{+1.0$}-{5}");
testBackspace("+1.0e-\b5", "{+1.0e$5}");
testBackspace("+1.0e-5\b", "{+1.0e-$}");
testMultiInsert("{1}e-6", "{$e}-{6}", "{1$e-6}");
testMultiInsert("1{e}-6", "{1$}-{6}", "{1e$-6}");
testMultiInsert("1e{-}6", "{1e$6}", "{1e-$6}");
testMultiInsert("1e-{6}", "{1e-$}", "{1e-6$}");
testMultiInsert("{x}1e-6", "{$1e-6}", "{x$1e}-{6}");
testMultiInsert("1{x}e-6", "{1$e-6}", "{1x$e}-{6}");
testMultiInsert("1e{x}-6", "{1e$-6}", "{1ex$}-{6}");
testMultiInsert("1e-{x}6", "{1e-$6}", "{1e-x$6}");
testInsert("1.0", "{1.0$}");
testInsert("1..0", "{1}..{0$}");
testBackspace("1..\b0", "{1.$0}", true, false);
testBackspace("1.\b.0", "{1$.0}", false, true);
testBackspace("a..\bc", "{a}.{$c}", true, false);
testBackspace("a.\b.c", "{a$}.{c}", false, true);
}
@Test
public void testDeleteBracket()
{
testInsert("a+(b*c)", "{a}+{}_({b}*{c})_{$}");
testBackspace("a+(b*c)\b", "{a}+{b}*{c$}");
testBackspace("a+(\bb*c)", "{a}+{$b}*{c}");
testInsert("((MyWorld)getWorld()).getWidth()",
"{}_({}_({MyWorld})_{getWorld}_({})_{})_{}.{getWidth}_({})_{$}");
testBackspace("((MyWorld)getWorld()).getWidth()\b",
"{}_({}_({MyWorld})_{getWorld}_({})_{})_{}.{getWidth$}");
testBackspace("((MyWorld)getWorld()).\bgetWidth()",
"{}_({}_({MyWorld})_{getWorld}_({})_{})_{$getWidth}_({})_{}");
testBackspace("((MyWorld)getWorld())\b.getWidth()",
"{}_({MyWorld})_{getWorld}_({})_{$}.{getWidth}_({})_{}");
testBackspace("((MyWorld)getWorld(\b)).getWidth()",
"{}_({}_({MyWorld})_{getWorld$})_{}.{getWidth}_({})_{}");
testBackspace("((MyWorld)\bgetWorld()).getWidth()",
"{}_({MyWorld$getWorld}_({})_{})_{}.{getWidth}_({})_{}");
testBackspace("((\bMyWorld)getWorld()).getWidth()",
"{}_({$MyWorldgetWorld}_({})_{})_{}.{getWidth}_({})_{}");
testBackspace("(\b(MyWorld)getWorld()).getWidth()",
"{$}_({MyWorld})_{getWorld}_({})_{}.{getWidth}_({})_{}");
}
@Test
public void testDeleteSelection()
{
testDeleteSelection("a{bc}d", "{a$d}");
testDeleteSelection("a{bc}", "{a$}");
testInsert("a+(b*c)-d", "{a}+{}_({b}*{c})_{}-{d$}");
testDeleteSelection("{a}+(b*c)-d", "{$}+{}_({b}*{c})_{}-{d}");
testDeleteSelection("{a+}(b*c)-d", "{$}_({b}*{c})_{}-{d}");
testDeleteSelection("a+{(b*c)}-d", "{a}+{$}-{d}");
testDeleteSelection("a{+(b*c)}-d", "{a$}-{d}");
testDeleteSelection("a{+(b*c)-}d", "{a$d}");
testDeleteSelection("a+({b*c})-d", "{a}+{}_({$})_{}-{d}");
testInsert("s+\"hello\"+t", "{s}+{}_\"hello\"_{}+{t$}");
testDeleteSelection("s+\"h{ell}o\"+t", "{s}+{}_\"h$o\"_{}+{t}");
}
@Test
public void testSelectionOperation()
{
testSelectionInsert('(', "a{bc}d", "{a}_({bc})_{$d}");
testSelectionInsert('(', "a{b+c}d", "{a}_({b}+{c})_{$d}");
testSelectionInsert('(', "a{b+}d", "{a}_({b}+{})_{$d}");
testSelectionInsert('(', "a{b+}", "{a}_({b}+{})_{$}");
testSelectionInsert('(', "a{b++}", "{a}_({b}+{}+{})_{$}");
testSelectionInsert('(', "a{b+}+", "{a}_({b}+{})_{$}+{}");
testSelectionInsert('\"', "a{bc}d", "{a}_\"bc\"_{$d}");
testSelectionInsert('\'', "a{bc}d", "{a}_\'bc\'_{$d}");
testInsert("ab+cd", "{ab}+{cd$}");
testSelectionInsert('\"', "a{b+c}d", "{a}_\"b+c\"_{$d}");
testSelectionInsert('\'', "a{b+c}d", "{a}_\'b+c\'_{$d}");
testInsert("a+\"hello\"+c", "{a}+{}_\"hello\"_{}+{c$}");
testSelectionInsert('\"', "a+\"h{ell}o\"+c", "{a}+{}_\"hell$o\"_{}+{c}");
testSelectionInsert('\"', "a+\"hello\"{+c}", "{a}+{}_\"hello\"_{}_\"+c\"_{$}");
}
private CaretPos makeCaretPos(int... xs)
{
CaretPos p = null;
for (int i = xs.length - 1; i >= 0; i--)
{
p = new CaretPos(xs[i], p);
}
return p;
}
@Test
public void testCaretPosMap()
{
testCaretPosMap("abc", "{abc}",
new CPM(0, makeCaretPos(0, 0)),
new CPM(1, makeCaretPos(0, 1)),
new CPM(2, makeCaretPos(0, 2))
);
testCaretPosMap("a+b+c", "{a}+{b}+{c}",
new CPM(0, makeCaretPos(0, 0)),
new CPM(1, makeCaretPos(0, 1)),
new CPM(2, makeCaretPos(1, 0)),
new CPM(3, makeCaretPos(1, 1)),
new CPM(4, makeCaretPos(2, 0)),
new CPM(5, makeCaretPos(2, 1))
);
testCaretPosMap("\"hi\"", "{}_\"hi\"_{}",
new CPM(0, makeCaretPos(0, 0)),
new CPM(1, makeCaretPos(1, 0)),
new CPM(2, makeCaretPos(1, 1)),
new CPM(3, makeCaretPos(1, 2)),
new CPM(4, makeCaretPos(2, 0))
);
testCaretPosMap("a*(b+\"cd\"+e)-(f)", "{a}*{}_({b}+{}_\"cd\"_{}+{e})_{}-{}_({f})_{}",
new CPM(0, makeCaretPos(0, 0)),
new CPM(1, makeCaretPos(0, 1)),
new CPM(2, makeCaretPos(1, 0)),
new CPM(3, makeCaretPos(2, 0, 0)),
new CPM(4, makeCaretPos(2, 0, 1)),
new CPM(5, makeCaretPos(2, 1, 0)),
new CPM(6, makeCaretPos(2, 2, 0)),
new CPM(7, makeCaretPos(2, 2, 1)),
new CPM(8, makeCaretPos(2, 2, 2)),
new CPM(9, makeCaretPos(2, 3, 0)),
new CPM(10, makeCaretPos(2, 4, 0)),
new CPM(11, makeCaretPos(2, 4, 1)),
new CPM(12, makeCaretPos(3, 0)),
new CPM(13, makeCaretPos(4, 0)),
new CPM(14, makeCaretPos(5, 0, 0)),
new CPM(15, makeCaretPos(5, 0, 1)),
new CPM(16, makeCaretPos(6, 0))
);
testCaretPosMap("gW().aO(a,b,c)", "{gW}_({})_{}.{aO}_({a},{b},{c})_{}",
new CPM(0, makeCaretPos(0, 0)),
new CPM(1, makeCaretPos(0, 1)),
new CPM(2, makeCaretPos(0, 2)),
new CPM(3, makeCaretPos(1, 0, 0)),
new CPM(4, makeCaretPos(2, 0)),
new CPM(5, makeCaretPos(3, 0)),
new CPM(6, makeCaretPos(3, 1)),
new CPM(7, makeCaretPos(3, 2)),
new CPM(8, makeCaretPos(4, 0, 0)),
new CPM(9, makeCaretPos(4, 0, 1)),
new CPM(10, makeCaretPos(4, 1, 0)),
new CPM(11, makeCaretPos(4, 1, 1)),
new CPM(12, makeCaretPos(4, 2, 0)),
new CPM(13, makeCaretPos(4, 2, 1)),
new CPM(14, makeCaretPos(5, 0))
);
testCaretPosMap("1+2", "{1}+{2}", "1 + 2",
new CPM(0, makeCaretPos(0, 0)),
new CPM(1, makeCaretPos(0, 1)),
new CPM(4, makeCaretPos(1, 0)),
new CPM(5, makeCaretPos(1, 1))
);
testCaretPosMap("1++2", "{1}+{+2}", "1 + +2",
new CPM(0, makeCaretPos(0, 0)),
new CPM(1, makeCaretPos(0, 1)),
new CPM(4, makeCaretPos(1, 0)),
new CPM(5, makeCaretPos(1, 1)),
new CPM(6, makeCaretPos(1, 2))
);
testCaretPosMap("a<:Crab", "{a}<:{Crab}",
new CPM(0, makeCaretPos(0, 0)),
new CPM(1, makeCaretPos(0, 1)),
new CPM(3, makeCaretPos(1, 0)),
new CPM(4, makeCaretPos(1, 1)),
new CPM(5, makeCaretPos(1, 2)),
new CPM(6, makeCaretPos(1, 3)),
new CPM(7, makeCaretPos(1, 4))
);
testCaretPosMap("a<:Crab", "{a}<:{Crab}", "a instanceof Crab",
new CPM(0, makeCaretPos(0, 0)),
new CPM(1, makeCaretPos(0, 1)),
new CPM(13, makeCaretPos(1, 0)),
new CPM(14, makeCaretPos(1, 1)),
new CPM(15, makeCaretPos(1, 2)),
new CPM(16, makeCaretPos(1, 3)),
new CPM(17, makeCaretPos(1, 4))
);
final int f = "lang.stride.Utility.makeRange".length();
testCaretPosMap("1..2", "{1}..{2}", "lang.stride.Utility.makeRange(1, 2)",
new CPM(f + 1, makeCaretPos(0, 0)),
new CPM(f + 2, makeCaretPos(0, 1)),
new CPM(f + 4, makeCaretPos(1, 0)),
new CPM(f + 5, makeCaretPos(1, 1))
);
testCaretPosMap("1,2..3+4,5,6..7..8", "{1},{2}..{3}+{4},{5},{6}..{7}..{8}",
"1, lang.stride.Utility.makeRange(2, 3 + 4), 5, lang.stride.Utility.makeRange(6, lang.stride.Utility.makeRange(7, 8))",
new CPM(0, makeCaretPos(0, 0)),
new CPM(1, makeCaretPos(0, 1)),
new CPM(3 + f + 1, makeCaretPos(1, 0)),
new CPM(3 + f + 2, makeCaretPos(1, 1)),
new CPM(3 + f + 4, makeCaretPos(2, 0)),
new CPM(3 + f + 5, makeCaretPos(2, 1)),
new CPM(3 + f + 8, makeCaretPos(3, 0)),
new CPM(3 + f + 9, makeCaretPos(3, 1)),
new CPM(3 + f + 12, makeCaretPos(4, 0)),
new CPM(3 + f + 13, makeCaretPos(4, 1)),
new CPM(3 + f + 15 + f + 1, makeCaretPos(5, 0)),
new CPM(3 + f + 15 + f + 2, makeCaretPos(5, 1)),
new CPM(3 + f + 15 + f + 4 + f + 1, makeCaretPos(6, 0)),
new CPM(3 + f + 15 + f + 4 + f + 2, makeCaretPos(6, 1)),
new CPM(3 + f + 15 + f + 4 + f + 4, makeCaretPos(7, 0)),
new CPM(3 + f + 15 + f + 4 + f + 5, makeCaretPos(7, 1))
);
testCaretPosMap("600+\"a\",40", "{600}+{}_\"a\"_{},{40}",
new CPM(0, makeCaretPos(0,0)),
new CPM(1, makeCaretPos(0,1)),
new CPM(2, makeCaretPos(0,2)),
new CPM(4, makeCaretPos(1,0)),
new CPM(5, makeCaretPos(2,0)),
new CPM(6, makeCaretPos(2,1)),
new CPM(7, makeCaretPos(3,0)),
new CPM(8, makeCaretPos(4,0)),
new CPM(9, makeCaretPos(4,1))
);
testCaretPosMap("600+\"a\",40", "{600}+{}_\"a\"_{},{40}", "600 + \"a\", 40",
new CPM(0, makeCaretPos(0,0)),
new CPM(1, makeCaretPos(0,1)),
new CPM(2, makeCaretPos(0,2)),
new CPM(6, makeCaretPos(1,0)),
new CPM(7, makeCaretPos(2,0)),
new CPM(8, makeCaretPos(2,1)),
new CPM(9, makeCaretPos(3,0)),
new CPM(11, makeCaretPos(4,0)),
new CPM(12, makeCaretPos(4,1))
);
}
private void testPrecedence(String src, Precedence... precedences)
{
src = src.replaceAll(" +", "").replaceAll("[a-zA-Z0-9]+", "x");
ArrayList<Boolean> unary = new ArrayList<>();
ArrayList<Operator> ops = new ArrayList<>();
boolean lastWasOperand = false;
while (src.length() > 0)
{
if (src.startsWith("x"))
{
lastWasOperand = true;
src = src.substring(1);
}
else if (InfixExpression.isExpressionOperator(src.substring(0, 2)))
{
ops.add(new Operator(src.substring(0, 2), null));
src = src.substring(2);
unary.add(!lastWasOperand);
lastWasOperand = false;
}
else if (InfixExpression.isExpressionOperator(src.substring(0, 1)))
{
ops.add(new Operator(src.substring(0, 1), null));
src = src.substring(1);
unary.add(!lastWasOperand);
lastWasOperand = false;
}
else if (src.startsWith("()"))
{
src = src.substring(2);
ops.add(null);
ops.add(null);
unary.add(false);
unary.add(false);
lastWasOperand = true;
}
else
{
throw new IllegalArgumentException("Unknown operator: " + src);
}
}
if (ops.size() != precedences.length)
throw new IllegalArgumentException("Incorrect number of precedences: " + ops.size() + " vs " + precedences.length);
InfixExpression.calculatePrecedences(ops, unary);
for (int i = 0; i < ops.size(); i++)
{
assertEquals("Operator precedence (" + (ops.get(i) == null ? "_" : ops.get(i).get()) + "), index" + i, precedences[i], ops.get(i) == null ? null : ops.get(i).getPrecedence());
}
}
@Test
public void testPrecedence()
{
testPrecedence("1+2", HIGH);
testPrecedence("1+2-3", HIGH, HIGH);
testPrecedence("1+2*3", MEDIUM, HIGH);
testPrecedence("1+2*3-4", MEDIUM, HIGH, MEDIUM);
testPrecedence("1*2+3*4", HIGH, MEDIUM, HIGH);
testPrecedence("1++2", MEDIUM, HIGH);
testPrecedence("3*-4", MEDIUM, HIGH);
testPrecedence("-3+4", HIGH, MEDIUM);
testPrecedence("1 < 2 && 3 <= 4 && 5 == 6", HIGH, MEDIUM, HIGH, MEDIUM, HIGH);
testPrecedence("1 < 2 && 3 <= 4 && 5 == 6 + 8", HIGH, LOW, HIGH, LOW, MEDIUM, HIGH);
testPrecedence("x+6*4", MEDIUM, HIGH);
testPrecedence("getX()+6", null, null, HIGH);
testPrecedence("getX()+6*4", null, null, MEDIUM, HIGH);
testPrecedence("a+b+c", HIGH, HIGH);
testPrecedence("a.b.c", DOT, DOT);
testPrecedence("a.b+c", DOT, HIGH);
testPrecedence("a+b.c", HIGH, DOT);
testPrecedence("a+b.c*d.e-f", MEDIUM, DOT, HIGH, DOT, MEDIUM);
}
@Test
public void testDot()
{
testInsert(".", "{}.{$}");
testInsert("0.", "{0.$}");
testInsert("a.", "{a}.{$}");
testInsert("foo()", "{foo}_({})_{$}");
testInsert("foo().bar()", "{foo}_({})_{}.{bar}_({})_{$}");
testInsert("foo+().", "{foo}+{}_({})_{}.{$}");
testMultiInsert("foo(){.}a", "{foo}_({})_{$a}", "{foo}_({})_{}.{$a}");
testInsert("foo()0.", "{foo}_({})_{0.$}");
testBackspace("foo()0\b.", "{foo}_({})_{$}.{}");
}
@Test
public void testOvertype()
{
testInsertExisting("$()", "move", "{move$}_({})_{}");
testInsertExisting("move$()", "(", "{move}_({$})_{}");
testInsertExisting("a$+z", "+", "{a}+{$}+{z}");
testInsertExisting("a$+z", "+b", "{a}+{b$}+{z}");
testInsertExisting("a$,", ",", "{a},{$}");
testInsertExisting("a$,b", ",", "{a},{$},{b}");
}
@Test
public void testSemicolon()
{
testInsert(";", "{$}");
testInsert("foo();", "{foo}_({})_{$}");
testInsert("\";", "{}_\";$\"_{}");
}
}
top,
use,
map,
class TestExpressionSlot
. apply
. evaluate
top,
use,
map,
class CompareWrapper
. CompareWrapper
. equals
. toString
. testInsert
. testMultiInsert
. testInsertExisting
. testBackspace
. testBackspace
. testDeleteSelection
. testSelectionInsert
top,
use,
map,
class CPM
. CPM
. toString
. testCaretPosMap
. testCaretPosMap
. testOperators
. testNew
. testStrings
. testBrackets
. testBackspace
. testFloating
. testDeleteBracket
. testDeleteSelection
. testSelectionOperation
. makeCaretPos
. testCaretPosMap
. testPrecedence
. testPrecedence
. testDot
. testOvertype
. testSemicolon
1208 neLoCode
+ 0 LoComm