325 lines
11 KiB
Java
325 lines
11 KiB
Java
package it.cavallium.warppi.math.parser;
|
|
|
|
import it.cavallium.warppi.WarpPI;
|
|
import it.cavallium.warppi.Platform.ConsoleUtils;
|
|
import it.cavallium.warppi.gui.expression.blocks.Block;
|
|
import it.cavallium.warppi.gui.expression.containers.InputContainer;
|
|
import it.cavallium.warppi.math.Function;
|
|
import it.cavallium.warppi.math.MathContext;
|
|
import it.cavallium.warppi.math.MathematicalSymbols;
|
|
import it.cavallium.warppi.math.functions.Expression;
|
|
import it.cavallium.warppi.math.functions.Variable.V_TYPE;
|
|
import it.cavallium.warppi.math.parser.features.FeatureChar;
|
|
import it.cavallium.warppi.math.parser.features.FeatureDivision;
|
|
import it.cavallium.warppi.math.parser.features.FeatureMultiplication;
|
|
import it.cavallium.warppi.math.parser.features.FeatureNumber;
|
|
import it.cavallium.warppi.math.parser.features.FeaturePower;
|
|
import it.cavallium.warppi.math.parser.features.FeaturePowerChar;
|
|
import it.cavallium.warppi.math.parser.features.FeatureSubtraction;
|
|
import it.cavallium.warppi.math.parser.features.FeatureSum;
|
|
import it.cavallium.warppi.math.parser.features.FeatureSumSubtraction;
|
|
import it.cavallium.warppi.math.parser.features.FeatureVariable;
|
|
import it.cavallium.warppi.math.parser.features.interfaces.Feature;
|
|
import it.cavallium.warppi.math.parser.steps.AddImplicitMultiplications;
|
|
import it.cavallium.warppi.math.parser.steps.FixMultiplicationsAndDivisions;
|
|
import it.cavallium.warppi.math.parser.steps.FixSingleFunctionArgs;
|
|
import it.cavallium.warppi.math.parser.steps.FixSumsAndSubtractions;
|
|
import it.cavallium.warppi.math.parser.steps.JoinNumberAndVariables;
|
|
import it.cavallium.warppi.math.parser.steps.RemoveParentheses;
|
|
import it.cavallium.warppi.util.Error;
|
|
import it.cavallium.warppi.util.Errors;
|
|
import it.cavallium.warppi.util.IntWrapper;
|
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
|
|
public class MathParser {
|
|
public static Expression parseInput(final MathContext context, final InputContainer c) throws Error {
|
|
Expression result;
|
|
|
|
final Function resultFunction = c.toFunction(context);
|
|
|
|
result = new Expression(context, resultFunction);
|
|
return result;
|
|
}
|
|
|
|
public static ObjectArrayList<ObjectArrayList<Block>> parseOutput(final MathContext context,
|
|
final ObjectArrayList<Function> resultExpressions) throws Error {
|
|
final ObjectArrayList<ObjectArrayList<Block>> result = new ObjectArrayList<>();
|
|
for (final Function resultExpression : resultExpressions) {
|
|
final ObjectArrayList<Block> resultBlocks = resultExpression.toBlock(context);
|
|
if (resultBlocks == null) {
|
|
throw new Error(Errors.NOT_IMPLEMENTED, "Unknown function " + resultExpression.getClass().getSimpleName());
|
|
}
|
|
result.add(resultBlocks);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public static Function joinFeatures(final MathContext context, ObjectArrayList<Feature> features) throws Error {
|
|
|
|
features = MathParser.fixFeatures(context, features);
|
|
|
|
ObjectArrayList<Function> process = new ObjectArrayList<>();
|
|
|
|
for (final Feature f : features) {
|
|
final Function fnc = f.toFunction(context);
|
|
if (fnc == null) {
|
|
throw new Error(Errors.SYNTAX_ERROR, "\"" + f.getClass().getSimpleName() + "\" can't be converted into a Function!");
|
|
}
|
|
process.add(fnc);
|
|
}
|
|
|
|
process = MathParser.fixStack(context, process);
|
|
|
|
if (process.size() > 1) {
|
|
throw new Error(Errors.UNBALANCED_STACK, "The stack is unbalanced. Not all the functions are nested correctly");
|
|
}
|
|
|
|
return process.get(0);
|
|
}
|
|
|
|
private static ObjectArrayList<Function> fixStack(final MathContext context,
|
|
final ObjectArrayList<Function> functionsList) throws Error {
|
|
final MathParserStep[] steps = new MathParserStep[] { new JoinNumberAndVariables(context), new FixSingleFunctionArgs(), new RemoveParentheses(context), new FixMultiplicationsAndDivisions(), new FixSumsAndSubtractions(), new AddImplicitMultiplications(context), };
|
|
boolean lastLoopDidSomething;
|
|
Function lastElement;
|
|
|
|
if (WarpPI.getPlatform().getSettings().isDebugEnabled()) {
|
|
WarpPI.getPlatform().getConsoleUtils().out().print(ConsoleUtils.OUTPUTLEVEL_DEBUG_VERBOSE, "\tStatus: ");
|
|
for (final Function f : functionsList) {
|
|
WarpPI.getPlatform().getConsoleUtils().out().print(ConsoleUtils.OUTPUTLEVEL_DEBUG_VERBOSE, f.toString());
|
|
}
|
|
WarpPI.getPlatform().getConsoleUtils().out().println(ConsoleUtils.OUTPUTLEVEL_DEBUG_VERBOSE);
|
|
}
|
|
|
|
for (final MathParserStep step : steps) {
|
|
if (WarpPI.getPlatform().getSettings().isDebugEnabled()) {
|
|
WarpPI.getPlatform().getConsoleUtils().out().println(2, "Stack fixing step \"" + step.getStepName() + "\"");
|
|
}
|
|
final int stepQty = step.requiresReversedIteration() ? -1 : 1,
|
|
initialIndex = step.requiresReversedIteration() ? functionsList.size() - 1 : 0;
|
|
do {
|
|
lastLoopDidSomething = false;
|
|
lastElement = null;
|
|
final IntWrapper curIndex = new IntWrapper(initialIndex);
|
|
while (curIndex.i >= 0 && curIndex.i < functionsList.size()) {
|
|
final int i = curIndex.i;
|
|
final Function f = functionsList.get(i);
|
|
|
|
if (step.eval(curIndex, lastElement, f, functionsList)) {
|
|
lastLoopDidSomething = true;
|
|
}
|
|
|
|
lastElement = i >= functionsList.size() ? null : functionsList.get(i);
|
|
curIndex.i += stepQty;
|
|
}
|
|
} while (lastLoopDidSomething);
|
|
|
|
if (WarpPI.getPlatform().getSettings().isDebugEnabled()) {
|
|
WarpPI.getPlatform().getConsoleUtils().out().print(ConsoleUtils.OUTPUTLEVEL_DEBUG_VERBOSE, "\tStatus: ");
|
|
for (final Function f : functionsList) {
|
|
WarpPI.getPlatform().getConsoleUtils().out().print(ConsoleUtils.OUTPUTLEVEL_DEBUG_VERBOSE, f.toString());
|
|
}
|
|
WarpPI.getPlatform().getConsoleUtils().out().println(ConsoleUtils.OUTPUTLEVEL_DEBUG_VERBOSE);
|
|
}
|
|
}
|
|
|
|
// //Phase 4
|
|
// do {
|
|
// lastLoopDidSomething = false;
|
|
// functionListIterator = functionsList.iterator();
|
|
// while (functionListIterator.hasNext()) {
|
|
// final Function f = functionListIterator.next();
|
|
//
|
|
// if (f instanceof Function2Args) {
|
|
//
|
|
// }
|
|
// }
|
|
// } while (lastLoopDidSomething);
|
|
|
|
return functionsList;
|
|
}
|
|
|
|
private static ObjectArrayList<Feature> fixFeatures(final MathContext context, ObjectArrayList<Feature> features)
|
|
throws Error {
|
|
|
|
features = MathParser.fixMinuses(context, features);
|
|
|
|
features = MathParser.makeNumbers(context, features);
|
|
|
|
features = MathParser.makePowers(context, features);
|
|
|
|
features = MathParser.convertFunctionChars(context, features);
|
|
|
|
return features;
|
|
}
|
|
|
|
/**
|
|
* Create function features from char features
|
|
*
|
|
* @param context
|
|
* @param features
|
|
* @return
|
|
*/
|
|
private static ObjectArrayList<Feature> convertFunctionChars(final MathContext context,
|
|
final ObjectArrayList<Feature> features) throws Error {
|
|
final ObjectArrayList<Feature> process = new ObjectArrayList<>();
|
|
|
|
for (final Feature f : features) {
|
|
if (f instanceof FeatureChar) {
|
|
final char featureChar = ((FeatureChar) f).ch;
|
|
Feature result = null;
|
|
switch (featureChar) {
|
|
case MathematicalSymbols.SUM:
|
|
result = new FeatureSum(null, null);
|
|
break;
|
|
case MathematicalSymbols.SUM_SUBTRACTION:
|
|
result = new FeatureSumSubtraction(null, null);
|
|
break;
|
|
case MathematicalSymbols.SUBTRACTION:
|
|
result = new FeatureSubtraction(null, null);
|
|
break;
|
|
case MathematicalSymbols.MULTIPLICATION:
|
|
result = new FeatureMultiplication(null, null);
|
|
break;
|
|
case MathematicalSymbols.DIVISION:
|
|
result = new FeatureDivision(null, null);
|
|
break;
|
|
}
|
|
|
|
for (final char var : MathematicalSymbols.variables) {
|
|
if (featureChar == var) {
|
|
result = new FeatureVariable(featureChar, V_TYPE.VARIABLE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (result == null) {
|
|
throw new Error(Errors.SYNTAX_ERROR, "Char " + featureChar + " isn't a known feature");
|
|
}
|
|
|
|
process.add(result);
|
|
} else {
|
|
process.add(f);
|
|
}
|
|
}
|
|
|
|
return process;
|
|
}
|
|
|
|
/**
|
|
* Make numbers [-][1][2][+][-][3] => [-12]
|
|
*
|
|
* @param context
|
|
* @param features
|
|
* @return
|
|
*/
|
|
private static ObjectArrayList<Feature> makeNumbers(final MathContext context,
|
|
final ObjectArrayList<Feature> features) {
|
|
final ObjectArrayList<Feature> process = new ObjectArrayList<>();
|
|
|
|
FeatureNumber numberBuffer = null;
|
|
for (final Feature f : features) {
|
|
if (f instanceof FeatureChar) {
|
|
final FeatureChar bcf = (FeatureChar) f;
|
|
final char[] numbers = MathematicalSymbols.numbers;
|
|
boolean isNumber = false;
|
|
for (final char n : numbers) {
|
|
if (bcf.ch == n) {
|
|
isNumber = true;
|
|
break;
|
|
}
|
|
}
|
|
if (bcf.ch == MathematicalSymbols.MINUS || bcf.ch == '.') {
|
|
isNumber = true;
|
|
}
|
|
if (isNumber) {
|
|
if (numberBuffer == null) {
|
|
numberBuffer = new FeatureNumber(bcf.ch);
|
|
process.add(numberBuffer);
|
|
} else {
|
|
numberBuffer.append(bcf.ch);
|
|
}
|
|
} else {
|
|
if (numberBuffer != null) {
|
|
numberBuffer = null;
|
|
}
|
|
process.add(f);
|
|
}
|
|
} else {
|
|
process.add(f);
|
|
}
|
|
}
|
|
|
|
return process;
|
|
}
|
|
|
|
/**
|
|
* Fix minuses [-][1][2][+][-][3][-][2] => [-][12][+][-][3][—][2]
|
|
*
|
|
* @param context
|
|
* @param features
|
|
* @return
|
|
* @throws Error
|
|
*/
|
|
private static ObjectArrayList<Feature> fixMinuses(final MathContext context,
|
|
final ObjectArrayList<Feature> features) throws Error {
|
|
final ObjectArrayList<Feature> process = new ObjectArrayList<>();
|
|
Feature lastFeature = null;
|
|
for (final Feature f : features) {
|
|
if (f instanceof FeatureChar && (((FeatureChar) f).ch == MathematicalSymbols.SUBTRACTION || ((FeatureChar) f).ch == MathematicalSymbols.MINUS)) {
|
|
boolean isNegativeOfNumber = false;
|
|
if (lastFeature == null) {
|
|
isNegativeOfNumber = true;
|
|
} else if (lastFeature instanceof FeatureChar) {
|
|
final FeatureChar lcf = (FeatureChar) lastFeature;
|
|
final char[] operators = MathematicalSymbols.functionsAndSignums;
|
|
for (final char operator : operators) {
|
|
if (lcf.ch == operator) {
|
|
isNegativeOfNumber = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (isNegativeOfNumber) {
|
|
process.add(new FeatureChar(MathematicalSymbols.MINUS));
|
|
} else {
|
|
process.add(new FeatureChar(MathematicalSymbols.SUBTRACTION));
|
|
}
|
|
} else {
|
|
process.add(f);
|
|
}
|
|
lastFeature = f;
|
|
}
|
|
return process;
|
|
}
|
|
|
|
/**
|
|
* Make powers [12][^[15]] => [[12]^[15]]
|
|
*
|
|
* @param context
|
|
* @param features
|
|
* @return
|
|
* @throws Error
|
|
*/
|
|
private static ObjectArrayList<Feature> makePowers(final MathContext context,
|
|
final ObjectArrayList<Feature> features) throws Error {
|
|
final ObjectArrayList<Feature> process = new ObjectArrayList<>();
|
|
|
|
Feature lastFeature = null;
|
|
for (final Feature f : features) {
|
|
if (f instanceof FeaturePowerChar) {
|
|
if (lastFeature != null) {
|
|
process.set(process.size() - 1, new FeaturePower(lastFeature.toFunction(context), ((FeaturePowerChar) f).getChild()));
|
|
} else {
|
|
process.add(f);
|
|
}
|
|
} else {
|
|
process.add(f);
|
|
}
|
|
lastFeature = f;
|
|
}
|
|
|
|
return process;
|
|
}
|
|
}
|