From e7812266f0159b3de091c9e33eef6aa7cf20cb51 Mon Sep 17 00:00:00 2001 From: Mario Welzig Date: Sat, 11 Nov 2023 14:06:58 +0100 Subject: [PATCH 1/4] add warning for assignments in control flow conditions --- .../coredsl/analysis/CoreDslAnalyzer.xtend | 40 +++++++++++++++---- .../coredsl/validation/IssueCodes.xtend | 1 + 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslAnalyzer.xtend b/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslAnalyzer.xtend index 17b686cf..652cf6d3 100644 --- a/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslAnalyzer.xtend +++ b/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslAnalyzer.xtend @@ -442,11 +442,14 @@ class CoreDslAnalyzer { } /** - * 1. The condition must be an expression with a scalar type. (NonScalarCondition) + * 1. The condition must be an expression with a scalar type. (NonScalarCondition)
+ * 2. [Warning] The condition should not be an assignment. (LikelyAccidentalAssignment) */ def static dispatch void analyzeStatement(AnalysisContext ctx, IfStatement statement) { val conditionType = analyzeExpression(ctx, statement.condition); + checkAccidentalAssignment(ctx, statement.condition); + if(!conditionType.isScalarType && !conditionType.isError) { ctx.acceptError("The condition must be a scalar type, but was " + conditionType, statement, CoreDslPackage.Literals.IF_STATEMENT__CONDITION, -1, IssueCodes.NonScalarCondition); @@ -550,11 +553,14 @@ class CoreDslAnalyzer { } /** - * 1. The condition must be an expression with a scalar type. (NonScalarCondition) + * 1. The condition must be an expression with a scalar type. (NonScalarCondition)
+ * 2. [Warning] The condition should not be an assignment. (LikelyAccidentalAssignment) */ def static dispatch void analyzeStatement(AnalysisContext ctx, WhileLoop statement) { val conditionType = analyzeExpression(ctx, statement.condition); + checkAccidentalAssignment(ctx, statement.condition); + if(!conditionType.isScalarType && !conditionType.isError) { ctx.acceptError("The condition must be a scalar type, but was " + conditionType, statement, CoreDslPackage.Literals.LOOP_STATEMENT__CONDITION, -1, IssueCodes.NonScalarCondition); @@ -566,7 +572,8 @@ class CoreDslAnalyzer { /** * 1. The condition must be an expression with a scalar type. (NonScalarCondition)
* 2. If present, the start expression must be a statement expression. (InvalidStatementExpression)
- * 3. All loop expressions must be statement expressions. (InvalidStatementExpression) + * 3. All loop expressions must be statement expressions. (InvalidStatementExpression)
+ * 4. [Warning] The condition should not be an assignment. (LikelyAccidentalAssignment) */ def static dispatch void analyzeStatement(AnalysisContext ctx, ForLoop statement) { @@ -584,6 +591,9 @@ class CoreDslAnalyzer { if(statement.condition !== null) { val conditionType = analyzeExpression(ctx, statement.condition); + + checkAccidentalAssignment(ctx, statement.condition); + if(!conditionType.isScalarType && !conditionType.isError) { ctx.acceptError("The condition must be a scalar type, but was " + conditionType, statement, CoreDslPackage.Literals.LOOP_STATEMENT__CONDITION, -1, IssueCodes.NonScalarCondition); @@ -603,11 +613,14 @@ class CoreDslAnalyzer { } /** - * 1. The condition must be an expression with a scalar type. (NonScalarCondition) + * 1. The condition must be an expression with a scalar type. (NonScalarCondition)
+ * 2. [Warning] The condition should not be an assignment. (LikelyAccidentalAssignment) */ def static dispatch void analyzeStatement(AnalysisContext ctx, DoLoop statement) { val conditionType = analyzeExpression(ctx, statement.condition); + checkAccidentalAssignment(ctx, statement.condition); + if(!conditionType.isScalarType && !conditionType.isError) { ctx.acceptError("The condition must be a scalar type, but was " + conditionType, statement, CoreDslPackage.Literals.LOOP_STATEMENT__CONDITION, -1, IssueCodes.NonScalarCondition); @@ -630,12 +643,14 @@ class CoreDslAnalyzer { if(isInstrBlockChild) { var block = statement.eContainer as CompoundStatement; if(block.statements.indexOf(statement) != block.statements.size - 1) { - ctx.acceptError("A spawn statement must be the last statement of an instruction's behavior block", statement, - CoreDslPackage.Literals.SPAWN_STATEMENT__TSPAWN, -1, IssueCodes.InvalidSpawnStatementPlacement) + ctx.acceptError("A spawn statement must be the last statement of an instruction's behavior block", + statement, CoreDslPackage.Literals.SPAWN_STATEMENT__TSPAWN, -1, + IssueCodes.InvalidSpawnStatementPlacement) } } else if(!isDirectInstrChild) { - ctx.acceptError("A spawn statement must be the last statement of an instruction's behavior block", statement, - CoreDslPackage.Literals.SPAWN_STATEMENT__TSPAWN, -1, IssueCodes.InvalidSpawnStatementPlacement) + ctx.acceptError("A spawn statement must be the last statement of an instruction's behavior block", + statement, CoreDslPackage.Literals.SPAWN_STATEMENT__TSPAWN, -1, + IssueCodes.InvalidSpawnStatementPlacement) } analyzeStatement(ctx, statement.body); @@ -894,6 +909,15 @@ class CoreDslAnalyzer { } } + def static checkAccidentalAssignment(AnalysisContext ctx, Expression booleanExpression) { + if(booleanExpression instanceof AssignmentExpression) { + if(booleanExpression.operator == "=") { + ctx.acceptWarning("Likely accidental assignment", booleanExpression, + CoreDslPackage.Literals.ASSIGNMENT_EXPRESSION__OPERATOR, -1, IssueCodes.LikelyAccidentalAssignment); + } + } + } + /** * 1. Only ISA state elements may be declared as aliases. (AliasLocalVariable)
* 2. The declarator must be initialized with an expression. (UninitializedAlias, InvalidListInitializer)
diff --git a/com.minres.coredsl/src/com/minres/coredsl/validation/IssueCodes.xtend b/com.minres.coredsl/src/com/minres/coredsl/validation/IssueCodes.xtend index 533ba283..bf9301ed 100644 --- a/com.minres.coredsl/src/com/minres/coredsl/validation/IssueCodes.xtend +++ b/com.minres.coredsl/src/com/minres/coredsl/validation/IssueCodes.xtend @@ -103,4 +103,5 @@ class IssueCodes { public static val SizeOfNotExact = _prefix + 'SizeOfNotExact'; public static val IndexOutOfRange = _prefix + 'IndexOutOfRange'; public static val IncompleteType = _prefix + 'IncompleteType'; + public static val LikelyAccidentalAssignment = _prefix + 'LikelyAccidentalAssignment'; } From e54825f8fc804ee2233c1cb79f3febb1e56a9e17 Mon Sep 17 00:00:00 2001 From: Mario Welzig Date: Sat, 11 Nov 2023 14:09:59 +0100 Subject: [PATCH 2/4] test: assignment condition warning --- .../tests/analysis/CoreDslStatementTest.xtend | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/com.minres.coredsl.tests/src/com/minres/coredsl/tests/analysis/CoreDslStatementTest.xtend b/com.minres.coredsl.tests/src/com/minres/coredsl/tests/analysis/CoreDslStatementTest.xtend index 20af76d6..6e632bda 100644 --- a/com.minres.coredsl.tests/src/com/minres/coredsl/tests/analysis/CoreDslStatementTest.xtend +++ b/com.minres.coredsl.tests/src/com/minres/coredsl/tests/analysis/CoreDslStatementTest.xtend @@ -608,4 +608,31 @@ class CoreDslStatementTest { .expectError(IssueCodes.InvalidSpawnStatementPlacement, 2) .run(); } + + @Test + def accidentalAssignmentWarning() { + ''' + int x = 1; + while(x=0) {} + ''' + .testStatements() + .expectWarning(IssueCodes.LikelyAccidentalAssignment, 2) + .run(); + + ''' + int x = 1; + for(;x=0;) {} + ''' + .testStatements() + .expectWarning(IssueCodes.LikelyAccidentalAssignment, 2) + .run(); + + ''' + int x = 1; + do {} while(x=0); + ''' + .testStatements() + .expectWarning(IssueCodes.LikelyAccidentalAssignment, 2) + .run(); + } } From ba53156e9d27686b68beffe16e82de682c4b190d Mon Sep 17 00:00:00 2001 From: Mario Welzig Date: Sat, 25 Nov 2023 20:58:52 +0100 Subject: [PATCH 3/4] rewrite parsing logic for integer literals --- .../src/com/minres/coredsl/CoreDsl.xtext | 6 +- .../coredsl/analysis/CoreDslAnalyzer.xtend | 9 ++ .../converter/BOOLEANValueConverter.java | 31 ------- .../converter/BooleanValueConverter.xtend | 16 ++++ .../converter/CoreDslTerminalConverters.java | 27 ------ .../converter/CoreDslTerminalConverters.xtend | 21 +++++ .../converter/INTEGERValueConverter.java | 88 ------------------- .../converter/IntegerValueConverter.xtend | 81 +++++++++++++++++ .../minres/coredsl/util/ModelExtensions.xtend | 4 +- .../minres/coredsl/util/TypedBigInteger.java | 50 +++++++---- 10 files changed, 166 insertions(+), 167 deletions(-) delete mode 100644 com.minres.coredsl/src/com/minres/coredsl/converter/BOOLEANValueConverter.java create mode 100644 com.minres.coredsl/src/com/minres/coredsl/converter/BooleanValueConverter.xtend delete mode 100644 com.minres.coredsl/src/com/minres/coredsl/converter/CoreDslTerminalConverters.java create mode 100644 com.minres.coredsl/src/com/minres/coredsl/converter/CoreDslTerminalConverters.xtend delete mode 100644 com.minres.coredsl/src/com/minres/coredsl/converter/INTEGERValueConverter.java create mode 100644 com.minres.coredsl/src/com/minres/coredsl/converter/IntegerValueConverter.xtend diff --git a/com.minres.coredsl/src/com/minres/coredsl/CoreDsl.xtext b/com.minres.coredsl/src/com/minres/coredsl/CoreDsl.xtext index 0e4a9d1b..397e23df 100644 --- a/com.minres.coredsl/src/com/minres/coredsl/CoreDsl.xtext +++ b/com.minres.coredsl/src/com/minres/coredsl/CoreDsl.xtext @@ -262,7 +262,11 @@ terminal fragment BINARYINT: ('0b' | '0B') BIN_DIGIT+ ('_' BIN_DIGIT+)*; terminal fragment OCTALINT: '0' ('_')? OCT_DIGIT+ ('_' OCT_DIGIT+)*; terminal fragment DECIMALINT: '0' | ('1'..'9') DEC_DIGIT* ('_' DEC_DIGIT+)*; terminal fragment HEXADECIMALINT: ('0x'|'0X') HEX_DIGIT+ ('_' HEX_DIGIT+)*; -terminal fragment VLOGINT: DEC_DIGIT+ "'" 's'? ('b' BIN_DIGIT+ | 'o' OCT_DIGIT+ | 'd' DEC_DIGIT+ | 'h' HEX_DIGIT+); +terminal fragment VLOGINT: DEC_DIGIT+ "'" 's'? ( + 'b' BIN_DIGIT+ ('_' BIN_DIGIT+)* | + 'o' OCT_DIGIT+ ('_' OCT_DIGIT+)* | + 'd' DEC_DIGIT+ ('_' DEC_DIGIT+)* | + 'h' HEX_DIGIT+ ('_' HEX_DIGIT+)*); terminal fragment BIN_DIGIT: '0'..'1'; terminal fragment OCT_DIGIT: '0'..'7'; diff --git a/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslAnalyzer.xtend b/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslAnalyzer.xtend index 652cf6d3..4610bb24 100644 --- a/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslAnalyzer.xtend +++ b/com.minres.coredsl/src/com/minres/coredsl/analysis/CoreDslAnalyzer.xtend @@ -1092,11 +1092,20 @@ class CoreDslAnalyzer { // ////////////////////////////// Expressions //////////////////////////////// // /////////////////////////////////////////////////////////////////////////// def static dispatch CoreDslType analyzeExpression(AnalysisContext ctx, BoolConstant expression) { + ctx.setExpressionValue(expression, new ConstantValue(expression.value ? 1 : 0)); return ctx.setExpressionType(expression, IntegerType.bool); } def static dispatch CoreDslType analyzeExpression(AnalysisContext ctx, IntegerConstant expression) { val value = expression.value as TypedBigInteger; + + if(value === null) { + if(!ctx.isExpressionValueSet(expression)) + ctx.setExpressionValue(expression, ConstantValue.invalid); + + return ctx.setExpressionType(expression, ErrorType.invalid); + } + var type = new IntegerType(value.size, value.signed); if(!ctx.isExpressionValueSet(expression)) diff --git a/com.minres.coredsl/src/com/minres/coredsl/converter/BOOLEANValueConverter.java b/com.minres.coredsl/src/com/minres/coredsl/converter/BOOLEANValueConverter.java deleted file mode 100644 index 29a597fa..00000000 --- a/com.minres.coredsl/src/com/minres/coredsl/converter/BOOLEANValueConverter.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.minres.coredsl.converter; - -import org.eclipse.xtext.conversion.ValueConverterException; -import org.eclipse.xtext.conversion.impl.AbstractLexerBasedConverter; -import org.eclipse.xtext.nodemodel.INode; -import org.eclipse.xtext.util.Strings; - -public class BOOLEANValueConverter extends AbstractLexerBasedConverter { - @Override - protected String toEscapedString(Boolean value) { - return Boolean.toString(value); - } - - @Override - protected void assertValidValue(Boolean value) { - super.assertValidValue(value); - } - - @Override - public Boolean toValue(String string, INode node) throws ValueConverterException { - if (Strings.isEmpty(string)) - throw new ValueConverterException("Couldn't convert empty string to an booolean value.", node, null); - try { - boolean value = Boolean.parseBoolean(string); - return Boolean.valueOf(value); - } catch (NumberFormatException e) { - throw new ValueConverterException("Couldn't convert '" + string + "' to an booolean value.", node, e); - } - } - -} \ No newline at end of file diff --git a/com.minres.coredsl/src/com/minres/coredsl/converter/BooleanValueConverter.xtend b/com.minres.coredsl/src/com/minres/coredsl/converter/BooleanValueConverter.xtend new file mode 100644 index 00000000..11e9790d --- /dev/null +++ b/com.minres.coredsl/src/com/minres/coredsl/converter/BooleanValueConverter.xtend @@ -0,0 +1,16 @@ +package com.minres.coredsl.converter + +import org.eclipse.xtext.conversion.ValueConverterException +import org.eclipse.xtext.conversion.impl.AbstractLexerBasedConverter +import org.eclipse.xtext.nodemodel.INode + +class BooleanValueConverter extends AbstractLexerBasedConverter { + + override toValue(String string, INode node) throws ValueConverterException { + try { + return Boolean.parseBoolean(string); + } catch(NumberFormatException e) { + throw new ValueConverterException('''Malformed boolean literal: «string»''', node, e); + } + } +} diff --git a/com.minres.coredsl/src/com/minres/coredsl/converter/CoreDslTerminalConverters.java b/com.minres.coredsl/src/com/minres/coredsl/converter/CoreDslTerminalConverters.java deleted file mode 100644 index 441b7209..00000000 --- a/com.minres.coredsl/src/com/minres/coredsl/converter/CoreDslTerminalConverters.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.minres.coredsl.converter; - -import org.eclipse.xtext.common.services.DefaultTerminalConverters; -import org.eclipse.xtext.conversion.IValueConverter; -import org.eclipse.xtext.conversion.ValueConverter; - -import com.google.inject.Inject; -import com.minres.coredsl.util.TypedBigInteger; - -public class CoreDslTerminalConverters extends DefaultTerminalConverters { - @Inject - private INTEGERValueConverter intValueConverter; - - @Inject - private BOOLEANValueConverter boolValueConverter; - - @ValueConverter(rule = "INTEGER") - public IValueConverter INTEGER() { - return intValueConverter; - } - - @ValueConverter(rule = "BOOLEAN") - public IValueConverter BOOLEAN() { - return boolValueConverter; - } - -} \ No newline at end of file diff --git a/com.minres.coredsl/src/com/minres/coredsl/converter/CoreDslTerminalConverters.xtend b/com.minres.coredsl/src/com/minres/coredsl/converter/CoreDslTerminalConverters.xtend new file mode 100644 index 00000000..ba95b20a --- /dev/null +++ b/com.minres.coredsl/src/com/minres/coredsl/converter/CoreDslTerminalConverters.xtend @@ -0,0 +1,21 @@ +package com.minres.coredsl.converter + +import org.eclipse.xtext.common.services.DefaultTerminalConverters +import com.minres.coredsl.util.TypedBigInteger +import org.eclipse.xtext.conversion.IValueConverter +import com.google.inject.Inject +import org.eclipse.xtext.conversion.ValueConverter + +class CoreDslTerminalConverters extends DefaultTerminalConverters { + @Inject + IntegerValueConverter intValueConverter; + + @Inject + BooleanValueConverter boolValueConverter; + + @ValueConverter(rule = "INTEGER") + def IValueConverter INTEGER() { return intValueConverter; } + + @ValueConverter(rule = "BOOLEAN") + def IValueConverter BOOLEAN() { return boolValueConverter; } +} \ No newline at end of file diff --git a/com.minres.coredsl/src/com/minres/coredsl/converter/INTEGERValueConverter.java b/com.minres.coredsl/src/com/minres/coredsl/converter/INTEGERValueConverter.java deleted file mode 100644 index 0a7f0ebc..00000000 --- a/com.minres.coredsl/src/com/minres/coredsl/converter/INTEGERValueConverter.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.minres.coredsl.converter; - -import java.math.BigInteger; - -import org.eclipse.xtext.conversion.ValueConverterException; -import org.eclipse.xtext.conversion.impl.AbstractLexerBasedConverter; -import org.eclipse.xtext.nodemodel.INode; -import org.eclipse.xtext.util.Strings; - -import com.minres.coredsl.util.TypedBigInteger; - -public class INTEGERValueConverter extends AbstractLexerBasedConverter { - @Override - protected String toEscapedString(TypedBigInteger value) { - return value.toString(); - } - - @Override - protected void assertValidValue(TypedBigInteger value) { - super.assertValidValue(value); - if (value.compareTo(BigInteger.ZERO) == -1) - throw new ValueConverterException(getRuleName() + "-value may not be negative (value: " + value + ").", - null, null); - } - - @Override - public TypedBigInteger toValue(String string, INode node) throws ValueConverterException { - if (Strings.isEmpty(string)) - throw new ValueConverterException("Couldn't convert empty string to an integer value.", node, null); - try { - String s = string.toLowerCase(); - - if (string.contains("'")) { - // Verilog-style literal ' - String[] parts = s.split("'"); - int size = Integer.parseInt(parts[0]); - boolean signed = parts[1].charAt(0) == 's'; - char radixChar = parts[1].charAt(signed ? 1 : 0); - String digits = parts[1].substring(signed ? 2 : 1); - - // TODO handle the case where the sign bit is set (6'sd42) - - if (size == 0) - throw new ValueConverterException("Size of verilog-style literal must not be zero", node, null); - - switch (radixChar) { - case 'h': - // hexadecimal - return new TypedBigInteger(digits, 16, size, signed); - case 'b': - // binary - return new TypedBigInteger(digits, 2, size, signed); - case 'o': - // octal - return new TypedBigInteger(digits, 8, size, signed); - case 'd': - // decimal - return new TypedBigInteger(digits, 10, size, signed); - default: - throw new ValueConverterException("Invalid radix char in verilog-style literal: " + radixChar, node, null); - } - } - else { - if (s.length() > 1 && s.charAt(0) == '0') { - switch (s.charAt(1)) { - case 'x': - // hexadecimal - return new TypedBigInteger(string.substring(2), 16); - case 'b': - // binary - return new TypedBigInteger(string.substring(2), 2); - default: - // octal - return new TypedBigInteger(s, 8); - } - } - else { - // decimal - return new TypedBigInteger(s, 10); - } - } - } - catch (NumberFormatException e) { - throw new ValueConverterException("Couldn't convert '" + string + "' to an integer value.", node, e); - } - } - -} \ No newline at end of file diff --git a/com.minres.coredsl/src/com/minres/coredsl/converter/IntegerValueConverter.xtend b/com.minres.coredsl/src/com/minres/coredsl/converter/IntegerValueConverter.xtend new file mode 100644 index 00000000..bb114f11 --- /dev/null +++ b/com.minres.coredsl/src/com/minres/coredsl/converter/IntegerValueConverter.xtend @@ -0,0 +1,81 @@ +package com.minres.coredsl.converter + +import com.minres.coredsl.util.TypedBigInteger +import java.util.regex.Pattern +import org.eclipse.xtext.conversion.impl.AbstractLexerBasedConverter +import org.eclipse.xtext.nodemodel.INode +import org.eclipse.xtext.conversion.ValueConverterException +import java.math.BigInteger + +class IntegerValueConverter extends AbstractLexerBasedConverter { + static Pattern numberPattern = Pattern.compile(''' + ^(?: + 0b(?[_01]+)| + 0(?[0-7]+)| + (?0|[1-9][_0-9]*)| + 0x(?[_0-9a-f]+)| + (?\d+)'(?s)?(?: + b(?[_0-1]+)| + o(?[_0-7]+)| + d(?[_0-9]+)| + h(?[_0-9a-f]+) + ))$ + ''', Pattern.CASE_INSENSITIVE + Pattern.COMMENTS); + + def TypedBigInteger tryParseBigInteger(String digits, INode node, int radix, int size, + boolean signed) throws ValueConverterException { + try { + if(digits === null) return null; + + var value = size >= 0 ? new TypedBigInteger(digits.replace("_", ""), radix, size, signed) : new TypedBigInteger(digits.replace("_", ""), radix); + + if(signed && value.bitLength == size) { + value = new TypedBigInteger(value - BigInteger.ONE.shiftLeft(value.bitLength), radix, size, signed); + } + + // TODO test this with negative numbers + if(size == 0 || size > 0 && value.bitLength > size) { + throw new ValueConverterException('''Value «value» does not fit into «size» bits''', node, null); + } + + return value; + } catch(NumberFormatException e) { + throw new ValueConverterException("Malformed integer literal", node, e); + } + } + + def TypedBigInteger tryParseBigInteger(String digits, INode node, int radix) throws ValueConverterException { + return tryParseBigInteger(digits, node, radix, -1, false); + } + + override toValue(String string, INode node) throws ValueConverterException { + val matcher = numberPattern.matcher(string); + if(!matcher.find()) + throw new ValueConverterException("Malformed integer literal: " + string, node, null); + val vsize = matcher.group('vsize'); + + if(vsize === null) { + val value = + matcher.group('bin')?.tryParseBigInteger(node, 2) ?: + matcher.group('oct')?.tryParseBigInteger(node, 8) ?: + matcher.group('dec')?.tryParseBigInteger(node, 10) ?: + matcher.group('hex')?.tryParseBigInteger(node, 16); + + if(value === null) throw new ValueConverterException("Failed to convert integer literal: " + string, node, null); + + return value; + } else { + val signed = matcher.group('vsigned') !== null; + val size = Integer.parseInt(vsize); + val value = + matcher.group('vbin')?.tryParseBigInteger(node, 2, size, signed) ?: + matcher.group('voct')?.tryParseBigInteger(node, 8, size, signed) ?: + matcher.group('vdec')?.tryParseBigInteger(node, 10, size, signed) ?: + matcher.group('vhex')?.tryParseBigInteger(node, 16, size, signed); + + if(value === null) throw new ValueConverterException("Failed to convert integer literal: " + string, node, null); + + return value; + } + } +} diff --git a/com.minres.coredsl/src/com/minres/coredsl/util/ModelExtensions.xtend b/com.minres.coredsl/src/com/minres/coredsl/util/ModelExtensions.xtend index febb4ebf..26648ab2 100644 --- a/com.minres.coredsl/src/com/minres/coredsl/util/ModelExtensions.xtend +++ b/com.minres.coredsl/src/com/minres/coredsl/util/ModelExtensions.xtend @@ -59,8 +59,8 @@ abstract class ModelExtensions { return null; } - static def Iterator descendantsOfType(EObject obj, Class type) { - return obj.eAllContents.filter(type); + static def List descendantsOfType(EObject obj, Class type) { + return obj.eAllContents.filter(type).toList; } static def boolean isDescendantOf(EObject obj, EObject potentialAncestor) { diff --git a/com.minres.coredsl/src/com/minres/coredsl/util/TypedBigInteger.java b/com.minres.coredsl/src/com/minres/coredsl/util/TypedBigInteger.java index 2f81ab04..bd12872e 100644 --- a/com.minres.coredsl/src/com/minres/coredsl/util/TypedBigInteger.java +++ b/com.minres.coredsl/src/com/minres/coredsl/util/TypedBigInteger.java @@ -4,26 +4,40 @@ public class TypedBigInteger extends BigInteger { private static final long serialVersionUID = -2568538931532373089L; - + final int radix; final int size; - final boolean signed; - - public TypedBigInteger(String digits, int radix) { - super(digits, radix); - this.radix = radix; - this.signed = signum() < 0; - this.size = Math.max(signed ? bitLength() + 1 : bitLength(), 1); - } - - public TypedBigInteger(String digits, int radix, int size, boolean signed) { - super(digits, radix); - this.radix = radix; - this.size = Math.max(size, 1); - this.signed = signed; - } - - public int getRadix() { + final boolean signed; + + public TypedBigInteger(String digits, int radix) { + super(digits, radix); + this.radix = radix; + this.signed = signum() < 0; + this.size = Math.max(signed ? bitLength() + 1 : bitLength(), 1); + } + + public TypedBigInteger(String digits, int radix, int size, boolean signed) { + super(digits, radix); + this.radix = radix; + this.size = Math.max(size, 1); + this.signed = signed; + } + + public TypedBigInteger(BigInteger value, int radix) { + super(value.toByteArray()); + this.radix = radix; + this.signed = signum() < 0; + this.size = Math.max(signed ? bitLength() + 1 : bitLength(), 1); + } + + public TypedBigInteger(BigInteger value, int radix, int size, boolean signed) { + super(value.toByteArray()); + this.radix = radix; + this.size = Math.max(size, 1); + this.signed = signed; + } + + public int getRadix() { return radix; } From 47e20beb0d4fa6166dd8c6247e3b2adcdc75c66e Mon Sep 17 00:00:00 2001 From: Mario Welzig Date: Sat, 25 Nov 2023 21:29:18 +0100 Subject: [PATCH 4/4] test: integer literals --- .../coredsl/tests/CoreDslTestCase.xtend | 28 +- .../analysis/CoreDslExpressionTest.xtend | 368 ++++++++++++++++++ 2 files changed, 391 insertions(+), 5 deletions(-) create mode 100644 com.minres.coredsl.tests/src/com/minres/coredsl/tests/analysis/CoreDslExpressionTest.xtend diff --git a/com.minres.coredsl.tests/src/com/minres/coredsl/tests/CoreDslTestCase.xtend b/com.minres.coredsl.tests/src/com/minres/coredsl/tests/CoreDslTestCase.xtend index 7063ebed..60086bbf 100644 --- a/com.minres.coredsl.tests/src/com/minres/coredsl/tests/CoreDslTestCase.xtend +++ b/com.minres.coredsl.tests/src/com/minres/coredsl/tests/CoreDslTestCase.xtend @@ -188,6 +188,24 @@ class CoreDslTestCase { return expectValue(isaName, findEObject(type, line), expectedValue); } + def expectTypeAndValue(String isaName, EObject object, Object expectedType, Object expectedValue) { + semanticExpectations.add(new TypeExpectation(findIsa(isaName), object, String.valueOf(expectedType))); + semanticExpectations.add(new ValueExpectation(findIsa(isaName), object, String.valueOf(expectedValue))); + return this; + } + + def expectTypeAndValue(String isaName, (TRoot)=>EObject objectLocator, Object expectedType, Object expectedValue) { + return expectTypeAndValue(isaName, objectLocator.apply(root), expectedType, expectedValue); + } + + def expectTypeAndValue(String isaName, String declaratorName, Object expectedType, Object expectedValue) { + return expectTypeAndValue(isaName, findDeclarator(declaratorName), expectedType, expectedValue); + } + + def expectTypeAndValue(String isaName, Class type, int line, Object expectedType, Object expectedValue) { + return expectTypeAndValue(isaName, findEObject(type, line), expectedType, expectedValue); + } + def private printProgram() { val lines = newLinePattern.split(program); val digits = String.valueOf(lines.length).length; @@ -356,7 +374,7 @@ class CoreDslTestCase { println(''' [OK] «description»'''); return true; } else { - println(''' Expectation failed: «description»'''); + println(''' [FAILED] «description»'''); return true; } } catch(Exception e) { @@ -383,14 +401,14 @@ class CoreDslTestCase { val description = object.shortDescription; if(!ctx._isTypeSet(object)) { - println(''' «description»: Expected type «expectedType», but no type was set'''); + println(''' [FAILED] «description»: Expected type «expectedType», but no type was set'''); return false; } val actualType = ctx._getType(object).toString(); if(actualType != expectedType) { - println(''' «description»: Expected type «expectedType», but got «actualType»'''); + println(''' [FAILED] «description»: Expected type «expectedType», but got «actualType»'''); return false; } @@ -420,14 +438,14 @@ class CoreDslTestCase { val description = object.shortDescription; if(!ctx._isValueSet(object)) { - println(''' «description»: Expected value «expectedValue», but no value was set'''); + println(''' [FAILED] «description»: Expected value «expectedValue», but no value was set'''); return false; } val actualValue = ctx._getValue(object).toString(); if(actualValue != expectedValue) { - println(''' «description»: Expected value «expectedValue», but got «actualValue»'''); + println(''' [FAILED] «description»: Expected value «expectedValue», but got «actualValue»'''); return false; } diff --git a/com.minres.coredsl.tests/src/com/minres/coredsl/tests/analysis/CoreDslExpressionTest.xtend b/com.minres.coredsl.tests/src/com/minres/coredsl/tests/analysis/CoreDslExpressionTest.xtend new file mode 100644 index 00000000..6eebff71 --- /dev/null +++ b/com.minres.coredsl.tests/src/com/minres/coredsl/tests/analysis/CoreDslExpressionTest.xtend @@ -0,0 +1,368 @@ +package com.minres.coredsl.tests.analysis + +import com.google.inject.Inject +import com.minres.coredsl.coreDsl.Declarator +import com.minres.coredsl.coreDsl.ExpressionInitializer +import com.minres.coredsl.coreDsl.Statement +import com.minres.coredsl.tests.CoreDslInjectorProvider +import com.minres.coredsl.tests.CoreDslTestHelper +import com.minres.coredsl.type.IntegerType +import java.math.BigInteger +import org.eclipse.emf.common.util.EList +import org.eclipse.emf.ecore.EObject +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.^extension.ExtendWith + +import static extension com.minres.coredsl.util.DataExtensions.* +import static extension com.minres.coredsl.util.ModelExtensions.* + +@ExtendWith(InjectionExtension) +@InjectWith(CoreDslInjectorProvider) +class CoreDslExpressionTest { + + @Inject extension CoreDslTestHelper testHelper; + + def (EList) => EObject initializerOf(String name) { + return [ + it.flatMap[it.descendantsOfType(Declarator)] + .findFirst[it.name == name] + ?.initializer.castOrNull(ExpressionInitializer) + ?.value + ] + } + + @Test + def booleanLiterals() { + ''' + int a = true; + int b = false; + ''' + .testStatements() + .expectTypeAndValue(null, initializerOf('a'), IntegerType.unsigned(1), 1) + .expectTypeAndValue(null, initializerOf('b'), IntegerType.unsigned(1), 0) + .run(); + } + + @Test + def void intLiteralsCStyle() { + ''' + int a = 0b0; + int b = 0B1; + int c = 0B10; + int d = 0b0011; + int e = 0b0101010; + unsigned<65> f = 0b1_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; + ''' + .testStatements() + .expectTypeAndValue(null, initializerOf('a'), IntegerType.unsigned(1), 0) + .expectTypeAndValue(null, initializerOf('b'), IntegerType.unsigned(1), 1) + .expectTypeAndValue(null, initializerOf('c'), IntegerType.unsigned(2), 2) + .expectTypeAndValue(null, initializerOf('d'), IntegerType.unsigned(2), 3) + .expectTypeAndValue(null, initializerOf('e'), IntegerType.unsigned(6), 42) + .expectTypeAndValue(null, initializerOf('f'), IntegerType.unsigned(65), BigInteger.ONE.shiftLeft(64)) + .run(); + + ''' + int a = 00; + int b = 01; + int c = 02; + int d = 0003; + int e = 0052; + unsigned<65> f = 02000000000000000000000; + ''' + .testStatements() + .expectTypeAndValue(null, initializerOf('a'), IntegerType.unsigned(1), 0) + .expectTypeAndValue(null, initializerOf('b'), IntegerType.unsigned(1), 1) + .expectTypeAndValue(null, initializerOf('c'), IntegerType.unsigned(2), 2) + .expectTypeAndValue(null, initializerOf('d'), IntegerType.unsigned(2), 3) + .expectTypeAndValue(null, initializerOf('e'), IntegerType.unsigned(6), 42) + .expectTypeAndValue(null, initializerOf('f'), IntegerType.unsigned(65), BigInteger.ONE.shiftLeft(64)) + .run(); + + ''' + int a = 0; + int b = 1; + int c = 2; + int d = 3; + int e = 42; + unsigned<65> f = 18_446_744_073_709_551_616; + ''' + .testStatements() + .expectTypeAndValue(null, initializerOf('a'), IntegerType.unsigned(1), 0) + .expectTypeAndValue(null, initializerOf('b'), IntegerType.unsigned(1), 1) + .expectTypeAndValue(null, initializerOf('c'), IntegerType.unsigned(2), 2) + .expectTypeAndValue(null, initializerOf('d'), IntegerType.unsigned(2), 3) + .expectTypeAndValue(null, initializerOf('e'), IntegerType.unsigned(6), 42) + .expectTypeAndValue(null, initializerOf('f'), IntegerType.unsigned(65), BigInteger.ONE.shiftLeft(64)) + .run(); + + ''' + int a = 0x0; + int b = 0X1; + int c = 0x2; + int d = 0X002a; + int e = 0x002A; + unsigned<65> f = 0x1_0000_0000_0000_0000; + unsigned int g = 0xDeadBeef; + ''' + .testStatements() + .expectTypeAndValue(null, initializerOf('a'), IntegerType.unsigned(1), 0) + .expectTypeAndValue(null, initializerOf('b'), IntegerType.unsigned(1), 1) + .expectTypeAndValue(null, initializerOf('c'), IntegerType.unsigned(2), 2) + .expectTypeAndValue(null, initializerOf('d'), IntegerType.unsigned(6), 42) + .expectTypeAndValue(null, initializerOf('e'), IntegerType.unsigned(6), 42) + .expectTypeAndValue(null, initializerOf('f'), IntegerType.unsigned(65), BigInteger.ONE.shiftLeft(64)) + .expectTypeAndValue(null, initializerOf('g'), IntegerType.unsigned(32), Integer.toUnsignedLong(0xdeadbeef)) + .run(); + } + + @Test + def intLiteralsVerilogStyleUnsignedValid() { + ''' + int a = 1'b0; + int b = 1'b1; + int c = 2'b1; + int d = 3'b0011; + int e = 6'b0101010; + unsigned<65> f = 65'b1_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; + ''' + .testStatements() + .expectTypeAndValue(null, initializerOf('a'), IntegerType.unsigned(1), 0) + .expectTypeAndValue(null, initializerOf('b'), IntegerType.unsigned(1), 1) + .expectTypeAndValue(null, initializerOf('c'), IntegerType.unsigned(2), 1) + .expectTypeAndValue(null, initializerOf('d'), IntegerType.unsigned(3), 3) + .expectTypeAndValue(null, initializerOf('e'), IntegerType.unsigned(6), 42) + .expectTypeAndValue(null, initializerOf('f'), IntegerType.unsigned(65), BigInteger.ONE.shiftLeft(64)) + .run(); + + ''' + int a = 1'o0; + int b = 1'o1; + int c = 2'o1; + int d = 3'o003; + int e = 6'o52; + unsigned<65> f = 65'o2000000000000000000000; + ''' + .testStatements() + .expectTypeAndValue(null, initializerOf('a'), IntegerType.unsigned(1), 0) + .expectTypeAndValue(null, initializerOf('b'), IntegerType.unsigned(1), 1) + .expectTypeAndValue(null, initializerOf('c'), IntegerType.unsigned(2), 1) + .expectTypeAndValue(null, initializerOf('d'), IntegerType.unsigned(3), 3) + .expectTypeAndValue(null, initializerOf('e'), IntegerType.unsigned(6), 42) + .expectTypeAndValue(null, initializerOf('f'), IntegerType.unsigned(65), BigInteger.ONE.shiftLeft(64)) + .run(); + + ''' + int a = 1'd0; + int b = 1'd1; + int c = 2'd1; + int d = 3'd003; + int e = 6'd42; + unsigned<65> f = 65'd18_446_744_073_709_551_616; + ''' + .testStatements() + .expectTypeAndValue(null, initializerOf('a'), IntegerType.unsigned(1), 0) + .expectTypeAndValue(null, initializerOf('b'), IntegerType.unsigned(1), 1) + .expectTypeAndValue(null, initializerOf('c'), IntegerType.unsigned(2), 1) + .expectTypeAndValue(null, initializerOf('d'), IntegerType.unsigned(3), 3) + .expectTypeAndValue(null, initializerOf('e'), IntegerType.unsigned(6), 42) + .expectTypeAndValue(null, initializerOf('f'), IntegerType.unsigned(65), BigInteger.ONE.shiftLeft(64)) + .run(); + + ''' + int a = 1'h0; + int b = 1'h1; + int c = 2'h1; + int d = 3'h003; + int e = 6'h2a; + unsigned<65> f = 65'h1_0000_0000_0000_0000; + unsigned int g = 32'hDeadBeef; + ''' + .testStatements() + .expectTypeAndValue(null, initializerOf('a'), IntegerType.unsigned(1), 0) + .expectTypeAndValue(null, initializerOf('b'), IntegerType.unsigned(1), 1) + .expectTypeAndValue(null, initializerOf('c'), IntegerType.unsigned(2), 1) + .expectTypeAndValue(null, initializerOf('d'), IntegerType.unsigned(3), 3) + .expectTypeAndValue(null, initializerOf('e'), IntegerType.unsigned(6), 42) + .expectTypeAndValue(null, initializerOf('f'), IntegerType.unsigned(65), BigInteger.ONE.shiftLeft(64)) + .expectTypeAndValue(null, initializerOf('g'), IntegerType.unsigned(32), Integer.toUnsignedLong(0xdeadbeef)) + .run(); + } + + @Test + def intLiteralsVerilogStyleUnsignedInvalid() { + ''' + int a = 0'b0; + ''' + .testStatements() + .expectSyntaxErrors() + .run(); + + ''' + int a = 1'b10; + ''' + .testStatements() + .expectSyntaxErrors() + .run(); + + ''' + int a = 2'o4; + ''' + .testStatements() + .expectSyntaxErrors() + .run(); + + ''' + int a = 3'd8; + ''' + .testStatements() + .expectSyntaxErrors() + .run(); + + ''' + int a = 5'h20; + ''' + .testStatements() + .expectSyntaxErrors() + .run(); + } + + @Test + def intLiteralsVerilogStyleSignedValid() { + ''' + int a = 1'sb0; + int b = 1'sb1; + int c = 2'sb1; + int d = 3'sb0011; + int e = 6'sb0101010; + signed<65> f = 65'sb1_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; + ''' + .testStatements() + .expectTypeAndValue(null, initializerOf('a'), IntegerType.signed(1), 0) + .expectTypeAndValue(null, initializerOf('b'), IntegerType.signed(1), -1) + .expectTypeAndValue(null, initializerOf('c'), IntegerType.signed(2), 1) + .expectTypeAndValue(null, initializerOf('d'), IntegerType.signed(3), 3) + .expectTypeAndValue(null, initializerOf('e'), IntegerType.signed(6), -22) + .expectTypeAndValue(null, initializerOf('f'), IntegerType.signed(65), -BigInteger.ONE.shiftLeft(64)) + .run(); + + ''' + int a = 1'so0; + int b = 1'so1; + int c = 2'so1; + int d = 3'so003; + int e = 6'so52; + signed<65> f = 65'so2000000000000000000000; + ''' + .testStatements() + .expectTypeAndValue(null, initializerOf('a'), IntegerType.signed(1), 0) + .expectTypeAndValue(null, initializerOf('b'), IntegerType.signed(1), -1) + .expectTypeAndValue(null, initializerOf('c'), IntegerType.signed(2), 1) + .expectTypeAndValue(null, initializerOf('d'), IntegerType.signed(3), 3) + .expectTypeAndValue(null, initializerOf('e'), IntegerType.signed(6), -22) + .expectTypeAndValue(null, initializerOf('f'), IntegerType.signed(65), -BigInteger.ONE.shiftLeft(64)) + .run(); + + ''' + int a = 1'sd0; + int b = 1'sd1; + int c = 2'sd1; + int d = 3'sd003; + int e = 6'sd42; + signed<65> f = 65'sd18_446_744_073_709_551_616; + ''' + .testStatements() + .expectTypeAndValue(null, initializerOf('a'), IntegerType.signed(1), 0) + .expectTypeAndValue(null, initializerOf('b'), IntegerType.signed(1), -1) + .expectTypeAndValue(null, initializerOf('c'), IntegerType.signed(2), 1) + .expectTypeAndValue(null, initializerOf('d'), IntegerType.signed(3), 3) + .expectTypeAndValue(null, initializerOf('e'), IntegerType.signed(6), -22) + .expectTypeAndValue(null, initializerOf('f'), IntegerType.signed(65), -BigInteger.ONE.shiftLeft(64)) + .run(); + + ''' + int a = 1'sh0; + int b = 1'sh1; + int c = 2'sh1; + int d = 3'sh003; + int e = 6'sh2a; + signed<65> f = 65'sh1_0000_0000_0000_0000; + int g = 32'shDeadBeef; + ''' + .testStatements() + .expectTypeAndValue(null, initializerOf('a'), IntegerType.signed(1), 0) + .expectTypeAndValue(null, initializerOf('b'), IntegerType.signed(1), -1) + .expectTypeAndValue(null, initializerOf('c'), IntegerType.signed(2), 1) + .expectTypeAndValue(null, initializerOf('d'), IntegerType.signed(3), 3) + .expectTypeAndValue(null, initializerOf('e'), IntegerType.signed(6), -22) + .expectTypeAndValue(null, initializerOf('f'), IntegerType.signed(65), -BigInteger.ONE.shiftLeft(64)) + .expectTypeAndValue(null, initializerOf('g'), IntegerType.signed(32), 0xdeadbeef) + .run(); + } + + @Test + def intLiteralsVerilogStyleSignedInvalid() { + ''' + int a = 0'sb0; + ''' + .testStatements() + .expectSyntaxErrors() + .run(); + + ''' + int a = 1'sb10; + ''' + .testStatements() + .expectSyntaxErrors() + .run(); + + ''' + int a = 2'so4; + ''' + .testStatements() + .expectSyntaxErrors() + .run(); + + ''' + int a = 3'sd8; + ''' + .testStatements() + .expectSyntaxErrors() + .run(); + + ''' + int a = 5'sh20; + ''' + .testStatements() + .expectSyntaxErrors() + .run(); + } +} + + + + + + + + + + + + + + + + + + + + + + + + + +