actually run the new parser (#4218)
* let the new parser run * s * ant * minor cleanups * allow passing string * tests found a genuine bug * test definitions * helper * test lots of parsing * test missing define * test typedefs, bits * fix bug with more than 32 consecutive bits overflowing a single field * s * test extra hex definitions * jar
This commit is contained in:
parent
5ee2236565
commit
aaa5afbf38
Binary file not shown.
|
@ -8,6 +8,7 @@ grammar RusefiConfigGrammar;
|
|||
ENDL: ('\n' | '\r\n' | '\r');
|
||||
|
||||
LINE_COMMENT: '!' ~[\r\n]* -> skip;
|
||||
LINE_COMMENT2: '//' ~[\r\n]* -> skip;
|
||||
WS: [ \t]+ -> skip ;
|
||||
|
||||
// Special tokens need highest priority
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.rusefi;
|
||||
|
||||
import com.rusefi.newparse.ParseState;
|
||||
import com.rusefi.newparse.parsing.Definition;
|
||||
import com.rusefi.output.*;
|
||||
import com.rusefi.trigger.TriggerWheelTSLogic;
|
||||
import com.rusefi.util.SystemOut;
|
||||
|
@ -189,11 +190,8 @@ public class ConfigDefinition {
|
|||
|
||||
new TriggerWheelTSLogic().execute(triggersFolder, state.variableRegistry);
|
||||
|
||||
/*
|
||||
|
||||
// Parse the input files
|
||||
{
|
||||
|
||||
// Load prepend files
|
||||
{
|
||||
// Ignore duplicates of definitions made during prepend phase
|
||||
|
@ -219,7 +217,6 @@ public class ConfigDefinition {
|
|||
// TsWriter writer = new TsWriter();
|
||||
// writer.writeTunerstudio(parseState, tsPath + "/rusefi.input", tsPath + "/" + TSProjectConsumer.TS_FILE_OUTPUT_NAME);
|
||||
}
|
||||
*/
|
||||
|
||||
if (tsOutputsDestination != null) {
|
||||
/**
|
||||
|
|
|
@ -17,13 +17,31 @@ import java.io.IOException;
|
|||
public class RusefiParseErrorStrategy extends DefaultErrorStrategy {
|
||||
private boolean hadError = false;
|
||||
|
||||
static void parseDefinitionFile(ParseTreeListener listener, String filePath) throws IOException {
|
||||
public static void parseDefinitionFile(ParseTreeListener listener, String filePath) throws IOException {
|
||||
SystemOut.println("Parsing file (Antlr) " + filePath);
|
||||
|
||||
CharStream in = new ANTLRInputStream(new FileInputStream(filePath));
|
||||
|
||||
long start = System.nanoTime();
|
||||
parse(listener, in);
|
||||
double durationMs = (System.nanoTime() - start) / 1e6;
|
||||
|
||||
SystemOut.println("Successfully parsed (Antlr) " + filePath + " in " + durationMs + "ms");
|
||||
}
|
||||
|
||||
public static void parseDefinitionString(ParseTreeListener listener, String content) throws IOException {
|
||||
SystemOut.println("Parsing string (Antlr)");
|
||||
|
||||
CharStream in = new ANTLRInputStream(content);
|
||||
|
||||
long start = System.nanoTime();
|
||||
parse(listener, in);
|
||||
double durationMs = (System.nanoTime() - start) / 1e6;
|
||||
|
||||
SystemOut.println("Successfully parsed (Antlr) in " + durationMs + "ms");
|
||||
}
|
||||
|
||||
private static void parse(ParseTreeListener listener, CharStream in) {
|
||||
RusefiConfigGrammarParser parser = new RusefiConfigGrammarParser(new CommonTokenStream(new RusefiConfigGrammarLexer(in)));
|
||||
|
||||
RusefiParseErrorStrategy errorStrategy = new RusefiParseErrorStrategy();
|
||||
|
@ -31,13 +49,10 @@ public class RusefiParseErrorStrategy extends DefaultErrorStrategy {
|
|||
|
||||
ParseTree tree = parser.content();
|
||||
new ParseTreeWalker().walk(listener, tree);
|
||||
double durationMs = (System.nanoTime() - start) / 1e6;
|
||||
|
||||
if (errorStrategy.hadError()) {
|
||||
throw new RuntimeException("Parse failed, see error output above!");
|
||||
}
|
||||
|
||||
SystemOut.println("Successfully parsed " + filePath + " in " + durationMs + "ms");
|
||||
}
|
||||
|
||||
public boolean hadError() {
|
||||
|
|
|
@ -29,6 +29,10 @@ public class ParseState {
|
|||
|
||||
private Struct lastStruct = null;
|
||||
|
||||
public ParseState() {
|
||||
this.enumsReader = null;
|
||||
}
|
||||
|
||||
public ParseState(EnumsReader enumsReader) {
|
||||
this.enumsReader = enumsReader;
|
||||
|
||||
|
@ -51,7 +55,7 @@ public class ParseState {
|
|||
addDefinition(name, value);
|
||||
|
||||
// Also add ints as 16b hex
|
||||
addDefinition(name + "_16_hex", String.format("\\\\x%02x\\\\x%02x", (value >> 8) & 0xFF, value & 0xFF));
|
||||
addDefinition(name + "_16_hex", String.format("\\x%02x\\x%02x", (value >> 8) & 0xFF, value & 0xFF));
|
||||
}
|
||||
|
||||
private static boolean isNumeric(String str) {
|
||||
|
@ -68,6 +72,10 @@ public class ParseState {
|
|||
}
|
||||
|
||||
private String[] resolveEnumValues(String enumName) {
|
||||
if (this.enumsReader == null) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
TreeMap<Integer, String> valueNameById = new TreeMap<>();
|
||||
|
||||
EnumsReader.EnumState stringValueMap = this.enumsReader.getEnums().get(enumName);
|
||||
|
@ -82,7 +90,7 @@ public class ParseState {
|
|||
} else {
|
||||
Definition def = this.definitions.get(value.getValue());
|
||||
if (def == null)
|
||||
throw new IllegalStateException("No value for " + value);;
|
||||
throw new IllegalStateException("No value for " + value);
|
||||
valueNameById.put((Integer)def.value, value.getName());
|
||||
}
|
||||
}
|
||||
|
@ -112,7 +120,8 @@ public class ParseState {
|
|||
case NotAllowed:
|
||||
throw new IllegalStateException("Tried to add definition for " + name + ", but one already existed.");
|
||||
case Replace:
|
||||
definitions.remove(existingDefinition);
|
||||
definitions.remove(name);
|
||||
break;
|
||||
case IgnoreNew:
|
||||
// ignore the new definition, do nothing
|
||||
return;
|
||||
|
@ -135,18 +144,11 @@ public class ParseState {
|
|||
|
||||
@Override
|
||||
public void exitContent(RusefiConfigGrammarParser.ContentContext ctx) {
|
||||
if (!scopes.empty())
|
||||
throw new IllegalStateException();
|
||||
if (scope != null)
|
||||
throw new IllegalStateException();
|
||||
|
||||
if (typedefName != null)
|
||||
throw new IllegalStateException();
|
||||
|
||||
if (!evalResults.isEmpty())
|
||||
throw new IllegalStateException();
|
||||
if (!evalStack.empty())
|
||||
throw new IllegalStateException();
|
||||
assert(scopes.empty());
|
||||
assert(scope == null);
|
||||
assert(typedefName == null);
|
||||
assert(evalResults.isEmpty());
|
||||
assert(evalStack.empty());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -159,11 +161,11 @@ public class ParseState {
|
|||
addDefinition(name, Double.parseDouble(ctx.floatNum().getText()));
|
||||
} else if (ctx.numexpr() != null) {
|
||||
double evalResult = evalResults.remove();
|
||||
double floored = Math.floor(evalResult);
|
||||
int floored = (int)Math.floor(evalResult);
|
||||
|
||||
if (Math.abs(floored - evalResult) < 0.001) {
|
||||
// value is an int, process as such
|
||||
handleIntDefinition(name, (int)floored);
|
||||
handleIntDefinition(name, floored);
|
||||
} else {
|
||||
// Value is a double, add it
|
||||
addDefinition(name, evalResult);
|
||||
|
@ -312,21 +314,34 @@ public class ParseState {
|
|||
|
||||
String sValue = fo.getChild(2).getText();
|
||||
|
||||
if (key.equals("unit")) {
|
||||
options.units = sValue;
|
||||
} else if (key.equals("comment")) {
|
||||
options.comment = sValue;
|
||||
} else if (key.equals("digits")) {
|
||||
options.digits = Integer.parseInt(sValue);
|
||||
} else {
|
||||
Double value = evalResults.remove();
|
||||
switch (key) {
|
||||
case "unit":
|
||||
options.units = sValue;
|
||||
break;
|
||||
case "comment":
|
||||
options.comment = sValue;
|
||||
break;
|
||||
case "digits":
|
||||
options.digits = Integer.parseInt(sValue);
|
||||
break;
|
||||
default:
|
||||
Double value = evalResults.remove();
|
||||
|
||||
switch (key) {
|
||||
case "min": options.min = value.floatValue(); break;
|
||||
case "max": options.max = value.floatValue(); break;
|
||||
case "scale": options.scale = value.floatValue(); break;
|
||||
case "offset": options.offset = value.floatValue(); break;
|
||||
}
|
||||
switch (key) {
|
||||
case "min":
|
||||
options.min = value.floatValue();
|
||||
break;
|
||||
case "max":
|
||||
options.max = value.floatValue();
|
||||
break;
|
||||
case "scale":
|
||||
options.scale = value.floatValue();
|
||||
break;
|
||||
case "offset":
|
||||
options.offset = value.floatValue();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -401,6 +416,11 @@ public class ParseState {
|
|||
|
||||
if (lastElement instanceof BitGroup) {
|
||||
group = (BitGroup)lastElement;
|
||||
|
||||
// If this group is full, create a new one instead of continuing on here.
|
||||
if (group.bitFields.size() == 32) {
|
||||
group = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -432,6 +452,10 @@ public class ParseState {
|
|||
boolean iterate = ctx.Iterate() != null;
|
||||
boolean autoscale = ctx.Autoscale() != null;
|
||||
|
||||
if (iterate && length.length != 1) {
|
||||
throw new IllegalStateException("Cannot iterate multi dimensional array: " + name);
|
||||
}
|
||||
|
||||
// First check if this is an array of structs
|
||||
if (structs.containsKey(type)) {
|
||||
// iterate required for structs
|
||||
|
@ -515,9 +539,8 @@ public class ParseState {
|
|||
String structName = ctx.identifier().getText();
|
||||
|
||||
assert(scope != null);
|
||||
assert(scope.structFields != null);
|
||||
|
||||
String comment = ctx.restOfLine() == null ? null : ctx.restOfLine().getText().toString();
|
||||
String comment = ctx.restOfLine() == null ? null : ctx.restOfLine().getText();
|
||||
|
||||
Struct s = new Struct(structName, scope.structFields, ctx.StructNoPrefix() != null, comment);
|
||||
structs.put(structName, s);
|
||||
|
@ -535,7 +558,6 @@ public class ParseState {
|
|||
@Override
|
||||
public void exitUnionField(RusefiConfigGrammarParser.UnionFieldContext ctx) {
|
||||
assert(scope != null);
|
||||
assert(scope.structFields != null);
|
||||
|
||||
// unions must have at least 1 member
|
||||
assert(!scope.structFields.isEmpty());
|
||||
|
@ -549,8 +571,7 @@ public class ParseState {
|
|||
scope.structFields.add(u);
|
||||
}
|
||||
|
||||
private Stack<Double> evalStack = new Stack<>();
|
||||
|
||||
private final Stack<Double> evalStack = new Stack<>();
|
||||
|
||||
@Override
|
||||
public void exitEvalNumber(RusefiConfigGrammarParser.EvalNumberContext ctx) {
|
||||
|
@ -615,9 +636,9 @@ public class ParseState {
|
|||
}
|
||||
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
static class Scope {
|
||||
public List<Field> structFields = new ArrayList<>();
|
||||
public final List<Field> structFields = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ public class FieldOptions {
|
|||
return other;
|
||||
}
|
||||
|
||||
private static String tryRound(float value) {
|
||||
public static String tryRound(float value) {
|
||||
int intVal = Math.round(value);
|
||||
|
||||
// If the rounded value can exactly represent this float, then print as an integer
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package com.rusefi.test.newParse;
|
||||
|
||||
import com.rusefi.RusefiParseErrorStrategy;
|
||||
import com.rusefi.newparse.ParseState;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class NewParseHelper {
|
||||
public static ParseState parse(String input) throws IOException {
|
||||
ParseState parseState = new ParseState();
|
||||
RusefiParseErrorStrategy.parseDefinitionString(parseState.getListener(), input);
|
||||
return parseState;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
package com.rusefi.test.newParse;
|
||||
|
||||
import com.rusefi.RusefiParseErrorStrategy;
|
||||
import com.rusefi.newparse.ParseState;
|
||||
import com.rusefi.newparse.parsing.Definition;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static com.rusefi.test.newParse.NewParseHelper.parse;
|
||||
|
||||
|
||||
public class ParseDefinitionsTest {
|
||||
@Test
|
||||
public void basicIntegerDefinition() throws IOException {
|
||||
ParseState state = parse("#define foo 123");
|
||||
|
||||
Assert.assertNull(state.findDefinition("notFoo"));
|
||||
|
||||
Definition def = state.findDefinition("foo");
|
||||
Assert.assertTrue(def.isNumeric());
|
||||
Assert.assertEquals(123, def.asDouble(), 1e-5);
|
||||
|
||||
Definition defHex = state.findDefinition("foo_16_hex");
|
||||
Assert.assertFalse(defHex.isNumeric());
|
||||
Assert.assertEquals("\\x00\\x7b", defHex.value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicDoubleDefinition() throws IOException {
|
||||
ParseState state = parse("#define foo 123.5");
|
||||
|
||||
Definition def = state.findDefinition("foo");
|
||||
|
||||
Assert.assertTrue(def.isNumeric());
|
||||
Assert.assertEquals(123.5, def.asDouble(), 1e-5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicStringDefinition() throws IOException {
|
||||
ParseState state = parse("#define foo \"fooValue\"");
|
||||
|
||||
Definition def = state.findDefinition("foo");
|
||||
|
||||
Assert.assertFalse(def.isNumeric());
|
||||
Assert.assertTrue(def.value instanceof String);
|
||||
Assert.assertEquals("\"fooValue\"", def.value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void definitionWithNumexprValue() throws IOException {
|
||||
ParseState state = parse("#define foo 5 + 8 * 2 + 3");
|
||||
|
||||
Definition def = state.findDefinition("foo");
|
||||
|
||||
Assert.assertTrue(def.isNumeric());
|
||||
Assert.assertEquals(24, def.asDouble(), 1e-5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void definitionWithAnotherDefinition() throws IOException {
|
||||
ParseState state = parse(
|
||||
"#define val1 20\n" +
|
||||
"#define val2 val1 + 1\n" +
|
||||
"#define val3 3 * @@val2@@");
|
||||
|
||||
Definition def1 = state.findDefinition("val1");
|
||||
Assert.assertTrue(def1.isNumeric());
|
||||
Assert.assertEquals(20, def1.asDouble(), 1e-5);
|
||||
|
||||
Definition def2 = state.findDefinition("val2");
|
||||
Assert.assertTrue(def2.isNumeric());
|
||||
Assert.assertEquals(21, def2.asDouble(), 1e-5);
|
||||
|
||||
Definition def3 = state.findDefinition("val3");
|
||||
Assert.assertTrue(def3.isNumeric());
|
||||
Assert.assertEquals(63, def3.asDouble(), 1e-5);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = RuntimeException.class)
|
||||
public void definitionEvalMissingDefinition() throws IOException {
|
||||
parse(
|
||||
"#define val1 20\n" +
|
||||
"#define val2 val1 + 1\n" +
|
||||
"#define val3 3 * valgggggg"
|
||||
);
|
||||
}
|
||||
|
||||
@Test(expected = RuntimeException.class)
|
||||
public void definitionEvalNonNumeric() throws IOException {
|
||||
parse(
|
||||
"#define val1 \"foo\"\n" +
|
||||
"#define val2 val1 + 1"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void definitionOverwritePolicyReplace() throws IOException {
|
||||
ParseState state = new ParseState();
|
||||
state.setDefinitionPolicy(Definition.OverwritePolicy.Replace);
|
||||
RusefiParseErrorStrategy.parseDefinitionString(state.getListener(), "#define val 20");
|
||||
|
||||
Definition def = state.findDefinition("val");
|
||||
Assert.assertEquals(20, def.asDouble(), 1e-5);
|
||||
|
||||
// Now parse another definition with the same name
|
||||
RusefiParseErrorStrategy.parseDefinitionString(state.getListener(), "#define val 40");
|
||||
|
||||
// Should get back the new definition, not the old one
|
||||
Definition def2 = state.findDefinition("val");
|
||||
Assert.assertTrue(def != def2);
|
||||
Assert.assertEquals(40, def2.asDouble(), 1e-5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void definitionOverwritePolicyIgnoreNew() throws IOException {
|
||||
ParseState state = new ParseState();
|
||||
state.setDefinitionPolicy(Definition.OverwritePolicy.IgnoreNew);
|
||||
RusefiParseErrorStrategy.parseDefinitionString(state.getListener(), "#define val 20");
|
||||
|
||||
Definition def = state.findDefinition("val");
|
||||
Assert.assertEquals(20, def.asDouble(), 1e-5);
|
||||
|
||||
// Now parse another definition with the same name
|
||||
state.setDefinitionPolicy(Definition.OverwritePolicy.IgnoreNew);
|
||||
RusefiParseErrorStrategy.parseDefinitionString(state.getListener(), "#define val 40");
|
||||
|
||||
// New one ignored, still returns the same old one.
|
||||
Assert.assertEquals(def, state.findDefinition("val"));
|
||||
Assert.assertEquals(20, def.asDouble(), 1e-5);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void definitionOverwritePolicyNotAllowed() throws IOException {
|
||||
ParseState state = new ParseState();
|
||||
state.setDefinitionPolicy(Definition.OverwritePolicy.NotAllowed);
|
||||
RusefiParseErrorStrategy.parseDefinitionString(state.getListener(), "#define val 20");
|
||||
|
||||
Definition def = state.findDefinition("val");
|
||||
Assert.assertEquals(20, def.asDouble(), 1e-5);
|
||||
|
||||
// Now parse another definition with the same name, this should throw
|
||||
RusefiParseErrorStrategy.parseDefinitionString(state.getListener(), "#define val 40");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,344 @@
|
|||
package com.rusefi.test.newParse;
|
||||
|
||||
import com.rusefi.RusefiParseErrorStrategy;
|
||||
import com.rusefi.newparse.ParseState;
|
||||
import com.rusefi.newparse.parsing.*;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.List;
|
||||
|
||||
import static com.rusefi.test.newParse.NewParseHelper.parse;
|
||||
|
||||
public class ParseStructTest {
|
||||
@Test
|
||||
public void structEmpty() throws IOException {
|
||||
ParseState state = parse(
|
||||
"struct myStruct\n\n" +
|
||||
"end_struct\n"
|
||||
);
|
||||
|
||||
// Parsed exactly one struct
|
||||
Assert.assertEquals(1, state.getStructs().size());
|
||||
|
||||
Struct myStruct = state.getStructs().get(0);
|
||||
|
||||
// It's a real struct!
|
||||
Assert.assertNotNull(myStruct);
|
||||
|
||||
// Check that it was parsed correctly
|
||||
Assert.assertEquals("myStruct", myStruct.name);
|
||||
Assert.assertEquals(0, myStruct.fields.size());
|
||||
Assert.assertEquals(false, myStruct.noPrefix);
|
||||
|
||||
Assert.assertEquals(state.getStructs().get(0), state.getLastStruct());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structEmptyNoPrefix() throws IOException {
|
||||
ParseState state = parse(
|
||||
"struct_no_prefix myStruct\n\n" +
|
||||
"end_struct\n"
|
||||
);
|
||||
|
||||
Struct myStruct = state.getStructs().get(0);
|
||||
|
||||
// Check that this one is marked as no prefix
|
||||
Assert.assertEquals(true, myStruct.noPrefix);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structMultiple() throws IOException {
|
||||
ParseState state = parse(
|
||||
"struct myStruct1\n\n" +
|
||||
"end_struct\n" +
|
||||
"struct myStruct2\n\n" +
|
||||
"end_struct\n"
|
||||
);
|
||||
|
||||
// Now there are two structs!
|
||||
Assert.assertEquals(2, state.getStructs().size());
|
||||
|
||||
Assert.assertEquals("myStruct1", state.getStructs().get(0).name);
|
||||
Assert.assertEquals("myStruct2", state.getStructs().get(1).name);
|
||||
|
||||
// Check that get last struct returns the last one
|
||||
Assert.assertEquals(state.getStructs().get(1), state.getLastStruct());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structNested() throws IOException {
|
||||
ParseState state = parse(
|
||||
"struct myStructOuter\n" +
|
||||
"struct myStructInner\n\n" +
|
||||
"end_struct\n" +
|
||||
"end_struct\n"
|
||||
);
|
||||
|
||||
// Now there are two structs!
|
||||
Assert.assertEquals(2, state.getStructs().size());
|
||||
|
||||
Assert.assertEquals("myStructInner", state.getStructs().get(0).name);
|
||||
Assert.assertEquals("myStructOuter", state.getStructs().get(1).name);
|
||||
|
||||
// Check that get last struct returns the outer one
|
||||
Assert.assertEquals(state.getStructs().get(1), state.getLastStruct());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structContainsStruct() throws IOException {
|
||||
ParseState state = parse(
|
||||
"struct myStruct\n\n" +
|
||||
"end_struct\n" +
|
||||
"struct myStruct2\n" +
|
||||
"myStruct foo\n" +
|
||||
"end_struct"
|
||||
);
|
||||
|
||||
Assert.assertEquals(2, state.getStructs().size());
|
||||
|
||||
Struct inner = state.getStructs().get(0);
|
||||
Assert.assertEquals(0, inner.fields.size());
|
||||
|
||||
// Check that the outer contains a struct
|
||||
Struct outer = state.getStructs().get(1);
|
||||
Assert.assertEquals(1, outer.fields.size());
|
||||
Assert.assertTrue(outer.fields.get(0) instanceof StructField);
|
||||
}
|
||||
|
||||
@Test(expected = RuntimeException.class)
|
||||
public void structMissingStruct() throws IOException {
|
||||
parse(
|
||||
"struct myStruct2\n" +
|
||||
"myStruct foo\n" +
|
||||
"end_struct"
|
||||
);
|
||||
}
|
||||
|
||||
private static Field parseSingleField(String fieldLine) throws IOException {
|
||||
ParseState state = parse(
|
||||
"struct myStruct\n" +
|
||||
fieldLine + "\n" +
|
||||
"end_struct\n"
|
||||
);
|
||||
|
||||
List<Field> fields = state.getLastStruct().fields;
|
||||
|
||||
Assert.assertEquals(1, fields.size());
|
||||
|
||||
return fields.get(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scalarOptions() throws IOException {
|
||||
Field f = parseSingleField("int foo;comment;\"units\", 1, 2, 3, 4, 5");
|
||||
ScalarField sf = (ScalarField) f;
|
||||
|
||||
FieldOptions opt = sf.options;
|
||||
|
||||
Assert.assertEquals("comment", opt.comment);
|
||||
Assert.assertEquals("\"units\"", opt.units);
|
||||
Assert.assertEquals(1, opt.scale, 0.001f);
|
||||
Assert.assertEquals(2, opt.offset, 0.001f);
|
||||
Assert.assertEquals(3, opt.min, 0.001f);
|
||||
Assert.assertEquals(4, opt.max, 0.001f);
|
||||
Assert.assertEquals(5, opt.digits);
|
||||
}
|
||||
|
||||
private static void checkType(String inputType, int size, String cType, String tsType) throws IOException {
|
||||
ScalarField sf = (ScalarField) parseSingleField(inputType + " foo");
|
||||
|
||||
Assert.assertEquals("foo", sf.name);
|
||||
Assert.assertEquals(false, sf.autoscale);
|
||||
Assert.assertEquals(size, sf.type.size);
|
||||
|
||||
Assert.assertEquals(cType, sf.type.cType);
|
||||
Assert.assertEquals(tsType, sf.type.tsType);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeMappings() throws IOException {
|
||||
checkType("int", 4, "int", "S32");
|
||||
|
||||
checkType("int8_t", 1, "int8_t", "S08");
|
||||
checkType("int16_t", 2, "int16_t", "S16");
|
||||
checkType("int32_t", 4, "int32_t", "S32");
|
||||
checkType("uint8_t", 1, "uint8_t", "U08");
|
||||
checkType("uint16_t", 2, "uint16_t", "U16");
|
||||
checkType("uint32_t", 4, "uint32_t", "U32");
|
||||
|
||||
checkType("float", 4, "float", "F32");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arraySingleDimension() throws IOException {
|
||||
Field f = parseSingleField("uint8_t[10] testBins");
|
||||
|
||||
Assert.assertTrue(f instanceof ArrayField);
|
||||
ArrayField af = (ArrayField)f;
|
||||
|
||||
Assert.assertEquals(1, af.length.length);
|
||||
Assert.assertEquals(10, af.length[0]);
|
||||
|
||||
PrototypeField prototype = af.prototype;
|
||||
|
||||
Assert.assertTrue(prototype instanceof ScalarField);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arrayMultiDimension() throws IOException {
|
||||
Field f = parseSingleField("uint8_t[5 x 8] testBins");
|
||||
|
||||
Assert.assertTrue(f instanceof ArrayField);
|
||||
ArrayField af = (ArrayField)f;
|
||||
|
||||
Assert.assertEquals(2, af.length.length);
|
||||
Assert.assertEquals(5, af.length[0]);
|
||||
Assert.assertEquals(8, af.length[1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arrayIterate() throws IOException {
|
||||
Field f = parseSingleField("uint8_t[10 iterate] testBins");
|
||||
|
||||
Assert.assertTrue(f instanceof ArrayField);
|
||||
ArrayField af = (ArrayField)f;
|
||||
|
||||
Assert.assertEquals(true, af.iterate);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void checkMultiDimensionArrayIterateThrows() throws IOException {
|
||||
parseSingleField("uint8_t[5 x 8 iterate] testBins");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arrayOfStructs() throws IOException {
|
||||
ParseState state = parse(
|
||||
"struct myStruct\n\n" +
|
||||
"end_struct\n" +
|
||||
"struct myStruct2\n" +
|
||||
"myStruct[5 iterate] foo\n" +
|
||||
"end_struct"
|
||||
);
|
||||
|
||||
Assert.assertEquals(2, state.getStructs().size());
|
||||
|
||||
// Inner should be empty
|
||||
Struct inner = state.getStructs().get(0);
|
||||
Assert.assertEquals(0, inner.fields.size());
|
||||
|
||||
// Check that the outer contains an array of structs
|
||||
Struct outer = state.getStructs().get(1);
|
||||
Assert.assertEquals(1, outer.fields.size());
|
||||
|
||||
ArrayField af = (ArrayField)outer.fields.get(0);
|
||||
|
||||
// Check length and that it contains a struct
|
||||
Assert.assertEquals(1, af.length.length);
|
||||
Assert.assertEquals(5, af.length[0]);
|
||||
Assert.assertTrue(af.prototype instanceof StructField);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typedefScalar() throws IOException {
|
||||
ParseState ps = parse("custom myTypedef 4 scalar, F32, @OFFSET@, \"unit\", 1, 2, 3, 4, 5\n" +
|
||||
"struct myStruct\n" +
|
||||
"myTypedef foo;comment\n" +
|
||||
"end_struct");
|
||||
ScalarField sf = (ScalarField)ps.getLastStruct().fields.get(0);
|
||||
|
||||
Assert.assertEquals("foo", sf.name);
|
||||
|
||||
Assert.assertEquals(false, sf.autoscale);
|
||||
Assert.assertEquals(4, sf.type.size);
|
||||
Assert.assertEquals("float", sf.type.cType);
|
||||
Assert.assertEquals("F32", sf.type.tsType);
|
||||
|
||||
FieldOptions opt = sf.options;
|
||||
Assert.assertEquals("comment", opt.comment);
|
||||
Assert.assertEquals("\"unit\"", opt.units);
|
||||
Assert.assertEquals(1, opt.scale, 0.001f);
|
||||
Assert.assertEquals(2, opt.offset, 0.001f);
|
||||
Assert.assertEquals(3, opt.min, 0.001f);
|
||||
Assert.assertEquals(4, opt.max, 0.001f);
|
||||
Assert.assertEquals(5, opt.digits);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typedefString() throws IOException {
|
||||
ParseState ps = parse("custom lua_script_t 1000 string, ASCII, @OFFSET@, 1000\n" +
|
||||
"struct myStruct\n" +
|
||||
"lua_script_t luaScript\n" +
|
||||
"end_struct");
|
||||
StringField sf = (StringField)ps.getLastStruct().fields.get(0);
|
||||
|
||||
Assert.assertEquals("luaScript", sf.name);
|
||||
Assert.assertEquals(1000, sf.size);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unused() throws IOException {
|
||||
ParseState state = parse(
|
||||
"struct_no_prefix myStruct\n" +
|
||||
"unused 27\n" +
|
||||
"end_struct\n"
|
||||
);
|
||||
|
||||
UnusedField uf = (UnusedField)state.getLastStruct().fields.get(0);
|
||||
|
||||
Assert.assertEquals(27, uf.size);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bitFieldsBasic() throws IOException {
|
||||
ParseState state = parse(
|
||||
"struct_no_prefix myStruct\n" +
|
||||
"bit myBit1\n" +
|
||||
"bit myBit2\n" +
|
||||
"end_struct\n"
|
||||
);
|
||||
|
||||
BitGroup bf = (BitGroup)state.getLastStruct().fields.get(0);
|
||||
|
||||
Assert.assertEquals(2, bf.bitFields.size());
|
||||
|
||||
Assert.assertEquals("myBit1", bf.bitFields.get(0).name);
|
||||
Assert.assertEquals("myBit2", bf.bitFields.get(1).name);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bitFieldsAdvanced() throws IOException {
|
||||
ParseState state = parse(
|
||||
"struct_no_prefix myStruct\n" +
|
||||
"bit myBit,\"a\",\"b\";comment\n" +
|
||||
"end_struct\n"
|
||||
);
|
||||
|
||||
BitGroup bg = (BitGroup)state.getLastStruct().fields.get(0);
|
||||
BitField bf = bg.bitFields.get(0);
|
||||
|
||||
Assert.assertEquals("myBit", bf.name);
|
||||
Assert.assertEquals("\"a\"", bf.trueValue);
|
||||
Assert.assertEquals("\"b\"", bf.falseValue);
|
||||
Assert.assertEquals("comment", bf.comment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bitFieldsThirtyThreeBits() throws IOException {
|
||||
StringBuilder input = new StringBuilder("struct myStruct\n");
|
||||
for (int i = 0; i < 33; i++) {
|
||||
input.append("bit myBit").append(i).append("\n");
|
||||
}
|
||||
input.append("end_struct\n");
|
||||
|
||||
ParseState state = parse(input.toString());
|
||||
|
||||
Assert.assertEquals(2, state.getLastStruct().fields.size());
|
||||
|
||||
Assert.assertEquals(32, ((BitGroup)state.getLastStruct().fields.get(0)).bitFields.size());
|
||||
Assert.assertEquals(1, ((BitGroup)state.getLastStruct().fields.get(1)).bitFields.size());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue