From 89ddea9e9da1e8ec7bed400c4a691484722dc6ea Mon Sep 17 00:00:00 2001 From: Tomohisa Tanaka Date: Sun, 14 Sep 2025 20:54:19 +0900 Subject: [PATCH] Fix throwing ArrayIndexOutOfBoundsException bug - Fixed an issue where IntExpr.eval(String) would throw ArrayIndexOutOfBoundsException for expressions like: - Binary operators missing right-hand operand (e.g., "1+") - Unary operators missing operand (e.g., "-") - Modify the exception detail message to include whether the operator is unary or binary when an unknown operator is detected - Add test cases --- .../maroontress/intexpr/impl/Compiler.java | 4 +- .../intexpr/impl/IntBinaryOperation.java | 4 ++ .../intexpr/impl/IntUnaryOperation.java | 4 ++ .../maroontress/intexpr/impl/Operator.java | 4 ++ .../intexpr/impl/PendedOperator.java | 2 +- .../com/maroontress/intexpr/IntExprTest.java | 52 +++++++++++++++++++ 6 files changed, 67 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/maroontress/intexpr/impl/Compiler.java b/src/main/java/com/maroontress/intexpr/impl/Compiler.java index e3c2b8a..fab3a91 100644 --- a/src/main/java/com/maroontress/intexpr/impl/Compiler.java +++ b/src/main/java/com/maroontress/intexpr/impl/Compiler.java @@ -95,8 +95,8 @@ private static Action newOperatorAction(OperatorType type) { return (c, token) -> { var maybeOperator = Operator.of(token, type); if (maybeOperator.isEmpty()) { - throw new IllegalArgumentException( - Messages.of(token, "unknown operator")); + var m = Messages.of(token, "unknown " + type + " operator"); + throw new IllegalArgumentException(m); } c.pushOperator(maybeOperator.get()); }; diff --git a/src/main/java/com/maroontress/intexpr/impl/IntBinaryOperation.java b/src/main/java/com/maroontress/intexpr/impl/IntBinaryOperation.java index b269f18..a1b6ab6 100644 --- a/src/main/java/com/maroontress/intexpr/impl/IntBinaryOperation.java +++ b/src/main/java/com/maroontress/intexpr/impl/IntBinaryOperation.java @@ -19,6 +19,10 @@ public interface IntBinaryOperation extends Operation { @Override default Executable toExecutable() { return (s, n, t) -> { + if (n < 2) { + var m = Messages.of(t, "operand is missing"); + throw new IllegalArgumentException(m); + } var k = n - 1; var left = s[k - 1]; var right = s[k]; diff --git a/src/main/java/com/maroontress/intexpr/impl/IntUnaryOperation.java b/src/main/java/com/maroontress/intexpr/impl/IntUnaryOperation.java index ade9725..aaeaabe 100644 --- a/src/main/java/com/maroontress/intexpr/impl/IntUnaryOperation.java +++ b/src/main/java/com/maroontress/intexpr/impl/IntUnaryOperation.java @@ -18,6 +18,10 @@ public interface IntUnaryOperation extends Operation { @Override default Executable toExecutable() { return (s, n, t) -> { + if (n < 1) { + var m = Messages.of(t, "operand is missing"); + throw new IllegalArgumentException(m); + } var k = n - 1; var operand = s[k]; s[k] = Operations.perform(t, () -> apply(operand)); diff --git a/src/main/java/com/maroontress/intexpr/impl/Operator.java b/src/main/java/com/maroontress/intexpr/impl/Operator.java index c3ad157..15e8b46 100644 --- a/src/main/java/com/maroontress/intexpr/impl/Operator.java +++ b/src/main/java/com/maroontress/intexpr/impl/Operator.java @@ -43,6 +43,10 @@ public OperatorSpec getSpec() { return spec; } + public Token getToken() { + return token; + } + /** {@inheritDoc} */ @Override public void accept(Deque stack) { diff --git a/src/main/java/com/maroontress/intexpr/impl/PendedOperator.java b/src/main/java/com/maroontress/intexpr/impl/PendedOperator.java index aa5a185..3cc1be5 100644 --- a/src/main/java/com/maroontress/intexpr/impl/PendedOperator.java +++ b/src/main/java/com/maroontress/intexpr/impl/PendedOperator.java @@ -86,7 +86,7 @@ public Optional toOperator() { @Override public Token getToken() { - throw new IllegalStateException(); + return operator.getToken(); } }; } diff --git a/src/test/java/com/maroontress/intexpr/IntExprTest.java b/src/test/java/com/maroontress/intexpr/IntExprTest.java index 2b5c80a..f98e1c4 100644 --- a/src/test/java/com/maroontress/intexpr/IntExprTest.java +++ b/src/test/java/com/maroontress/intexpr/IntExprTest.java @@ -298,4 +298,56 @@ public void rightParenMismatch() { } throw new AssertionError(); } + + @Test + public void noRightOperandWithBinaryOperator() { + var expr = "1+"; + // 12 + try { + IntExpr.eval(expr); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), is("L1:2: operand is missing: \"+\"")); + return; + } + throw new AssertionError(); + } + + @Test + public void noOperandWithUnaryOperator() { + var expr = "-"; + // 1 + try { + IntExpr.eval(expr); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), is("L1:1: operand is missing: \"-\"")); + return; + } + throw new AssertionError(); + } + + @Test + public void unknownUnaryOperator() { + var expr = "*"; + // 1 + try { + IntExpr.eval(expr); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), is("L1:1: unknown UNARY operator: \"*\"")); + return; + } + throw new AssertionError(); + } + + @Test + public void unknownBinaryOperator() { + var expr = "1!"; + // 12 + try { + IntExpr.eval(expr); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), is("L1:2: unknown BINARY operator: \"!\"")); + return; + } + throw new AssertionError(); + } }