From d33214f656d0bb7108094d8b82ce9955213272b8 Mon Sep 17 00:00:00 2001 From: "Meredith L. Patterson" <mlp@thesmartpolitenerd.com> Date: Wed, 8 Jan 2014 17:40:40 +0100 Subject: [PATCH] All java tests pass --- lib/test-suite | 28 +- lib/tsgenjava.pl | 44 +-- src/bindings/java/.gitignore | 2 + .../com/upstandinghackers/hammer/Hammer.java | 21 +- .../upstandinghackers/hammer/ParsedToken.java | 58 +++- .../com/upstandinghackers/hammer/Parser.java | 5 +- ...com_upstandinghackers_hammer_ParsedToken.c | 69 ++++- .../java/native/src/test/java/HammerTest.java | 279 ++++++++---------- .../native/src/test/java/TestSupport.java | 63 ++++ 9 files changed, 353 insertions(+), 216 deletions(-) create mode 100644 src/bindings/java/.gitignore create mode 100644 src/bindings/java/native/src/test/java/TestSupport.java diff --git a/lib/test-suite b/lib/test-suite index 6c15b3d6..7f00b8e3 100644 --- a/lib/test-suite +++ b/lib/test-suite @@ -19,7 +19,7 @@ token { parser token("95\xa2"); test "95\xa2" --> "95\xa2"; - test "95\xa2" --> fail; + test "95\xa3" --> fail; } ch { @@ -87,7 +87,7 @@ uint8 { } int_range { - parser int_range(uint8(), 0x3, 0x10); + parser int_range(uint8(), 0x3, 0xa); test <05> --> u0x05; test <0b> --> fail; } @@ -299,17 +299,17 @@ rightrec { test "aa" --> ['a',['a']]; test "aaa" --> ['a',['a',['a']]]; } - -ambiguous { - subparser $d = ch('d'); - subparser $p = ch('+'); - subparser $e = choice(sequence($e, $p, $e), $d); - # TODO: implement action/h_act_flatten - parser $e; - - test "d" --> 'd'; - test "d+d" --> ['d','+','d']; - test "d+d+d" --> [['d','+','d'],'+','d']; -} +## Only for GLR +#ambiguous { +# subparser $d = ch('d'); +# subparser $p = ch('+'); +# subparser $e = choice(sequence($e, $p, $e), $d); +# # TODO: implement action/h_act_flatten +# parser $e; +# +# test "d" --> 'd'; +# test "d+d" --> ['d','+','d']; +# test "d+d+d" --> [['d','+','d'],'+','d']; +#} diff --git a/lib/tsgenjava.pl b/lib/tsgenjava.pl index b8b0af52..8caa5561 100644 --- a/lib/tsgenjava.pl +++ b/lib/tsgenjava.pl @@ -132,15 +132,15 @@ pp_test_elem(init, test(_,_)) --> !. pp_test_elem(decl, testFail(_)) --> !. pp_test_elem(init, testFail(_)) --> !. pp_test_elem(exec, test(Str, Result)) --> - !, indent(2), - "Assert.assertTrue(handle(parser.parse(", pp_parser(string(Str)), - ").getAst(), ", + !, indent(3), + "checkParseOK(parser, ", pp_parser(string(Str)), + ", ", pp_parse_result(Result), - "));\n". + ");\n". pp_test_elem(exec, testFail(Str)) --> - !, indent(2), - "Assert.assertNull(parser.parse(", pp_parser(string(Str)), - "));\n". + !, indent(3), + "checkParseFail(parser, ", pp_parser(string(Str)), + ");\n". % pp_test_elem(_, _) --> !. @@ -171,8 +171,11 @@ pp_parser_bigint_str(num(Num)) --> !, pp_hexnum_guts(RNum). pp_parse_result(char(C)) --> !, - pp_parser(char(C)), - ".getBytes()[0]". + "new BigInteger(\"", + pp_parser_bigint_str(num(C)), + "\", 16)". + %pp_parser(char(C)), + %".getBytes()[0]". pp_parse_result(seq(Args)) --> !, "new Object[]{ ", pp_result_seq(Args), "}". pp_parse_result(none) --> !, @@ -220,30 +223,11 @@ pp_test_suite(Suite) --> "import java.util.Arrays;\n", "import org.testng.annotations.*;\n", "import org.testng.Assert;\n\n", - "public class HammerTest {\n\n", + "public class HammerTest extends TestSupport {\n\n", indent(1), "static {\n", indent(2), "System.loadLibrary(\"hammer-java\");\n", indent(1), "}\n\n", - indent(1), "private boolean handle(ParsedToken p, Object known) {\n", - indent(2), "switch (p.getTokenType()) {\n", - indent(2), "case BYTES:\n", - indent(3), "return Arrays.toString(p.getBytesValue()).equals((String)known);\n", - indent(2), "case SINT:\n", - indent(3), "return ((Long)p.getSIntValue()).equals(known);\n", - indent(2), "case UINT:\n", - indent(3), "return ((Long)p.getUIntValue()).equals(known);\n", - indent(2), "case SEQUENCE:\n", - indent(3), "int i=0;\n", - indent(3), "for (ParsedToken tok : p.getSeqValue()) {\n", - indent(4), "if (!handle(tok, ((Object[])known)[i]))\n", - indent(5), "return false;\n", - indent(4), "++i;\n", - indent(3), "}\n", - indent(3), "return true;\n", - indent(2), "default:\n", - indent(3), "return false;\n", - indent(2), "}\n", - indent(1), "}\n\n", + pp_test_cases(Suite), "}\n". diff --git a/src/bindings/java/.gitignore b/src/bindings/java/.gitignore new file mode 100644 index 00000000..c44dfcd2 --- /dev/null +++ b/src/bindings/java/.gitignore @@ -0,0 +1,2 @@ +java/target +native/linux/target diff --git a/src/bindings/java/java/src/main/java/com/upstandinghackers/hammer/Hammer.java b/src/bindings/java/java/src/main/java/com/upstandinghackers/hammer/Hammer.java index 8cff9b30..e73cd232 100644 --- a/src/bindings/java/java/src/main/java/com/upstandinghackers/hammer/Hammer.java +++ b/src/bindings/java/java/src/main/java/com/upstandinghackers/hammer/Hammer.java @@ -33,24 +33,37 @@ public class Hammer } } + private static byte[] toRawBytes(String input) { + byte[] bytes = new byte[input.length()]; + for (int i = 0; i < input.length(); i++) + bytes[i] = (byte)input.charAt(i); + return bytes; + } + public static native ParseResult parse(Parser parser, byte[] input, int length); public static ParseResult parse(Parser parser, String input) { - return parse(parser, input.getBytes(), input.length()); + return parse(parser, Hammer.toRawBytes(input), input.length()); } public static native Parser token(byte[] str, int length); public static Parser token(String str) { - return token(str.getBytes(), str.length()); + return token(Hammer.toRawBytes(str), str.length()); } public static native Parser ch(byte c); public static Parser ch(String s) { - return ch(s.getBytes()[0]); + return ch((byte)s.charAt(0)); } public static Parser ch(int c) { return ch((byte)c); } + public static Parser ch(char c) { + return ch((byte)c); + } public static native Parser chRange(byte from, byte to); public static Parser chRange(String from, String to) { - return chRange(from.getBytes()[0], to.getBytes()[0]); + return chRange(from.charAt(0), to.charAt(0)); + } + public static Parser chRange(char from, char to) { + return chRange((byte)from, (byte)to); } public static Parser chRange(int from, int to) { return chRange((byte)from, (byte)to); diff --git a/src/bindings/java/java/src/main/java/com/upstandinghackers/hammer/ParsedToken.java b/src/bindings/java/java/src/main/java/com/upstandinghackers/hammer/ParsedToken.java index efbc8ed7..6767980b 100644 --- a/src/bindings/java/java/src/main/java/com/upstandinghackers/hammer/ParsedToken.java +++ b/src/bindings/java/java/src/main/java/com/upstandinghackers/hammer/ParsedToken.java @@ -1,4 +1,5 @@ package com.upstandinghackers.hammer; +import java.math.BigInteger; public class ParsedToken { @@ -14,8 +15,8 @@ public class ParsedToken public native int getIndex(); public native byte getBitOffset(); public native byte[] getBytesValue(); - public native long getSIntValue(); - public native long getUIntValue(); + public native BigInteger getSIntValue(); + public native BigInteger getUIntValue(); public native double getDoubleValue(); public native float getFloatValue(); public native ParsedToken[] getSeqValue(); @@ -25,8 +26,8 @@ public class ParsedToken native void setIndex(int index); native void setBitOffset(byte offset); native void setBytesValue(byte[] value); - native void setSIntValue(long value); - native void setUIntValue(long value); + native void setSIntValue(long value); // TODO: Change these to take a biginteger + native void setUIntValue(long value); // TODO: Change these to take a biginteger native void setDoubleValue(double value); native void setFloatValue(float value); native void setSeqValue(ParsedToken value[]); @@ -37,4 +38,53 @@ public class ParsedToken private long inner; ParsedToken(long inner) {this.inner=inner;} + + private void write(StringBuilder b) { + switch (getTokenType()) { + case BYTES: + byte[] bytes = getBytesValue(); + for (int i = 0; i < bytes.length; i++) { + b.append(i == 0 ? "<" : "."); + String byteStr = Integer.toHexString(((int)bytes[i] + 256)%256); + if (byteStr.length() < 2) { + b.append("0"); + } + b.append(byteStr); + } + b.append(">"); + break; + case SINT: + b.append("s"); + b.append(getSIntValue()); + break; + case UINT: + b.append("u"); + b.append(getUIntValue()); + break; + case SEQUENCE: + boolean first = true; + b.append("["); + for (ParsedToken tok : getSeqValue()) { + if (!first) + b.append(' '); + first = false; + if (tok == null) + // I don't think this can ever be the case + b.append("null"); + else + tok.write(b); + } + b.append("]"); + break; + case NONE: + b.append("NONE"); + } + } + + public String toString() { + StringBuilder b = new StringBuilder(); + write(b); + return b.toString(); + } } + diff --git a/src/bindings/java/java/src/main/java/com/upstandinghackers/hammer/Parser.java b/src/bindings/java/java/src/main/java/com/upstandinghackers/hammer/Parser.java index eb5f89bb..519e635e 100644 --- a/src/bindings/java/java/src/main/java/com/upstandinghackers/hammer/Parser.java +++ b/src/bindings/java/java/src/main/java/com/upstandinghackers/hammer/Parser.java @@ -6,7 +6,10 @@ public class Parser public native void free(); public long getInner() {return this.inner;} public ParseResult parse(String input) { - return Hammer.parse(this, input.getBytes(), input.length()); + byte[] bytes = new byte[input.length()]; + for (int i = 0; i < input.length(); i++) + bytes[i] = (byte)input.charAt(i); + return Hammer.parse(this, bytes, bytes.length); } public ParseResult parse(byte[] input, int length) { return Hammer.parse(this, input, length); diff --git a/src/bindings/java/native/src/main/native/com_upstandinghackers_hammer_ParsedToken.c b/src/bindings/java/native/src/main/native/com_upstandinghackers_hammer_ParsedToken.c index e863738a..f69856c1 100644 --- a/src/bindings/java/native/src/main/native/com_upstandinghackers_hammer_ParsedToken.c +++ b/src/bindings/java/native/src/main/native/com_upstandinghackers_hammer_ParsedToken.c @@ -1,6 +1,22 @@ #include "jhammer.h" #include "com_upstandinghackers_hammer_ParsedToken.h" +#ifdef __GNUC__ +#ifndef BRANCH_LIKELY +#define BRANCH_LIKELY(cond) __builtin_expect((cond),1) +#endif +#ifndef BRANCH_UNLIKELY +#define BRANCH_UNLIKELY(cond) __builtin_expect((cond),0) +#endif +#else /* unknown compiler */ +#ifndef BRANCH_LIKELY +#define BRANCH_LIKELY(cond) (cond) +#endif +#ifndef BRANCH_UNLIKELY +#define BRANCH_UNLIKELY(cond) (cond) +#endif +#endif + #define HPT_UNWRAP(env, this) HParsedToken *inner = unwrap_parsed_token(env, this); assert(inner!=NULL) HParsedToken *unwrap_parsed_token(JNIEnv *env, jobject obj) @@ -45,19 +61,64 @@ JNIEXPORT jbyteArray JNICALL Java_com_upstandinghackers_hammer_ParsedToken_getBy (*env)->SetByteArrayRegion(env, outArray, (jsize) 0, (jsize)(inner->bytes.len), (jbyte *)(inner->bytes.token)); return outArray; } +#if 0 +static jchar* format_number(uint64_t number, int length, int signedp, int *tlen, int had_neg) { + if (BRANCH_UNLIKELY(signedp && (number & 0x8000000000000000UL /* sign bit */) && length == 0)) { + jchar* ret = format_number((~number) + 1, 1, 0, tlen, 1); + ret[0] = '-'; + return ret; + } else { + // no negative sign possible + if (BRANH_UNLIKELY(number == 0 && (length - had_neg) > 0)) { + *tlen = length; + return (jchar*)malloc(sizeof(jchar) * *tlen); + } + const jchar hexchars = {'0','1','2','3','4','5','6','7', + '8','9','A','B','C','D','E','F'}; + jchar* ret - format_number(number >> 4, length + 1, 0, tlen, had_neg); + ret[*tlen - length - 1 + had_neg] = hexchars[number & 0xF]; + return ret; + } +} +#endif +static jobject make_bignum(JNIEnv *env, uint64_t num, int signedp) { + jbyte bytebuf[9]; + for (int i = 0; i < 8; i++) { + bytebuf[i+1] = (num >> (8 * (7 - i))) & 0xFF; + } + + bytebuf[0] = (signedp && (num & (1LL << 63))) ? 0xFF : 0; + jbyteArray byteArray = (*env)->NewByteArray(env, 9); + (*env)->SetByteArrayRegion(env, byteArray, (jsize)0, (jsize)9, bytebuf); + + printf("Formatted %s 0x%016lx as [", signedp ? "signed" : "unsigned", num); + for (int i = 0; i < 9; i++) + printf("%02hhx%s", bytebuf[i], (i == 8) ? "]\n":"."); + /* + int buflen = 0; + jchar* numbuf = format_number(num, 0, signedp, *buflen, 0); + jstring numstr = env->NewString(env, numbuf, buflen); + */ + jclass BigNum; + FIND_CLASS(BigNum, env, "java/math/BigInteger"); + jmethodID bignum_ctor = (*env)->GetMethodID(env, BigNum, "<init>", "([B)V"); + jobject ret = (*env)->NewObject(env, BigNum, bignum_ctor, byteArray); + return ret; + +} -JNIEXPORT jlong JNICALL Java_com_upstandinghackers_hammer_ParsedToken_getSIntValue +JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_ParsedToken_getSIntValue (JNIEnv *env, jobject this) { HPT_UNWRAP(env, this); - return (jlong) (inner->sint); + return make_bignum(env, inner->uint, 1); } -JNIEXPORT jlong JNICALL Java_com_upstandinghackers_hammer_ParsedToken_getUIntValue +JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_ParsedToken_getUIntValue (JNIEnv *env, jobject this) { HPT_UNWRAP(env, this); - return (jlong) (inner->uint); + return make_bignum(env, inner->uint, 0); } JNIEXPORT jdouble JNICALL Java_com_upstandinghackers_hammer_ParsedToken_getDoubleValue diff --git a/src/bindings/java/native/src/test/java/HammerTest.java b/src/bindings/java/native/src/test/java/HammerTest.java index d9b3ac07..81f7253f 100644 --- a/src/bindings/java/native/src/test/java/HammerTest.java +++ b/src/bindings/java/native/src/test/java/HammerTest.java @@ -5,322 +5,297 @@ import java.util.Arrays; import org.testng.annotations.*; import org.testng.Assert; -public class HammerTest { +public class HammerTest extends TestSupport { static { System.loadLibrary("hammer-java"); } - private boolean handle(ParsedToken p, Object known) { - System.out.println("Known: " + known); - switch (p.getTokenType()) { - case BYTES: - System.out.println("Parsed: " + Arrays.toString(p.getBytesValue())); - return Arrays.toString(p.getBytesValue()).equals((String)known); - case SINT: - System.out.println("Parsed: " + p.getSIntValue()); - return ((Long)p.getSIntValue()).equals(known); - case UINT: - System.out.println("Parsed: " + p.getUIntValue()); - return ((Long)p.getUIntValue()).equals(known); - case SEQUENCE: - int i=0; - for (ParsedToken tok : p.getSeqValue()) { - if (!handle(tok, ((Object[])known)[i])) - return false; - ++i; - } - return true; - default: - return false; - } - } - @Test public void TestToken() { Parser parser; parser = Hammer.token("95\u00a2"); - Assert.assertTrue(handle(parser.parse("95\u00a2").getAst(), "95\u00a2")); - Assert.assertNull(parser.parse("95\u00a2")); + checkParseOK(parser, "95\u00a2", "95\u00a2"); + checkParseFail(parser, "95\u00a3"); } @Test public void TestCh() { Parser parser; parser = Hammer.ch(0xa2); - Assert.assertTrue(handle(parser.parse("\u00a2").getAst(), "\u00a2".getBytes()[0])); - Assert.assertNull(parser.parse("\u00a3")); + checkParseOK(parser, "\u00a2", new BigInteger("a2", 16)); + checkParseFail(parser, "\u00a3"); } @Test public void TestChRange() { Parser parser; parser = Hammer.chRange(0x61, 0x63); - Assert.assertTrue(handle(parser.parse("b").getAst(), "b".getBytes()[0])); - Assert.assertNull(parser.parse("d")); + checkParseOK(parser, "b", new BigInteger("62", 16)); + checkParseFail(parser, "d"); } @Test public void TestInt64() { Parser parser; parser = Hammer.int64(); - Assert.assertTrue(handle(parser.parse("\u00ff\u00ff\u00ff\u00fe\u0000\u0000\u0000\u0000").getAst(), new BigInteger("-200000000", 16))); - Assert.assertNull(parser.parse("\u00ff\u00ff\u00ff\u00fe\u0000\u0000\u0000")); + checkParseOK(parser, "\u00ff\u00ff\u00ff\u00fe\u0000\u0000\u0000\u0000", new BigInteger("-200000000", 16)); + checkParseFail(parser, "\u00ff\u00ff\u00ff\u00fe\u0000\u0000\u0000"); } @Test public void TestInt32() { Parser parser; parser = Hammer.int32(); - Assert.assertTrue(handle(parser.parse("\u00ff\u00fe\u0000\u0000").getAst(), new BigInteger("-20000", 16))); - Assert.assertNull(parser.parse("\u00ff\u00fe\u0000")); - Assert.assertTrue(handle(parser.parse("\u0000\u0002\u0000\u0000").getAst(), new BigInteger("20000", 16))); - Assert.assertNull(parser.parse("\u0000\u0002\u0000")); + checkParseOK(parser, "\u00ff\u00fe\u0000\u0000", new BigInteger("-20000", 16)); + checkParseFail(parser, "\u00ff\u00fe\u0000"); + checkParseOK(parser, "\u0000\u0002\u0000\u0000", new BigInteger("20000", 16)); + checkParseFail(parser, "\u0000\u0002\u0000"); } @Test public void TestInt16() { Parser parser; parser = Hammer.int16(); - Assert.assertTrue(handle(parser.parse("\u00fe\u0000").getAst(), new BigInteger("-200", 16))); - Assert.assertNull(parser.parse("\u00fe")); - Assert.assertTrue(handle(parser.parse("\u0002\u0000").getAst(), new BigInteger("200", 16))); - Assert.assertNull(parser.parse("\u0002")); + checkParseOK(parser, "\u00fe\u0000", new BigInteger("-200", 16)); + checkParseFail(parser, "\u00fe"); + checkParseOK(parser, "\u0002\u0000", new BigInteger("200", 16)); + checkParseFail(parser, "\u0002"); } @Test public void TestInt8() { Parser parser; parser = Hammer.int8(); - Assert.assertTrue(handle(parser.parse("\u0088").getAst(), new BigInteger("-78", 16))); - Assert.assertNull(parser.parse("")); + checkParseOK(parser, "\u0088", new BigInteger("-78", 16)); + checkParseFail(parser, ""); } @Test public void TestUint64() { Parser parser; parser = Hammer.uint64(); - Assert.assertTrue(handle(parser.parse("\u0000\u0000\u0000\u0002\u0000\u0000\u0000\u0000").getAst(), new BigInteger("200000000", 16))); - Assert.assertNull(parser.parse("\u0000\u0000\u0000\u0002\u0000\u0000\u0000")); + checkParseOK(parser, "\u0000\u0000\u0000\u0002\u0000\u0000\u0000\u0000", new BigInteger("200000000", 16)); + checkParseFail(parser, "\u0000\u0000\u0000\u0002\u0000\u0000\u0000"); } @Test public void TestUint32() { Parser parser; parser = Hammer.uint32(); - Assert.assertTrue(handle(parser.parse("\u0000\u0002\u0000\u0000").getAst(), new BigInteger("20000", 16))); - Assert.assertNull(parser.parse("\u0000\u0002\u0000")); + checkParseOK(parser, "\u0000\u0002\u0000\u0000", new BigInteger("20000", 16)); + checkParseFail(parser, "\u0000\u0002\u0000"); } @Test public void TestUint16() { Parser parser; parser = Hammer.uint16(); - Assert.assertTrue(handle(parser.parse("\u0002\u0000").getAst(), new BigInteger("200", 16))); - Assert.assertNull(parser.parse("\u0002")); + checkParseOK(parser, "\u0002\u0000", new BigInteger("200", 16)); + checkParseFail(parser, "\u0002"); } @Test public void TestUint8() { Parser parser; parser = Hammer.uint8(); - Assert.assertTrue(handle(parser.parse("x").getAst(), new BigInteger("78", 16))); - Assert.assertNull(parser.parse("")); + checkParseOK(parser, "x", new BigInteger("78", 16)); + checkParseFail(parser, ""); } @Test public void TestIntRange() { Parser parser; - parser = Hammer.intRange(Hammer.uint8(), 0x3, 0x10); - Assert.assertTrue(handle(parser.parse("\u0005").getAst(), new BigInteger("5", 16))); - Assert.assertNull(parser.parse("\u000b")); + parser = Hammer.intRange(Hammer.uint8(), 0x3, 0xa); + checkParseOK(parser, "\u0005", new BigInteger("5", 16)); + checkParseFail(parser, "\u000b"); } @Test public void TestWhitespace() { Parser parser; parser = Hammer.whitespace(Hammer.ch(0x61)); - Assert.assertTrue(handle(parser.parse("a").getAst(), "a".getBytes()[0])); - Assert.assertTrue(handle(parser.parse(" a").getAst(), "a".getBytes()[0])); - Assert.assertTrue(handle(parser.parse(" a").getAst(), "a".getBytes()[0])); - Assert.assertTrue(handle(parser.parse("\u0009a").getAst(), "a".getBytes()[0])); - Assert.assertNull(parser.parse("_a")); + checkParseOK(parser, "a", new BigInteger("61", 16)); + checkParseOK(parser, " a", new BigInteger("61", 16)); + checkParseOK(parser, " a", new BigInteger("61", 16)); + checkParseOK(parser, "\u0009a", new BigInteger("61", 16)); + checkParseFail(parser, "_a"); parser = Hammer.whitespace(Hammer.endP()); - Assert.assertTrue(handle(parser.parse("").getAst(), null)); - Assert.assertTrue(handle(parser.parse(" ").getAst(), null)); - Assert.assertNull(parser.parse(" x")); + checkParseOK(parser, "", null); + checkParseOK(parser, " ", null); + checkParseFail(parser, " x"); } @Test public void TestLeft() { Parser parser; parser = Hammer.left(Hammer.ch(0x61), Hammer.ch(0x20)); - Assert.assertTrue(handle(parser.parse("a ").getAst(), "a".getBytes()[0])); - Assert.assertNull(parser.parse("a")); - Assert.assertNull(parser.parse(" ")); - Assert.assertNull(parser.parse("ba")); + checkParseOK(parser, "a ", new BigInteger("61", 16)); + checkParseFail(parser, "a"); + checkParseFail(parser, " "); + checkParseFail(parser, "ba"); } @Test public void TestMiddle() { Parser parser; parser = Hammer.middle(Hammer.ch(" "), Hammer.ch("a"), Hammer.ch(" ")); - Assert.assertTrue(handle(parser.parse(" a ").getAst(), "a".getBytes()[0])); - Assert.assertNull(parser.parse("a")); - Assert.assertNull(parser.parse(" a")); - Assert.assertNull(parser.parse("a ")); - Assert.assertNull(parser.parse(" b ")); - Assert.assertNull(parser.parse("ba ")); - Assert.assertNull(parser.parse(" ab")); + checkParseOK(parser, " a ", new BigInteger("61", 16)); + checkParseFail(parser, "a"); + checkParseFail(parser, " a"); + checkParseFail(parser, "a "); + checkParseFail(parser, " b "); + checkParseFail(parser, "ba "); + checkParseFail(parser, " ab"); } @Test public void TestIn() { Parser parser; parser = Hammer.in("abc"); - Assert.assertTrue(handle(parser.parse("b").getAst(), "b".getBytes()[0])); - Assert.assertNull(parser.parse("d")); + checkParseOK(parser, "b", new BigInteger("62", 16)); + checkParseFail(parser, "d"); } @Test public void TestNotIn() { Parser parser; parser = Hammer.notIn("abc"); - Assert.assertTrue(handle(parser.parse("d").getAst(), "d".getBytes()[0])); - Assert.assertNull(parser.parse("a")); + checkParseOK(parser, "d", new BigInteger("64", 16)); + checkParseFail(parser, "a"); } @Test public void TestEndP() { Parser parser; parser = Hammer.sequence(Hammer.ch("a"), Hammer.endP()); - Assert.assertTrue(handle(parser.parse("a").getAst(), new Object[]{ "a".getBytes()[0]})); - Assert.assertNull(parser.parse("aa")); + checkParseOK(parser, "a", new Object[]{ new BigInteger("61", 16)}); + checkParseFail(parser, "aa"); } @Test public void TestNothingP() { Parser parser; parser = Hammer.nothingP(); - Assert.assertNull(parser.parse("a")); + checkParseFail(parser, "a"); } @Test public void TestSequence() { Parser parser; parser = Hammer.sequence(Hammer.ch("a"), Hammer.ch("b")); - Assert.assertTrue(handle(parser.parse("ab").getAst(), new Object[]{ "a".getBytes()[0], "b".getBytes()[0]})); - Assert.assertNull(parser.parse("a")); - Assert.assertNull(parser.parse("b")); + checkParseOK(parser, "ab", new Object[]{ new BigInteger("61", 16), new BigInteger("62", 16)}); + checkParseFail(parser, "a"); + checkParseFail(parser, "b"); parser = Hammer.sequence(Hammer.ch("a"), Hammer.whitespace(Hammer.ch("b"))); - Assert.assertTrue(handle(parser.parse("ab").getAst(), new Object[]{ "a".getBytes()[0], "b".getBytes()[0]})); - Assert.assertTrue(handle(parser.parse("a b").getAst(), new Object[]{ "a".getBytes()[0], "b".getBytes()[0]})); - Assert.assertTrue(handle(parser.parse("a b").getAst(), new Object[]{ "a".getBytes()[0], "b".getBytes()[0]})); + checkParseOK(parser, "ab", new Object[]{ new BigInteger("61", 16), new BigInteger("62", 16)}); + checkParseOK(parser, "a b", new Object[]{ new BigInteger("61", 16), new BigInteger("62", 16)}); + checkParseOK(parser, "a b", new Object[]{ new BigInteger("61", 16), new BigInteger("62", 16)}); } @Test public void TestChoice() { Parser parser; parser = Hammer.choice(Hammer.ch("a"), Hammer.ch("b")); - Assert.assertTrue(handle(parser.parse("a").getAst(), "a".getBytes()[0])); - Assert.assertTrue(handle(parser.parse("b").getAst(), "b".getBytes()[0])); - Assert.assertTrue(handle(parser.parse("ab").getAst(), "a".getBytes()[0])); - Assert.assertNull(parser.parse("c")); + checkParseOK(parser, "a", new BigInteger("61", 16)); + checkParseOK(parser, "b", new BigInteger("62", 16)); + checkParseOK(parser, "ab", new BigInteger("61", 16)); + checkParseFail(parser, "c"); } @Test public void TestButnot() { Parser parser; parser = Hammer.butnot(Hammer.ch("a"), Hammer.token("ab")); - Assert.assertTrue(handle(parser.parse("a").getAst(), "a".getBytes()[0])); - Assert.assertNull(parser.parse("ab")); - Assert.assertTrue(handle(parser.parse("aa").getAst(), "a".getBytes()[0])); + checkParseOK(parser, "a", new BigInteger("61", 16)); + checkParseFail(parser, "ab"); + checkParseOK(parser, "aa", new BigInteger("61", 16)); parser = Hammer.butnot(Hammer.chRange("0", "9"), Hammer.ch("6")); - Assert.assertTrue(handle(parser.parse("5").getAst(), "5".getBytes()[0])); - Assert.assertNull(parser.parse("6")); + checkParseOK(parser, "5", new BigInteger("35", 16)); + checkParseFail(parser, "6"); } @Test public void TestDifference() { Parser parser; parser = Hammer.difference(Hammer.token("ab"), Hammer.ch("a")); - Assert.assertTrue(handle(parser.parse("ab").getAst(), "ab")); - Assert.assertNull(parser.parse("a")); + checkParseOK(parser, "ab", "ab"); + checkParseFail(parser, "a"); } @Test public void TestXor() { Parser parser; parser = Hammer.xor(Hammer.chRange("0", "6"), Hammer.chRange("5", "9")); - Assert.assertTrue(handle(parser.parse("0").getAst(), "0".getBytes()[0])); - Assert.assertTrue(handle(parser.parse("9").getAst(), "9".getBytes()[0])); - Assert.assertNull(parser.parse("5")); - Assert.assertNull(parser.parse("a")); + checkParseOK(parser, "0", new BigInteger("30", 16)); + checkParseOK(parser, "9", new BigInteger("39", 16)); + checkParseFail(parser, "5"); + checkParseFail(parser, "a"); } @Test public void TestMany() { Parser parser; parser = Hammer.many(Hammer.choice(Hammer.ch("a"), Hammer.ch("b"))); - Assert.assertTrue(handle(parser.parse("").getAst(), new Object[]{ })); - Assert.assertTrue(handle(parser.parse("a").getAst(), new Object[]{ "a".getBytes()[0]})); - Assert.assertTrue(handle(parser.parse("b").getAst(), new Object[]{ "b".getBytes()[0]})); - Assert.assertTrue(handle(parser.parse("aabbaba").getAst(), new Object[]{ "a".getBytes()[0], "a".getBytes()[0], "b".getBytes()[0], "b".getBytes()[0], "a".getBytes()[0], "b".getBytes()[0], "a".getBytes()[0]})); + checkParseOK(parser, "", new Object[]{ }); + checkParseOK(parser, "a", new Object[]{ new BigInteger("61", 16)}); + checkParseOK(parser, "b", new Object[]{ new BigInteger("62", 16)}); + checkParseOK(parser, "aabbaba", new Object[]{ new BigInteger("61", 16), new BigInteger("61", 16), new BigInteger("62", 16), new BigInteger("62", 16), new BigInteger("61", 16), new BigInteger("62", 16), new BigInteger("61", 16)}); } @Test public void TestMany1() { Parser parser; parser = Hammer.many1(Hammer.choice(Hammer.ch("a"), Hammer.ch("b"))); - Assert.assertNull(parser.parse("")); - Assert.assertTrue(handle(parser.parse("a").getAst(), new Object[]{ "a".getBytes()[0]})); - Assert.assertTrue(handle(parser.parse("b").getAst(), new Object[]{ "b".getBytes()[0]})); - Assert.assertTrue(handle(parser.parse("aabbaba").getAst(), new Object[]{ "a".getBytes()[0], "a".getBytes()[0], "b".getBytes()[0], "b".getBytes()[0], "a".getBytes()[0], "b".getBytes()[0], "a".getBytes()[0]})); - Assert.assertNull(parser.parse("daabbabadef")); + checkParseFail(parser, ""); + checkParseOK(parser, "a", new Object[]{ new BigInteger("61", 16)}); + checkParseOK(parser, "b", new Object[]{ new BigInteger("62", 16)}); + checkParseOK(parser, "aabbaba", new Object[]{ new BigInteger("61", 16), new BigInteger("61", 16), new BigInteger("62", 16), new BigInteger("62", 16), new BigInteger("61", 16), new BigInteger("62", 16), new BigInteger("61", 16)}); + checkParseFail(parser, "daabbabadef"); } @Test public void TestRepeatN() { Parser parser; parser = Hammer.repeatN(Hammer.choice(Hammer.ch("a"), Hammer.ch("b")), 0x2); - Assert.assertNull(parser.parse("adef")); - Assert.assertTrue(handle(parser.parse("abdef").getAst(), new Object[]{ "a".getBytes()[0], "b".getBytes()[0]})); - Assert.assertNull(parser.parse("dabdef")); + checkParseFail(parser, "adef"); + checkParseOK(parser, "abdef", new Object[]{ new BigInteger("61", 16), new BigInteger("62", 16)}); + checkParseFail(parser, "dabdef"); } @Test public void TestOptional() { Parser parser; parser = Hammer.sequence(Hammer.ch("a"), Hammer.optional(Hammer.choice(Hammer.ch("b"), Hammer.ch("c"))), Hammer.ch("d")); - Assert.assertTrue(handle(parser.parse("abd").getAst(), new Object[]{ "a".getBytes()[0], "b".getBytes()[0], "d".getBytes()[0]})); - Assert.assertTrue(handle(parser.parse("acd").getAst(), new Object[]{ "a".getBytes()[0], "c".getBytes()[0], "d".getBytes()[0]})); - Assert.assertTrue(handle(parser.parse("ad").getAst(), new Object[]{ "a".getBytes()[0], null, "d".getBytes()[0]})); - Assert.assertNull(parser.parse("aed")); - Assert.assertNull(parser.parse("ab")); - Assert.assertNull(parser.parse("ac")); + checkParseOK(parser, "abd", new Object[]{ new BigInteger("61", 16), new BigInteger("62", 16), new BigInteger("64", 16)}); + checkParseOK(parser, "acd", new Object[]{ new BigInteger("61", 16), new BigInteger("63", 16), new BigInteger("64", 16)}); + checkParseOK(parser, "ad", new Object[]{ new BigInteger("61", 16), null, new BigInteger("64", 16)}); + checkParseFail(parser, "aed"); + checkParseFail(parser, "ab"); + checkParseFail(parser, "ac"); } @Test public void TestIgnore() { Parser parser; parser = Hammer.sequence(Hammer.ch("a"), Hammer.ignore(Hammer.ch("b")), Hammer.ch("c")); - Assert.assertTrue(handle(parser.parse("abc").getAst(), new Object[]{ "a".getBytes()[0], "c".getBytes()[0]})); - Assert.assertNull(parser.parse("ac")); + checkParseOK(parser, "abc", new Object[]{ new BigInteger("61", 16), new BigInteger("63", 16)}); + checkParseFail(parser, "ac"); } @Test public void TestSepBy() { Parser parser; parser = Hammer.sepBy(Hammer.choice(Hammer.ch("1"), Hammer.ch("2"), Hammer.ch("3")), Hammer.ch(",")); - Assert.assertTrue(handle(parser.parse("1,2,3").getAst(), new Object[]{ "1".getBytes()[0], "2".getBytes()[0], "3".getBytes()[0]})); - Assert.assertTrue(handle(parser.parse("1,3,2").getAst(), new Object[]{ "1".getBytes()[0], "3".getBytes()[0], "2".getBytes()[0]})); - Assert.assertTrue(handle(parser.parse("1,3").getAst(), new Object[]{ "1".getBytes()[0], "3".getBytes()[0]})); - Assert.assertTrue(handle(parser.parse("3").getAst(), new Object[]{ "3".getBytes()[0]})); - Assert.assertTrue(handle(parser.parse("").getAst(), new Object[]{ })); + checkParseOK(parser, "1,2,3", new Object[]{ new BigInteger("31", 16), new BigInteger("32", 16), new BigInteger("33", 16)}); + checkParseOK(parser, "1,3,2", new Object[]{ new BigInteger("31", 16), new BigInteger("33", 16), new BigInteger("32", 16)}); + checkParseOK(parser, "1,3", new Object[]{ new BigInteger("31", 16), new BigInteger("33", 16)}); + checkParseOK(parser, "3", new Object[]{ new BigInteger("33", 16)}); + checkParseOK(parser, "", new Object[]{ }); } @Test public void TestSepBy1() { Parser parser; parser = Hammer.sepBy1(Hammer.choice(Hammer.ch("1"), Hammer.ch("2"), Hammer.ch("3")), Hammer.ch(",")); - Assert.assertTrue(handle(parser.parse("1,2,3").getAst(), new Object[]{ "1".getBytes()[0], "2".getBytes()[0], "3".getBytes()[0]})); - Assert.assertTrue(handle(parser.parse("1,3,2").getAst(), new Object[]{ "1".getBytes()[0], "3".getBytes()[0], "2".getBytes()[0]})); - Assert.assertTrue(handle(parser.parse("1,3").getAst(), new Object[]{ "1".getBytes()[0], "3".getBytes()[0]})); - Assert.assertTrue(handle(parser.parse("3").getAst(), new Object[]{ "3".getBytes()[0]})); - Assert.assertNull(parser.parse("")); + checkParseOK(parser, "1,2,3", new Object[]{ new BigInteger("31", 16), new BigInteger("32", 16), new BigInteger("33", 16)}); + checkParseOK(parser, "1,3,2", new Object[]{ new BigInteger("31", 16), new BigInteger("33", 16), new BigInteger("32", 16)}); + checkParseOK(parser, "1,3", new Object[]{ new BigInteger("31", 16), new BigInteger("33", 16)}); + checkParseOK(parser, "3", new Object[]{ new BigInteger("33", 16)}); + checkParseFail(parser, ""); } @Test public void TestAnd() { Parser parser; parser = Hammer.sequence(Hammer.and(Hammer.ch("0")), Hammer.ch("0")); - Assert.assertTrue(handle(parser.parse("0").getAst(), new Object[]{ "0".getBytes()[0]})); - Assert.assertNull(parser.parse("1")); + checkParseOK(parser, "0", new Object[]{ new BigInteger("30", 16)}); + checkParseFail(parser, "1"); parser = Hammer.sequence(Hammer.and(Hammer.ch("0")), Hammer.ch("1")); - Assert.assertNull(parser.parse("0")); - Assert.assertNull(parser.parse("1")); + checkParseFail(parser, "0"); + checkParseFail(parser, "1"); parser = Hammer.sequence(Hammer.ch("1"), Hammer.and(Hammer.ch("2"))); - Assert.assertTrue(handle(parser.parse("12").getAst(), new Object[]{ "1".getBytes()[0]})); - Assert.assertNull(parser.parse("13")); + checkParseOK(parser, "12", new Object[]{ new BigInteger("31", 16)}); + checkParseFail(parser, "13"); } @Test public void TestNot() { Parser parser; parser = Hammer.sequence(Hammer.ch("a"), Hammer.choice(Hammer.token("+"), Hammer.token("++")), Hammer.ch("b")); - Assert.assertTrue(handle(parser.parse("a+b").getAst(), new Object[]{ "a".getBytes()[0], "+", "b".getBytes()[0]})); - Assert.assertNull(parser.parse("a++b")); + checkParseOK(parser, "a+b", new Object[]{ new BigInteger("61", 16), "+", new BigInteger("62", 16)}); + checkParseFail(parser, "a++b"); parser = Hammer.sequence(Hammer.ch("a"), Hammer.choice(Hammer.sequence(Hammer.token("+"), Hammer.not(Hammer.ch("+"))), Hammer.token("++")), Hammer.ch("b")); - Assert.assertTrue(handle(parser.parse("a+b").getAst(), new Object[]{ "a".getBytes()[0], new Object[]{ "+"}, "b".getBytes()[0]})); - Assert.assertTrue(handle(parser.parse("a++b").getAst(), new Object[]{ "a".getBytes()[0], "++", "b".getBytes()[0]})); + checkParseOK(parser, "a+b", new Object[]{ new BigInteger("61", 16), new Object[]{ "+"}, new BigInteger("62", 16)}); + checkParseOK(parser, "a++b", new Object[]{ new BigInteger("61", 16), "++", new BigInteger("62", 16)}); } @Test public void TestRightrec() { @@ -328,22 +303,8 @@ public class HammerTest { Parser sp_rr = Hammer.indirect(); sp_rr.bindIndirect(Hammer.choice(Hammer.sequence(Hammer.ch("a"), sp_rr), Hammer.epsilonP())); parser = sp_rr; - Assert.assertTrue(handle(parser.parse("a").getAst(), new Object[]{ "a".getBytes()[0]})); - Assert.assertTrue(handle(parser.parse("aa").getAst(), new Object[]{ "a".getBytes()[0], new Object[]{ "a".getBytes()[0]}})); - Assert.assertTrue(handle(parser.parse("aaa").getAst(), new Object[]{ "a".getBytes()[0], new Object[]{ "a".getBytes()[0], new Object[]{ "a".getBytes()[0]}}})); - } - @Test - public void TestAmbiguous() { - Parser parser; - Parser sp_d = Hammer.indirect(); - Parser sp_p = Hammer.indirect(); - Parser sp_e = Hammer.indirect(); - sp_d.bindIndirect(Hammer.ch("d")); - sp_p.bindIndirect(Hammer.ch("+")); - sp_e.bindIndirect(Hammer.choice(Hammer.sequence(sp_e, sp_p, sp_e), sp_d)); - parser = sp_e; - Assert.assertTrue(handle(parser.parse("d").getAst(), "d".getBytes()[0])); - Assert.assertTrue(handle(parser.parse("d+d").getAst(), new Object[]{ "d".getBytes()[0], "+".getBytes()[0], "d".getBytes()[0]})); - Assert.assertTrue(handle(parser.parse("d+d+d").getAst(), new Object[]{ new Object[]{ "d".getBytes()[0], "+".getBytes()[0], "d".getBytes()[0]}, "+".getBytes()[0], "d".getBytes()[0]})); + checkParseOK(parser, "a", new Object[]{ new BigInteger("61", 16)}); + checkParseOK(parser, "aa", new Object[]{ new BigInteger("61", 16), new Object[]{ new BigInteger("61", 16)}}); + checkParseOK(parser, "aaa", new Object[]{ new BigInteger("61", 16), new Object[]{ new BigInteger("61", 16), new Object[]{ new BigInteger("61", 16)}}}); } } diff --git a/src/bindings/java/native/src/test/java/TestSupport.java b/src/bindings/java/native/src/test/java/TestSupport.java new file mode 100644 index 00000000..d3b1a037 --- /dev/null +++ b/src/bindings/java/native/src/test/java/TestSupport.java @@ -0,0 +1,63 @@ +package com.upstandinghackers.hammer; + +import java.util.Arrays; +import java.math.BigInteger; +import org.testng.Assert; + +public class TestSupport { + protected boolean deepEqual(ParsedToken p, Object known) { + //if (known != null || p == null) + // throw new RuntimeException("Actually got here!"); + if (p == null) { + return known == null; + } + System.err.println("Saw token of type: " + p.getTokenType()); + System.err.println("Arg of type: " + (known == null ? "null" : known.getClass().toString())); + switch (p.getTokenType()) { + case BYTES: + + byte[] tmp_byte_buffer = p.getBytesValue(); + String knowns = (String)known; + for (int i = 0; i < tmp_byte_buffer.length; i++) { + System.err.println("Comparing char " + i); + if (tmp_byte_buffer[i] != (byte)knowns.charAt(i)) { + System.err.println("Compare failed at char " + i + ": " + (byte)knowns.charAt(i) + " != " + tmp_byte_buffer[i]); + return false; + } + } + System.err.println("Compare succeeded"); + return true; + case SINT: + return p.getSIntValue().equals(known); + case UINT: + return p.getUIntValue().equals(known); + case SEQUENCE: + int i=0; + for (ParsedToken tok : p.getSeqValue()) { + if (!deepEqual(tok, ((Object[])known)[i])) + return false; + ++i; + } + return true; + case NONE: + return known == null; + default: + System.err.println("Unkown token type " + p.getTokenType()); + return false; + } + } + + public void checkParseOK(Parser p, String input, Object expected) { + ParseResult res = p.parse(input); + if (res == null) + Assert.fail("parse failed"); + if (!deepEqual(res.getAst(), expected)) + Assert.fail("Parse does not match expected; got " + res.getAst().toString()); + } + + public void checkParseFail(Parser p, String input) { + ParseResult res = p.parse(input); + if (res != null) + Assert.fail("Parse unexpectedly succeeded; got " + res.getAst().toString()); + } +} -- GitLab