diff --git a/src/main/interpreter/ExpressionParser.java b/src/main/interpreter/ExpressionParser.java deleted file mode 100644 index af987c5..0000000 --- a/src/main/interpreter/ExpressionParser.java +++ /dev/null @@ -1,28 +0,0 @@ -package interpreter; - -import interpreter.antlr.MineScriptBaseVisitor; -import interpreter.types.MSBool; -import interpreter.types.MSNumber; - -public class ExpressionParser extends MineScriptBaseVisitor { - - public MSBool getBoolean(T ctx) { - if (ctx instanceof MSBool b) { - return b; - } else if (ctx instanceof MSNumber i) { - return new MSBool(i.getValue() != 0); - } else { - throw new RuntimeException("Condition must be a bool"); - } - } - - public MSBool getNegatedBoolean(T ctx) { - if (ctx instanceof MSBool b) { - return new MSBool(!b.getValue()); - } else if (ctx instanceof MSNumber i) { - return new MSBool(i.getValue() == 0); - } else { - throw new RuntimeException("Condition must be a bool"); - } - } -} diff --git a/src/main/interpreter/Visitor.java b/src/main/interpreter/Visitor.java index cfe4eea..8fb1b74 100644 --- a/src/main/interpreter/Visitor.java +++ b/src/main/interpreter/Visitor.java @@ -22,7 +22,6 @@ import java.util.stream.Collectors; public class Visitor extends MineScriptBaseVisitor { - private final ExpressionParser parser = new ExpressionParser(); private final Random random = new Random(System.currentTimeMillis()); private final SymbolTable symbolTable; private boolean hasReturned = false; @@ -136,10 +135,13 @@ public MSType visitWhile(MineScriptParser.WhileContext ctx) { MSType value = null; /*Also checks for the returned state in case a return has been called inside the loop body*/ - while (parser.getBoolean(visit(ctx.expression())).getValue() && !hasReturned) { + while (visit(ctx.expression()) instanceof MSBool c && c.getValue() && !hasReturned) { value = visit(ctx.statements()); } + if (!(visit(ctx.expression()) instanceof MSBool)) + throw new RuntimeException("While condition must be a boolean"); + return value; } @@ -147,7 +149,12 @@ public MSType visitWhile(MineScriptParser.WhileContext ctx) { public MSType visitIf(MineScriptParser.IfContext ctx) { /*Handle if and else if*/ for (var expression : ctx.expression()) { - if (parser.getBoolean(visit(expression)).getValue()) { + MSType condition = visit(expression); + + if (!(condition instanceof MSBool)) + throw new RuntimeException("If condition must be a boolean"); + + if (((MSBool) condition).getValue()) { return visit(ctx.statements(ctx.expression().indexOf(expression))); } } @@ -183,10 +190,15 @@ public MSType visitBool(MineScriptParser.BoolContext ctx) { return new MSBool(Boolean.parseBoolean(ctx.getText())); } - @Override public MSType visitNotExpr(MineScriptParser.NotExprContext ctx) { - return parser.getNegatedBoolean(visit(ctx.expression())); + MSType value = visit(ctx.expression()); + + if (!(value instanceof MSBool)) + throw new RuntimeException("Cannot negate non-boolean value"); + + boolean bool = ((MSBool) value).getValue(); + return new MSBool(!bool); } @Override @@ -257,6 +269,7 @@ public MSType visitAddSub(MineScriptParser.AddSubContext ctx) { MSType left = visit(ctx.expression(0)); MSType right = visit(ctx.expression(1)); + /*Check that both sides are numbers otherwise throw an error*/ if (!(left instanceof MSNumber l) || !(right instanceof MSNumber r)) { throw new RuntimeException("Cannot use '" + ctx.op.getText() + "' operator on " + left.getTypeName() + " and " + right.getTypeName()); } @@ -312,32 +325,46 @@ public MSType visitPow(MineScriptParser.PowContext ctx) { } return calculateArithmeticExpression(l, r, "^"); - } @Override public MSType visitAnd(MineScriptParser.AndContext ctx) { /*Handles and with short-circuit, that's why the left side is handled first*/ MSType left = visit(ctx.expression(0)); - /*If the left part is false, it returns without the need to evaluate the right side*/ - if (!parser.getBoolean(left).getValue()) { - return new MSBool(false); - } - MSType right = visit(ctx.expression(1)); - return new MSBool(parser.getBoolean(right).getValue()); + if (left instanceof MSBool l) {/*If the left part is false, it returns without the need to evaluate the right side*/ + if (!l.getValue()) { + return l; + } + + MSType right = visit(ctx.expression(1)); + if (!(right instanceof MSBool r)) { + throw new RuntimeException("Cannot use 'and' operator on " + left.getTypeName() + " and " + right.getTypeName()); + } + return r; + } else { + throw new RuntimeException("Cannot use 'and' operator on " + left.getTypeName() + " and " + visit(ctx.expression(1)).getTypeName()); + } } @Override public MSType visitOr(MineScriptParser.OrContext ctx) { /*Handles or with short-circuit, much like in the visitAnd method*/ MSType left = visit(ctx.expression(0)); - if (parser.getBoolean(left).getValue()) { - return new MSBool(true); - } - MSType right = visit(ctx.expression(1)); - return new MSBool(parser.getBoolean(right).getValue()); + if (left instanceof MSBool l) { + if (l.getValue()) { + return l; + } + + MSType right = visit(ctx.expression(1)); + if (!(right instanceof MSBool r)) { + throw new RuntimeException("Cannot use 'or' operator on " + left.getTypeName() + " and " + right.getTypeName()); + } + return r; + } else { + throw new RuntimeException("Cannot use 'or' operator on " + left.getTypeName() + " and " + visit(ctx.expression(1)).getTypeName()); + } } @Override @@ -634,16 +661,6 @@ public MSType visitFuncDecl(MineScriptParser.FuncDeclContext ctx) { return null; } - @Override - public MSType visitActual_parameters(MineScriptParser.Actual_parametersContext ctx) { - return null; - } - - @Override - public MSType visitFormal_paramaters(MineScriptParser.Formal_paramatersContext ctx) { - return null; - } - /** * @param ctx Formal parameters context * @return List of formal parameters @@ -712,7 +729,6 @@ private MSNumber calculateArithmeticExpression(MSNumber left, MSNumber right, St throw new RuntimeException("Result of '" + leftBig + " " + operator + " " + rightBig + "' is too small. Minimum number is " + Integer.MIN_VALUE); } return new MSNumber(result.intValue()); - } /** diff --git a/src/test/interpreter/VisitorUnitTest.java b/src/test/interpreter/VisitorUnitTest.java index d285165..1bdfe7e 100644 --- a/src/test/interpreter/VisitorUnitTest.java +++ b/src/test/interpreter/VisitorUnitTest.java @@ -3,16 +3,22 @@ import interpreter.antlr.MineScriptParser; import interpreter.types.*; import interpreter.utils.MockTerminalNode; +import interpreter.utils.MockToken; +import org.antlr.v4.runtime.tree.TerminalNode; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; - +import org.mockito.stubbing.OngoingStubbing; +import java.util.ArrayList; +import java.lang.reflect.Field; import java.util.concurrent.atomic.AtomicReference; @ExtendWith(MockitoExtension.class) @@ -23,6 +29,8 @@ class VisitorUnitTest { @Spy private final Visitor spyVisitor = visitor; @Mock private MineScriptParser.AssignContext mockAssignContext; @Mock private MineScriptParser.ExpressionContext mockExpressionContext; + @Mock private MineScriptParser.ExpressionContext mockExpressionContext1; + @Mock private MineScriptParser.ExpressionContext mockExpressionContext2; @Mock private MineScriptParser.BoolContext mockBoolContext; @Mock private MineScriptParser.AbsDirContext mockAbsDirContext; @Mock private MineScriptParser.RelDirContext mockRelDirContext; @@ -30,14 +38,35 @@ class VisitorUnitTest { @Mock private MineScriptParser.IdContext mockIdContext; @Mock private MineScriptParser.NegContext mockNegContext; @Mock private MineScriptParser.NotExprContext mockNotExprContext; + @Mock private MineScriptParser.AddSubContext mockAddSubContext; + @Mock private MineScriptParser.PowContext mockPowContext; + @Mock private MineScriptParser.ParenExprContext mockParenExprContext; + @Mock private MineScriptParser.CompContext mockCompContext; + @Mock private MineScriptParser.FuncCallContext mockFuncCallContext; + @Mock private MineScriptParser.FuncDeclContext mockFuncDeclContext; + @Mock private MineScriptParser.Actual_parametersContext mockActualParametersContext; + @Mock private MineScriptParser.Formal_paramatersContext mockFormalParametersContext; + @Mock private MineScriptParser.StatementsContext mockStatementsContext; + @Mock private MineScriptParser.StatementsContext mockStatementsContext1; + @Mock private MineScriptParser.StatementsContext mockStatementsContext2; + @Mock private MineScriptParser.StatementsContext mockStatementsContext3; + @Mock private MineScriptParser.AndContext mockAndContext; + @Mock private MineScriptParser.OrContext mockOrContext; + @Mock private MineScriptParser.MultDivModContext mockMultDivModContext; + @Mock private MineScriptParser.IsIsNotContext mockIsIsNotContext; + @Mock private MineScriptParser.ReturnContext mockReturnContext; + @Mock private MineScriptParser.IfContext mockIfContext; + @Mock private MineScriptParser.StatementContext mockStatementContext; + @Mock private MineScriptParser.WhileContext mockWhileContext; + @Mock private MineScriptParser.RepeatContext mockRepeatContext; @ParameterizedTest @ValueSource(ints = {-1000, -10, 0, 10, 1000}) void visitAssignStoresCorrectNumber(int value) { // Mock functions ID(), expression(), and visit() Mockito.when(mockAssignContext.ID()).thenReturn(new MockTerminalNode("varName")); - Mockito.when(mockAssignContext.expression()).thenReturn(mockExpressionContext); - Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSNumber(value)); + Mockito.when(mockAssignContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value)); // Call the visitAssign method on the spy MSType result = spyVisitor.visitAssign(mockAssignContext); @@ -51,38 +80,81 @@ void visitAssignStoresCorrectNumber(int value) { Assertions.assertNull(result); } - @Test - void visitNotExprValidBoolReturnsNegatedBool() { - Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext); - Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSBool(false)); - MSType result = spyVisitor.visitNotExpr(mockNotExprContext); - Assertions.assertTrue(((MSBool) result).getValue()); + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void visitAssignStoresCorrectBoolean(boolean value) { + // Mock functions ID(), expression(), and visit() + Mockito.when(mockAssignContext.ID()).thenReturn(new MockTerminalNode("varName")); + Mockito.when(mockAssignContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(value)); + + // Call the visitAssign method on the spy + MSType result = spyVisitor.visitAssign(mockAssignContext); + + // Initialize mock value and assert that no exceptions are thrown when value is retrieved + AtomicReference mockValue = new AtomicReference<>(); + Assertions.assertDoesNotThrow(() -> mockValue.set(symbolTable.retrieveSymbolValue(symbolTable.retrieveSymbol("varName")))); + + // Assert that the symbol value is equal to the initial assign value, and assert that result is null + Assertions.assertEquals(value, ((MSBool) mockValue.get()).getValue()); + Assertions.assertNull(result); } - @Test - void visitNotExprPassZeroReturnsTrue() { - Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext); - Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSNumber(0)); + @ParameterizedTest + @EnumSource(MSRelDir.Direction.class) + void visitAssignStoresCorrectRelDir(MSRelDir.Direction value) { + // Mock functions ID(), expression(), and visit() + Mockito.when(mockAssignContext.ID()).thenReturn(new MockTerminalNode("varName")); + Mockito.when(mockAssignContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSRelDir(value.toString().toLowerCase())); - MSType result = spyVisitor.visitNotExpr(mockNotExprContext); - Assertions.assertTrue(((MSBool) result).getValue()); + // Call the visitAssign method on the spy + MSType result = spyVisitor.visitAssign(mockAssignContext); + + // Initialize mock value and assert that no exceptions are thrown when value is retrieved + AtomicReference mockValue = new AtomicReference<>(); + Assertions.assertDoesNotThrow(() -> mockValue.set(symbolTable.retrieveSymbolValue(symbolTable.retrieveSymbol("varName")))); + + // Assert that the symbol value is equal to the initial assign value, and assert that result is null + Assertions.assertEquals(value, ((MSRelDir) mockValue.get()).getValue()); + Assertions.assertNull(result); + } + + @ParameterizedTest + @EnumSource(MSAbsDir.Direction.class) + void visitAssignStoresCorrectAbsDir(MSAbsDir.Direction value) { + // Mock functions ID(), expression(), and visit() + Mockito.when(mockAssignContext.ID()).thenReturn(new MockTerminalNode("varName")); + Mockito.when(mockAssignContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSAbsDir(value.toString().toLowerCase())); + + // Call the visitAssign method on the spy + MSType result = spyVisitor.visitAssign(mockAssignContext); + + // Initialize mock value and assert that no exceptions are thrown when value is retrieved + AtomicReference mockValue = new AtomicReference<>(); + Assertions.assertDoesNotThrow(() -> mockValue.set(symbolTable.retrieveSymbolValue(symbolTable.retrieveSymbol("varName")))); + + // Assert that the symbol value is equal to the initial assign value, and assert that result is null + Assertions.assertEquals(value, ((MSAbsDir) mockValue.get()).getValue()); + Assertions.assertNull(result); } @ParameterizedTest - @ValueSource(ints = {-1000, -100, 100, 1000}) - void visitNotExprPassNonZeroNumberReturnsFalse(int value) { - Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext); - Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSNumber(value)); + @ValueSource(booleans = {true, false}) + void visitNotExprValidBoolReturnsNegatedBool(boolean value) { + Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(value)); MSType result = spyVisitor.visitNotExpr(mockNotExprContext); - Assertions.assertFalse(((MSBool) result).getValue()); + Assertions.assertEquals(!value, ((MSBool) result).getValue()); } @Test void visitNotExprInvalidTypeThrowsRuntimeException() { - Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext); - Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSRelDir("right")); + Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSRelDir("right")); Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitNotExpr(mockNotExprContext)); } @@ -90,8 +162,8 @@ void visitNotExprInvalidTypeThrowsRuntimeException() { @ParameterizedTest @ValueSource(ints = {-1000, -10, 0, 10, 1000}) void visitNegNegatesNumber(int value) { - Mockito.when(mockNegContext.expression()).thenReturn(mockExpressionContext); - Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSNumber(value)); + Mockito.when(mockNegContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value)); MSType result = spyVisitor.visitNeg(mockNegContext); Assertions.assertEquals(-value, ((MSNumber) result).getValue()); @@ -99,8 +171,8 @@ void visitNegNegatesNumber(int value) { @Test void visitNegInvalidTypeThrowsRuntimeException() { - Mockito.when(mockNegContext.expression()).thenReturn(mockExpressionContext); - Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSBool(false)); + Mockito.when(mockNegContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(false)); Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitNeg(mockNegContext)); } @@ -189,4 +261,543 @@ void visitNumberInvalidInputThrowsRuntimeException(){ Mockito.when(mockNumberContext.NUMBER()).thenReturn(new MockTerminalNode("abc")); Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitNumber(mockNumberContext)); } + + @ParameterizedTest + @CsvSource ({"1, 2, +", "2, 1, -", "0, 0, +", "-1, -2, +", "-2, -1, -"}) + void visitAddSubValidInputReturnsNumber(int value1, int value2, String operator){ + mockAddSubContext.op = new MockToken(operator); + Mockito.when(mockAddSubContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockAddSubContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); + + MSType result = spyVisitor.visitAddSub(mockAddSubContext); + if (operator.equals("+")){ + Assertions.assertEquals(value1 + value2, ((MSNumber) result).getValue()); + } else if (operator.equals("-")){ + Assertions.assertEquals(value1 - value2, ((MSNumber) result).getValue()); + } + } + + @Test + void visitAddSubInvalidTypeThrowsRuntimeException(){ + mockAddSubContext.op = new MockToken("+"); + Mockito.when(mockAddSubContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockAddSubContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(false)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(2)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitAddSub(mockAddSubContext)); + } + + @ParameterizedTest + @CsvSource ({"2147483641, 2, *", "2147483647, 12, +", "2147483647, 4, *", "2147483647, 1000, ^", "2147483647, -12, -"}) + void calculateArithmeticExpressionInvalidInputOverflowError(int value1, int value2, String operator){ + mockAddSubContext.op = new MockToken(operator); + Mockito.when(mockAddSubContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockAddSubContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitAddSub(mockAddSubContext)); + + } + + @ParameterizedTest + @CsvSource ({"1, 2", "2, 1", "0, 0", "-1, 1", "-2, 2"}) + void visitPowValidInputReturnsNumber(int value1, int value2){ + Mockito.when(mockPowContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockPowContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); + + MSType result = spyVisitor.visitPow(mockPowContext); + Assertions.assertEquals(Math.pow(value1, value2), ((MSNumber) result).getValue()); + } + + @Test + void visitPowInvalidInputThrowsRuntimeException(){ + Mockito.when(mockPowContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockPowContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(1)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(-1)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitPow(mockPowContext)); + } + + @ParameterizedTest + @CsvSource ({"true, false", "false, false"}) + void visitPowInvalidTypeThrowsRuntimeException(boolean bool1, boolean bool2){ + Mockito.when(mockPowContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockPowContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(bool1)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSBool(bool2)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitPow(mockPowContext)); + + } + + @ParameterizedTest + @CsvSource ({"1", "2", "0", "-1", "-2"}) + void visitParenExprValidInputReturnsNumber(int value){ + Mockito.when(mockParenExprContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value)); + + MSType result = spyVisitor.visitParenExpr(mockParenExprContext); + Assertions.assertEquals(value, ((MSNumber) result).getValue()); + } + + @ParameterizedTest + @ValueSource (booleans = {true, false}) + void visitParenExprValidInputReturnsBoolean(boolean value){ + Mockito.when(mockParenExprContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(value)); + + MSType result = spyVisitor.visitParenExpr(mockParenExprContext); + Assertions.assertEquals(value, ((MSBool) result).getValue()); + } + + @ParameterizedTest + @CsvSource ({"1, 2, <", "2, 1, >", "0, 0, >=", "-1, 1, <", "-2, 2, <="}) + void visitCompValidInputReturnsTrue(int value1, int value2, String operator){ + mockCompContext.op = new MockToken(operator); + Mockito.when(mockCompContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockCompContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); + + MSType result = spyVisitor.visitComp(mockCompContext); + Assertions.assertTrue(((MSBool) result).getValue()); + } + + @ParameterizedTest + @CsvSource ({"1, 2, >=", "2, 1, <", "0, 0, <", "-1, 1, >=", "-2, 2, >"}) + void visitCompValidInputReturnsFalse(int value1, int value2, String operator){ + mockCompContext.op = new MockToken(operator); + Mockito.when(mockCompContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockCompContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); + + MSType result = spyVisitor.visitComp(mockCompContext); + Assertions.assertFalse(((MSBool) result).getValue()); + } + + @Test + void visitCompInvalidTypeThrowsException(){ + mockCompContext.op = new MockToken(">"); + Mockito.when(mockCompContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockCompContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(true)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(14)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitComp(mockCompContext)); + } + + @Test + void visitFuncCallValidInputNoParamsReturnsNumber(){ + Mockito.when(mockFuncCallContext.actual_parameters()).thenReturn(mockActualParametersContext); + Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("testFunction")); + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(), mockStatementsContext1)); + + Mockito.when(spyVisitor.visit(mockStatementsContext1)).thenReturn(new MSNumber(1)); + + MSType result = spyVisitor.visitFuncCall(mockFuncCallContext); + Assertions.assertEquals(1, ((MSNumber) result).getValue()); + } + @ParameterizedTest + @CsvSource ({"1", "2", "0", "-1", "-2", "123", "-123"}) + void visitFuncCallValidInputOneParamReturnsNumber(int value){ + + Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("testFunction")); + Mockito.when(mockFuncCallContext.actual_parameters()).thenReturn(mockActualParametersContext); + Mockito.when(mockFuncCallContext.actual_parameters().expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1);}}); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value)); + + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1");}}, mockStatementsContext1)); + + Mockito.when(spyVisitor.visit(mockStatementsContext1)).thenReturn(new MSNumber(value)); + + MSType result = spyVisitor.visitFuncCall(mockFuncCallContext); + Assertions.assertEquals(value, ((MSNumber) result).getValue()); + } + + @ParameterizedTest + @CsvSource ({"1, 2", "2, 1", "0, 0", "-1, 1", "-2, 2", "123, 122", "123, 124", "123, 123"}) + void visitFuncCallValidInputTwoParamReturnsNumber(int value1, int value2){ + + Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("testFunction")); + Mockito.when(mockFuncCallContext.actual_parameters()).thenReturn(mockActualParametersContext); + Mockito.when(mockFuncCallContext.actual_parameters().expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1); add(mockExpressionContext2);}}); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); + + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1"); add("Param2");}}, mockStatementsContext1)); + + Mockito.when(spyVisitor.visit(mockStatementsContext1)).thenReturn(new MSNumber(value1+value2)); + + MSType result = spyVisitor.visitFuncCall(mockFuncCallContext); + Assertions.assertEquals(value1+value2, ((MSNumber) result).getValue()); + } + + @ParameterizedTest + @CsvSource ({"1, 2", "2, 1", "0, 0", "-1, 1", "-2, 2"}) + void visitFuncCallInvalidInputThrowsException(int value1, int value2){ + + Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("testFunction")); + Mockito.when(mockFuncCallContext.actual_parameters()).thenReturn(mockActualParametersContext); + Mockito.when(mockFuncCallContext.actual_parameters().expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1); add(mockExpressionContext2);}}); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); + + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1");}}, mockStatementsContext1)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitFuncCall(mockFuncCallContext)); + } + + @Test + void visitFuncDeclNoParamsStoresCorrectFunction() { + Mockito.when(mockFuncDeclContext.ID()).thenReturn(new MockTerminalNode("testFunction")); + Mockito.when(mockFuncDeclContext.formal_paramaters()).thenReturn(mockFormalParametersContext); + + spyVisitor.visitFuncDecl(mockFuncDeclContext); + + AtomicReference mockValue = new AtomicReference<>(); + Assertions.assertDoesNotThrow(() -> mockValue.set(symbolTable.retrieveSymbolValue(symbolTable.retrieveSymbol("testFunction")))); + Assertions.assertTrue(mockValue.get() instanceof MSFunction); + Assertions.assertEquals("testFunction", ((MSFunction) mockValue.get()).getName()); + } + + @Test + void visitFuncDeclOneParamStoresCorrectFunction() { + Mockito.when(mockFuncDeclContext.ID()).thenReturn(new MockTerminalNode("testFunction")); + Mockito.when(mockFuncDeclContext.formal_paramaters()).thenReturn(mockFormalParametersContext); + Mockito.when(mockFormalParametersContext.ID()).thenReturn(new ArrayList<>() {{add(new MockTerminalNode("Param1"));}}); + + spyVisitor.visitFuncDecl(mockFuncDeclContext); + + AtomicReference mockValue = new AtomicReference<>(); + Assertions.assertDoesNotThrow(() -> mockValue.set(symbolTable.retrieveSymbolValue(symbolTable.retrieveSymbol("testFunction")))); + Assertions.assertTrue(mockValue.get() instanceof MSFunction); + Assertions.assertEquals("testFunction", ((MSFunction) mockValue.get()).getName()); + Assertions.assertEquals(1, ((MSFunction) mockValue.get()).getParameters().size()); + Assertions.assertEquals("Param1", ((MSFunction) mockValue.get()).getParameters().get(0)); + } + + @Test + void visitFuncDeclTwoParamsStoresCorrectFunction() { + Mockito.when(mockFuncDeclContext.ID()).thenReturn(new MockTerminalNode("testFunction")); + Mockito.when(mockFuncDeclContext.formal_paramaters()).thenReturn(mockFormalParametersContext); + Mockito.when(mockFormalParametersContext.ID()).thenReturn(new ArrayList<>() {{add(new MockTerminalNode("Param1")); add(new MockTerminalNode("Param2"));}}); + + spyVisitor.visitFuncDecl(mockFuncDeclContext); + + AtomicReference mockValue = new AtomicReference<>(); + Assertions.assertDoesNotThrow(() -> mockValue.set(symbolTable.retrieveSymbolValue(symbolTable.retrieveSymbol("testFunction")))); + Assertions.assertTrue(mockValue.get() instanceof MSFunction); + Assertions.assertEquals("testFunction", ((MSFunction) mockValue.get()).getName()); + Assertions.assertEquals(2, ((MSFunction) mockValue.get()).getParameters().size()); + Assertions.assertEquals("Param1", ((MSFunction) mockValue.get()).getParameters().get(0)); + Assertions.assertEquals("Param2", ((MSFunction) mockValue.get()).getParameters().get(1)); + } + + @Test + void visitFuncCallInvalidFunction(){ + Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("testFunctionFake")); + Mockito.when(mockFuncCallContext.actual_parameters()).thenReturn(mockActualParametersContext); + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1"); }}, mockStatementsContext1)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitFuncCall(mockFuncCallContext)); + } + + @Test + void visitBreakBuiltInFuncCall(){ + Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("Break")); + Mockito.when(mockFuncCallContext.actual_parameters()).thenReturn(mockActualParametersContext); + + Mockito.when(mockFuncCallContext.actual_parameters().expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1);}}); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(true)); + + MSType result = spyVisitor.visitFuncCall(mockFuncCallContext); + Assertions.assertEquals(true, ((MSBool) result).getValue()); + } + + @Test + void visitAndValidBoolsReturnsTrue(){ + Mockito.when(mockAndContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockAndContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(true)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSBool(true)); + + MSType result = spyVisitor.visitAnd(mockAndContext); + Assertions.assertTrue(((MSBool) result).getValue()); + } + + @Test + void visitAndBoolsTrueAndFalseReturnsFalse(){ + Mockito.when(mockAndContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockAndContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(true)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSBool(false)); + + MSType result = spyVisitor.visitAnd(mockAndContext); + Assertions.assertFalse(((MSBool) result).getValue()); + } + @Test + void visitAndFalseWithShortCircuitReturnsFalse() { + Mockito.when(mockAndContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(false)); + + MSType result = spyVisitor.visitAnd(mockAndContext); + Assertions.assertFalse(((MSBool) result).getValue()); + } + + @ParameterizedTest + @CsvSource({"false, false", "false, true", "true, false", "true, true"}) + void visitOrValidInputReturnsOrValue(boolean left, boolean right) { + Mockito.when(mockOrContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(left)); + + if (!left) { + Mockito.when(mockOrContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSBool(right)); + } + + MSType result = spyVisitor.visitOr(mockOrContext); + Assertions.assertEquals(left || right, ((MSBool) result).getValue()); + } + + @ParameterizedTest + @CsvSource({"num,0,0,is", "num,0,1,is not", "num,1,0,is not", "num,1,1,is", "num,-1,1,is not", + "rel,left,right,is", "rel,up,down,is not", "rel,left,left,is not", "rel,right,right,is", + "abs,north,south,is", "abs,east,west,is not", "abs,top,bottom,is not", "abs,north,north,is", + "bool,false,false,is", "bool,false,true,is not", "bool,true,false,is not", "bool,true,true,is"}) + void visitIsIsNotCorrectTypesReturnsCorrectBool(String type, String left, String right, String operator) { + mockIsIsNotContext.op = new MockToken(operator); + Mockito.when(mockIsIsNotContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockIsIsNotContext.expression(1)).thenReturn(mockExpressionContext2); + + switch (type) { + case "num" -> { + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(Integer.parseInt(left))); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(Integer.parseInt(right))); + } + case "rel" -> { + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSRelDir(left)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSRelDir(right)); + } + case "abs" -> { + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSAbsDir(left)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSAbsDir(right)); + } + case "bool" -> { + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(Boolean.parseBoolean(left))); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSBool(Boolean.parseBoolean(right))); + } + } + + MSType result = spyVisitor.visitIsIsNot(mockIsIsNotContext); + switch (operator) { + case "is" -> Assertions.assertEquals(left.equals(right), ((MSBool) result).getValue()); + case "is not" -> Assertions.assertEquals(!left.equals(right), ((MSBool) result).getValue()); + } + } + + @ParameterizedTest + @CsvSource ({"2,2,*", "2,2,/", "2,2,%"}) + void visitMultDivModValidInputReturnsNumber(int left, int right, String operator) { + mockMultDivModContext.op = new MockToken(operator); + Mockito.when(mockMultDivModContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockMultDivModContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(left)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(right)); + + MSType result = spyVisitor.visitMultDivMod(mockMultDivModContext); + switch (operator) { + case "*" -> Assertions.assertEquals(left * right, ((MSNumber) result).getValue()); + case "/" -> Assertions.assertEquals(left / right, ((MSNumber) result).getValue()); + case "%" -> Assertions.assertEquals(left % right, ((MSNumber) result).getValue()); + } + } + + @ParameterizedTest + @CsvSource ({"2,0,/", "2,0,%"}) + void visitMultDivModDivByZeroThrowsRuntimeException(int left, int right, String operator) { + mockMultDivModContext.op = new MockToken(operator); + Mockito.when(mockMultDivModContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockMultDivModContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(left)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(right)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitMultDivMod(mockMultDivModContext)); + } + + @ParameterizedTest + @CsvSource ({"2,true,/", "2,true,%", "2,false,/", "2,false,%", "true,2,/", "true,2,%", "false,2,/", "false,2,%"}) + void visitMultDivModInvalidInputThrowsRuntimeException(String left, String right, String operator) { + mockMultDivModContext.op = new MockToken(operator); + Mockito.when(mockMultDivModContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockMultDivModContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(Boolean.parseBoolean(left))); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSBool(Boolean.parseBoolean(right))); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitMultDivMod(mockMultDivModContext)); + } + + @ParameterizedTest + @CsvSource ({"bool,true", "bool,false", + "num,-10", "num,0", "num,10", + "rel,left", "rel,right", "rel,up", "rel,down", + "abs,north", "abs,south", "abs,east", "abs,west", "abs,top", "abs,bottom"}) + void visitReturnCorrectTypesReturnValue(String type, String value) { + Field hasReturnedField; + boolean hasReturned; + + // Setup mocks for the context and visit method + Mockito.when(mockReturnContext.expression()).thenReturn(mockExpressionContext1); + switch (type) { + case "num" -> Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(Integer.parseInt(value))); + case "rel" -> Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSRelDir(value)); + case "abs" -> Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSAbsDir(value)); + case "bool" -> Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(Boolean.parseBoolean(value))); + } + + // Get the initial value of the hasReturned field of the visitor + try { + hasReturnedField = spyVisitor.getClass().getDeclaredField("hasReturned"); + hasReturnedField.setAccessible(true); + hasReturned = hasReturnedField.getBoolean(spyVisitor); + } catch (Exception e) { + throw new RuntimeException(e); + } + + // Check that the hasReturned field is false before the visit method is called + Assertions.assertFalse(hasReturned); + + // Call the visit method and check that the correct type and value is returned + MSType result = spyVisitor.visitReturn(mockReturnContext); + switch (type) { + case "num" -> Assertions.assertEquals(Integer.parseInt(value), ((MSNumber) result).getValue()); + case "rel" -> Assertions.assertEquals(MSRelDir.Direction.valueOf(value.toUpperCase()), ((MSRelDir) result).getValue()); + case "abs" -> Assertions.assertEquals(MSAbsDir.Direction.valueOf(value.toUpperCase()), ((MSAbsDir) result).getValue()); + case "bool" -> Assertions.assertEquals(Boolean.parseBoolean(value), ((MSBool) result).getValue()); + } + + // Check that the hasReturned field is true after the visit method is called + try { hasReturned = hasReturnedField.getBoolean(spyVisitor); } + catch (Exception e) { throw new RuntimeException(e); } + Assertions.assertTrue(hasReturned); + } + + @Test + void visitReturnNullExpressionThrowsRuntimeException() { + Mockito.when(mockReturnContext.expression()).thenReturn(null); + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitReturn(mockReturnContext)); + } + + @Test + void visitIfStatements() { + Mockito.when(mockIfContext.expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1);}}); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(true)); + Mockito.when(mockIfContext.statements(0)).thenReturn(mockStatementsContext1); + Mockito.when(spyVisitor.visit(mockStatementsContext1)).thenReturn(new MSNumber(1)); + Mockito.when(spyVisitor.visit(mockStatementsContext1)).thenReturn(new MSNumber(1)); + + MSType result = spyVisitor.visitIf(mockIfContext); + + Assertions.assertEquals(1, ((MSNumber) result).getValue()); + } + + @Test + void visitElseStatementsInIfElseCase() { + Mockito.when(mockIfContext.expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1);}}); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(false)); + Mockito.when(mockIfContext.statements()).thenReturn(new ArrayList(){{add(mockStatementsContext1);add(mockStatementsContext2);}}); + Mockito.when(mockIfContext.statements(1)).thenReturn(mockStatementsContext2); + Mockito.when(spyVisitor.visit(mockStatementsContext2)).thenReturn(new MSNumber(2)); + + MSType result = spyVisitor.visitIf(mockIfContext); + + Assertions.assertEquals(2, ((MSNumber) result).getValue()); + } + + @Test + void visitElseIfStatements() { + Mockito.when(mockIfContext.expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1);add(mockExpressionContext2);}}); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(false)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSBool(true)); + + Mockito.when(mockIfContext.statements(1)).thenReturn(mockStatementsContext2); + Mockito.when(spyVisitor.visit(mockStatementsContext2)).thenReturn(new MSNumber(2)); + + MSType result = spyVisitor.visitIf(mockIfContext); + + Assertions.assertEquals(2, ((MSNumber) result).getValue()); + } + + @Test + void visitIfNonBooleanCondition() { + Mockito.when(mockIfContext.expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1);}}); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(1)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitIf(mockIfContext)); + } + + @Test + void visitWhileConditionTrue(){ + Mockito.when(mockWhileContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(true), new MSBool(false)); + Mockito.when(mockWhileContext.statements()).thenReturn(mockStatementsContext1); + Mockito.when(spyVisitor.visit(mockStatementsContext1)).thenReturn(new MSNumber(1)); + + MSType result = spyVisitor.visitWhile(mockWhileContext); + + Assertions.assertEquals(1, ((MSNumber) result).getValue()); + } + + @Test + void visitWhileConditionFalse(){ + Mockito.when(mockWhileContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(false)); + + MSType result = spyVisitor.visitWhile(mockWhileContext); + + Assertions.assertNull(result); + } + + @Test + void visitWhileConditionNonBooleanCondition(){ + Mockito.when(mockWhileContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(1)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitWhile(mockWhileContext)); + } + + @ParameterizedTest + @CsvSource({"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "100", "1000"}) + void visitRepeatStatementValueTimes(int value) { + Mockito.when(mockRepeatContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value)); + Mockito.when(mockRepeatContext.statements()).thenReturn(mockStatementsContext1); + + OngoingStubbing stubbing = Mockito.when(spyVisitor.visit(mockStatementsContext1)); + for (int i = 1; i <= value; i++) { + stubbing = stubbing.thenReturn(new MSNumber(i)); + } + + MSType result = spyVisitor.visitRepeat(mockRepeatContext); + + Assertions.assertEquals(value, ((MSNumber) result).getValue()); + } + + @Test + void visitRepeatStatementNonIntegerValue() { + Mockito.when(mockRepeatContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(true)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitRepeat(mockRepeatContext)); + } + + + } diff --git a/src/test/interpreter/utils/MockToken.java b/src/test/interpreter/utils/MockToken.java new file mode 100644 index 0000000..b28687f --- /dev/null +++ b/src/test/interpreter/utils/MockToken.java @@ -0,0 +1,58 @@ +package interpreter.utils; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenSource; + +public record MockToken(String token) implements Token { + + @Override + public String getText() { + return token; + } + + @Override + public int getType() { + return 0; + } + + @Override + public int getLine() { + return 0; + } + + @Override + public int getCharPositionInLine() { + return 0; + } + + @Override + public int getChannel() { + return 0; + } + + @Override + public int getTokenIndex() { + return 0; + } + + @Override + public int getStartIndex() { + return 0; + } + + @Override + public int getStopIndex() { + return 0; + } + + @Override + public TokenSource getTokenSource() { + return null; + } + + @Override + public CharStream getInputStream() { + return null; + } +}