Skip to content

Commit

Permalink
Fixed crash on -+ input & alignment display bug
Browse files Browse the repository at this point in the history
- If you entered a `+-+` as a command rpncalc would crash.  I've added a
  catch for this and just display an error
- with large scientific notation numbers, the decimal and right
  alignment could be incorrect as it counted every zero in the expanded
  display.  This is now handled correctly.  Root cause was the
  Output.Comma method.
  • Loading branch information
frossm committed Jun 15, 2023
1 parent 761c52b commit d857314
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 57 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<groupId>org.fross</groupId>
<artifactId>rpncalc</artifactId>
<version>5.0.9</version>
<version>5.0.10</version>
<packaging>jar</packaging>

<name>rpncalc</name>
Expand Down
2 changes: 1 addition & 1 deletion snap/snapcraft.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: rpncalc
version: '5.0.9'
version: '5.0.10'
summary: The command line Reverse Polish Notation (RPN) calculator
description: |
RPNCalc is an easy to use command line based Reverse Polish
Expand Down
33 changes: 20 additions & 13 deletions src/main/java/org/fross/rpncalc/CommandParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -427,16 +427,14 @@ public static void Parse(StackObj calcStack, StackObj calcStack2, String cmdInpu
Output.debugPrint("Executing User Defined Function: '" + cmdInput + "'");
UserFunctions.FunctionRun(calcStack, calcStack2, cmdInput);

// Check for a fraction. If number entered contains a '/' but it's not at the end, then it must be a fraction.
// Check for a fraction. If number entered contains a '/' but it's not at the end, then it must be a fraction
} else if (cmdInput.contains("/") && !cmdInput.substring(cmdInput.length() - 1).matches("/")) {
Output.debugPrint("Fraction has been entered");

try {
BigDecimal fracInteger = BigDecimal.ZERO;
BigDecimal fracDecimalEquiv = BigDecimal.ZERO;

// If there wasn't an integer entered, move the fraction to the parameter
// variable
// If there wasn't an integer entered, move the fraction to the parameter variable
if (cmdInputCmd.contains("/")) {
cmdInputParam = cmdInputCmd;
} else {
Expand Down Expand Up @@ -466,30 +464,39 @@ public static void Parse(StackObj calcStack, StackObj calcStack2, String cmdInpu
break;
}

// Number entered, add to stack.
// Number entered, add to stack
} else if (cmdInputCmd.matches("^-?\\d*\\.?\\d*")) {
// Save current calcStack to the undoStack
calcStack.saveUndo();

Output.debugPrint("Placing the number '" + cmdInputCmd + "' onto the stack");
calcStack.push(new BigDecimal(cmdInputCmd));

// Handle numbers with a single operand at the end (a NumOp)
// Handle NumOps - numbers with a single operand at the end (*, /, +, -, ^)
} else if (cmdInputCmd.matches("^-?\\d*\\.?\\d*[Ee]?\\d*[\\*\\+\\-\\/\\^]")) {
// Save current calcStack to the undoStack
calcStack.saveUndo();

Output.debugPrint("CalcStack has " + calcStack.size() + " elements");

// Verify stack contains at least one element
if (calcStack.size() >= 1) {
String TempOp = cmdInputCmd.substring(cmdInputCmd.length() - 1, cmdInputCmd.length());
String TempNum = cmdInput.substring(0, cmdInput.length() - 1);
Output.debugPrint("NumOp Found: Num= '" + TempNum + "'");
Output.debugPrint("NumOp Found: Op = '" + TempOp + "'");
calcStack.push(new BigDecimal(TempNum));
calcStack = Math.Parse(TempOp, calcStack);
try {
String tempOp = cmdInputCmd.substring(cmdInputCmd.length() - 1, cmdInputCmd.length());
String tempNum = cmdInput.substring(0, cmdInput.length() - 1);
Output.debugPrint("NumOp Found: Num= '" + tempNum + "'");
Output.debugPrint("NumOp Found: Op = '" + tempOp + "'");
calcStack.push(new BigDecimal(tempNum));
calcStack = Math.Parse(tempOp, calcStack);

} catch (NumberFormatException ex) {
// Prevents a crash if user enters "-+" (which they shouldn't do)
Output.printColorln(Ansi.Color.RED, "Unknown Command: '" + cmdInput + "'");
break;
}

} else {
Output.printColorln(Ansi.Color.RED, "One number is required for this NumOp function");
Output.printColorln(Ansi.Color.RED, "One number is required to be on the stack to usea a NumOp");
}

// Scientific notation number entered
Expand Down
85 changes: 44 additions & 41 deletions src/main/java/org/fross/rpncalc/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public class Main {
public static final int CONFIG_DEFAULT_PROGRAM_WIDTH = 80;
public static final int CONFIG_DEFAULT_MEMORY_SLOTS = 10;
public static final String CONFIG_DEFAULT_ALIGNMENT = "l";
public static final int LINE_NUMBER_DIGITS = 2;
public static final String INPUT_PROMPT = ">> ";

// Class Variables
public static String VERSION;
Expand Down Expand Up @@ -177,18 +179,30 @@ public static void main(String[] args) {

// Loop through the stack and count the max digits before the decimal for use with the decimal
// alignment mode & overall length for right alignment mode
for (int k = 0; k < calcStack.size(); k++) {
int decimalIndex = Format.Comma(calcStack.get(k).toString()).indexOf(".");
for (int i = 0; i < calcStack.size(); i++) {
int decimalIndex = 0;
String currentStackItem = "";

// Add a comma to the current stack item if it's not in scientific notation
if (calcStack.getAsString(i).toLowerCase().contains("e")) {
currentStackItem = calcStack.getAsString(i);
} else {
currentStackItem = Format.Comma(calcStack.getAsString(i));
}

// Determine where the decimal point is located
decimalIndex = currentStackItem.indexOf(".");

// If current stack item has more digits ahead of decimal make that the max - commas are included.
if (maxDigitsBeforeDecimal < decimalIndex) {
maxDigitsBeforeDecimal = decimalIndex;
}

// Determine the length of the longest item in the stack for right alignment
if (Format.Comma(calcStack.get(k).toString()).length() > maxLenOfNumbers) {
maxLenOfNumbers = Format.Comma(calcStack.get(k).toString()).length();
if (currentStackItem.length() > maxLenOfNumbers) {
maxLenOfNumbers = currentStackItem.length();
}

}

// Uncomment to debug alignment issues
Expand All @@ -197,59 +211,48 @@ public static void main(String[] args) {

// Display the current stack contents
for (int i = 0; i < calcStack.size(); i++) {
String currentStackItem = "";

// Add commas to the number if it's not in scientific notation
if (calcStack.getAsString(i).toLowerCase().contains("e")) {
currentStackItem = calcStack.getAsString(i).toLowerCase();
} else {
currentStackItem = Format.Comma(calcStack.getAsString(i).toLowerCase());
}

// Display Stack Row Number
String stkLineNumber = String.format("%02d: ", calcStack.size() - i);
// Display Stack Row Number without a newline
String stkLineNumber = String.format("%0" + LINE_NUMBER_DIGITS + "d: ", calcStack.size() - i);
Output.printColor(Ansi.Color.CYAN, stkLineNumber);

// Decimal Alignment
// Decimal Alignment - insert spaces before number to align to the decimal point
if (configAlignment.compareTo("d") == 0) {
int decimalLocation = 0;

// Put in spaces to align the decimals
if (calcStack.get(i).toString().toLowerCase().contains("e")) {
stkLineNumber = calcStack.get(i).toString();
decimalLocation = stkLineNumber.indexOf(".");

} else {
stkLineNumber = Format.Comma(calcStack.get(i).toString());
decimalLocation = stkLineNumber.indexOf(".");
for (int k = 0; k < (maxDigitsBeforeDecimal - currentStackItem.indexOf(".")); k++) {
Output.print(" ");
}
}

// Insert the right number of spaces
for (int k = 0; k < maxDigitsBeforeDecimal - decimalLocation; k++) {
// Right Alignment - insert spaces before number to right align
if (configAlignment.compareTo("r") == 0) {
for (int k = 0; k < (maxLenOfNumbers - currentStackItem.length()); k++) {
Output.print(" ");
}

} else if (configAlignment.compareTo("r") == 0) {
// Right Alignment
if (calcStack.get(i).toString().toLowerCase().contains("e"))
stkLineNumber = String.format("%" + maxLenOfNumbers + "s", calcStack.get(i).toString());
else
stkLineNumber = String.format("%" + maxLenOfNumbers + "s", Format.Comma(calcStack.get(i).toString()));

} else {
// Left Alignment
if (calcStack.get(i).toString().toLowerCase().contains("e"))
stkLineNumber = calcStack.get(i).toString();
else
stkLineNumber = Format.Comma(calcStack.get(i).toString());
}

// Finally display the current stack item after removing any spaces at the end
stkLineNumber = stkLineNumber.replaceAll("\\s+$", "");
Output.printColorln(Ansi.Color.WHITE, stkLineNumber);
// Now that the spaces are inserted (for decimal/right) display the number
// currentStackItem = currentStackItem.replaceAll("\\s+$", ""); // TODO: Delete this in the future if the below trim() works
Output.printColorln(Ansi.Color.WHITE, currentStackItem.trim());
}

// Input command from user
try {
cmdInput = scanner.readLine("\n>> ");
cmdInput = scanner.readLine("\n" + INPUT_PROMPT);
} catch (UserInterruptException ex) {
// User entered Ctrl-c so exit the program gracefully
// User entered Ctrl-c so exit the program gracefully by adding "exit" as the input
cmdInput = "exit";
Output.printColorln(Ansi.Color.CYAN, "Ctrl-C Detected. Exiting RPNCalc...");

} catch (Exception ex) {
// Should never get this error...
Output.fatalError("Could not read user input\n" + ex.getMessage(), 5);
}

Expand All @@ -258,14 +261,14 @@ public static void main(String[] args) {
// Remove any commas from the string allowing for numbers such as "12,123" to be entered
// TODO: Should make this more international at some point
cmdInput = cmdInput.replaceAll(",", "");

// Break the string into a command (cmdInputCmd) and a parameter (cmdInputParam)
String[] ci = cmdInput.toLowerCase().trim().split("\\s+", 2);
cmdInputCmd = ci[0];
cmdInputParam = ci[1];

} catch (ArrayIndexOutOfBoundsException e) {
// Ignore if there is no command or parameter entered
// Ignore this exception if there is no command or parameter entered
if (cmdInputCmd.isEmpty()) {
Output.debugPrint("Blank line entered");
continue;
Expand All @@ -274,7 +277,7 @@ public static void main(String[] args) {

// While in debug mode, show the entered text along with the broken up command and parameter
Output.debugPrint(
"Complete cmdInput: '" + cmdInput + "' | cmdInputCommand: '" + cmdInputCmd + "' | cmdInputParameter: '" + cmdInputParam + "'");
"Full cmdInput: '" + cmdInput + "' | cmdInputCommand: '" + cmdInputCmd + "' | cmdInputParameter: '" + cmdInputParam + "'");

// If recording is enabled, send the user input to be recorded
if (UserFunctions.recordingIsEnabled() == true) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/fross/rpncalc/UserFunctions.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public static void cmdRecord(String args) {

// Read the user input. If ctrl-C is entered, discard the function
try {
functionName = Main.scanner.readLine(">> ");
functionName = Main.scanner.readLine(Main.INPUT_PROMPT);
} catch (UserInterruptException ex) {
functionName = "";
} catch (Exception e) {
Expand Down

0 comments on commit d857314

Please sign in to comment.