Antlr 2 (#2823)
* grammar and libs * gitignore * parsing * allow empty line as root statement * tolerate #if * config def changes * s * ant build * workaround * compiled tool * grammar for unions * parse unions * layout logic * fix union alignment * union in config * jar * comment * jar * jar * no prefix on root struct * stray space * normalize some enums
This commit is contained in:
parent
94d7a413b2
commit
d3d6c1694a
|
@ -66,7 +66,7 @@
|
|||
! all the sub-structures are going to be nested within the primary structure, that's
|
||||
! needed to get a proper TunerStudio file
|
||||
|
||||
struct persistent_config_s
|
||||
struct_no_prefix persistent_config_s
|
||||
|
||||
struct_no_prefix engine_configuration_s
|
||||
|
||||
|
@ -1620,6 +1620,11 @@ lambda_table_t lambdaTable;
|
|||
afr_table_t lambdaTable;
|
||||
#endif
|
||||
|
||||
! union
|
||||
! lambda_table_t lambdaTable
|
||||
! afr_table_t afrTable
|
||||
! end_union
|
||||
|
||||
float[FUEL_LOAD_COUNT] lambdaLoadBins;;"", 1, 0.0, 0, 500.0, 2
|
||||
float[FUEL_RPM_COUNT] lambdaRpmBins;;"RPM", 1, 0.0, 0, 18000.0, 2
|
||||
|
||||
|
|
Binary file not shown.
|
@ -95,10 +95,17 @@ scalarField: identifier FsioVisible? identifier (fieldOptionsList)?;
|
|||
arrayField: identifier '[' arrayLengthSpec Iterate? ']' identifier SemicolonedString? (fieldOptionsList)?;
|
||||
bitField: Bit identifier (',' QuotedString ',' QuotedString)? ('(' 'comment' ':' QuotedString ')')? SemicolonedSuffix?;
|
||||
|
||||
unionField: 'union' ENDL+ fields 'end_union';
|
||||
|
||||
field
|
||||
: scalarField
|
||||
| arrayField
|
||||
| bitField
|
||||
| unionField
|
||||
;
|
||||
|
||||
fields
|
||||
: (field ENDL+)+
|
||||
;
|
||||
|
||||
// Indicates X bytes of free space
|
||||
|
|
|
@ -151,6 +151,13 @@ public class ParseState extends RusefiConfigGrammarBaseListener {
|
|||
scope = new Scope();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterUnionField(RusefiConfigGrammarParser.UnionFieldContext ctx) {
|
||||
// Unions behave like a struct as far as scope is concerned (but is processed differently later
|
||||
// to overlap all members, instead of placing them in sequence as in a struct)
|
||||
enterStruct(null);
|
||||
}
|
||||
|
||||
void handleFieldOptionsList(FieldOptions options, RusefiConfigGrammarParser.FieldOptionsListContext ctx) {
|
||||
// Null means no options were configured, use defaults
|
||||
if (ctx == null) {
|
||||
|
@ -412,6 +419,23 @@ public class ParseState extends RusefiConfigGrammarBaseListener {
|
|||
}
|
||||
}
|
||||
|
||||
@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());
|
||||
|
||||
Union u = new Union(scope.structFields);
|
||||
|
||||
// Restore the containing scope
|
||||
scope = scopes.pop();
|
||||
|
||||
// Lastly, add the union to the scope
|
||||
scope.structFields.add(u);
|
||||
}
|
||||
|
||||
private Stack<Float> evalStack = new Stack<>();
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package com.rusefi.newparse.layout;
|
||||
|
||||
import com.rusefi.newparse.parsing.*;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
public class ArrayIterateScalarLayout extends ArrayLayout {
|
||||
public ArrayIterateScalarLayout(PrototypeField prototype, int length) {
|
||||
super(prototype, length);
|
||||
}
|
||||
|
||||
private void emitOne(PrintStream ps, StructNamePrefixer prefixer, int offset, int idx) {
|
||||
// Set element's position within the array
|
||||
this.prototypeLayout.setOffset(offset + this.prototypeLayout.getSize() * idx);
|
||||
|
||||
// Put a 1-based index on the end of the name to distinguish in TS
|
||||
prefixer.setSuffix(Integer.toString(idx + 1));
|
||||
this.prototypeLayout.writeTunerstudioLayout(ps, prefixer);
|
||||
prefixer.resetSuffix();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTunerstudioLayout(PrintStream ps, StructNamePrefixer prefixer) {
|
||||
// Time to iterate: emit one scalar per array element, with the name modified accordingly
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
emitOne(ps, prefixer, this.offset, i);
|
||||
}
|
||||
}
|
||||
|
||||
// C layout is the same if iterated or not, use default implementation
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package com.rusefi.newparse.layout;
|
||||
|
||||
import com.rusefi.newparse.parsing.*;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
public class ArrayIterateStructLayout extends ArrayLayout {
|
||||
public ArrayIterateStructLayout(StructField prototype, int length) {
|
||||
super(prototype, length);
|
||||
}
|
||||
|
||||
private void emitOne(PrintStream ps, StructNamePrefixer prefixer, int offset, int idx) {
|
||||
// Set element's position within the array
|
||||
this.prototypeLayout.setOffset(offset + this.prototypeLayout.getSize() * idx);
|
||||
|
||||
// Put a 1-based index on the end of the name to distinguish in TS
|
||||
prefixer.setSuffix(Integer.toString(idx + 1));
|
||||
this.prototypeLayout.writeTunerstudioLayout(ps, prefixer);
|
||||
prefixer.resetSuffix();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTunerstudioLayout(PrintStream ps, StructNamePrefixer prefixer) {
|
||||
// Time to iterate: emit one scalar per array element, with the name modified accordingly
|
||||
|
||||
for (int i = 0; i < this.length; i++) {
|
||||
emitOne(ps, prefixer, this.offset, i);
|
||||
}
|
||||
}
|
||||
|
||||
// C layout is the same if iterated or not, use default implementation
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package com.rusefi.newparse.layout;
|
||||
|
||||
import com.rusefi.newparse.parsing.*;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
public class ArrayLayout extends Layout {
|
||||
protected final int length;
|
||||
|
||||
protected final Layout prototypeLayout;
|
||||
|
||||
public ArrayLayout(PrototypeField prototype, int length) {
|
||||
this.length = length;
|
||||
|
||||
if (prototype instanceof ScalarField) {
|
||||
prototypeLayout = new ScalarLayout((ScalarField)prototype);
|
||||
} else if (prototype instanceof EnumField) {
|
||||
prototypeLayout = new EnumLayout((EnumField) prototype);
|
||||
} else if (prototype instanceof StringField) {
|
||||
prototypeLayout = new StringLayout((StringField) prototype);
|
||||
} else if (prototype instanceof StructField) {
|
||||
StructField structPrototype = (StructField)prototype;
|
||||
prototypeLayout = new StructLayout(0, prototype.name, structPrototype.struct);
|
||||
} else {
|
||||
throw new RuntimeException("unexpected field type during array layout");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return this.prototypeLayout.getSize() * this.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlignment() {
|
||||
// Arrays only need to be aligned on the alignment of the element, not the whole array
|
||||
return this.prototypeLayout.getAlignment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOffset(int offset) {
|
||||
super.setOffset(offset);
|
||||
this.prototypeLayout.setOffset(offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOffsetWithinStruct(int offset) {
|
||||
super.setOffsetWithinStruct(offset);
|
||||
this.prototypeLayout.setOffsetWithinStruct(offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Array of " + this.prototypeLayout.toString() + " length " + this.length + " " + super.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTunerstudioLayout(PrintStream ps, StructNamePrefixer prefixer) {
|
||||
this.prototypeLayout.writeTunerstudioLayout(ps, prefixer, this.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeCLayout(PrintStream ps) {
|
||||
// Skip zero length arrays, they may be used for padding
|
||||
if (this.length > 0) {
|
||||
this.prototypeLayout.writeCLayout(ps, this.length);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package com.rusefi.newparse.layout;
|
||||
|
||||
import com.rusefi.newparse.parsing.BitGroup;
|
||||
import com.rusefi.newparse.parsing.EnumField;
|
||||
import com.rusefi.newparse.parsing.Type;
|
||||
import com.rusefi.newparse.parsing.UnusedField;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BitGroupLayout extends Layout {
|
||||
private class BitLayout {
|
||||
public final String name;
|
||||
public final String comment;
|
||||
|
||||
public BitLayout(String name, String comment) {
|
||||
this.name = name;
|
||||
this.comment = comment;
|
||||
}
|
||||
}
|
||||
|
||||
private final List<BitLayout> bits;
|
||||
|
||||
public BitGroupLayout(BitGroup bitGroup) {
|
||||
int size = bitGroup.bitFields.size();
|
||||
if (size > 32) {
|
||||
throw new RuntimeException("tried to create bit group starting with " + bitGroup.bitFields.get(0).name + " but it contained " + size + " which is more than the maximum of 32.");
|
||||
}
|
||||
|
||||
this.bits = bitGroup.bitFields.stream().map(bf -> new BitLayout(bf.name, bf.comment)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Bit group " + super.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTunerstudioLayout(PrintStream ps, StructNamePrefixer prefixer) {
|
||||
for (int i = 0; i < bits.size(); i++) {
|
||||
BitLayout bit = bits.get(i);
|
||||
ps.print(prefixer.get(bit.name));
|
||||
ps.print(" = bits, U32, ");
|
||||
ps.print(this.offset);
|
||||
ps.print(", [");
|
||||
ps.print(i + ":" + i);
|
||||
|
||||
// TODO: print actual bit options
|
||||
ps.print("], \"false\", \"true\"");
|
||||
|
||||
ps.println();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeCLayout(PrintStream ps) {
|
||||
// always emit all 32 bits
|
||||
for (int i = 0; i < 32; i++) {
|
||||
ps.print("\t/**\n\t");
|
||||
|
||||
if (i < bits.size()) {
|
||||
BitLayout bit = this.bits.get(i);
|
||||
|
||||
if (bit.comment != null) {
|
||||
ps.println(" * " + bit.comment.replaceAll("[+]", "").replaceAll(";", "").replace("\\n", "\n\t * "));
|
||||
ps.print('\t');
|
||||
}
|
||||
|
||||
ps.println("offset " + this.offsetWithinStruct + " bit " + i + " */");
|
||||
ps.println("\tbool " + bit.name + " : 1;");
|
||||
} else {
|
||||
// Force pad out all bit groups to a full 32b/4B
|
||||
ps.println("offset " + this.offsetWithinStruct + " bit " + i + " */");
|
||||
ps.println("\tbool unusedBit_" + this.offsetWithinStruct + "_" + i + " : 1;");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package com.rusefi.newparse.layout;
|
||||
|
||||
import com.rusefi.newparse.parsing.EnumField;
|
||||
import com.rusefi.newparse.parsing.FieldOptions;
|
||||
import com.rusefi.newparse.parsing.Type;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
public class EnumLayout extends Layout {
|
||||
private final String name;
|
||||
private final Type type;
|
||||
private final String enumType;
|
||||
private final String values;
|
||||
private final FieldOptions options;
|
||||
|
||||
public EnumLayout(EnumField field) {
|
||||
this.name = field.name;
|
||||
this.type = field.type;
|
||||
this.enumType = field.enumType;
|
||||
this.values = field.values;
|
||||
this.options = field.options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return this.type.size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTunerstudioLayout(PrintStream ps, StructNamePrefixer prefixer) {
|
||||
ps.print(prefixer.get(this.name));
|
||||
ps.print(" = bits, ");
|
||||
ps.print(this.type.tsType);
|
||||
ps.print(", ");
|
||||
ps.print(this.offset);
|
||||
ps.print(", ");
|
||||
|
||||
// TODO: automatically compute number of bits required?
|
||||
ps.print("[0:7], ");
|
||||
|
||||
// TODO: where should value define resolution happen?
|
||||
ps.print(this.values);
|
||||
|
||||
ps.println();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeCLayout(PrintStream ps) {
|
||||
this.writeCOffsetHeader(ps, this.options.comment, this.options.units);
|
||||
ps.println("\t" + this.enumType + " " + this.name + ";");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeCLayout(PrintStream ps, int arrayLength) {
|
||||
this.writeCOffsetHeader(ps, this.options.comment, this.options.units);
|
||||
ps.println("\t" + this.enumType + " " + this.name + "[" + arrayLength + "];");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package com.rusefi.newparse.layout;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
public abstract class Layout {
|
||||
public int offset = -1;
|
||||
public int offsetWithinStruct = -1;
|
||||
|
||||
public abstract int getSize();
|
||||
public int getAlignment() {
|
||||
// Default to size
|
||||
return this.getSize();
|
||||
}
|
||||
|
||||
public void setOffset(int offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public void setOffsetWithinStruct(int offset) {
|
||||
offsetWithinStruct = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "offset = " + offset + " size = " + this.getSize();
|
||||
}
|
||||
|
||||
public final void writeTunerstudioLayout(PrintStream ps) {
|
||||
writeTunerstudioLayout(ps, new StructNamePrefixer());
|
||||
}
|
||||
|
||||
public void writeTunerstudioLayout(PrintStream ps, StructNamePrefixer prefixer) {}
|
||||
|
||||
public void writeTunerstudioLayout(PrintStream ps, StructNamePrefixer prefixer, int arrayLength) {
|
||||
throw new IllegalStateException("This type can't be in an array!");
|
||||
}
|
||||
|
||||
protected void writeCOffsetHeader(PrintStream ps, String comment, String units) {
|
||||
ps.println("\t/**");
|
||||
|
||||
if (comment != null) {
|
||||
comment = comment.replaceAll(";", "");
|
||||
comment = comment.replaceAll("[+]", "");
|
||||
comment = comment.replaceAll("\\n", "\n\t * ");
|
||||
if (comment.length() == 0) {
|
||||
comment = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (comment != null) {
|
||||
comment = comment.replaceAll("\\\\n", "\n\t * ");
|
||||
|
||||
ps.println("\t * " + comment);
|
||||
}
|
||||
|
||||
if (units != null && units.length() > 2) {
|
||||
ps.println("\t" + units.replace("\"", ""));
|
||||
}
|
||||
|
||||
ps.println("\t * offset " + this.offsetWithinStruct);
|
||||
ps.println("\t */");
|
||||
}
|
||||
|
||||
public void writeCLayout(PrintStream ps) { }
|
||||
|
||||
public void writeCLayout(PrintStream ps, int arrayLength) {
|
||||
throw new IllegalStateException("This type can't be in an array!");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package com.rusefi.newparse.layout;
|
||||
|
||||
import com.rusefi.newparse.parsing.FieldOptions;
|
||||
import com.rusefi.newparse.parsing.ScalarField;
|
||||
import com.rusefi.newparse.parsing.Type;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
public class ScalarLayout extends Layout {
|
||||
private String name;
|
||||
private Type type;
|
||||
private FieldOptions options;
|
||||
|
||||
public ScalarLayout(ScalarField field) {
|
||||
this.name = field.name;
|
||||
this.options = field.options;
|
||||
this.type = field.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return this.type.size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Scalar " + type.cType + " " + super.toString();
|
||||
}
|
||||
|
||||
private void printBeforeArrayLength(PrintStream ps, StructNamePrefixer prefixer) {
|
||||
ps.print(prefixer.get(this.name));
|
||||
ps.print(" = scalar, ");
|
||||
ps.print(this.type.tsType);
|
||||
ps.print(", ");
|
||||
ps.print(this.offset);
|
||||
ps.print(", ");
|
||||
}
|
||||
|
||||
private void printAfterArrayLength(PrintStream ps) {
|
||||
options.printTsFormat(ps);
|
||||
|
||||
ps.println();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTunerstudioLayout(PrintStream ps, StructNamePrefixer prefixer, int arrayLength) {
|
||||
printBeforeArrayLength(ps, prefixer);
|
||||
|
||||
ps.print("[");
|
||||
ps.print(arrayLength);
|
||||
ps.print("], ");
|
||||
|
||||
printAfterArrayLength(ps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTunerstudioLayout(PrintStream ps, StructNamePrefixer prefixer) {
|
||||
printBeforeArrayLength(ps, prefixer);
|
||||
printAfterArrayLength(ps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeCLayout(PrintStream ps) {
|
||||
this.writeCOffsetHeader(ps, this.options.comment, this.options.units);
|
||||
ps.println("\t" + this.type.cType.replaceAll("^int32_t$", "int") + " " + this.name + ";");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeCLayout(PrintStream ps, int arrayLength) {
|
||||
this.writeCOffsetHeader(ps, this.options.comment, this.options.units);
|
||||
ps.println("\t" + this.type.cType.replaceAll("^int32_t$", "int") + " " + this.name + "[" + arrayLength + "];");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package com.rusefi.newparse.layout;
|
||||
|
||||
import com.rusefi.newparse.parsing.StringField;
|
||||
import com.rusefi.newparse.parsing.UnusedField;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
public class StringLayout extends Layout {
|
||||
private final String name;
|
||||
private final int size;
|
||||
|
||||
public StringLayout(StringField field) {
|
||||
this.name = field.name;
|
||||
this.size = field.size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlignment() {
|
||||
// char can be single aligned
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "String " + super.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTunerstudioLayout(PrintStream ps, StructNamePrefixer prefixer) {
|
||||
ps.print(prefixer.get(this.name));
|
||||
ps.print(" = scalar, ASCII, ");
|
||||
ps.print(this.offset);
|
||||
ps.print(", ");
|
||||
ps.print(size);
|
||||
|
||||
ps.println();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeCLayout(PrintStream ps) {
|
||||
this.writeCOffsetHeader(ps, null, null);
|
||||
ps.println("\tchar " + this.name + "[" + this.size + "];");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeCLayout(PrintStream ps, int arrayLength) {
|
||||
this.writeCOffsetHeader(ps, null, null);
|
||||
ps.println("\tchar " + this.name + "[" + arrayLength + "][" + this.size + "];");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
package com.rusefi.newparse.layout;
|
||||
|
||||
import com.rusefi.newparse.parsing.*;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class StructLayout extends Layout {
|
||||
/*private*/public List<Layout> children = new ArrayList<>();
|
||||
|
||||
public final String typeName;
|
||||
private final String name;
|
||||
private final String comment;
|
||||
private final Boolean noPrefix;
|
||||
private final int size;
|
||||
|
||||
private static int getAlignedOffset(int offset, int alignment) {
|
||||
// Align each element to its own size
|
||||
if ((offset % alignment) != 0) {
|
||||
return offset + alignment - (offset % alignment);
|
||||
} else {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
|
||||
int padOffsetWithUnused(int offset, int align) {
|
||||
int alignedOffset = getAlignedOffset(offset, align);
|
||||
|
||||
int needsUnused = alignedOffset - offset;
|
||||
|
||||
if (needsUnused > 0) {
|
||||
UnusedLayout ul = new UnusedLayout(needsUnused);
|
||||
ul.setOffset(offset);
|
||||
ul.setOffsetWithinStruct(offset - this.offset);
|
||||
children.add(ul);
|
||||
return alignedOffset;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
public StructLayout(int offset, String name, Struct parsedStruct) {
|
||||
setOffset(offset);
|
||||
|
||||
this.typeName = parsedStruct.name;
|
||||
this.name = name;
|
||||
this.comment = parsedStruct.comment;
|
||||
this.noPrefix = parsedStruct.noPrefix;
|
||||
|
||||
int initialOffest = offset;
|
||||
|
||||
for (Field f : parsedStruct.fields) {
|
||||
if (f instanceof ArrayField) {
|
||||
ArrayField asf = (ArrayField)f;
|
||||
|
||||
// If not a scalar, you must iterate
|
||||
assert(asf.prototype instanceof ScalarField || asf.iterate);
|
||||
|
||||
if (asf.iterate) {
|
||||
if (asf.prototype instanceof StructField) {
|
||||
// Struct: special case of a struct array
|
||||
offset = addItem(offset, new ArrayIterateStructLayout((StructField)asf.prototype, asf.length));
|
||||
} else {
|
||||
// array of scalars (or enums)
|
||||
offset = addItem(offset, new ArrayIterateScalarLayout(asf.prototype, asf.length));
|
||||
}
|
||||
} else /* not iterate */ {
|
||||
// If not a scalar, you must iterate
|
||||
assert(asf.prototype instanceof ScalarField);
|
||||
|
||||
ScalarField prototype = (ScalarField)asf.prototype;
|
||||
offset = addItem(offset, new ArrayLayout(prototype, asf.length));
|
||||
}
|
||||
} else {
|
||||
offset = addItem(offset, f);
|
||||
}
|
||||
}
|
||||
|
||||
// Structs are always a multiple of 4 bytes long, pad the end appropriately
|
||||
offset = padOffsetWithUnused(offset, 4);
|
||||
|
||||
size = offset - initialOffest;
|
||||
}
|
||||
|
||||
private int addItem(int offset, Field f) {
|
||||
if (f instanceof StructField) {
|
||||
// Special case for structs - we have to compute base offset first
|
||||
StructField sf = (StructField) f;
|
||||
|
||||
return addStruct(offset, sf.struct, sf.name);
|
||||
}
|
||||
|
||||
Layout l = null;
|
||||
if (f instanceof ScalarField) {
|
||||
l = new ScalarLayout((ScalarField)f);
|
||||
} else if (f instanceof EnumField) {
|
||||
l = new EnumLayout((EnumField)f);
|
||||
} else if (f instanceof UnusedField) {
|
||||
l = new UnusedLayout((UnusedField) f);
|
||||
} else if (f instanceof BitGroup) {
|
||||
l = new BitGroupLayout((BitGroup) f);
|
||||
} else if (f instanceof StringField) {
|
||||
l = new StringLayout((StringField) f);
|
||||
} else if (f instanceof Union) {
|
||||
l = new UnionLayout((Union)f);
|
||||
} else {
|
||||
throw new RuntimeException("unexpected field type during layout");
|
||||
}
|
||||
|
||||
return addItem(offset, l);
|
||||
}
|
||||
|
||||
private int addItem(int offset, Layout l) {
|
||||
// Slide the offset up by the required alignment of this element
|
||||
offset = padOffsetWithUnused(offset, l.getAlignment());
|
||||
|
||||
// place the element
|
||||
l.setOffset(offset);
|
||||
l.setOffsetWithinStruct(offset - this.offset);
|
||||
children.add(l);
|
||||
|
||||
return offset + l.getSize();
|
||||
}
|
||||
|
||||
private int addStruct(int offset, Struct struct, String name) {
|
||||
offset = padOffsetWithUnused(offset, 4);
|
||||
|
||||
// Recurse and build this new struct
|
||||
StructLayout sl = new StructLayout(offset, name, struct);
|
||||
|
||||
sl.setOffsetWithinStruct(offset - this.offset);
|
||||
this.children.add(sl);
|
||||
|
||||
// Update offset with the struct size - it's guaranteed to be a multiple of 4 bytes
|
||||
int structSize = sl.getSize();
|
||||
return offset + structSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlignment() {
|
||||
// All structs should be aligned on a 4 byte boundary
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Struct " + this.typeName + " " + super.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void writeTunerstudioLayout(PrintStream ps, StructNamePrefixer prefixer) {
|
||||
if (!this.noPrefix) {
|
||||
ps.println("; start struct " + this.typeName);
|
||||
prefixer.push(this.name);
|
||||
}
|
||||
|
||||
// print all children in sequence
|
||||
this.children.forEach(c -> c.writeTunerstudioLayout(ps, prefixer));
|
||||
|
||||
if (!this.noPrefix) {
|
||||
prefixer.pop();
|
||||
}
|
||||
|
||||
if (!this.noPrefix) {
|
||||
ps.println("; end struct " + this.typeName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeCLayout(PrintStream ps) {
|
||||
this.writeCOffsetHeader(ps, null, null);
|
||||
ps.println("\t" + this.typeName + " " + this.name + ";");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeCLayout(PrintStream ps, int arrayLength) {
|
||||
this.writeCOffsetHeader(ps, null, null);
|
||||
ps.println("\t" + this.typeName + " " + this.name + "[" + arrayLength + "];");
|
||||
}
|
||||
|
||||
public void writeCLayoutRoot(PrintStream ps) {
|
||||
if (this.comment != null) {
|
||||
ps.println("/**\n * @brief " + this.comment);
|
||||
ps.println("*/");
|
||||
}
|
||||
|
||||
ps.println("// start of " + this.typeName);
|
||||
ps.println("struct " + this.typeName + " {");
|
||||
|
||||
this.children.forEach(c -> c.writeCLayout(ps));
|
||||
|
||||
ps.println("\t/** total size " + getSize() + "*/");
|
||||
ps.println("};");
|
||||
ps.println();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package com.rusefi.newparse.layout;
|
||||
|
||||
import java.util.Stack;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class StructNamePrefixer {
|
||||
private Stack<String> stack = new Stack<>();
|
||||
|
||||
public void pop() {
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
public void push(String name) {
|
||||
stack.push(name + "_");
|
||||
}
|
||||
|
||||
private String suffix = new String();
|
||||
|
||||
public void setSuffix(String suffix) {
|
||||
this.suffix = suffix;
|
||||
}
|
||||
|
||||
public void resetSuffix() {
|
||||
this.suffix = new String();
|
||||
}
|
||||
|
||||
String get(String name) {
|
||||
return stack.stream().collect(Collectors.joining()) + name + suffix;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package com.rusefi.newparse.layout;
|
||||
|
||||
import com.rusefi.newparse.parsing.ArrayField;
|
||||
import com.rusefi.newparse.parsing.Field;
|
||||
import com.rusefi.newparse.parsing.ScalarField;
|
||||
import com.rusefi.newparse.parsing.Union;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class UnionLayout extends Layout {
|
||||
private List<Layout> children = new ArrayList<>();
|
||||
|
||||
public UnionLayout(Union u) {
|
||||
for (Field f : u.fields) {
|
||||
// For every child, check if it is the largest, and grow if so
|
||||
if (f instanceof ArrayField) {
|
||||
ArrayField af = (ArrayField)f;
|
||||
|
||||
// we probably don't need union of iterate?
|
||||
assert(!af.iterate);
|
||||
|
||||
ScalarField prototype = (ScalarField)af.prototype;
|
||||
this.children.add(new ArrayLayout(prototype, af.length));
|
||||
} else if (f instanceof ScalarField) {
|
||||
this.children.add(new ScalarLayout((ScalarField)f));
|
||||
} else {
|
||||
throw new RuntimeException("Tried to create union with member type " + f.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOffset(int offset) {
|
||||
super.setOffset(offset);
|
||||
this.children.stream().forEach(c -> c.setOffset(offset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOffsetWithinStruct(int offset) {
|
||||
super.setOffsetWithinStruct(offset);
|
||||
this.children.stream().forEach(c -> c.setOffsetWithinStruct(offset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return this.children.stream().map(l -> l.getSize()).max(Integer::compare).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlignment() {
|
||||
// The alignment of the union is the largest alignment required by one of the members
|
||||
return this.children.stream().map(l -> l.getAlignment()).max(Integer::compare).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTunerstudioLayout(PrintStream ps, StructNamePrefixer prefixer) {
|
||||
// Simply write out all children - no container necessary as fields can overlap in TS
|
||||
this.children.forEach(c -> c.writeTunerstudioLayout(ps, prefixer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeCLayout(PrintStream ps) {
|
||||
this.writeCOffsetHeader(ps, "union size " + this.getSize() + ", " + this.children.size() + " members", null);
|
||||
|
||||
// emit an anonymous union that contains all our members
|
||||
ps.println("\tunion {");
|
||||
this.children.forEach(c -> c.writeCLayout(ps));
|
||||
ps.println("\t};");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package com.rusefi.newparse.layout;
|
||||
|
||||
import com.rusefi.newparse.parsing.EnumField;
|
||||
import com.rusefi.newparse.parsing.Type;
|
||||
import com.rusefi.newparse.parsing.UnusedField;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.Random;
|
||||
|
||||
public class UnusedLayout extends Layout {
|
||||
private final int size;
|
||||
|
||||
public UnusedLayout(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public UnusedLayout(UnusedField field) {
|
||||
this.size = field.size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlignment() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Unused " + super.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTunerstudioLayout(PrintStream ps, StructNamePrefixer prefixer) {
|
||||
ps.println("; unused " + this.size + " bytes at offset " + this.offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeCLayout(PrintStream ps) {
|
||||
this.writeCOffsetHeader(ps, null, null);
|
||||
ps.println("\tchar unused" + this.offsetWithinStruct + "[" + this.size + "];");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.rusefi.newparse.parsing;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Union implements Field {
|
||||
public final List<Field> fields;
|
||||
|
||||
public Union(List<Field> fields) {
|
||||
this.fields = fields;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue