From bf4a7ef7dedf265a6f29f48d56264da89d2dc450 Mon Sep 17 00:00:00 2001 From: Claes65 Date: Fri, 21 Jul 2023 13:17:42 +0200 Subject: [PATCH 1/8] binary Compatibility --- .../amshove/natparse/natural/IDataType.java | 30 ++++++++++++++++++- .../natparse/parsing/typing/simpleAssignments | 12 ++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/libs/natparse/src/main/java/org/amshove/natparse/natural/IDataType.java b/libs/natparse/src/main/java/org/amshove/natparse/natural/IDataType.java index 15cde33d4..0e8a31a89 100644 --- a/libs/natparse/src/main/java/org/amshove/natparse/natural/IDataType.java +++ b/libs/natparse/src/main/java/org/amshove/natparse/natural/IDataType.java @@ -65,8 +65,11 @@ default boolean hasCompatibleFormat(IDataType other) case TIME, DATE -> targetFormat == ALPHANUMERIC || targetFormat == NUMERIC || targetFormat == PACKED - || targetFormat == INTEGER; // this one can fail, but not for early times + || targetFormat == INTEGER // this one can fail, but not for early times + || targetFormat == TIME + || targetFormat == DATE; case LOGIC -> targetFormat == ALPHANUMERIC; + case BINARY -> binaryCompatibility(other); default -> false; // we don't know whats implicitly compatible yet }; } @@ -139,4 +142,29 @@ private int calculateDigitsAfterDecimalPoint() { return Integer.parseInt((Double.toString(length()).split("\\.")[1])); } + + /** + * Returns true if the binary is compatible with target - otherwise false
+ * This depends on the length of the binary + */ + private boolean binaryCompatibility(IDataType other) + { + var targetFormat = other.format(); + if (length() < 5) + { + return switch (targetFormat) + { + case NUMERIC, PACKED, ALPHANUMERIC, UNICODE, INTEGER, TIME, FLOAT -> true; + default -> false; + }; + } + else + { + return switch (targetFormat) + { + case ALPHANUMERIC, UNICODE -> true; + default -> false; + }; + } + } } diff --git a/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments b/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments index 08376e2fa..5e44749fe 100644 --- a/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments +++ b/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments @@ -4,6 +4,8 @@ DEFINE DATA LOCAL 1 #I1 (I1) 1 #A1 (A1) 1 #P1 (P1) +1 #B4 (B4) +1 #B5 (B5) 1 #T (T) 1 #D (D) 1 #A10 (A10) @@ -42,12 +44,22 @@ END-DEFINE #D := 10 /* !{D:ERROR:NPP037} #D := #N1 /* !{D:ERROR:NPP037} +#I1 := #B4 +#P1 := #B4 +#T := #B4 +#I1 := #B5 /* !{D:ERROR:NPP037} +#P1 := #B5 /* !{D:ERROR:NPP037} +#D := #B4 /* !{D:ERROR:NPP037} + #A1 := TRUE /* Converted to single character #A1 := FALSE /* Converted to single character #N12 := *TIMX /* fine #P12 := *TIMX /* fine +#D := #T +#T := #D + #A20 := 99999999999999999999 /* Results in a P11, not truncation needed to to A20 END From fdc6eb84744588484effda5cc6a9f9e711a395de Mon Sep 17 00:00:00 2001 From: Claes65 Date: Fri, 21 Jul 2023 13:28:56 +0200 Subject: [PATCH 2/8] missing test --- .../org/amshove/natparse/parsing/typing/simpleAssignments | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments b/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments index 5e44749fe..d6d16175a 100644 --- a/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments +++ b/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments @@ -14,6 +14,7 @@ DEFINE DATA LOCAL 1 #P12 (P12) 1 #I4 (I4) 1 #A20 (A20) +1 #U20 (U20) 1 THEVIEW VIEW OF THE-DDM 2 #TYPED-IN-VIEW END-DEFINE @@ -47,6 +48,8 @@ END-DEFINE #I1 := #B4 #P1 := #B4 #T := #B4 +#A20 := B5 +#U20 := B5 #I1 := #B5 /* !{D:ERROR:NPP037} #P1 := #B5 /* !{D:ERROR:NPP037} #D := #B4 /* !{D:ERROR:NPP037} From 8eace4b72af8c3b77cf9f9c4efadf9426817b8d9 Mon Sep 17 00:00:00 2001 From: Claes65 Date: Fri, 21 Jul 2023 13:51:37 +0200 Subject: [PATCH 3/8] Update simpleAssignments --- .../org/amshove/natparse/parsing/typing/simpleAssignments | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments b/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments index d6d16175a..94cbc4a7e 100644 --- a/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments +++ b/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments @@ -48,8 +48,8 @@ END-DEFINE #I1 := #B4 #P1 := #B4 #T := #B4 -#A20 := B5 -#U20 := B5 +#A20 := #B5 +#U20 := #B5 #I1 := #B5 /* !{D:ERROR:NPP037} #P1 := #B5 /* !{D:ERROR:NPP037} #D := #B4 /* !{D:ERROR:NPP037} From d2847034657a50531cb85134d5b3cde755753f9b Mon Sep 17 00:00:00 2001 From: Claes65 Date: Fri, 21 Jul 2023 15:14:21 +0200 Subject: [PATCH 4/8] have a look, Markus --- .../amshove/natparse/natural/IDataType.java | 91 ++++++++++--------- .../natparse/parsing/typing/decideOnTyping | 2 +- .../natparse/parsing/typing/simpleAssignments | 7 ++ 3 files changed, 55 insertions(+), 45 deletions(-) diff --git a/libs/natparse/src/main/java/org/amshove/natparse/natural/IDataType.java b/libs/natparse/src/main/java/org/amshove/natparse/natural/IDataType.java index 0e8a31a89..aec0a7893 100644 --- a/libs/natparse/src/main/java/org/amshove/natparse/natural/IDataType.java +++ b/libs/natparse/src/main/java/org/amshove/natparse/natural/IDataType.java @@ -30,26 +30,6 @@ default boolean fitsInto(IDataType target) return lengthFits && formatIsCompatible; } - /** - * Determines if both types have the same family, e.g. N, I, P are all numeric. - */ - default boolean hasSameFamily(IDataType other) - { - var targetFormat = other.format(); - return format() == targetFormat || switch (format()) - { - case PACKED, FLOAT, INTEGER, NUMERIC -> targetFormat == PACKED - || targetFormat == FLOAT - || targetFormat == INTEGER - || targetFormat == NUMERIC - || targetFormat == BINARY; - case ALPHANUMERIC, UNICODE, BINARY -> targetFormat == ALPHANUMERIC - || targetFormat == UNICODE - || targetFormat == BINARY; - default -> false; - }; - } - /** * Takes implicit conversion into account, e.g. N -> A */ @@ -60,20 +40,43 @@ default boolean hasCompatibleFormat(IDataType other) { case PACKED, FLOAT, INTEGER, NUMERIC -> targetFormat == ALPHANUMERIC || targetFormat == UNICODE - || targetFormat == BINARY - || targetFormat == TIME; - case TIME, DATE -> targetFormat == ALPHANUMERIC - || targetFormat == NUMERIC + || isShortBinary(other); + case TIME, DATE -> targetFormat == NUMERIC || targetFormat == PACKED + || targetFormat == ALPHANUMERIC + || targetFormat == UNICODE + || isShortBinary(other) || targetFormat == INTEGER // this one can fail, but not for early times + || targetFormat == DATE || targetFormat == TIME - || targetFormat == DATE; - case LOGIC -> targetFormat == ALPHANUMERIC; + || targetFormat == FLOAT; + case LOGIC -> targetFormat == ALPHANUMERIC + || targetFormat == UNICODE; case BINARY -> binaryCompatibility(other); default -> false; // we don't know whats implicitly compatible yet }; } + /** + * Determines if both types have the same family, e.g. N, I, P are all numeric. + */ + default boolean hasSameFamily(IDataType other) + { + var targetFormat = other.format(); + return format() == targetFormat || switch (format()) + { + case PACKED, FLOAT, INTEGER, NUMERIC, TIME -> targetFormat == PACKED + || targetFormat == FLOAT + || targetFormat == INTEGER + || targetFormat == NUMERIC + || targetFormat == TIME; + case ALPHANUMERIC, UNICODE -> targetFormat == ALPHANUMERIC + || targetFormat == UNICODE + || targetFormat == BINARY; + default -> false; + }; + } + default String toShortString() { var details = ""; @@ -143,28 +146,28 @@ private int calculateDigitsAfterDecimalPoint() return Integer.parseInt((Double.toString(length()).split("\\.")[1])); } - /** - * Returns true if the binary is compatible with target - otherwise false
- * This depends on the length of the binary - */ + private boolean isShortBinary(IDataType other) + { + return format() == BINARY && length() < 5; + } + + private boolean isLongBinary(IDataType other) + { + return format() == BINARY && length() > 4; + } + private boolean binaryCompatibility(IDataType other) { var targetFormat = other.format(); - if (length() < 5) + return (isLongBinary(other) && switch (targetFormat) { - return switch (targetFormat) - { - case NUMERIC, PACKED, ALPHANUMERIC, UNICODE, INTEGER, TIME, FLOAT -> true; - default -> false; - }; - } - else - { - return switch (targetFormat) + case ALPHANUMERIC, UNICODE -> true; + default -> false; + }) || + (isShortBinary(other) && switch (targetFormat) { - case ALPHANUMERIC, UNICODE -> true; - default -> false; - }; - } + case NUMERIC, PACKED, ALPHANUMERIC, UNICODE, INTEGER, TIME, FLOAT -> true; + default -> false; + }); } } diff --git a/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/decideOnTyping b/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/decideOnTyping index 4d8593b80..465dbd2df 100644 --- a/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/decideOnTyping +++ b/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/decideOnTyping @@ -41,7 +41,7 @@ DECIDE ON FIRST VALUE OF #N-10 END-DECIDE DECIDE ON FIRST VALUE OF #B-10 - VALUE 5 + VALUE 5 /* !{D:ERROR:NPP037} IGNORE VALUE 'Hi' IGNORE diff --git a/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments b/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments index 94cbc4a7e..36365007e 100644 --- a/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments +++ b/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments @@ -27,6 +27,13 @@ END-DEFINE #I1 := 128 /* !{D:WARNING:NPP045} #I1 := 127 #N1 := 'Hi' /* !{D:ERROR:NPP037} +#N12 := #N1; +/*#N12 := #U20; +#N12 := #N1; +#P12 := #P1; +/* #P12 := #U20; +#P12 := #T; +#P12 := #D #N1 := #CONST-N1-I4 /* Should work, the constant *value* is N1 From 6b528e91c366b47baa6950fff6b468aa64c1178d Mon Sep 17 00:00:00 2001 From: Claes65 Date: Mon, 24 Jul 2023 09:09:27 +0200 Subject: [PATCH 5/8] undo Merge branch 'main' into bugfix-type-compatibility --- build.gradle | 1 + .../org/amshove/natlint/cli/CliAnalyzer.java | 6 + .../natparse/natural/IArrayDimension.java | 11 + .../natparse/parsing/DefineDataParser.java | 307 +++++++++++++----- .../natparse/parsing/ParserErrors.java | 4 +- .../natparse/parsing/TypedVariableNode.java | 48 +++ .../natparse/parsing/VariableNode.java | 4 +- .../natparse/parsing/AbstractParserTest.java | 2 +- .../parsing/DefineDataParserShould.java | 120 ++++++- 9 files changed, 404 insertions(+), 99 deletions(-) diff --git a/build.gradle b/build.gradle index 699bbc5b8..e60181e8b 100644 --- a/build.gradle +++ b/build.gradle @@ -121,4 +121,5 @@ sonarqube { } } +project.tasks.getByPath('natlint:test').dependsOn(project.tasks.getByPath('natparse:test')) project.tasks['sonarqube'].dependsOn cover diff --git a/libs/natlint/src/main/java/org/amshove/natlint/cli/CliAnalyzer.java b/libs/natlint/src/main/java/org/amshove/natlint/cli/CliAnalyzer.java index 5d5c48108..39c401ee8 100644 --- a/libs/natlint/src/main/java/org/amshove/natlint/cli/CliAnalyzer.java +++ b/libs/natlint/src/main/java/org/amshove/natlint/cli/CliAnalyzer.java @@ -233,6 +233,8 @@ private TokenList lex(NaturalFile file, ArrayList allDiagnosticsInF { fileStatusSink.printError(file.getPath(), MessageType.LEX_EXCEPTION, e); exceptions.incrementAndGet(); + System.out.println(file.getPath()); + e.printStackTrace(); return null; } } @@ -275,6 +277,8 @@ private INaturalModule parse(NaturalFile file, TokenList tokens, ArrayList lint(NaturalFile file, INaturalModule mod { fileStatusSink.printError(file.getPath(), MessageType.LINT_EXCEPTION, e); exceptions.incrementAndGet(); + System.out.println(file.getPath()); + e.printStackTrace(); return null; } } diff --git a/libs/natparse/src/main/java/org/amshove/natparse/natural/IArrayDimension.java b/libs/natparse/src/main/java/org/amshove/natparse/natural/IArrayDimension.java index 98c275f7d..c6b608f99 100644 --- a/libs/natparse/src/main/java/org/amshove/natparse/natural/IArrayDimension.java +++ b/libs/natparse/src/main/java/org/amshove/natparse/natural/IArrayDimension.java @@ -3,6 +3,7 @@ public interface IArrayDimension extends ISyntaxNode { int UNBOUND_VALUE = Integer.MAX_VALUE; + int VARIABLE_BOUND = Integer.MAX_VALUE - 1; /** * Specifies the lower bound of the array. If the array is unbound, it returns int.MAX_VALUE. Use `isLowerUnbound()` @@ -30,6 +31,16 @@ default boolean isUpperUnbound() return upperBound() == UNBOUND_VALUE; } + /** + * This indicates that the upper bound is V for PDAs, not that the upper bound is a variable.
+ * Documentation + */ + default boolean isUpperVariable() + { + return upperBound() == VARIABLE_BOUND; + } + default int occurerences() { if (isLowerUnbound() || isUpperUnbound()) diff --git a/libs/natparse/src/main/java/org/amshove/natparse/parsing/DefineDataParser.java b/libs/natparse/src/main/java/org/amshove/natparse/parsing/DefineDataParser.java index d07050cbb..a10dfb18a 100644 --- a/libs/natparse/src/main/java/org/amshove/natparse/parsing/DefineDataParser.java +++ b/libs/natparse/src/main/java/org/amshove/natparse/parsing/DefineDataParser.java @@ -7,9 +7,7 @@ import org.amshove.natparse.natural.*; import org.amshove.natparse.natural.project.NaturalFileType; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; public class DefineDataParser extends AbstractParser { @@ -20,11 +18,10 @@ public class DefineDataParser extends AbstractParser * addDeclaredVariable for error handling. */ private Map declaredVariables; + private Deque groupStack; private VariableScope currentScope; - private RedefinitionNode currentRedefineNode; - private DefineDataNode defineData; public DefineDataParser(IModuleProvider moduleProvider) @@ -37,6 +34,7 @@ protected IDefineData parseInternal() { defineData = new DefineDataNode(); declaredVariables = new HashMap<>(); + groupStack = new ArrayDeque<>(); advanceToDefineData(tokens); if (!isAtStartOfDefineData(tokens)) @@ -86,9 +84,40 @@ protected IDefineData parseInternal() // it's okay, we're done here. } + for (var variable : defineData.variables()) + { + if (variable instanceof GroupNode groupNode) + { + checkGroupIsNotEmpty(groupNode); + if (groupNode.level() == 1 && !(groupNode instanceof IRedefinitionNode)) + { + ensureAllConstOrNoneConst(groupNode.variables(), new GroupConstStatistic()); + } + } + + if (variable instanceof RedefinitionNode redefinitionNode) + { + addTargetToRedefine(redefinitionNode); + checkRedefineLength(redefinitionNode); + } + + } + return defineData; } + private void checkGroupIsNotEmpty(GroupNode groupNode) + { + if (groupNode instanceof ViewNode) + { + return; + } + if (groupNode.variables().size() == 0) + { + report(ParserErrors.emptyGroupVariable(groupNode)); + } + } + private BaseSyntaxNode dataDefinition() throws ParseError { if (!isScopeToken(peek()) && !peekKind(SyntaxKind.BLOCK)) @@ -117,13 +146,15 @@ private ScopeNode scope() throws ParseError { try { + popGroupsIfNecessary(); + if (peekKind(SyntaxKind.BLOCK)) { /*var block = */ block(); // TODO: Maybe do something with block } - var variable = variable(); + var variable = variable(currentGroupsDimensions()); variable.setScope(currentScope); for (var dimension : variable.dimensions()) { @@ -135,13 +166,37 @@ private ScopeNode scope() throws ParseError checkIndependentVariable(variable); } - if (variable instanceof RedefinitionNode redefinitionNode) + if (peekKind(1, SyntaxKind.FILLER)) + { + var currentRedefineNode = currentRedefine(variable); + if (currentRedefineNode != null) + { + if (mightBeFillerBytes(peek(1), peek(2))) + { + parseRedefineFiller(currentRedefineNode); + } + } + else + { + // TODO: Diagnostic: Filler can only be used on redefines + } + } + + if (variable.level() == 1) + { + scopeNode.addVariable(variable); + } + else { - addTargetToRedefine(scopeNode, redefinitionNode); + addVariableToCurrentGroup(variable); } - scopeNode.addVariable(variable); addDeclaredVariable(variable); + + if (variable instanceof GroupNode groupNode) + { + groupStack.addLast(groupNode); + } } catch (ParseError e) { @@ -154,6 +209,7 @@ private ScopeNode scope() throws ParseError } passDownArrayDimensions(scopeNode); + groupStack.clear(); return scopeNode; } @@ -215,14 +271,18 @@ private UsingNode using() throws ParseError if (defineDataModule != null) { using.setReferencingModule((NaturalModule) defineDataModule); - using.setDefineData(defineDataModule.defineData()); - for (var variable : defineDataModule.defineData().variables()) + for (var diagnostic : ((NaturalModule) defineDataModule).diagnostics()) { - if (variable.level() == 1) + if (diagnostic instanceof ParserDiagnostic pd) { - addDeclaredVariable((VariableNode) variable, using); + report(pd.relocate(identifierTokenNode.diagnosticPosition())); } } + using.setDefineData(defineDataModule.defineData()); + for (var variable : defineDataModule.defineData().variables()) + { + addDeclaredVariable((VariableNode) variable, using); + } if (using.isParameterUsing() && ((NaturalModule) defineDataModule).file().getFiletype() != NaturalFileType.PDA) @@ -267,11 +327,6 @@ private BlockNode block() throws ParseError return block; } - private VariableNode variable() throws ParseError - { - return variable(List.of()); - } - private VariableNode variable(List inheritedDimensions) throws ParseError { if (peek(2).kind() == SyntaxKind.VIEW) @@ -339,16 +394,10 @@ private ViewNode view() throws ParseError private GroupNode groupVariable(VariableNode variable) throws ParseError { - var groupNode = variable instanceof RedefinitionNode - ? (RedefinitionNode) variable + var groupNode = variable instanceof RedefinitionNode redefine + ? redefine : new GroupNode(variable); - var previousRedefine = currentRedefineNode; - if (groupNode instanceof RedefinitionNode redefine) - { - currentRedefineNode = redefine; - } - if (variable.dimensions().hasItems()) { for (var dimension : variable.dimensions()) @@ -366,45 +415,6 @@ private GroupNode groupVariable(VariableNode variable) throws ParseError consumeMandatory(groupNode, SyntaxKind.RPAREN); } - while (peekKind(SyntaxKind.NUMBER_LITERAL)) - { - if (peek().intValue() <= groupNode.level()) - { - break; - } - - if (peek(1).kind() == SyntaxKind.FILLER && currentRedefineNode != null) - { - if (mightBeFillerBytes(peek(1), peek(2))) - { - parseRedefineFiller(currentRedefineNode); - continue; - } - } - - var nestedVariable = variable(groupNode.getDimensions()); - groupNode.addVariable(nestedVariable); - - if (peek().line() == previousToken().line() - && peek().kind() != SyntaxKind.NUMBER_LITERAL) // multiple variables declared in the same line... - { - // Error handling for trailing stuff that shouldn't be there - skipToNextLineReportingEveryToken(); - } - } - - if (groupNode.variables().size() == 0) - { - report(ParserErrors.emptyGroupVariable(groupNode)); - } - - currentRedefineNode = previousRedefine; - - if (groupNode.level() == 1 && !(groupNode instanceof IRedefinitionNode)) - { - ensureAllConstOrNoneConst(groupNode.variables(), new GroupConstStatistic()); - } - return groupNode; } @@ -419,7 +429,8 @@ private void ensureAllConstOrNoneConst(Iterable variables, GroupC } if (variable instanceof ITypedVariableNode typedVar - && typedVar.type() != null) + && typedVar.type() != null + && !(typedVar.parent() instanceof IRedefinitionNode)) // doesn't matter for REDEFINE children { if (typedVar.type().isConstant()) { @@ -517,6 +528,7 @@ private VariableNode typedVariable(VariableNode variable) throws ParseError length = getLengthFromDataType(dataType + "." + number.source()); } type.setLength(length); + typedVariable.setType(type); if (!arrayConsumed && consumeOptionally(typedVariable, SyntaxKind.SLASH)) { @@ -588,7 +600,6 @@ private VariableNode typedVariable(VariableNode variable) throws ParseError } } - typedVariable.setType(type); if (consumeOptionally(typedVariable, SyntaxKind.LPAREN)) { // TODO(masks): Parse for real and add to variable @@ -872,7 +883,7 @@ private void addArrayDimension(VariableNode variable) throws ParseError { var dimension = new ArrayDimension(); var lowerBound = extractArrayBound(new TokenNode(peek()), dimension); - var upperBound = ArrayDimension.UNBOUND_VALUE; + var upperBound = 0; consume(dimension); if (consumeOptionally(dimension, SyntaxKind.COLON)) { @@ -893,6 +904,7 @@ private void addArrayDimension(VariableNode variable) throws ParseError dimension.setLowerBound(lowerBound); dimension.setUpperBound(upperBound); + variable.addDimension(dimension); while (!isAtEnd() && !peekKind(SyntaxKind.COMMA) && !peekKind(SyntaxKind.RPAREN)) { @@ -912,17 +924,17 @@ private int extractArrayBound(ITokenNode token, ArrayDimension dimension) if (token.token().kind().isIdentifier()) { - var isUnboundV = token.token().symbolName().equals("V"); // (1:V) is allowed in parameter scope, where V stands for unbound + var isUnboundV = token.token().symbolName().equals("V"); // (1:V) is allowed in parameter scope, where V stands for variable if (currentScope.isParameter() && isUnboundV && !isVariableDeclared(token.token().symbolName())) { - return ArrayDimension.UNBOUND_VALUE; + return IArrayDimension.VARIABLE_BOUND; } if (!isVariableDeclared(token.token().symbolName())) { report(ParserErrors.unresolvedReference(token)); - return ArrayDimension.UNBOUND_VALUE; + return IArrayDimension.UNBOUND_VALUE; } var constReference = getDeclaredVariable(token); @@ -939,7 +951,7 @@ private int extractArrayBound(ITokenNode token, ArrayDimension dimension) } } - return ArrayDimension.UNBOUND_VALUE; + return IArrayDimension.UNBOUND_VALUE; } private void checkBounds(IArrayDimension dimension) @@ -1144,6 +1156,11 @@ private void addArrayDimensionWorkaroundComma(VariableNode variable) throws Pars private void checkIndependentVariable(VariableNode variable) { + if (currentRedefine(variable) != null) + { + return; + } + if (!variable.name().startsWith("+")) { report(ParserErrors.invalidAivNaming(variable)); @@ -1155,13 +1172,26 @@ private void checkIndependentVariable(VariableNode variable) } } - private void addTargetToRedefine(ScopeNode scopeNode, RedefinitionNode redefinitionNode) + private void addTargetToRedefine(RedefinitionNode redefinitionNode) + { + if (redefinitionNode.parent()instanceof ScopeNode scope) + { + addTargetToRedefine(scope.variables(), redefinitionNode); + } + + if (redefinitionNode.parent()instanceof IGroupNode group) + { + addTargetToRedefine(group.variables(), redefinitionNode); + } + } + + private void addTargetToRedefine(Iterable possibleVariables, RedefinitionNode redefinitionNode) { IVariableNode target = null; - for (var variable : scopeNode.variables()) + for (var variable : possibleVariables) { - if (variable.name().equalsIgnoreCase(redefinitionNode.name())) + if (variable.name() != null && variable.name().equalsIgnoreCase(redefinitionNode.name())) { target = variable; break; @@ -1174,7 +1204,9 @@ private void addTargetToRedefine(ScopeNode scopeNode, RedefinitionNode redefinit return; } - if (target instanceof TypedVariableNode typedTarget && typedTarget.type().hasDynamicLength()) + if (target instanceof TypedVariableNode typedTarget + && typedTarget.type() != null // TODO: no types for view stuff yet :( + && typedTarget.type().hasDynamicLength()) { report(ParserErrors.redefineCantTargetDynamic(redefinitionNode)); return; @@ -1182,6 +1214,24 @@ private void addTargetToRedefine(ScopeNode scopeNode, RedefinitionNode redefinit redefinitionNode.setTarget(target); + // length check for redefine will be done afterward + } + + private void checkRedefineLength(IRedefinitionNode redefinitionNode) + { + if (redefinitionNode.isInView()) + { + return; + } + + var target = redefinitionNode.target(); + + if (target instanceof ITypedVariableNode typedTarget && typedTarget.type() == null) + { + // The target is a VIEW variable which has no explicit type + return; + } + var targetLength = calculateVariableLengthInBytes(target); var redefineLength = calculateVariableLengthInBytes(redefinitionNode); var skipLengthCheck = false; @@ -1267,7 +1317,7 @@ private int calculateLengthInBytesWithArray(ITypedVariableNode target) } else { - totalOccurrences += dimension.occurerences(); + totalOccurrences = totalOccurrences == 0 ? dimension.occurerences() : totalOccurrences * dimension.occurerences(); } } @@ -1292,25 +1342,32 @@ private void addDeclaredVariable(VariableNode variable) private void addDeclaredVariable(VariableNode variable, ISyntaxNode diagnosticPosition) { - if (variable instanceof GroupNode groupNode) + if (variable.level() > 1 && variable.parent() == null) { - for (var nestedVariable : groupNode.variables()) - { - addDeclaredVariable((VariableNode) nestedVariable, diagnosticPosition); - } + // will be added by the group in `groupVariable()` after it has been assigned its parent + return; } - if (variable instanceof IRedefinitionNode) + if (variable instanceof RedefinitionNode) { - // Nested variables are already handled above. The #VAR in `REDEFINE #VAR` doesn't need to be added + // REDEFINE doesn't need to be a declared variable, because + // the target of REDEFINE has been declared. return; } if (declaredVariables.containsKey(variable.name())) { var alreadyDefined = declaredVariables.get(variable.name()); + if (alreadyDefined.position().isSamePositionAs(variable.position())) + { + return; + } + if (!variable.qualifiedName().equals(alreadyDefined.qualifiedName())) { + // Variable with the same name exists, but qualified names differ. + // Re-add the old with the qualified name and also add the new one + // qualified declaredVariables.remove(variable.name()); declaredVariables.put(alreadyDefined.qualifiedName(), alreadyDefined); declaredVariables.put(variable.qualifiedName(), variable); @@ -1326,6 +1383,70 @@ private void addDeclaredVariable(VariableNode variable, ISyntaxNode diagnosticPo declaredVariables.put(variable.name(), variable); } + private void addVariableToCurrentGroup(VariableNode variable) + { + if (groupStack.isEmpty()) + { + return; + } + + if (groupStack.peekLast().level() == variable.level() - 1) + { + groupStack.peekLast().addVariable(variable); + } + } + + private void popGroupsIfNecessary() + { + if (groupStack.isEmpty()) + { + return; + } + + if (!peekKind(SyntaxKind.NUMBER_LITERAL)) + { + return; + } + + var newLevel = peek().intValue(); + while (!groupStack.isEmpty() && newLevel <= groupStack.peekLast().level()) + { + groupStack.removeLast(); + } + } + + private RedefinitionNode currentRedefine(VariableNode currentVar) + { + if (currentVar instanceof RedefinitionNode redefine) + { + return redefine; + } + + if (groupStack.isEmpty()) + { + return null; + } + + for (var group : groupStack) + { + if (group instanceof RedefinitionNode redefine) + { + return redefine; + } + } + + return null; + } + + private List currentGroupsDimensions() + { + if (groupStack.isEmpty()) + { + return List.of(); + } + return groupStack.peekLast().getDimensions(); + } + private static class GroupConstStatistic { private int constEncountered; @@ -1336,4 +1457,22 @@ private boolean hasMixedConst() return constEncountered > 0 && nonConstEncountered > 0; } } + + @Override + protected ITokenNode consumeMandatoryIdentifierTokenNode(BaseSyntaxNode node) + { + var currentToken = tokens.peek(); + if (tokens.isAtEnd() || (currentToken.kind() != SyntaxKind.IDENTIFIER && !currentToken.kind().canBeIdentifier())) + { + // In case of DEFINE DATA we don't throw here to keep parsing a whole DEFINE DATA. + // These variables won't be resolvable though, because the original implementation + // that the StatementListParser uses is throwing, which is fine. + report(ParserErrors.unexpectedToken(SyntaxKind.IDENTIFIER, tokens)); + } + + tokens.advance(); + var tokenNode = new TokenNode(currentToken.withKind(SyntaxKind.IDENTIFIER)); + node.addNode(tokenNode); + return tokenNode; + } } diff --git a/libs/natparse/src/main/java/org/amshove/natparse/parsing/ParserErrors.java b/libs/natparse/src/main/java/org/amshove/natparse/parsing/ParserErrors.java index c304b0199..ac60b8578 100644 --- a/libs/natparse/src/main/java/org/amshove/natparse/parsing/ParserErrors.java +++ b/libs/natparse/src/main/java/org/amshove/natparse/parsing/ParserErrors.java @@ -192,12 +192,12 @@ public static ParserDiagnostic noTargetForRedefineFound(RedefinitionNode redefin { return ParserDiagnostic.create( "No target for REDEFINE found. The redefined variable must be declared beforehand", - redefinitionNode.identifierNode(), + redefinitionNode.identifierNode() != null ? redefinitionNode.identifierNode() : redefinitionNode, ParserError.NO_TARGET_VARIABLE_FOR_REDEFINE_FOUND ); } - public static ParserDiagnostic redefinitionLengthIsTooLong(RedefinitionNode node, double redefinitionLength, double maxLength) + public static ParserDiagnostic redefinitionLengthIsTooLong(IRedefinitionNode node, double redefinitionLength, double maxLength) { return ParserDiagnostic.create( "Length of redefinition (%s bytes) exceeds target length (%s bytes) of %s".formatted(DataFormat.formatLength(redefinitionLength), DataFormat.formatLength(maxLength), node.declaration().source()), diff --git a/libs/natparse/src/main/java/org/amshove/natparse/parsing/TypedVariableNode.java b/libs/natparse/src/main/java/org/amshove/natparse/parsing/TypedVariableNode.java index be8e12e1b..854876fdf 100644 --- a/libs/natparse/src/main/java/org/amshove/natparse/parsing/TypedVariableNode.java +++ b/libs/natparse/src/main/java/org/amshove/natparse/parsing/TypedVariableNode.java @@ -1,5 +1,7 @@ package org.amshove.natparse.parsing; +import org.amshove.natparse.ReadOnlyList; +import org.amshove.natparse.natural.IArrayDimension; import org.amshove.natparse.natural.ITypedVariableNode; import org.amshove.natparse.natural.IVariableType; @@ -31,4 +33,50 @@ void setType(VariableType type) { this.type = type; } + + @Override + void inheritDimensions(ReadOnlyList dimensions) + { + for (var dimension : dimensions) + { + if (this.dimensions.contains(dimension)) + { + continue; + } + + if (dimension.isUpperVariable()) + { + var boundDimension = new ArrayDimension(); + boundDimension.setLowerBound(dimension.lowerBound()); + boundDimension.setUpperBound(type.byteSize()); + this.dimensions.add(0, boundDimension); + } + else + { + this.dimensions.add(0, dimension); + } + } + } + + @Override + void addDimension(ArrayDimension dimension) + { + addNode(dimension); + if (dimension.isUpperVariable()) + { + if (type == null) + { + // Will be evaluated once typed + return; + } + var boundDimension = new ArrayDimension(); + boundDimension.setLowerBound(dimension.lowerBound()); + boundDimension.setUpperBound(type.byteSize()); + dimensions.add(boundDimension); + } + else + { + dimensions.add(dimension); + } + } } diff --git a/libs/natparse/src/main/java/org/amshove/natparse/parsing/VariableNode.java b/libs/natparse/src/main/java/org/amshove/natparse/parsing/VariableNode.java index 1b8afe3a8..b28404254 100644 --- a/libs/natparse/src/main/java/org/amshove/natparse/parsing/VariableNode.java +++ b/libs/natparse/src/main/java/org/amshove/natparse/parsing/VariableNode.java @@ -8,9 +8,7 @@ import org.amshove.natparse.natural.*; import javax.annotation.Nonnull; -import java.nio.file.Path; import java.util.ArrayList; -import java.util.Collection; import java.util.List; class VariableNode extends BaseSyntaxNode implements IVariableNode @@ -85,7 +83,7 @@ public String qualifiedName() return qualifiedName; } - parent = ((ISyntaxNode) parent).parent(); + parent = parent.parent(); } throw new NaturalParseException("Could not determine qualified name"); diff --git a/libs/natparse/src/test/java/org/amshove/natparse/parsing/AbstractParserTest.java b/libs/natparse/src/test/java/org/amshove/natparse/parsing/AbstractParserTest.java index a9405064e..b493f793c 100644 --- a/libs/natparse/src/test/java/org/amshove/natparse/parsing/AbstractParserTest.java +++ b/libs/natparse/src/test/java/org/amshove/natparse/parsing/AbstractParserTest.java @@ -78,7 +78,7 @@ protected NodeType assertDiagnostic(String source, ParserError expectedError) protected void assertDiagnosticsContain(ReadOnlyList diagnostics, ParserError expectedError) { assertThat(diagnostics.size()) - .as("Expected to get at least one diagnostic, but found none") + .as("Expected to get at least one diagnostic of type <%s>, but found none".formatted(expectedError.name())) .isGreaterThan(0); assertThat(diagnostics) .as("Diagnostic %s(%s) not found".formatted(expectedError.name(), expectedError.id())) diff --git a/libs/natparse/src/test/java/org/amshove/natparse/parsing/DefineDataParserShould.java b/libs/natparse/src/test/java/org/amshove/natparse/parsing/DefineDataParserShould.java index c4f740b0c..2733790aa 100644 --- a/libs/natparse/src/test/java/org/amshove/natparse/parsing/DefineDataParserShould.java +++ b/libs/natparse/src/test/java/org/amshove/natparse/parsing/DefineDataParserShould.java @@ -4,7 +4,6 @@ import org.amshove.natparse.natural.*; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -240,6 +239,21 @@ void parseALocalVariable() assertThat(variable.type().length()).isEqualTo(10.0); } + @Test + void raiseADiagnosticForKeywordsUsedAsIdentifierButStillParseOn() + { + var defineData = assertDiagnostic(""" + DEFINE DATA LOCAL + 1 PROCESS + 2 #VAR (A10) + END-DEFINE + """, ParserError.UNEXPECTED_TOKEN); + + assertThat(defineData.variables().first().name()).isEqualTo("PROCESS"); + assertThat(defineData.variables().last().name()).isEqualTo("#VAR"); + assertThat(defineData.variables().last().qualifiedName()).isEqualTo("PROCESS.#VAR"); + } + @Test void addADiagnosticForMissingDataFormats() { @@ -928,6 +942,35 @@ void notReportALengthDiagnosticForNestedRedefineVariables() """); } + @Test + void parseTheUpperBoundOfVariableParameterDimensions() + { + var defineData = assertParsesWithoutDiagnostics(""" + DEFINE DATA PARAMETER + 1 #PARM (A10 / 1:V) + END-DEFINE + """); + + var parameter = defineData.findVariable("#PARM"); + assertThat(parameter).isNotNull(); + assertThat(parameter.dimensions().first().upperBound()).isEqualTo(10); + } + + @Test + void parseTheUpperBoundOfVariableParameterDimensionsInGroups() + { + var defineData = assertParsesWithoutDiagnostics(""" + DEFINE DATA PARAMETER + 1 #GRP (1:V) + 2 #PARM (A10) + END-DEFINE + """); + + var parameter = defineData.findVariable("#PARM"); + assertThat(parameter).isNotNull(); + assertThat(parameter.dimensions().first().upperBound()).isEqualTo(10); + } + @Test void inheritArrayDimensionsInNestedRedefines() { @@ -1015,7 +1058,7 @@ void redefineGroups() 01 #FIRSTVAR 02 #FIRSTVAR-A (N2) INIT <5> 02 #FIRSTVAR-B (P6) INIT <10> - 01 REDEFINE #FIRSTVAR + 01 REDEFINE #FIRSTVAR 02 #FIRSTVAR-ALPHA (A6) END-DEFINE """; @@ -1028,6 +1071,42 @@ void redefineGroups() assertThat(redefinition.variables().first().qualifiedName()).isEqualTo("#FIRSTVAR.#FIRSTVAR-ALPHA"); } + @Test + void parseVariablesAfterNestedRedefines() + { + var defineData = assertParsesWithoutDiagnostics(""" + DEFINE DATA LOCAL + 1 #GRP + 2 #VAR1 (N8) + 2 REDEFINE #VAR1 + 3 #VAR-A (A8) + 3 REDEFINE #VAR-A + 4 #VAR-R-1 (A8) + 2 #VAR-2 (A10) + 2 #VAR-3(L) + END-DEFINE + """); + + assertThat(defineData.findVariable("#VAR-2")).as("#VAR-2 not found").isNotNull(); + assertThat(defineData.findVariable("#VAR-3")).as("#VAR-3 not found").isNotNull(); + } + + @Test + void allowToRedefineWithXArraysHavingConstBounds() + { + assertParsesWithoutDiagnostics(""" + DEFINE DATA LOCAL + 1 DTAC + 2 VAR-MAX (I2) CONST<2> + 2 VAR + 3 VAR-1 (A8) CONST<'ABC'> + 3 VAR-2 (A8) CONST<'DEF'> + 2 REDEFINE VAR + 3 PROCESS-ALL (A8/1:VAR-MAX) + END-DEFINE + """); + } + @Test void redefineIndependentVariables() { @@ -1036,7 +1115,7 @@ void redefineIndependentVariables() INDEPENDENT 1 +MY-AIV (A10) 1 REDEFINE +MY-AIV - 2 #INSIDE (A2) + 2 #INSIDE (A2) END-DEFINE """; @@ -1249,7 +1328,7 @@ void allowVAsUnboundInParameterScope() """); var variable = findVariable(defineData, "#p-unbound-array", ITypedVariableNode.class); - assertThat(variable.dimensions().first().isUpperUnbound()).isTrue(); + assertThat(variable.dimensions().first().upperBound()).isEqualTo(3); } @Test @@ -1393,6 +1472,7 @@ void parseInitializerOfVariablesInGroupArray() var inside = data.variables().last(); assertThat(inside.name()).isEqualTo("#INSIDE"); + assertThat(inside.dimensions()).hasSize(1); assertThat(inside.dimensions().first().lowerBound()).isEqualTo(1); assertThat(inside.dimensions().first().upperBound()).isEqualTo(10); } @@ -1418,6 +1498,28 @@ void addMultipleDimensionsForGroupArraysContainingArrays() assertThat(inside.dimensions().last().upperBound()).isEqualTo(5); } + @Test + void addMultipleDimensionsForGroupArraysContainingGroupArray() + { + var data = assertParsesWithoutDiagnostics(""" + define data + local + 1 #myarraygroup (1:10) + 2 #insidegrp (1:5) + 3 #insidevar (A5) /* This is considered a second dimension, so (1:10,1:5) + end-define + """); + // TODO(array-initializer): Check values + + var inside = data.variables().last(); + assertThat(inside.name()).isEqualTo("#INSIDEVAR"); + assertThat(inside.dimensions().size()).isEqualTo(2); + assertThat(inside.dimensions().first().lowerBound()).isEqualTo(1); + assertThat(inside.dimensions().first().upperBound()).isEqualTo(10); + assertThat(inside.dimensions().last().lowerBound()).isEqualTo(1); + assertThat(inside.dimensions().last().upperBound()).isEqualTo(5); + } + @ParameterizedTest @ValueSource(strings = { @@ -1626,14 +1728,14 @@ void showNoDiagnosticForRedefinesWithGroupsInvolved() 2 #bytes2 (A1/1:850) 2 redefine #bytes2 3 #bytes-str (A750) - 3 #r1 (a1/101:450) + 3 #r1 (a1/101:150) 1 redefine #grp 2 #var3 (A300) end-define """); } - @Disabled("This should fail, but does not. Seems the nested REDEFINE for #bytes2 is not handled.") + @Test void showADiagnosticForRedefinesWithGroupsInvolved() { assertDiagnostic( @@ -1821,9 +1923,9 @@ void allowFillerWithinAGroupWithinARedefine() define data local 1 #var1 (A10) 1 redefine #var1 - 2 #thegroup - 3 filler 5X - 3 rest (a5) + 2 #thegroup + 3 filler 5X + 3 rest (a5) end-define """); } From af002d016291093543695aef1c9e917633590391 Mon Sep 17 00:00:00 2001 From: Claes65 Date: Mon, 24 Jul 2023 12:21:51 +0200 Subject: [PATCH 6/8] tests cleared --- .../amshove/natparse/natural/IDataType.java | 69 ++--- .../parsing/DataTypeCheckingShould.java | 15 +- .../parsing/LiteralTypeInferenceShould.java | 2 +- .../natparse/parsing/typing/simpleAssignments | 252 +++++++++++++++++- 4 files changed, 285 insertions(+), 53 deletions(-) diff --git a/libs/natparse/src/main/java/org/amshove/natparse/natural/IDataType.java b/libs/natparse/src/main/java/org/amshove/natparse/natural/IDataType.java index aec0a7893..031b1a4f2 100644 --- a/libs/natparse/src/main/java/org/amshove/natparse/natural/IDataType.java +++ b/libs/natparse/src/main/java/org/amshove/natparse/natural/IDataType.java @@ -30,53 +30,53 @@ default boolean fitsInto(IDataType target) return lengthFits && formatIsCompatible; } + /** + * Determines if both types have the same family, e.g. N, I, P are all numeric. + */ + default boolean hasSameFamily(IDataType target) + { + var targetFormat = target.format(); + return format() == targetFormat || switch (format()) + { + case PACKED, FLOAT, INTEGER, NUMERIC, TIME -> targetFormat == PACKED + || targetFormat == FLOAT + || targetFormat == INTEGER + || targetFormat == NUMERIC + || targetFormat == TIME; + case ALPHANUMERIC, UNICODE -> targetFormat == ALPHANUMERIC + || targetFormat == UNICODE + || targetFormat == BINARY; + default -> false; + }; + } + /** * Takes implicit conversion into account, e.g. N -> A */ - default boolean hasCompatibleFormat(IDataType other) + default boolean hasCompatibleFormat(IDataType target) { - var targetFormat = other.format(); - return hasSameFamily(other) || switch (format()) + var targetFormat = target.format(); + return hasSameFamily(target) || switch (format()) { case PACKED, FLOAT, INTEGER, NUMERIC -> targetFormat == ALPHANUMERIC || targetFormat == UNICODE - || isShortBinary(other); + || isShortBinary(target); case TIME, DATE -> targetFormat == NUMERIC || targetFormat == PACKED || targetFormat == ALPHANUMERIC || targetFormat == UNICODE - || isShortBinary(other) + || isShortBinary(target) || targetFormat == INTEGER // this one can fail, but not for early times || targetFormat == DATE || targetFormat == TIME || targetFormat == FLOAT; case LOGIC -> targetFormat == ALPHANUMERIC || targetFormat == UNICODE; - case BINARY -> binaryCompatibility(other); + case BINARY -> binaryCompatibility(target); default -> false; // we don't know whats implicitly compatible yet }; } - /** - * Determines if both types have the same family, e.g. N, I, P are all numeric. - */ - default boolean hasSameFamily(IDataType other) - { - var targetFormat = other.format(); - return format() == targetFormat || switch (format()) - { - case PACKED, FLOAT, INTEGER, NUMERIC, TIME -> targetFormat == PACKED - || targetFormat == FLOAT - || targetFormat == INTEGER - || targetFormat == NUMERIC - || targetFormat == TIME; - case ALPHANUMERIC, UNICODE -> targetFormat == ALPHANUMERIC - || targetFormat == UNICODE - || targetFormat == BINARY; - default -> false; - }; - } - default String toShortString() { var details = ""; @@ -146,25 +146,30 @@ private int calculateDigitsAfterDecimalPoint() return Integer.parseInt((Double.toString(length()).split("\\.")[1])); } - private boolean isShortBinary(IDataType other) + private boolean isShortBinary() { return format() == BINARY && length() < 5; } - private boolean isLongBinary(IDataType other) + private boolean isShortBinary(IDataType target) + { + return target.format() == BINARY && target.length() < 5; + } + + private boolean isLongBinary() { return format() == BINARY && length() > 4; } - private boolean binaryCompatibility(IDataType other) + private boolean binaryCompatibility(IDataType target) { - var targetFormat = other.format(); - return (isLongBinary(other) && switch (targetFormat) + var targetFormat = target.format(); + return (isLongBinary() && switch (targetFormat) { case ALPHANUMERIC, UNICODE -> true; default -> false; }) || - (isShortBinary(other) && switch (targetFormat) + (isShortBinary() && switch (targetFormat) { case NUMERIC, PACKED, ALPHANUMERIC, UNICODE, INTEGER, TIME, FLOAT -> true; default -> false; diff --git a/libs/natparse/src/test/java/org/amshove/natparse/parsing/DataTypeCheckingShould.java b/libs/natparse/src/test/java/org/amshove/natparse/parsing/DataTypeCheckingShould.java index 99c0cc3c4..0aaed5c03 100644 --- a/libs/natparse/src/test/java/org/amshove/natparse/parsing/DataTypeCheckingShould.java +++ b/libs/natparse/src/test/java/org/amshove/natparse/parsing/DataTypeCheckingShould.java @@ -118,8 +118,11 @@ void seeImplicitlyCompatibleDataTypesAsIncompatibleWithBiggerLength(String numer @ParameterizedTest @CsvSource( { - "N,N", "N,I", "N,F", "N,P", "N,B", - "A,U", "A,B", "A,A" + "N,N", "N,I", "N,F", "N,P", "N,T", + "P,N", "P,I", "P,F", "P,P", "P,T", + "T,N", "T,I", "T,F", "T,P", "T,T", + "A,A", "A,U", "A,B", "B,B", + "U,A", "U,B", "U,U" } ) void recognizeDataFormatsAsTheSameFamily(String firstFormat, String secondFormat) @@ -130,11 +133,11 @@ void recognizeDataFormatsAsTheSameFamily(String firstFormat, String secondFormat @ParameterizedTest @CsvSource( { - "N,A", "N,U", "N,L", "N,C", "N,D", "N,T", + "N,A", "N,U", "N,L", "N,C", "N,D", "A,L", "A,C", "A,D", "A,F", "A,I", "A,P", "A,T", - "I,A", "I,U", "I,L", "I,C", "I,D", "I,T", - "P,A", "P,U", "P,L", "P,C", "P,D", "P,T", - "F,A", "F,U", "F,L", "F,C", "F,D", "F,T", + "I,A", "I,U", "I,L", "I,C", "I,D", + "P,A", "P,U", "P,L", "P,C", "P,D", + "F,A", "F,U", "F,L", "F,C", "F,D", "L,B", "L,D", "L,T", "L,U", "L,A", "L,P" } ) diff --git a/libs/natparse/src/test/java/org/amshove/natparse/parsing/LiteralTypeInferenceShould.java b/libs/natparse/src/test/java/org/amshove/natparse/parsing/LiteralTypeInferenceShould.java index 9e280881a..a3496a7ee 100644 --- a/libs/natparse/src/test/java/org/amshove/natparse/parsing/LiteralTypeInferenceShould.java +++ b/libs/natparse/src/test/java/org/amshove/natparse/parsing/LiteralTypeInferenceShould.java @@ -40,7 +40,7 @@ void inferTheCorrectTypeBasedOnTargetTypeForIntegers(String source, String targe "200,B2", "12345,B2", "2147483647,B4", - "2147483648,B10", + "2147483648,P10", } ) void inferTheCorrectTypeBasedOnTargetTypeForBinary(String source, String targetType) diff --git a/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments b/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments index 36365007e..d23dfd20b 100644 --- a/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments +++ b/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/simpleAssignments @@ -1,13 +1,17 @@ DEFINE DATA LOCAL 1 #CONST-N1-I4 (I4) CONST<1> 1 #N1 (N1) -1 #I1 (I1) -1 #A1 (A1) 1 #P1 (P1) +1 #A1 (A1) +1 #U1 (U1) 1 #B4 (B4) 1 #B5 (B5) -1 #T (T) +1 #I1 (I1) +1 #L (L) +1 #C (C) 1 #D (D) +1 #T (T) +1 #F (F4) 1 #A10 (A10) 1 #ARR (N10/*) 1 #N12 (N12) @@ -22,18 +26,241 @@ END-DEFINE /* NPP037: Type mismatch /* NPP045: Literal value truncation +/* Format N +#N1 := #N1 +#N1 := #P1 +#N1 := 1 +#N1 := #A1 /* !{D:ERROR:NPP037} +#N1 := #U1 /* !{D:ERROR:NPP037} +#N1 := 'X' /* !{D:ERROR:NPP037} +#N1 := #B4 +#N1 := #B5 /* !{D:ERROR:NPP037} +#N1 := #I1 +#N1 := #L /* !{D:ERROR:NPP037} +#N1 := TRUE /* !{D:ERROR:NPP037} +#N1 := #C /* !{D:ERROR:NPP037} +#N1 := #D +#N1 := #T +#N1 := #F +#N1 := *ISN +#N1 := *PROGRAM /* !{D:ERROR:NPP037} + +/* Format P +#P1 := #N1 +#P1 := #P1 +#P1 := 1 +#P1 := #A1 /* !{D:ERROR:NPP037} +#P1 := #U1 /* !{D:ERROR:NPP037} +#P1 := 'X' /* !{D:ERROR:NPP037} +#P1 := #B4 +#P1 := #B5 /* !{D:ERROR:NPP037} +#P1 := #I1 +#P1 := #L /* !{D:ERROR:NPP037} +#P1 := TRUE /* !{D:ERROR:NPP037} +#P1 := #C /* !{D:ERROR:NPP037} +#P1 := #D +#P1 := #T +#P1 := #F +#P1 := *ISN +#P1 := *PROGRAM /* !{D:ERROR:NPP037} + +/* Format A +#A1 := #N1 +#A1 := #P1 +#A1 := 1 +#A1 := #A1 +#A1 := #U1 +#A1 := 'X' +#A1 := #B4 +#A1 := #B5 +#A1 := #I1 +#A1 := #L +#A1 := TRUE +#A1 := #C /* !{D:ERROR:NPP037} +#A1 := #D +#A1 := #T +#A1 := #F +#A1 := *ISN +#A1 := *PROGRAM + +/* Format U +#U1 := #N1 +#U1 := #P1 +#U1 := 1 +#U1 := #A1 +#U1 := #U1 +#U1 := 'X' +#U1 := #B4 +#U1 := #B5 +#U1 := #I1 +#U1 := #L +#U1 := TRUE +#U1 := #C /* !{D:ERROR:NPP037} +#U1 := #D +#U1 := #T +#U1 := #F +#U1 := *ISN +#U1 := *PROGRAM + +/* Format Bn (n<5) +#B4 := #N1 +#B4 := #P1 +#B4 := 1 +#B4 := #A1 +#B4 := #U1 +#B4 := 'X' +#B4 := #B4 +#B4 := #B5 +#B4 := #I1 +#B4 := #L /* !{D:ERROR:NPP037} +#B4 := TRUE /* !{D:ERROR:NPP037} +#B4 := #C /* !{D:ERROR:NPP037} +#B4 := #D +#B4 := #T +#B4 := #F +#B4 := *ISN +#B4 := *PROGRAM + +/* Format Bn (n>4) +#B5 := #N1 /* !{D:ERROR:NPP037} +#B5 := #P1 /* !{D:ERROR:NPP037} +#B5 := 1 /* !{D:ERROR:NPP037} +#B5 := #A1 +#B5 := #U1 +#B5 := 'X' +#B5 := #B4 +#B5 := #B5 +#B5 := #I1 /* !{D:ERROR:NPP037} +#B5 := #L /* !{D:ERROR:NPP037} +#B5 := TRUE /* !{D:ERROR:NPP037} +#B5 := #C /* !{D:ERROR:NPP037} +#B5 := #D /* !{D:ERROR:NPP037} +#B5 := #T /* !{D:ERROR:NPP037} +#B5 := #F /* !{D:ERROR:NPP037} +#B5 := *ISN /* !{D:ERROR:NPP037} +#B5 := *PROGRAM + +/* Format I +#I1 := #N1 +#I1 := #P1 +#I1 := 1 +#I1 := #A1 /* !{D:ERROR:NPP037} +#I1 := #U1 /* !{D:ERROR:NPP037} +#I1 := 'X' /* !{D:ERROR:NPP037} +#I1 := #B4 +#I1 := #B5 /* !{D:ERROR:NPP037} +#I1 := #I1 +#I1 := #L /* !{D:ERROR:NPP037} +#I1 := TRUE /* !{D:ERROR:NPP037} +#I1 := #C /* !{D:ERROR:NPP037} +#I1 := #D +#I1 := #T +#I1 := #F +#I1 := *ISN +#I1 := *PROGRAM /* !{D:ERROR:NPP037} + +/* Format L +#L := #N1 /* !{D:ERROR:NPP037} +#L := #P1 /* !{D:ERROR:NPP037} +#L := 1 /* !{D:ERROR:NPP037} +#L := #A1 /* !{D:ERROR:NPP037} +#L := #U1 /* !{D:ERROR:NPP037} +#L := 'X' /* !{D:ERROR:NPP037} +#L := #B4 /* !{D:ERROR:NPP037} +#L := #B5 /* !{D:ERROR:NPP037} +#L := #I1 /* !{D:ERROR:NPP037} +#L := #L +#L := TRUE +#L := FALSE +#L := #C /* !{D:ERROR:NPP037} +#L := #D /* !{D:ERROR:NPP037} +#L := #T /* !{D:ERROR:NPP037} +#L := #F /* !{D:ERROR:NPP037} +#L := *ISN /* !{D:ERROR:NPP037} +#L := *PROGRAM /* !{D:ERROR:NPP037} + +/* Format C +#C := #N1 /* !{D:ERROR:NPP037} +#C := #P1 /* !{D:ERROR:NPP037} +#C := 1 /* !{D:ERROR:NPP037} +#C := #A1 /* !{D:ERROR:NPP037} +#C := #U1 /* !{D:ERROR:NPP037} +#C := 'X' /* !{D:ERROR:NPP037} +#C := #B4 /* !{D:ERROR:NPP037} +#C := #B5 /* !{D:ERROR:NPP037} +#C := #I1 /* !{D:ERROR:NPP037} +#C := #L /* !{D:ERROR:NPP037} +#C := TRUE /* !{D:ERROR:NPP037} +#C := #C +#C := (AD=P) +#C := (CD=RE) +#C := #D /* !{D:ERROR:NPP037} +#C := #T /* !{D:ERROR:NPP037} +#C := #F /* !{D:ERROR:NPP037} +#C := *ISN /* !{D:ERROR:NPP037} +#C := *PROGRAM /* !{D:ERROR:NPP037} + +/* Format D +#D := #N1 /* !{D:ERROR:NPP037} +#D := #P1 /* !{D:ERROR:NPP037} +#D := 1 /* !{D:ERROR:NPP037} +#D := #A1 /* !{D:ERROR:NPP037} +#D := #U1 /* !{D:ERROR:NPP037} +#D := 'X' /* !{D:ERROR:NPP037} +#D := #B4 /* !{D:ERROR:NPP037} +#D := #B5 /* !{D:ERROR:NPP037} +#D := #I1 /* !{D:ERROR:NPP037} +#D := #L /* !{D:ERROR:NPP037} +#D := TRUE /* !{D:ERROR:NPP037} +#D := #C /* !{D:ERROR:NPP037} +#D := #D +#D := #T +#D := #F /* !{D:ERROR:NPP037} +#D := *ISN /* !{D:ERROR:NPP037} +#D := *PROGRAM /* !{D:ERROR:NPP037} + +/* Format T +#T := #N1 +#T := #P1 +#T := 1 +#T := #A1 /* !{D:ERROR:NPP037} +#T := #U1 /* !{D:ERROR:NPP037} +#T := 'X' /* !{D:ERROR:NPP037} +#T := #B4 +#T := #B5 /* !{D:ERROR:NPP037} +#T := #I1 +#T := #L /* !{D:ERROR:NPP037} +#T := TRUE /* !{D:ERROR:NPP037} +#T := #C /* !{D:ERROR:NPP037} +#T := #D +#T := #T +#T := #F +#T := *ISN +#T := *PROGRAM /* !{D:ERROR:NPP037} + +/* Format F +#F := #N1 +#F := #P1 +#F := 1 +#F := #A1 /* !{D:ERROR:NPP037} +#F := #U1 /* !{D:ERROR:NPP037} +#F := 'X' /* !{D:ERROR:NPP037} +#F := #B4 +#F := #B5 /* !{D:ERROR:NPP037} +#F := #I1 +#F := #L /* !{D:ERROR:NPP037} +#F := TRUE /* !{D:ERROR:NPP037} +#F := #C /* !{D:ERROR:NPP037} +#F := #D +#F := #T +#F := #F +#F := *ISN +#F := *PROGRAM /* !{D:ERROR:NPP037} + #N1 := 23 /* !{D:WARNING:NPP045} #N1 := 2 #I1 := 128 /* !{D:WARNING:NPP045} #I1 := 127 -#N1 := 'Hi' /* !{D:ERROR:NPP037} -#N12 := #N1; -/*#N12 := #U20; -#N12 := #N1; -#P12 := #P1; -/* #P12 := #U20; -#P12 := #T; -#P12 := #D #N1 := #CONST-N1-I4 /* Should work, the constant *value* is N1 @@ -67,9 +294,6 @@ END-DEFINE #N12 := *TIMX /* fine #P12 := *TIMX /* fine -#D := #T -#T := #D - #A20 := 99999999999999999999 /* Results in a P11, not truncation needed to to A20 END From 7fc438478aaf5f661e318c3817efa9f0a1a23a3d Mon Sep 17 00:00:00 2001 From: Claes65 Date: Mon, 31 Jul 2023 15:03:35 +0200 Subject: [PATCH 7/8] Bugfix DECIDE ON typeChecker --- .../src/main/java/org/amshove/natparse/parsing/TypeChecker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/natparse/src/main/java/org/amshove/natparse/parsing/TypeChecker.java b/libs/natparse/src/main/java/org/amshove/natparse/parsing/TypeChecker.java index 66d847c59..27d347855 100644 --- a/libs/natparse/src/main/java/org/amshove/natparse/parsing/TypeChecker.java +++ b/libs/natparse/src/main/java/org/amshove/natparse/parsing/TypeChecker.java @@ -256,7 +256,7 @@ private void checkDecideOnBranches(IDecideOnNode decideOn) for (var value : branch.values()) { var inferredType = inferDataType(value); - if (inferredType.format() != DataFormat.NONE && !inferredType.hasSameFamily(typedTarget.type())) + if (inferredType.format() != DataFormat.NONE && !inferredType.hasCompatibleFormat(typedTarget.type())) { report( ParserErrors.typeMismatch( From e9c5270ff41e6d88a9461e5a0171dcc3656ca0bf Mon Sep 17 00:00:00 2001 From: Claes65 Date: Mon, 31 Jul 2023 16:18:43 +0200 Subject: [PATCH 8/8] Update decideOnTyping fixed tests --- .../natparse/parsing/typing/decideOnTyping | 63 +++++++++++-------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/decideOnTyping b/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/decideOnTyping index 465dbd2df..1d4ced1fb 100644 --- a/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/decideOnTyping +++ b/libs/natparse/src/test/resources/org/amshove/natparse/parsing/typing/decideOnTyping @@ -1,52 +1,65 @@ DEFINE DATA LOCAL +1 #B-01 (B01) 1 #B-10 (B10) 1 #A-10 (A10) 1 #N-10 (N10) +1 #CV (C) END-DEFINE DECIDE ON FIRST VALUE OF #A-10 - - VALUE 5 /* !{D:ERROR:NPP037} - IGNORE - - VALUE TRUE /* !{D:ERROR:NPP037} - IGNORE - - VALUE FALSE /* !{D:ERROR:NPP037} - IGNORE - + VALUE 5 + IGNORE + VALUE TRUE + IGNORE + VALUE FALSE + IGNORE + VALUE #B-10 + IGNORE + VALUE #CV /* !{D:ERROR:NPP037} + IGNORE NONE - IGNORE + IGNORE END-DECIDE DECIDE ON FIRST VALUE OF #N-10 - VALUE 5 - IGNORE - + IGNORE VALUE 500 - IGNORE - + IGNORE VALUE 'Hi' /* !{D:ERROR:NPP037} - IGNORE - + IGNORE VALUE TRUE /* !{D:ERROR:NPP037} - IGNORE - + IGNORE VALUE FALSE /* !{D:ERROR:NPP037} - IGNORE + IGNORE + NONE + IGNORE +END-DECIDE +DECIDE ON FIRST VALUE OF #B-01 + VALUE 5 + IGNORE + VALUE 'Hi' + IGNORE + VALUE H'00' + IGNORE + VALUE #B-10 + IGNORE NONE - IGNORE + IGNORE END-DECIDE DECIDE ON FIRST VALUE OF #B-10 VALUE 5 /* !{D:ERROR:NPP037} - IGNORE + IGNORE VALUE 'Hi' - IGNORE + IGNORE + VALUE H'00' + IGNORE + VALUE #B-01 + IGNORE NONE - IGNORE + IGNORE END-DECIDE END