done
parent
5b04785ab9
commit
9248d036f1
Binary file not shown.
Binary file not shown.
|
@ -2,10 +2,12 @@ package com.mouseboy.assignment1;
|
||||||
|
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.PersistableBundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.activity.EdgeToEdge;
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.core.graphics.Insets;
|
import androidx.core.graphics.Insets;
|
||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
|
@ -21,7 +23,7 @@ import java.util.ArrayList;
|
||||||
public class MainCalculatorActivity extends AppCompatActivity {
|
public class MainCalculatorActivity extends AppCompatActivity {
|
||||||
|
|
||||||
// I missing having decltype already
|
// I missing having decltype already
|
||||||
private final StateFarm state = new StateFarm(this,"");
|
private StateFarm state = new StateFarm(this,"");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
@ -34,6 +36,7 @@ public class MainCalculatorActivity extends AppCompatActivity {
|
||||||
return insets;
|
return insets;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// setup button listeners
|
||||||
findViewById(R.id.b0).setOnClickListener((View view) -> state.addNumber("0"));
|
findViewById(R.id.b0).setOnClickListener((View view) -> state.addNumber("0"));
|
||||||
findViewById(R.id.b1).setOnClickListener((View view) -> state.addNumber("1"));
|
findViewById(R.id.b1).setOnClickListener((View view) -> state.addNumber("1"));
|
||||||
findViewById(R.id.b2).setOnClickListener((View view) -> state.addNumber("2"));
|
findViewById(R.id.b2).setOnClickListener((View view) -> state.addNumber("2"));
|
||||||
|
@ -59,4 +62,17 @@ public class MainCalculatorActivity extends AppCompatActivity {
|
||||||
findViewById(R.id.bpar).setOnClickListener((View view) -> state.paren());
|
findViewById(R.id.bpar).setOnClickListener((View view) -> state.paren());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
// only equation string needs to be saved / restored
|
||||||
|
outState.putString("equ", state.getString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
|
||||||
|
super.onRestoreInstanceState(savedInstanceState);
|
||||||
|
state = new StateFarm(this, savedInstanceState.getString("equ"));
|
||||||
|
state.updateDisplay();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package com.mouseboy.assignment1.helpers;
|
package com.mouseboy.assignment1.helpers;
|
||||||
|
|
||||||
|
// numbers are special tokens which store data. It is annoying you can't store data inside enums
|
||||||
public class Number implements Token {
|
public class Number implements Token {
|
||||||
|
|
||||||
public final String value;
|
public final String value;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.mouseboy.assignment1.helpers;
|
package com.mouseboy.assignment1.helpers;
|
||||||
|
|
||||||
|
// thanks to my bf for telling me you can implement an interface. I hate it.
|
||||||
public enum Operator implements Token {
|
public enum Operator implements Token {
|
||||||
Plus,
|
Plus,
|
||||||
Minus,
|
Minus,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
package com.mouseboy.assignment1.helpers;
|
package com.mouseboy.assignment1.helpers;
|
||||||
|
|
||||||
|
// empty interface representing a token for the tokenizer / parser
|
||||||
public interface Token {
|
public interface Token {
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,26 +7,17 @@ import java.text.DecimalFormatSymbols;
|
||||||
public class Utils {
|
public class Utils {
|
||||||
|
|
||||||
// why does java not have a nice decimal formatter
|
// why does java not have a nice decimal formatter
|
||||||
|
// function makes decimals nice, only displays 2 digits of precision
|
||||||
public static String formatDecimal(double value) {
|
public static String formatDecimal(double value) {
|
||||||
BigDecimal decimalValue = BigDecimal.valueOf(value);
|
BigDecimal decimalValue = BigDecimal.valueOf(value);
|
||||||
|
|
||||||
String pattern;
|
String pattern;
|
||||||
|
if (decimalValue.stripTrailingZeros().scale() <= 0)
|
||||||
if (decimalValue.stripTrailingZeros().scale() <= 0) {
|
|
||||||
pattern = "#,##0";
|
pattern = "#,##0";
|
||||||
} else {
|
else
|
||||||
pattern = "#,##0.##";
|
pattern = "#,##0.##";
|
||||||
}
|
|
||||||
|
|
||||||
DecimalFormat df = new DecimalFormat(pattern);
|
return new DecimalFormat(pattern).format(value);
|
||||||
|
|
||||||
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
|
|
||||||
symbols.setDecimalSeparator('.');
|
|
||||||
symbols.setGroupingSeparator(',');
|
|
||||||
|
|
||||||
df.setDecimalFormatSymbols(symbols);
|
|
||||||
|
|
||||||
return df.format(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,14 @@ import com.mouseboy.assignment1.R;
|
||||||
import com.mouseboy.assignment1.helpers.Number;
|
import com.mouseboy.assignment1.helpers.Number;
|
||||||
import com.mouseboy.assignment1.helpers.Operator;
|
import com.mouseboy.assignment1.helpers.Operator;
|
||||||
import com.mouseboy.assignment1.helpers.Token;
|
import com.mouseboy.assignment1.helpers.Token;
|
||||||
|
import com.mouseboy.assignment1.helpers.Utils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
// yes this could've been done in a more OOP way but I miss C++ and don't want to Java :3
|
||||||
public class StateFarm {
|
public class StateFarm {
|
||||||
|
|
||||||
|
// none of this state needs to be stored as it is only ever generated when public methods are called / is reset between calls
|
||||||
private StringBuilder currentData = new StringBuilder();
|
private StringBuilder currentData = new StringBuilder();
|
||||||
private final AppCompatActivity parent;
|
private final AppCompatActivity parent;
|
||||||
private final ArrayList<Token> tokens = new ArrayList<>();
|
private final ArrayList<Token> tokens = new ArrayList<>();
|
||||||
|
@ -23,79 +26,36 @@ public class StateFarm {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void tokenize() {
|
|
||||||
currentToken = 0;
|
|
||||||
tokens.clear();
|
|
||||||
for (int i = 0; i < currentData.length(); i++) {
|
|
||||||
char c = currentData.charAt(i);
|
|
||||||
switch (c) {
|
|
||||||
case '*':
|
|
||||||
tokens.add(Operator.Mul);
|
|
||||||
break;
|
|
||||||
case '+':
|
|
||||||
tokens.add(Operator.Plus);
|
|
||||||
break;
|
|
||||||
case '-':
|
|
||||||
tokens.add(Operator.Minus);
|
|
||||||
break;
|
|
||||||
case '÷':
|
|
||||||
tokens.add(Operator.Div);
|
|
||||||
break;
|
|
||||||
case '(':
|
|
||||||
tokens.add(Operator.ParenLeft);
|
|
||||||
break;
|
|
||||||
case ')':
|
|
||||||
tokens.add(Operator.ParenRight);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
StringBuilder data = new StringBuilder();
|
|
||||||
while (Character.isDigit(c) || c == '.') {
|
|
||||||
data.append(c);
|
|
||||||
if (++i >= currentData.length())
|
|
||||||
break;
|
|
||||||
c = currentData.charAt(i);
|
|
||||||
}
|
|
||||||
--i;
|
|
||||||
tokens.add(new Number(data.toString()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Token peek() {
|
|
||||||
if (!hasNext())
|
|
||||||
return null;
|
|
||||||
return tokens.get(currentToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void next() {
|
|
||||||
currentToken++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasNext() {
|
|
||||||
return currentToken < tokens.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Token last() {
|
|
||||||
return tokens.get(tokens.size() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getString() {
|
public String getString() {
|
||||||
return currentData.toString();
|
return currentData.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateDisplay() {
|
||||||
|
((TextView) parent.findViewById(R.id.output)).setText(currentData.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backspace function
|
||||||
|
*/
|
||||||
public void clearCurrent() {
|
public void clearCurrent() {
|
||||||
|
checkAndClearExceptions();
|
||||||
if (currentData.length() == 0)
|
if (currentData.length() == 0)
|
||||||
return;
|
return;
|
||||||
currentData.deleteCharAt(currentData.length() - 1);
|
currentData.deleteCharAt(currentData.length() - 1);
|
||||||
updateDisplay();
|
updateDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the entire string
|
||||||
|
*/
|
||||||
public void clearAll() {
|
public void clearAll() {
|
||||||
currentData = new StringBuilder();
|
currentData = new StringBuilder();
|
||||||
updateDisplay();
|
updateDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a number to the formula. Should be a single character
|
||||||
|
*/
|
||||||
public void addNumber(String number) {
|
public void addNumber(String number) {
|
||||||
tokenize();
|
tokenize();
|
||||||
// adds a multiply for you if you add a number after a paren express ()
|
// adds a multiply for you if you add a number after a paren express ()
|
||||||
|
@ -105,10 +65,14 @@ public class StateFarm {
|
||||||
updateDisplay();
|
updateDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an operator to the formula. Should be a single character
|
||||||
|
*/
|
||||||
public void addOperator(String operator) {
|
public void addOperator(String operator) {
|
||||||
tokenize();
|
tokenize();
|
||||||
|
// don't add operators if there is no operands
|
||||||
if (!tokens.isEmpty()) {
|
if (!tokens.isEmpty()) {
|
||||||
// changes operators if you haven't typed an expression
|
// Remove unused opening braces
|
||||||
while (last() == Operator.ParenLeft) {
|
while (last() == Operator.ParenLeft) {
|
||||||
currentData.deleteCharAt(currentData.length() - 1);
|
currentData.deleteCharAt(currentData.length() - 1);
|
||||||
tokens.remove(tokens.size() - 1);
|
tokens.remove(tokens.size() - 1);
|
||||||
|
@ -127,8 +91,12 @@ public class StateFarm {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a dot to the current number in the formula. Does nothing if it is already a decimal
|
||||||
|
*/
|
||||||
public void dot() {
|
public void dot() {
|
||||||
tokenize();
|
tokenize();
|
||||||
|
// don't do anything if the number already has a dot in it
|
||||||
if (!tokens.isEmpty() && last() instanceof Number) {
|
if (!tokens.isEmpty() && last() instanceof Number) {
|
||||||
Number v = (Number) last();
|
Number v = (Number) last();
|
||||||
if (!v.value.contains(".")) {
|
if (!v.value.contains(".")) {
|
||||||
|
@ -138,35 +106,15 @@ public class StateFarm {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleNegativeWithSubtract(int v, boolean matching) {
|
/**
|
||||||
if (currentData.charAt(v) == '-') {
|
* Negates the current number or parenthesis expression. removes the negative if it already exists.
|
||||||
if (v == 0)
|
*/
|
||||||
currentData.deleteCharAt(0);
|
|
||||||
else {
|
|
||||||
char c = currentData.charAt(v - 1);
|
|
||||||
if (!(Character.isDigit(c) || c == ')'))
|
|
||||||
currentData.deleteCharAt(v);
|
|
||||||
else
|
|
||||||
currentData.insert(v, '-');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
char c = currentData.charAt(v);
|
|
||||||
if (matching && c == '(' && currentData.charAt(v + 1) == '(') {
|
|
||||||
currentData.insert(v + 1, '-');
|
|
||||||
} else {
|
|
||||||
if (!(Character.isDigit(c) || c == '('))
|
|
||||||
currentData.insert(v + 1, '-');
|
|
||||||
else
|
|
||||||
currentData.insert(v, '-');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void neg() {
|
public void neg() {
|
||||||
tokenize();
|
tokenize();
|
||||||
if (tokens.isEmpty())
|
if (tokens.isEmpty())
|
||||||
return;
|
return;
|
||||||
if (last() instanceof Number) {
|
if (last() instanceof Number) {
|
||||||
|
// find beginning of number. Should store begins/end inside tokens but meh it's Java
|
||||||
int v = currentData.length() - 1;
|
int v = currentData.length() - 1;
|
||||||
while (v > 0 &&
|
while (v > 0 &&
|
||||||
(Character.isDigit(currentData.charAt(v)) || currentData.charAt(v) == '.')) {
|
(Character.isDigit(currentData.charAt(v)) || currentData.charAt(v) == '.')) {
|
||||||
|
@ -176,6 +124,7 @@ public class StateFarm {
|
||||||
updateDisplay();
|
updateDisplay();
|
||||||
}
|
}
|
||||||
if (last() == Operator.ParenRight) {
|
if (last() == Operator.ParenRight) {
|
||||||
|
// same thing as if it is a number but we need to find the matching opening brace to the closing brace we are on
|
||||||
int outstanding = 0;
|
int outstanding = 0;
|
||||||
int v = currentData.length() - 1;
|
int v = currentData.length() - 1;
|
||||||
do {
|
do {
|
||||||
|
@ -185,17 +134,30 @@ public class StateFarm {
|
||||||
outstanding--;
|
outstanding--;
|
||||||
v--;
|
v--;
|
||||||
} while (v > 0 && outstanding != 0);
|
} while (v > 0 && outstanding != 0);
|
||||||
for (int i = v; i < currentData.length(); i++)
|
|
||||||
System.out.println(currentData.charAt(i));
|
|
||||||
handleNegativeWithSubtract(v, outstanding == 0);
|
handleNegativeWithSubtract(v, outstanding == 0);
|
||||||
updateDisplay();
|
updateDisplay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates the current equation, removes the formula and replaces with the result
|
||||||
|
*/
|
||||||
public void equals() {
|
public void equals() {
|
||||||
|
StringBuilder newData = new StringBuilder();
|
||||||
|
try {
|
||||||
double value = parse();
|
double value = parse();
|
||||||
currentData = new StringBuilder();
|
newData.append(Utils.formatDecimal(value));
|
||||||
currentData.append(value);
|
} catch (Exception e) {
|
||||||
|
newData.append(e.getMessage());
|
||||||
|
// used as a tombstone to identify if the formula entry has an error message in it
|
||||||
|
// plus:
|
||||||
|
// https://en.wikipedia.org/wiki/Tilde#Other_uses
|
||||||
|
// In modern internet slang, the tilde can be used to signify endearment or love,
|
||||||
|
// i.e. "Hello master~". It is commonly used in the furry and femboy communities
|
||||||
|
// and can also be used as a diminutive, akin to adding the "ee" sound to the end of a word.[citation needed]
|
||||||
|
newData.append("~");
|
||||||
|
}
|
||||||
|
currentData = newData;
|
||||||
updateDisplay();
|
updateDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,6 +176,8 @@ public class StateFarm {
|
||||||
tokenize();
|
tokenize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// when determining what kind of parenthesis to add we need to know the current outstanding count.
|
||||||
|
// outstanding being the ones which do not have a matching closing bracket
|
||||||
int outstanding_paren = 0;
|
int outstanding_paren = 0;
|
||||||
for (Token op : tokens) {
|
for (Token op : tokens) {
|
||||||
if (op == Operator.ParenLeft)
|
if (op == Operator.ParenLeft)
|
||||||
|
@ -222,20 +186,26 @@ public class StateFarm {
|
||||||
outstanding_paren--;
|
outstanding_paren--;
|
||||||
}
|
}
|
||||||
if (last() instanceof Number) {
|
if (last() instanceof Number) {
|
||||||
|
// numbers have a special case where we will infer number(expr) means number*(expr)
|
||||||
|
// which is fairly standard math and a nice QOL feature
|
||||||
if (outstanding_paren == 0)
|
if (outstanding_paren == 0)
|
||||||
currentData.append("*(");
|
currentData.append("*(");
|
||||||
else
|
else
|
||||||
currentData.append(")");
|
currentData.append(")");
|
||||||
} else {
|
} else {
|
||||||
switch ((Operator) last()) {
|
switch ((Operator) last()) {
|
||||||
|
// operators can be all the same
|
||||||
case Plus:
|
case Plus:
|
||||||
case Minus:
|
case Minus:
|
||||||
case Mul:
|
case Mul:
|
||||||
case Div:
|
case Div:
|
||||||
case ParenLeft:
|
case ParenLeft:
|
||||||
|
// we just want to append a new opening brace
|
||||||
currentData.append("(");
|
currentData.append("(");
|
||||||
break;
|
break;
|
||||||
case ParenRight:
|
case ParenRight:
|
||||||
|
// but if all paren are closed we can assume multiplying a new expression
|
||||||
|
// otherwise we should continue to close the parenthesis
|
||||||
if (outstanding_paren == 0)
|
if (outstanding_paren == 0)
|
||||||
currentData.append("*(");
|
currentData.append("*(");
|
||||||
else
|
else
|
||||||
|
@ -260,63 +230,205 @@ public class StateFarm {
|
||||||
// (https://github.com/Tri11Paragon/blt-gp/blob/094fa76b5823f81f653ef8b4065cd15d7501cfec/include/blt/gp/program.h#L143C1-L153C22)
|
// (https://github.com/Tri11Paragon/blt-gp/blob/094fa76b5823f81f653ef8b4065cd15d7501cfec/include/blt/gp/program.h#L143C1-L153C22)
|
||||||
// (yes i do realize this code ^ makes zero sense without a complex understanding of the library internals)
|
// (yes i do realize this code ^ makes zero sense without a complex understanding of the library internals)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the parser
|
||||||
|
* @return the evaluated value using precedence of the current formula
|
||||||
|
*/
|
||||||
public double parse() {
|
public double parse() {
|
||||||
tokenize();
|
tokenize();
|
||||||
return parse_pres_1();
|
if (currentData.length() == 0)
|
||||||
|
throw new RuntimeException("Expression Required");
|
||||||
|
return parsePres1();
|
||||||
}
|
}
|
||||||
|
|
||||||
private double parse_pres_1() {
|
/**
|
||||||
double v = parse_pres_2();
|
* Evaluate precedence of level 1 (lowest level of precedence)
|
||||||
|
*/
|
||||||
|
private double parsePres1() {
|
||||||
|
// get the left hand side of the equation by parsing down a level in the tree.
|
||||||
|
// This will be a value an expression
|
||||||
|
double v = parsePres2();
|
||||||
|
// if after getting the value for the lhs we find a plus or minus, apply the operator
|
||||||
if (peek() == Operator.Plus) {
|
if (peek() == Operator.Plus) {
|
||||||
next();
|
next();
|
||||||
v += parse_pres_2();
|
v += parsePres2();
|
||||||
} else if (peek() == Operator.Minus) {
|
} else if (peek() == Operator.Minus) {
|
||||||
next();
|
next();
|
||||||
v -= parse_pres_2();
|
v -= parsePres2();
|
||||||
}
|
}
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double parse_pres_2() {
|
/**
|
||||||
double v = parse_pres_3();
|
* Evaluate precedence of level 2 (second lowest precedence)
|
||||||
if (peek() == Operator.Mul) {
|
*/
|
||||||
|
private double parsePres2() {
|
||||||
|
// same as above, lvl 3 contains the terminals though
|
||||||
|
double v = parsePres3();
|
||||||
|
// same as above; BEDMAS. Divide goes first.
|
||||||
|
if (peek() == Operator.Div) {
|
||||||
next();
|
next();
|
||||||
v *= parse_pres_3();
|
double d = parsePres3();
|
||||||
} else if (peek() == Operator.Div) {
|
|
||||||
next();
|
|
||||||
double d = parse_pres_3();
|
|
||||||
if (d == 0)
|
if (d == 0)
|
||||||
throw new ArithmeticException("Cannot divide by zero!");
|
throw new ArithmeticException("Cannot divide by zero");
|
||||||
v /= d;
|
v /= d;
|
||||||
|
} else if (peek() == Operator.Mul) {
|
||||||
|
next();
|
||||||
|
v *= parsePres3();
|
||||||
}
|
}
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double parse_pres_3() {
|
/**
|
||||||
|
* Evaluate precedence of level 3 (highest precedence)
|
||||||
|
*/
|
||||||
|
private double parsePres3() {
|
||||||
|
// look for either an expression, a number, or a minus sign
|
||||||
if (peek() == Operator.ParenLeft) {
|
if (peek() == Operator.ParenLeft) {
|
||||||
next(); // consume (
|
next(); // consume (
|
||||||
double d = parse_pres_1(); // consume expression
|
double d = parsePres1(); // consume expression
|
||||||
next(); // consume )
|
next(); // consume )
|
||||||
return d;
|
return d;
|
||||||
} else if (peek() == Operator.Minus) {
|
} else if (peek() == Operator.Minus) {
|
||||||
// negating a value
|
// negating a value
|
||||||
next();
|
next();
|
||||||
return -parse_pres_3();
|
return -parsePres3();
|
||||||
} else if (peek() instanceof Number) {
|
} else if (peek() instanceof Number) {
|
||||||
double value = Double.parseDouble(((Number) peek()).value);
|
double value = Double.parseDouble(((Number) peek()).value);
|
||||||
next();
|
next();
|
||||||
return value;
|
return value;
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("There was an error parsing the expression!");
|
throw new RuntimeException("Invalid Expression");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void printAllTokens() {
|
/**
|
||||||
|
* Handles adding or removing the negative sign on numbers or (parenthesis groups)
|
||||||
|
* @param v one past the start of your value
|
||||||
|
* @param matching true if the two open paren are matched, otherwise false.
|
||||||
|
*/
|
||||||
|
private void handleNegativeWithSubtract(int v, boolean matching) {
|
||||||
|
// im not able to explain mess of edge cases. just accept it works like i did and move on :3
|
||||||
|
if (currentData.charAt(v) == '-') {
|
||||||
|
if (v == 0)
|
||||||
|
currentData.deleteCharAt(0);
|
||||||
|
else {
|
||||||
|
char c = currentData.charAt(v - 1);
|
||||||
|
if (!(Character.isDigit(c) || c == ')'))
|
||||||
|
currentData.deleteCharAt(v);
|
||||||
|
else
|
||||||
|
currentData.insert(v, '-');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
char c = currentData.charAt(v);
|
||||||
|
if (matching && c == '(' && currentData.charAt(v + 1) == '(') {
|
||||||
|
currentData.insert(v + 1, '-');
|
||||||
|
} else {
|
||||||
|
if (!(Character.isDigit(c) || c == '('))
|
||||||
|
currentData.insert(v + 1, '-');
|
||||||
|
else
|
||||||
|
currentData.insert(v, '-');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return next token without moving the parser forward
|
||||||
|
*/
|
||||||
|
private Token peek() {
|
||||||
|
// returning null if there is none will result in all equals failing,
|
||||||
|
// which will throw an invalid expression at some point.
|
||||||
|
if (!hasNext())
|
||||||
|
return null;
|
||||||
|
return tokens.get(currentToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the parser forward one in the token stream
|
||||||
|
*/
|
||||||
|
private void next() {
|
||||||
|
currentToken++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there is a next value in the steam
|
||||||
|
*/
|
||||||
|
private boolean hasNext() {
|
||||||
|
return currentToken < tokens.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return token at the top of the token stream. useful for input validation.
|
||||||
|
*/
|
||||||
|
private Token last() {
|
||||||
|
return tokens.get(tokens.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for an exception then clears it from the screen.
|
||||||
|
*/
|
||||||
|
private void checkAndClearExceptions() {
|
||||||
|
// this is why i used the ~. Was the simplest way.
|
||||||
|
if (currentData.toString().contains("~")) {
|
||||||
|
currentData = new StringBuilder();
|
||||||
|
updateDisplay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function which handles all the tokenization of the current input expression string.
|
||||||
|
*/
|
||||||
|
private void tokenize() {
|
||||||
|
checkAndClearExceptions();
|
||||||
|
currentToken = 0;
|
||||||
|
tokens.clear();
|
||||||
|
for (int i = 0; i < currentData.length(); i++) {
|
||||||
|
char c = currentData.charAt(i);
|
||||||
|
switch (c) {
|
||||||
|
case '*':
|
||||||
|
tokens.add(Operator.Mul);
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
tokens.add(Operator.Plus);
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
tokens.add(Operator.Minus);
|
||||||
|
break;
|
||||||
|
case '÷':
|
||||||
|
tokens.add(Operator.Div);
|
||||||
|
break;
|
||||||
|
case '(':
|
||||||
|
tokens.add(Operator.ParenLeft);
|
||||||
|
break;
|
||||||
|
case ')':
|
||||||
|
tokens.add(Operator.ParenRight);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// find extends of the number then add it as a token.
|
||||||
|
StringBuilder data = new StringBuilder();
|
||||||
|
while (Character.isDigit(c) || c == '.') {
|
||||||
|
data.append(c);
|
||||||
|
if (++i >= currentData.length())
|
||||||
|
break;
|
||||||
|
c = currentData.charAt(i);
|
||||||
|
}
|
||||||
|
--i;
|
||||||
|
tokens.add(new Number(data.toString()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unused helper functions below
|
||||||
|
* -----------------------------
|
||||||
|
*/
|
||||||
|
private void printAllTokens() {
|
||||||
for (Token t : tokens)
|
for (Token t : tokens)
|
||||||
printToken(t);
|
printToken(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void printToken(Token t) {
|
private void printToken(Token t) {
|
||||||
if (t instanceof Number)
|
if (t instanceof Number)
|
||||||
System.out.println(((Number) t).value);
|
System.out.println(((Number) t).value);
|
||||||
else {
|
else {
|
||||||
|
@ -343,8 +455,4 @@ public class StateFarm {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateDisplay() {
|
|
||||||
((TextView) parent.findViewById(R.id.output)).setText(currentData.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,7 @@
|
||||||
android:id="@+id/bc"
|
android:id="@+id/bc"
|
||||||
style="@style/CalculatorButtonStyle"
|
style="@style/CalculatorButtonStyle"
|
||||||
android:backgroundTint="@color/clearButtons"
|
android:backgroundTint="@color/clearButtons"
|
||||||
android:text="C" />
|
android:text="BS" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue