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> parseOutput(final MathContext context, final ObjectArrayList resultExpressions) throws Error { final ObjectArrayList> result = new ObjectArrayList<>(); for (final Function resultExpression : resultExpressions) { final ObjectArrayList 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 features) throws Error { features = MathParser.fixFeatures(context, features); ObjectArrayList 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 fixStack(final MathContext context, final ObjectArrayList 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 fixFeatures(final MathContext context, ObjectArrayList 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 convertFunctionChars(final MathContext context, final ObjectArrayList features) throws Error { final ObjectArrayList 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 makeNumbers(final MathContext context, final ObjectArrayList features) { final ObjectArrayList 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 fixMinuses(final MathContext context, final ObjectArrayList features) throws Error { final ObjectArrayList 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 makePowers(final MathContext context, final ObjectArrayList features) throws Error { final ObjectArrayList 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; } }