Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend type checking of COMPRESS to operands and target #357

Merged
merged 3 commits into from
Aug 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,11 @@ protected void nodeAdded(BaseSyntaxNode node)
{
addAllVariablesFromScope((IScopeNode) node);
}

if (node instanceof IVariableNode variable)
{
variables.add(variable);
}
}

private void addAllVariablesFromUsing(IUsingNode usingNode)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ private NaturalModule parseModule(NaturalFile file, IModuleProvider moduleProvid
return naturalModule;
}

VariableNode functionReturnVariable = null;
if (file.getFiletype() == NaturalFileType.FUNCTION) // skip over DEFINE FUNCTION
{
// TODO: Implement proper when implementing different NaturalModules
Expand All @@ -63,8 +64,15 @@ private NaturalModule parseModule(NaturalFile file, IModuleProvider moduleProvid
{
break;
}
if (tokens.peek().kind() == SyntaxKind.RETURNS)

if (tokens.peek(1).kind() == SyntaxKind.RETURNS)
{
var functionName = tokens.advance();
functionReturnVariable = new VariableNode();
functionReturnVariable.setLevel(1);
functionReturnVariable.setScope(VariableScope.LOCAL);
functionReturnVariable.setDeclaration(new TokenNode(functionName));

tokens.advance(); // RETURNS
if (tokens.peek().kind() == SyntaxKind.LPAREN)
{
Expand All @@ -76,12 +84,37 @@ private NaturalModule parseModule(NaturalFile file, IModuleProvider moduleProvid
typeTokenSource += tokens.advance().source(); // next number
}
var type = DataType.fromString(typeTokenSource);
var typedReturnVariable = new TypedVariableNode(functionReturnVariable);

if (typeTokenSource.contains("/") || tokens.peek().kind() == SyntaxKind.SLASH)
{
var firstDimension = new ArrayDimension();
// Parsing array dimensions is currently too tightly coupled into DefineDataParser
// so we do a rudimentary implementation to revisit later.
firstDimension.setLowerBound(IArrayDimension.UNBOUND_VALUE);
firstDimension.setUpperBound(IArrayDimension.UNBOUND_VALUE);
typedReturnVariable.addDimension(firstDimension);
while (tokens.peek().kind() != SyntaxKind.RPAREN && !tokens.isAtEnd())
{
if (tokens.peek().kind() == SyntaxKind.COMMA)
{
var nextDimension = new ArrayDimension();
nextDimension.setLowerBound(IArrayDimension.UNBOUND_VALUE);
nextDimension.setUpperBound(IArrayDimension.UNBOUND_VALUE);
typedReturnVariable.addDimension(nextDimension);
}
tokens.advance();
}
}

tokens.advance(); // )
if (tokens.peek().kind() == SyntaxKind.DYNAMIC)
{
type = DataType.ofDynamicLength(type.format());
}
naturalModule.setReturnType(type);
typedReturnVariable.setType(new VariableType(type));
functionReturnVariable = typedReturnVariable;
}
advanceToDefineData(tokens);
break;
Expand All @@ -97,6 +130,10 @@ private NaturalModule parseModule(NaturalFile file, IModuleProvider moduleProvid
if (advanceToDefineData(tokens))
{
topLevelNodes.add(parseDefineData(tokens, moduleProvider, naturalModule));
if (file.getFiletype() == NaturalFileType.FUNCTION && naturalModule.defineData() != null && functionReturnVariable != null)
{
((DefineDataNode) naturalModule.defineData()).addNode(functionReturnVariable);
}
}

if (file.getFiletype().canHaveBody())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,37 @@ private void checkStatement(IStatementNode statement)
if (statement instanceof IDecideOnNode decideOn)
{
checkDecideOnBranches(decideOn);
return;
}

if (statement instanceof ICompressStatementNode compress)
{
checkCompress(compress);
}
}

private void checkCompress(ICompressStatementNode compress)
{
for (var operand : compress.operands())
{
var operandType = inferDataType(operand);
if (operandType.format() == DataFormat.LOGIC || operandType.format() == DataFormat.CONTROL)
{
report(ParserErrors.typeMismatch("COMPRESS operand can't be of type %s".formatted(operandType.format().identifier()), operand));
}
}

var targetType = inferDataType(compress.intoTarget());
if (targetType.format() != DataFormat.ALPHANUMERIC
&& targetType.format() != DataFormat.BINARY
&& targetType.format() != DataFormat.UNICODE)
{
report(
ParserErrors.typeMismatch(
"COMPRESS target needs to have type A, B or U but got %s".formatted(targetType.toShortString()),
compress.intoTarget()
)
);
}
}

Expand Down Expand Up @@ -647,6 +678,11 @@ private IDataType inferDataType(IOperandNode operand)
return BuiltInFunctionTable.getDefinition(sysVar.systemVariable()).type();
}

if (operand instanceof ISubstringOperandNode substr)
{
return inferDataType(substr.operand());
}

return new DataType(DataFormat.NONE, IDataType.ONE_GIGABYTE); // couldn't infer, don't raise something yet
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public TypedVariableNode(VariableNode variable)
{
setLevel(variable.level());
setDeclaration(variable.identifierNode());
setScope(variable.scope());
for (var dimension : variable.dimensions())
{
addDimension((ArrayDimension) dimension);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.amshove.natparse.parsing;

import org.amshove.natparse.natural.DataFormat;
import org.amshove.natparse.natural.IDataType;
import org.amshove.natparse.natural.IOperandNode;
import org.amshove.natparse.natural.IVariableType;

Expand All @@ -13,6 +14,16 @@ class VariableType implements IVariableType
private IOperandNode initialValue;
private boolean isConstant = false;

VariableType()
{}

VariableType(IDataType other)
{
format = other.format();
length = other.length();
hasDynamicLength = other.hasDynamicLength();
}

@Override
public DataFormat format()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import org.amshove.natparse.natural.DataFormat;
import org.amshove.natparse.natural.IFunction;
import org.amshove.natparse.natural.ITypedVariableNode;
import org.amshove.natparse.natural.project.NaturalProject;
import org.amshove.testhelpers.ProjectName;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;

@SuppressWarnings("DataFlowIssue")
class NaturalParserShould extends ParserIntegrationTest
{
@Test
Expand Down Expand Up @@ -68,6 +70,42 @@ void parseTheReturnTypesOfFunctionsWithFixedLength(@ProjectName("naturalParserTe
assertThat(function.returnType().length()).isEqualTo(12.7);
}

@Test
void addTheFunctionAsVariableToItsDefineData(@ProjectName("naturalParserTests") NaturalProject project)
{
var module = parse(project.findModule("TEST", "FUNCSET"));
assertThat(module).isInstanceOf(IFunction.class);
var function = (IFunction) module;
assertThat(function.returnType().format()).isEqualTo(DataFormat.NUMERIC);
assertThat(function.returnType().length()).isEqualTo(12.7);
var variable = (ITypedVariableNode) function.defineData().findVariable("FUNCSET");
assertThat(variable).as("Function name as variable not found").isNotNull();
assertThat(variable.type().format()).isEqualTo(DataFormat.NUMERIC);
assertThat(variable.type().length()).isEqualTo(12.7);
}

@Test
void parseTheFunctionReturnDimensions(@ProjectName("naturalParserTests") NaturalProject project)
{
var module = parse(project.findModule("TEST", "FUNC1DIM"));
assertThat(module).isInstanceOf(IFunction.class);
var function = (IFunction) module;
var variable = (ITypedVariableNode) function.defineData().findVariable("FUNC1DIM");
assertThat(variable).as("Function name as variable not found").isNotNull();
assertThat(variable.dimensions()).hasSize(1);
}

@Test
void parseTheFunctionReturnDimensionsForMultipleDimensions(@ProjectName("naturalParserTests") NaturalProject project)
{
var module = parse(project.findModule("TEST", "FUNC2DIM"));
assertThat(module).isInstanceOf(IFunction.class);
var function = (IFunction) module;
var variable = (ITypedVariableNode) function.defineData().findVariable("FUNC2DIM");
assertThat(variable).as("Function name as variable not found").isNotNull();
assertThat(variable.dimensions()).hasSize(2);
}

@Test
void reportADiagnosticsForUnreferencedVariablesInFunctions(@ProjectName("variablereferencetests") NaturalProject project)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ END-DEFINE

SUBSTRING(#MUTABLE, 1, 5) := 'hello' /* okay, is mutable
COMPRESS 'Hi' INTO SUBSTRING(#C-CONST, 1, 1) /* !{D:ERROR:NPP039}
COMPRESS 'Hi' INTO 5 /* !{D:ERROR:NPP039}
COMPRESS 'Hi' INTO 'Literal' /* !{D:ERROR:NPP039}

#MUTABLE := 'Hello' /* Okay, #MUTABLE is not const
#C-CONST := 'CD' /* !{D:ERROR:NPP039} this variable is const, this is not allowed
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
DEFINE DATA
LOCAL
1 #ALPHA (A) DYNAMIC
1 #BIN (B) DYNAMIC
1 #UNI (U) DYNAMIC
1 #NUM (N12)
1 #CTRL (C)
1 #LOGIC (L)
END-DEFINE

COMPRESS #NUM #ALPHA INTO #ALPHA
COMPRESS #NUM #BIN INTO #BIN
COMPRESS #NUM #UNI INTO #UNI

COMPRESS #ALPHA INTO #NUM /* !{D:ERROR:NPP037} target needs to be A, U or B

COMPRESS #CTRL INTO #ALPHA /* !{D:ERROR:NPP037} (C) can't be used as operand
COMPRESS #LOGIC INTO #ALPHA /* !{D:ERROR:NPP037} (L) can't be used as operand


END
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
* >Natural Source Header 000000
* :Mode S
* :CP
* <Natural Source Header
DEFINE FUNCTION FUNC1DIM

RETURNS (N12,7/1:10)

DEFINE DATA
LOCAL
END-DEFINE

FUNC1DIM := 'A'

END-FUNCTION

END
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
DEFINE FUNCTION FUNC
DEFINE FUNCTION FUNC2
RETURNS (A) DYNAMIC
DEFINE DATA PARAMETER
1 P-PARM (A) DYNAMIC
Expand All @@ -7,6 +7,6 @@ DEFINE FUNCTION FUNC
END-DEFINE

#VAR1 := *LENGTH(P-PARM)
FUNC := P-PARM
FUNC2 := P-PARM
END-FUNCTION
END
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
* >Natural Source Header 000000
* :Mode S
* :CP
* <Natural Source Header
DEFINE FUNCTION FUNC2DIM

RETURNS (N12,7/1:10,1:15)

DEFINE DATA
LOCAL
END-DEFINE

FUNC2DIM := 'A'

END-FUNCTION

END
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
* :Mode S
* :CP
* <Natural Source Header
DEFINE FUNCTION FUNC
DEFINE FUNCTION FUNCDYN

RETURNS (A) DYNAMIC

DEFINE DATA
LOCAL
END-DEFINE

FUNC := 'A'
FUNCDYN := 'A'

END-FUNCTION

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
* :Mode S
* :CP
* <Natural Source Header
DEFINE FUNCTION FUNC
DEFINE FUNCTION FUNCSET

RETURNS (N12,7)

DEFINE DATA
LOCAL
END-DEFINE

FUNC := 'A'
FUNCSET := 'A'

END-FUNCTION

Expand Down