├── .gitignore ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── src ├── main │ └── java │ │ └── io │ │ └── sancta │ │ └── sanctorum │ │ └── calculator │ │ ├── lexer │ │ ├── LexemeRule.java │ │ ├── LexemeType.java │ │ ├── Lexeme.java │ │ └── Lexer.java │ │ ├── dom │ │ ├── XExpression.java │ │ ├── ExpressionVisitor.java │ │ ├── Expression.java │ │ ├── FunctionExpression.java │ │ ├── NumberExpression.java │ │ ├── BinaryExpression.java │ │ ├── ExpressionBrackets.java │ │ ├── BooleanExpression.java │ │ ├── FunctionType.java │ │ ├── BinaryOperator.java │ │ ├── processing │ │ │ ├── ExpressionRebuildVisitor.java │ │ │ ├── CalculateExpressionVisitor.java │ │ │ └── StringExpressionVisitor.java │ │ └── BooleanOperator.java │ │ ├── parser │ │ ├── sequence │ │ │ ├── EmptySequence.java │ │ │ ├── DoubleSequence.java │ │ │ ├── BracketSequence.java │ │ │ ├── Sequence.java │ │ │ ├── BooleanSequence.java │ │ │ ├── FunctionSequence.java │ │ │ ├── BinarySequence.java │ │ │ └── ParametersSequence.java │ │ ├── ParserException.java │ │ ├── alternative │ │ │ ├── ParametersAlternative.java │ │ │ ├── Alternative.java │ │ │ └── RootAlternative.java │ │ ├── ParseRule.java │ │ ├── terminal │ │ │ ├── XTerminal.java │ │ │ ├── IntegerTerminal.java │ │ │ └── Terminal.java │ │ └── Parser.java │ │ └── Console.java └── test │ └── java │ └── io │ └── sancta │ └── sanctorum │ └── calculator │ ├── parser │ └── ParserTest.java │ ├── lexer │ └── LexerTest.java │ └── dom │ ├── CalculateExpressionVisitorTest.java │ └── StringExpressionVisitorTest.java ├── README.md ├── LICENSE.md ├── pom.xml ├── .github └── workflows │ └── jekyll-gh-pages.yml ├── mvnw.cmd └── mvnw /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .idea 3 | *.iml 4 | *.class 5 | /target/ -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oldSorcerer/Calculator/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/lexer/LexemeRule.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.lexer; 2 | 3 | public interface LexemeRule { 4 | 5 | LexemeType getType(); 6 | 7 | int getSymbolCount(String text); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/dom/XExpression.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.dom; 2 | 3 | public class XExpression extends Expression { 4 | 5 | @Override 6 | public T accept(ExpressionVisitor visitor) { 7 | return visitor.visit(this); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/parser/sequence/EmptySequence.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.parser.sequence; 2 | 3 | public class EmptySequence extends Sequence { 4 | 5 | @Override 6 | protected Object collect(Object[] results) { 7 | return new Object(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/lexer/LexemeType.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.lexer; 2 | 3 | public enum LexemeType { 4 | Digits, 5 | Dot, 6 | Space, 7 | Operator, 8 | CompareOperator, 9 | OpenBracket, 10 | CloseBracket, 11 | X, 12 | NameFunc, 13 | Comma, 14 | Error 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/lexer/Lexeme.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.lexer; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | @Getter 7 | @AllArgsConstructor 8 | public class Lexeme { 9 | 10 | private final String text; 11 | private final LexemeType type; 12 | private final int position; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/parser/ParserException.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.parser; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class ParserException extends Exception { 7 | 8 | private final int[] position; 9 | 10 | public ParserException(String text, int[] position) { 11 | super(text); 12 | this.position = position; 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/dom/ExpressionVisitor.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.dom; 2 | 3 | public interface ExpressionVisitor { 4 | 5 | T visit(BinaryExpression binaryExpression); 6 | 7 | T visit(BooleanExpression booleanExpression); 8 | 9 | T visit(NumberExpression numberExpression); 10 | 11 | T visit(XExpression xExpression); 12 | 13 | T visit(FunctionExpression functionExpression); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/dom/Expression.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.dom; 2 | 3 | import io.sancta.sanctorum.calculator.dom.processing.StringExpressionVisitor; 4 | 5 | public abstract class Expression { 6 | 7 | public abstract T accept(ExpressionVisitor visitor); 8 | 9 | @Override 10 | public String toString() { 11 | return this.accept(new StringExpressionVisitor()); 12 | } 13 | 14 | public boolean isEmpty(){ 15 | return false; 16 | } 17 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Calculator 2 | 3 | План: 4 | dom 5 | 1. Объетная модель (О.М.) (extends Expression 0) 6 | 2. Вычисление О.М. (CalculateExpressionVisitor) 7 | 3. Преоброзование О.М. в текст (StringExpressionVisitor) 8 | 9 | 4. Лексер текст -> Lex[] 10 | 5. Парсер Lex[] -> О.М. 11 | 6. Обработка ошибок 12 | 7. Поддержка Х 13 | 8. Поддержка мат. функций 14 | 15 | 9. Тернарный оператор - в процессе (? :) 16 | 10. Графики - в процессе 17 | 18 | * text -> lexer -> Lexeme -> parser -> dom -> результат 19 | 20 | * добавить возведение в степень (^), min max -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/parser/alternative/ParametersAlternative.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.parser.alternative; 2 | 3 | import io.sancta.sanctorum.calculator.parser.sequence.ParametersSequence; 4 | 5 | public class ParametersAlternative extends Alternative { 6 | 7 | public ParametersAlternative(RootAlternative root) { 8 | // getAlternatives().add(new BooleanSequence(root)); // if(a > b) 9 | getAlternatives().add(new ParametersSequence(root, this)); 10 | getAlternatives().add(root); 11 | } 12 | } -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/dom/FunctionExpression.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.dom; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | @Getter 10 | public class FunctionExpression extends Expression { 11 | 12 | @Setter 13 | private FunctionType type; 14 | private final List parameters = new ArrayList<>(); 15 | 16 | @Override 17 | public T accept(ExpressionVisitor visitor) { 18 | return visitor.visit(this); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/dom/NumberExpression.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.dom; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class NumberExpression extends Expression { 7 | 8 | private final Number value; 9 | 10 | public NumberExpression(Number value) { 11 | if (value == null) 12 | throw new IllegalArgumentException("Number cannot be Null!"); 13 | this.value = value; 14 | } 15 | 16 | @Override 17 | public T accept(ExpressionVisitor visitor) { 18 | return visitor.visit(this); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/parser/ParseRule.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.parser; 2 | 3 | public abstract class ParseRule { 4 | 5 | public final Object apply(Parser parser) { 6 | if (parser.getRecognized() >= parser.getLexemes().size()) return null; 7 | 8 | int count = parser.getRecognized(); 9 | Object result = applySpecial(parser); 10 | if (result == null) { 11 | parser.setRecognized(count); 12 | } 13 | return result; 14 | } 15 | 16 | protected abstract Object applySpecial(Parser parser); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/parser/terminal/XTerminal.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.parser.terminal; 2 | 3 | import io.sancta.sanctorum.calculator.dom.XExpression; 4 | import io.sancta.sanctorum.calculator.lexer.LexemeType; 5 | import io.sancta.sanctorum.calculator.parser.Parser; 6 | 7 | public class XTerminal extends Terminal { 8 | 9 | public XTerminal() { 10 | super(LexemeType.X); 11 | } 12 | 13 | @Override 14 | protected Object applySpecial(Parser parser) { 15 | return super.applySpecial(parser) == null ? null : new XExpression(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/dom/BinaryExpression.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.dom; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | @Getter 7 | @Setter 8 | public class BinaryExpression extends Expression { 9 | 10 | private Expression left; 11 | private Expression right; 12 | private BinaryOperator operator; 13 | 14 | @Override 15 | public T accept(ExpressionVisitor visitor) { 16 | return visitor.visit(this); 17 | } 18 | 19 | @Override 20 | public boolean isEmpty() { 21 | return left == null || right == null || operator == null; 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/dom/ExpressionBrackets.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.dom; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class ExpressionBrackets extends Expression { 7 | 8 | private final Expression expression; 9 | 10 | public ExpressionBrackets(Expression expression) { 11 | if (expression == null) 12 | throw new IllegalArgumentException("Expression = null"); 13 | this.expression = expression; 14 | } 15 | 16 | @Override 17 | public T accept(ExpressionVisitor visitor) { 18 | return expression.accept(visitor); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/dom/BooleanExpression.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.dom; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | @Getter 7 | @Setter 8 | public class BooleanExpression extends Expression { 9 | 10 | private Expression left; 11 | private Expression right; 12 | private BooleanOperator operator; 13 | 14 | @Override 15 | public T accept(ExpressionVisitor visitor) { 16 | return visitor.visit(this); 17 | } 18 | 19 | @Override 20 | public boolean isEmpty() { 21 | return left == null || right == null || operator == null; 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/parser/terminal/IntegerTerminal.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.parser.terminal; 2 | 3 | import io.sancta.sanctorum.calculator.dom.NumberExpression; 4 | import calculator.lexer.*; 5 | import io.sancta.sanctorum.calculator.parser.Parser; 6 | 7 | public class IntegerTerminal extends Terminal { 8 | 9 | public IntegerTerminal() { 10 | super(LexemeType.Digits); 11 | } 12 | 13 | @Override 14 | protected Object applySpecial(Parser parser) { 15 | Lexeme lex = (Lexeme) super.applySpecial(parser); 16 | return lex != null ? new NumberExpression(Integer.parseInt(lex.getText())) : null; 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/dom/FunctionType.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.dom; 2 | 3 | import static java.lang.Math.*; 4 | 5 | import java.util.function.Function; 6 | 7 | public enum FunctionType { 8 | Sqrt(x -> sqrt(x[0])), 9 | Log(x -> log(x[0]) / log(x[1])), 10 | Sin(x -> sin(x[0])), 11 | Cos(x -> cos(x[0])), 12 | Exp(x -> exp(x[0])), 13 | Abs(x -> abs(x[0])), 14 | Pi(x -> PI), 15 | E(x -> Math.E), 16 | If(x -> x[0] != 0 ? x[1] : x[2]); 17 | 18 | private final Function calc; 19 | 20 | FunctionType(Function function) { 21 | calc = function; 22 | } 23 | 24 | public Double calculate(double[] x) { 25 | return calc.apply(x); 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/parser/alternative/Alternative.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.parser.alternative; 2 | 3 | import io.sancta.sanctorum.calculator.parser.ParseRule; 4 | import io.sancta.sanctorum.calculator.parser.Parser; 5 | import lombok.Getter; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | @Getter 11 | public class Alternative extends ParseRule { 12 | 13 | private final List alternatives = new ArrayList<>(); 14 | 15 | @Override 16 | protected Object applySpecial(Parser parser) { 17 | for (ParseRule alt : alternatives) { 18 | Object result = alt.apply(parser); 19 | 20 | if (result != null) 21 | return result; 22 | } 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/parser/sequence/DoubleSequence.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.parser.sequence; 2 | 3 | import io.sancta.sanctorum.calculator.dom.NumberExpression; 4 | import calculator.lexer.*; 5 | import io.sancta.sanctorum.calculator.parser.terminal.Terminal; 6 | 7 | public class DoubleSequence extends Sequence { 8 | 9 | public DoubleSequence() { 10 | getMembers().add(new Terminal(LexemeType.Digits)); 11 | getMembers().add(new Terminal(LexemeType.Dot)); 12 | getMembers().add(new Terminal(LexemeType.Digits)); 13 | } 14 | 15 | @Override 16 | protected Object collect(Object[] results) { 17 | return new NumberExpression(Double.parseDouble( 18 | ((Lexeme) results[0]).getText() + 19 | "." + 20 | ((Lexeme) results[2]).getText()) 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/parser/sequence/BracketSequence.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.parser.sequence; 2 | 3 | import io.sancta.sanctorum.calculator.dom.Expression; 4 | import io.sancta.sanctorum.calculator.dom.ExpressionBrackets; 5 | import io.sancta.sanctorum.calculator.parser.alternative.RootAlternative; 6 | import io.sancta.sanctorum.calculator.lexer.LexemeType; 7 | import io.sancta.sanctorum.calculator.parser.terminal.Terminal; 8 | 9 | public class BracketSequence extends Sequence { 10 | 11 | public BracketSequence(RootAlternative root) { //(x + y) 12 | getMembers().add(new Terminal(LexemeType.OpenBracket)); 13 | getMembers().add(root); 14 | getMembers().add(new Terminal(LexemeType.CloseBracket)); 15 | } 16 | 17 | @Override 18 | protected Object collect(Object[] results) { 19 | return new ExpressionBrackets((Expression) results[1]); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/parser/sequence/Sequence.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.parser.sequence; 2 | 3 | import io.sancta.sanctorum.calculator.parser.ParseRule; 4 | import io.sancta.sanctorum.calculator.parser.Parser; 5 | import lombok.Getter; 6 | 7 | import java.util.ArrayList; 8 | 9 | @Getter 10 | public abstract class Sequence extends ParseRule { 11 | 12 | private final ArrayList members = new ArrayList<>(); // звенья последовательности 13 | 14 | @Override 15 | protected Object applySpecial(Parser parser) { 16 | Object[] results = new Object[members.size()]; 17 | 18 | for (int i = 0; i < results.length; i++) { 19 | results[i] = members.get(i).apply(parser); 20 | if (results[i] == null) 21 | return null; 22 | } 23 | return collect(results); 24 | } 25 | 26 | protected abstract Object collect(Object[] results); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/parser/terminal/Terminal.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.parser.terminal; 2 | 3 | import io.sancta.sanctorum.calculator.lexer.Lexeme; 4 | import io.sancta.sanctorum.calculator.lexer.LexemeType; 5 | import io.sancta.sanctorum.calculator.parser.ParseRule; 6 | import io.sancta.sanctorum.calculator.parser.Parser; 7 | import lombok.AllArgsConstructor; 8 | 9 | @AllArgsConstructor 10 | public class Terminal extends ParseRule { 11 | 12 | private final LexemeType lexType; 13 | 14 | @Override 15 | protected Object applySpecial(Parser parser) { 16 | if (parser.getLexemes().get(parser.getRecognized()).getType() == lexType) { 17 | Lexeme lexeme = parser.getLexemes().get(parser.getRecognized()); 18 | parser.setRecognized(parser.getRecognized() + 1); 19 | return lexeme; 20 | } 21 | return null; 22 | } 23 | } 24 | // return parser.getLexemes().get(parser.recognized).getType() == lexType ? parser.getLexemes().get(parser.recognized++) : null; 25 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/parser/alternative/RootAlternative.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.parser.alternative; 2 | 3 | import io.sancta.sanctorum.calculator.parser.terminal.XTerminal; 4 | import io.sancta.sanctorum.calculator.parser.terminal.IntegerTerminal; 5 | import calculator.parser.sequence.*; 6 | 7 | public class RootAlternative extends Alternative { 8 | 9 | public RootAlternative(boolean isLeft) { 10 | this(isLeft, null); 11 | } 12 | 13 | public RootAlternative(boolean isLeft, RootAlternative root) { 14 | if (!isLeft) { 15 | getAlternatives().add(new BinarySequence(this)); // a + c 16 | } 17 | getAlternatives().add(new DoubleSequence()); //3.14 18 | getAlternatives().add(new IntegerTerminal()); //55444 19 | getAlternatives().add(new XTerminal()); // x 20 | getAlternatives().add(new BracketSequence(isLeft && root != null ? root : this)); // ( что-то ) 21 | getAlternatives().add(new FunctionSequence(root != null ? root : this)); // sin() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.2/apache-maven-3.9.2-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar 19 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/dom/BinaryOperator.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.dom; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | @AllArgsConstructor 7 | public enum BinaryOperator { 8 | 9 | Plus (0, true, '+', Double::sum), 10 | Minus (0, false, '-', (x, y) -> x - y), 11 | Mul (1, true, '*', (x, y) -> x * y), 12 | Div (1, false, '/', (x, y) -> x / y); 13 | 14 | @Getter 15 | private final int priority; 16 | @Getter 17 | private final boolean commutative; 18 | private final char symbol; 19 | private final java.util.function.BinaryOperator calc; 20 | 21 | public static BinaryOperator fromChar(char symbol) { 22 | return switch (symbol) { 23 | case '+' -> Plus; 24 | case '-' -> Minus; 25 | case '*' -> Mul; 26 | case '/' -> Div; 27 | default -> null; 28 | }; 29 | } 30 | 31 | public Double calculate(Double x, Double y) { 32 | return calc.apply(x, y); 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return Character.toString(this.symbol); 38 | } 39 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.sancta-sanctorum 8 | calculator 9 | 1.0 10 | 11 | 12 | 13 | 14 | org.junit.jupiter 15 | junit-jupiter-api 16 | 5.9.0 17 | test 18 | 19 | 20 | 21 | org.projectlombok 22 | lombok 23 | 1.18.24 24 | provided 25 | 26 | 27 | 28 | 29 | 30 | 31 | 17 32 | 17 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/Console.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator; 2 | 3 | import io.sancta.sanctorum.calculator.lexer.Lexer; 4 | import io.sancta.sanctorum.calculator.dom.processing.CalculateExpressionVisitor; 5 | import io.sancta.sanctorum.calculator.parser.Parser; 6 | import io.sancta.sanctorum.calculator.parser.ParserException; 7 | 8 | import java.io.*; 9 | 10 | public class Console { 11 | public static void main(String[] args) throws IOException { 12 | String text = ""; 13 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) { 14 | System.out.println("Введите выражения:"); 15 | double x = 0; 16 | Parser p = new Parser(Lexer.run(text = reader.readLine())); 17 | if (text.contains("x")) { 18 | System.out.println("Введите x:"); 19 | x = Double.parseDouble(reader.readLine()); 20 | } 21 | System.out.println(p.parse().accept(new CalculateExpressionVisitor(x))); 22 | } catch (ParserException exception) { 23 | System.out.println(exception + ""); 24 | for (int position : exception.getPosition()) 25 | System.out.println(text.charAt(position)); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/parser/sequence/BooleanSequence.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.parser.sequence; 2 | 3 | import io.sancta.sanctorum.calculator.dom.BooleanExpression; 4 | import io.sancta.sanctorum.calculator.dom.BooleanOperator; 5 | import io.sancta.sanctorum.calculator.dom.Expression; 6 | import io.sancta.sanctorum.calculator.parser.alternative.RootAlternative; 7 | import io.sancta.sanctorum.calculator.lexer.Lexeme; 8 | import io.sancta.sanctorum.calculator.lexer.LexemeType; 9 | import io.sancta.sanctorum.calculator.parser.terminal.Terminal; 10 | 11 | public class BooleanSequence extends Sequence { 12 | 13 | public BooleanSequence(RootAlternative root) { 14 | getMembers().add(root); 15 | getMembers().add(new Terminal(LexemeType.CompareOperator)); 16 | getMembers().add(root); 17 | } 18 | 19 | @Override 20 | protected Object collect(Object[] results) { 21 | BooleanExpression booleanExpression = new BooleanExpression(); 22 | 23 | booleanExpression.setLeft((Expression) results[0]); 24 | booleanExpression.setOperator(BooleanOperator.fromChar( ((Lexeme)results[1]).getText().charAt(0)) ); 25 | booleanExpression.setRight((Expression) results[2]); 26 | 27 | 28 | return booleanExpression; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/dom/processing/ExpressionRebuildVisitor.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.dom.processing; 2 | 3 | import calculator.dom.*; 4 | 5 | public class ExpressionRebuildVisitor implements ExpressionVisitor { 6 | 7 | @Override 8 | public Expression visit(BinaryExpression binaryExpression) { 9 | binaryExpression.setLeft(binaryExpression.getLeft().accept(this)); 10 | binaryExpression.setRight(binaryExpression.getRight().accept(this)); 11 | return binaryExpression; 12 | } 13 | 14 | @Override 15 | public Expression visit(BooleanExpression booleanExpression) { 16 | booleanExpression.setLeft(booleanExpression.getLeft().accept(this)); 17 | booleanExpression.setRight(booleanExpression.getRight().accept(this)); 18 | return booleanExpression; 19 | } 20 | 21 | @Override 22 | public Expression visit(NumberExpression numberExpression) { 23 | return numberExpression; 24 | } 25 | 26 | @Override 27 | public Expression visit(XExpression xExpression) { 28 | return xExpression; 29 | } 30 | 31 | @Override 32 | public Expression visit(FunctionExpression function) { 33 | function.getParameters().replaceAll(expression -> expression.accept(this)); 34 | return function; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/dom/BooleanOperator.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.dom; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | import java.util.Objects; 7 | import java.util.function.BiPredicate; 8 | 9 | @AllArgsConstructor 10 | public enum BooleanOperator { 11 | 12 | Greater (false, '>', (x, y) -> x > y), 13 | Less (false, '<', (x, y) -> x < y), 14 | MoreEquals (false, '≥', (x, y) -> x >= y), 15 | LessEquals (false, '≤', (x, y) -> x <= y), 16 | Equals (true, '=', Objects::equals), 17 | NotEquals (true, '≠', (x, y) -> !Objects.equals(x, y)); 18 | 19 | @Getter 20 | private final boolean commutative; 21 | private final char symbol; 22 | private final BiPredicate calc; 23 | 24 | public static BooleanOperator fromChar(char symbol) { 25 | return switch (symbol) { 26 | case '>' -> Greater; 27 | case '<' -> Less; 28 | case '≥' -> MoreEquals; 29 | case '≤' -> LessEquals; 30 | case '=' -> Equals; 31 | case '≠' -> NotEquals; 32 | default -> null; 33 | }; 34 | } 35 | 36 | public boolean calculate(Double x, Double y) { 37 | return calc.test(x, y); 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return Character.toString(this.symbol); 43 | } 44 | } -------------------------------------------------------------------------------- /.github/workflows/jekyll-gh-pages.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a Jekyll site to GitHub Pages 2 | name: Deploy Jekyll with GitHub Pages dependencies preinstalled 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["master"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: false 23 | 24 | jobs: 25 | # Build job 26 | build: 27 | runs-on: ubuntu-latest 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v3 31 | - name: Setup Pages 32 | uses: actions/configure-pages@v3 33 | - name: Build with Jekyll 34 | uses: actions/jekyll-build-pages@v1 35 | with: 36 | source: ./ 37 | destination: ./_site 38 | - name: Upload artifact 39 | uses: actions/upload-pages-artifact@v2 40 | 41 | # Deployment job 42 | deploy: 43 | environment: 44 | name: github-pages 45 | url: ${{ steps.deployment.outputs.page_url }} 46 | runs-on: ubuntu-latest 47 | needs: build 48 | steps: 49 | - name: Deploy to GitHub Pages 50 | id: deployment 51 | uses: actions/deploy-pages@v2 52 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/dom/processing/CalculateExpressionVisitor.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.dom.processing; 2 | 3 | import io.sancta.sanctorum.calculator.dom.*; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Getter 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | public class CalculateExpressionVisitor implements ExpressionVisitor { 12 | 13 | private double x; 14 | 15 | @Override 16 | public Double visit(BinaryExpression binary) { 17 | if (binary.isEmpty()) 18 | return null; 19 | return binary.getOperator().calculate(binary.getLeft().accept(this), binary.getRight().accept(this)); 20 | } 21 | 22 | @Override 23 | public Double visit(BooleanExpression booleanExpression) { 24 | if (booleanExpression.isEmpty()) 25 | return null; 26 | return booleanExpression 27 | .getOperator() 28 | .calculate(booleanExpression.getLeft().accept(this), booleanExpression.getRight().accept(this)) ? 1.0 : 0; 29 | 30 | } 31 | 32 | @Override 33 | public Double visit(NumberExpression number) { 34 | return number.getValue().doubleValue(); 35 | } 36 | 37 | @Override 38 | public Double visit(XExpression xExpression) { 39 | return x; 40 | } 41 | 42 | @Override 43 | public Double visit(FunctionExpression function) { 44 | double[] x = new double[function.getParameters().size()]; 45 | for (int i = 0; i < x.length; i++) 46 | x[i] = function.getParameters().get(i).accept(this); 47 | 48 | return function.getType().calculate(x); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/io/sancta/sanctorum/calculator/parser/ParserTest.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.parser; 2 | 3 | import io.sancta.sanctorum.calculator.lexer.Lexer; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.junit.jupiter.api.Assertions.*; 7 | 8 | class ParserTest { 9 | 10 | void check(String text) { 11 | try { 12 | Parser parser = new Parser(Lexer.run(text)); 13 | 14 | assertEquals(text, parser.parse().toString()); 15 | } catch (ParserException ex) { 16 | assertNull(ex); 17 | } 18 | } 19 | 20 | @Test 21 | void integer() { 22 | check("215"); 23 | } 24 | 25 | @Test 26 | void doubleValue() { 27 | check("18.45"); 28 | } 29 | 30 | @Test 31 | void intPlusInt() { 32 | check("12 + 5"); 33 | } 34 | 35 | @Test 36 | void leftBrackets() { 37 | check("(2 + 3) * 5.7"); 38 | } 39 | 40 | @Test 41 | void leftMultiply() { 42 | check("2 * 5.7 + 3"); 43 | } 44 | 45 | 46 | @Test 47 | void rightBrackets() { 48 | check("2 * (5.7 + 3)"); 49 | } 50 | 51 | @Test 52 | void doubleBrackets() { 53 | check("2 * (2.5 + 9) / (5.7 - 8)"); 54 | } 55 | 56 | @Test 57 | void doubleBrackets2() throws ParserException { 58 | Parser parser = new Parser(Lexer.run("2 * ((2.5 + 9)) / (((5.7 - 8)))")); 59 | 60 | assertEquals("2 * (2.5 + 9) / (5.7 - 8)", parser.parse().toString()); 61 | } 62 | 63 | @Test 64 | void X() { 65 | check("x * x + 85"); 66 | } 67 | 68 | @Test 69 | void Fun() { 70 | check("sin(x) + log(x + 5, 3.4)"); 71 | } 72 | 73 | @Test 74 | void Pi() { 75 | check("pi()"); 76 | } 77 | 78 | @Test 79 | void If() { 80 | check("if(2 * 2 = 5, 3 + 7, 4 * 8)"); 81 | } 82 | 83 | @Test 84 | void If2() { 85 | check("if(5 > 2 + 2)"); 86 | } 87 | 88 | } -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/parser/sequence/FunctionSequence.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.parser.sequence; 2 | 3 | import io.sancta.sanctorum.calculator.dom.Expression; 4 | import io.sancta.sanctorum.calculator.dom.FunctionExpression; 5 | import io.sancta.sanctorum.calculator.dom.FunctionType; 6 | import io.sancta.sanctorum.calculator.parser.alternative.Alternative; 7 | import io.sancta.sanctorum.calculator.parser.alternative.ParametersAlternative; 8 | import io.sancta.sanctorum.calculator.parser.alternative.RootAlternative; 9 | import io.sancta.sanctorum.calculator.lexer.Lexeme; 10 | import io.sancta.sanctorum.calculator.lexer.LexemeType; 11 | import io.sancta.sanctorum.calculator.parser.terminal.Terminal; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | public class FunctionSequence extends Sequence { 17 | 18 | public FunctionSequence(RootAlternative root) { 19 | getMembers().add(new Terminal(LexemeType.NameFunc)); //0 20 | getMembers().add(new Terminal(LexemeType.OpenBracket)); // 1 ( 21 | Alternative parameters = new Alternative(); 22 | parameters.getAlternatives().add(new ParametersAlternative(root)); // name(a, b, c) 23 | parameters.getAlternatives().add(new EmptySequence()); // name() 24 | getMembers().add(parameters); //2 25 | getMembers().add(new Terminal(LexemeType.CloseBracket)); //3 ) 26 | } 27 | 28 | @Override 29 | protected Object collect(Object[] results) { 30 | FunctionExpression result = new FunctionExpression(); 31 | 32 | for (FunctionType fun : FunctionType.values()) { 33 | if (((Lexeme) results[0]).getText().equals(fun.toString().toLowerCase())) { 34 | result.setType(fun); 35 | break; 36 | } 37 | } 38 | 39 | if (results[2] instanceof Expression expression) 40 | result.getParameters().add(expression); 41 | else if (results[2].getClass().equals(ArrayList.class)) 42 | result.getParameters().addAll((List) results[2]); 43 | 44 | return result; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/parser/sequence/BinarySequence.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.parser.sequence; 2 | 3 | import io.sancta.sanctorum.calculator.dom.BinaryExpression; 4 | import io.sancta.sanctorum.calculator.dom.BinaryOperator; 5 | import io.sancta.sanctorum.calculator.dom.Expression; 6 | import io.sancta.sanctorum.calculator.parser.alternative.RootAlternative; 7 | import io.sancta.sanctorum.calculator.parser.terminal.Terminal; 8 | import io.sancta.sanctorum.calculator.lexer.Lexeme; 9 | import io.sancta.sanctorum.calculator.lexer.LexemeType; 10 | 11 | public class BinarySequence extends Sequence { 12 | 13 | public BinarySequence(RootAlternative root) { // {a} + {{b} + {c}} 14 | getMembers().add(new RootAlternative(true, root)); 15 | getMembers().add(new Terminal(LexemeType.Operator)); 16 | getMembers().add(root); 17 | } 18 | 19 | @Override 20 | protected Object collect(Object[] results) { 21 | BinaryExpression result = new BinaryExpression(); 22 | result.setLeft((Expression) results[0]); 23 | result.setOperator(BinaryOperator.fromChar(((Lexeme) results[1]).getText().charAt(0))); 24 | result.setRight((Expression) results[2]); 25 | 26 | if (result.getRight().getClass().equals(BinaryExpression.class)) { 27 | BinaryExpression rightBinary = (BinaryExpression) result.getRight(); 28 | 29 | if (result.getOperator().getPriority() > rightBinary.getOperator().getPriority() // a * b + c 30 | || (result.getOperator().getPriority() == rightBinary.getOperator().getPriority() // a - b + c 31 | && !result.getOperator().isCommutative())) { 32 | BinaryExpression left = new BinaryExpression(); 33 | left.setLeft(result.getLeft()); 34 | left.setOperator(result.getOperator()); 35 | left.setRight(rightBinary.getLeft()); 36 | result.setOperator(rightBinary.getOperator()); 37 | result.setLeft(left); 38 | result.setRight(rightBinary.getRight()); 39 | } 40 | } 41 | return result; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/parser/Parser.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.parser; 2 | 3 | import io.sancta.sanctorum.calculator.dom.Expression; 4 | import io.sancta.sanctorum.calculator.dom.processing.ExpressionRebuildVisitor; 5 | import io.sancta.sanctorum.calculator.parser.alternative.Alternative; 6 | import io.sancta.sanctorum.calculator.parser.alternative.RootAlternative; 7 | import io.sancta.sanctorum.calculator.lexer.Lexeme; 8 | import io.sancta.sanctorum.calculator.lexer.LexemeType; 9 | import lombok.Getter; 10 | import lombok.Setter; 11 | 12 | import java.util.ArrayList; 13 | import java.util.LinkedList; 14 | import java.util.List; 15 | 16 | public class Parser { 17 | 18 | @Getter 19 | @Setter 20 | private int recognized; // колличество распознаных лексем! 21 | 22 | @Getter 23 | private final List lexemes; 24 | 25 | public Parser(List lexemes) throws ParserException { 26 | if (lexemes == null) throw new IllegalArgumentException("Lexes cannot be Null!"); 27 | 28 | this.lexemes = new ArrayList<>(); 29 | LinkedList position = new LinkedList<>(); 30 | 31 | for (Lexeme lex : lexemes) { 32 | if (lex.getType().equals(LexemeType.Error)) position.add(lex.getPosition()); 33 | if (!lex.getType().equals(LexemeType.Space)) this.lexemes.add(lex); 34 | } 35 | if (!position.isEmpty()) { 36 | int[] pos2 = new int[position.size()]; 37 | int i = 0; 38 | for (Integer x : position) 39 | pos2[i++] = x; 40 | throw new ParserException("Invalid character in expression!", pos2); 41 | } 42 | } 43 | 44 | public Expression parse() throws ParserException { 45 | Alternative root = new RootAlternative(false); 46 | Expression result = (Expression) root.apply(this); 47 | 48 | if (result == null) { 49 | throw new ParserException("Failed to parse", new int[0]); 50 | } 51 | 52 | if (recognized < lexemes.size()) { 53 | throw new ParserException("Failed to parse whole", 54 | new int[]{lexemes.get(recognized).getPosition()}); 55 | } 56 | 57 | return result.accept(new ExpressionRebuildVisitor()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/parser/sequence/ParametersSequence.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.parser.sequence; 2 | 3 | import io.sancta.sanctorum.calculator.dom.Expression; 4 | import io.sancta.sanctorum.calculator.parser.alternative.Alternative; 5 | import io.sancta.sanctorum.calculator.parser.alternative.ParametersAlternative; 6 | import io.sancta.sanctorum.calculator.parser.alternative.RootAlternative; 7 | import io.sancta.sanctorum.calculator.parser.terminal.Terminal; 8 | import io.sancta.sanctorum.calculator.lexer.LexemeType; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | public class ParametersSequence extends Sequence { 14 | 15 | public ParametersSequence(RootAlternative root, ParametersAlternative tail) { 16 | Alternative parameters = new Alternative(); 17 | parameters.getAlternatives().add(new BooleanSequence(root)); // if(a > b) 18 | parameters.getAlternatives().add(root); 19 | getMembers().add(parameters); 20 | getMembers().add(new Terminal(LexemeType.Comma)); 21 | getMembers().add(tail); 22 | } 23 | 24 | @Override 25 | protected Object collect(Object[] results) { 26 | // 27 | // LinkedList result = new LinkedList<>(); // из за хвоста 28 | // result.add((Expression) results[0]); 29 | // 30 | // if (results[2] instanceof Expression expression) 31 | // result.add(expression); 32 | // else if (results[2].getClass().equals(LinkedList.class)) 33 | // result.addAll((LinkedList) results[2]); 34 | // return result; 35 | 36 | if (results[2] instanceof Expression expression) { 37 | 38 | List list = new ArrayList<>(); 39 | list.add((Expression) results[0]); 40 | list.add(expression); 41 | return list; 42 | // return new ArrayList<>(Arrays.asList((Expression) results[0], expression)); 43 | 44 | // return Arrays.asList((Expression) results[0], expression); 45 | } else if (results[2].getClass().equals(ArrayList.class)) { 46 | List list = (List) results[2]; 47 | list.add(0, (Expression) results[0]); 48 | return list; 49 | } 50 | return null; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/dom/processing/StringExpressionVisitor.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.dom.processing; 2 | 3 | import calculator.dom.*; 4 | 5 | import java.util.ArrayList; 6 | 7 | public class StringExpressionVisitor implements ExpressionVisitor { 8 | 9 | @Override 10 | public String visit(BinaryExpression binaryExpression) { 11 | 12 | if (binaryExpression.isEmpty()) 13 | return ""; 14 | 15 | BinaryExpression left = null; 16 | BinaryExpression right = null; 17 | 18 | if (binaryExpression.getLeft().getClass().equals(BinaryExpression.class)) { 19 | left = (BinaryExpression) binaryExpression.getLeft(); 20 | } 21 | 22 | if (binaryExpression.getRight().getClass().equals(BinaryExpression.class)) { 23 | right = (BinaryExpression) binaryExpression.getRight(); 24 | } 25 | 26 | String leftstr = binaryExpression.getLeft().accept(this); 27 | String rightstr = binaryExpression.getRight().accept(this); 28 | 29 | int priority = binaryExpression.getOperator().getPriority(); 30 | 31 | if (left != null && left.getOperator() != null 32 | && left.getOperator().getPriority() < priority) { 33 | 34 | leftstr = "(" + leftstr + ")"; 35 | } 36 | if (right != null && right.getOperator() != null 37 | && (right.getOperator().getPriority() < priority 38 | || (right.getOperator().getPriority() == priority 39 | && !binaryExpression.getOperator().isCommutative()))) { 40 | 41 | rightstr = "(" + rightstr + ")"; 42 | } 43 | 44 | return (leftstr + " " + binaryExpression.getOperator() + " " + rightstr); 45 | } 46 | 47 | @Override 48 | public String visit(BooleanExpression booleanExpression) { 49 | if (booleanExpression.isEmpty()) 50 | return ""; 51 | 52 | String left = booleanExpression.getLeft().accept(this); 53 | String right = booleanExpression.getRight().accept(this); 54 | 55 | return left + " " + booleanExpression.getOperator() + " " + right; 56 | } 57 | 58 | @Override 59 | public String visit(NumberExpression numberExpression) { 60 | return numberExpression.getValue().toString(); 61 | } 62 | 63 | @Override 64 | public String visit(XExpression xExpression) { 65 | return "x"; 66 | } 67 | 68 | @Override 69 | public String visit(FunctionExpression functionExpression) { 70 | ArrayList parameters = new ArrayList<>(functionExpression.getParameters().size()); 71 | 72 | for (Expression p : functionExpression.getParameters()) 73 | parameters.add(p.accept(this)); 74 | return functionExpression.getType().toString().toLowerCase() + "(" + String.join(", ", parameters) + ")"; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/test/java/io/sancta/sanctorum/calculator/lexer/LexerTest.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.lexer; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.util.List; 6 | 7 | import static org.junit.jupiter.api.Assertions.*; 8 | 9 | class LexerTest { 10 | @Test 11 | void singleInt() { 12 | List lexes = Lexer.run("38"); 13 | 14 | assertEquals(1, lexes.size()); 15 | assertEquals("38", lexes.get(0).getText()); 16 | assertEquals(LexemeType.Digits, lexes.get(0).getType()); 17 | } 18 | 19 | @Test 20 | void singleFloat() { 21 | List lexes = Lexer.run("18.3"); 22 | 23 | assertEquals(3, lexes.size()); 24 | assertEquals("18", lexes.get(0).getText()); 25 | assertEquals(LexemeType.Digits, lexes.get(0).getType()); 26 | assertEquals(LexemeType.Dot, lexes.get(1).getType()); 27 | assertEquals("3", lexes.get(2).getText()); 28 | assertEquals(LexemeType.Digits, lexes.get(2).getType()); 29 | } 30 | 31 | @Test 32 | void numberError() { 33 | List lexes = Lexer.run("18.3zz33"); 34 | 35 | assertEquals(6, lexes.size()); 36 | assertEquals(LexemeType.Error, lexes.get(3).getType()); 37 | assertEquals(LexemeType.Error, lexes.get(4).getType()); 38 | assertEquals("33", lexes.get(5).getText()); 39 | assertEquals(LexemeType.Digits, lexes.get(5).getType()); 40 | } 41 | 42 | @Test 43 | void space() { 44 | List lexes = Lexer.run("18 + 4"); 45 | 46 | assertEquals(5, lexes.size()); 47 | assertEquals("18", lexes.get(0).getText()); 48 | assertEquals(LexemeType.Digits, lexes.get(0).getType()); 49 | assertEquals(LexemeType.Space, lexes.get(1).getType()); 50 | assertEquals(LexemeType.Operator, lexes.get(2).getType()); 51 | assertEquals(LexemeType.Space, lexes.get(3).getType()); 52 | assertEquals("4", lexes.get(4).getText()); 53 | assertEquals(LexemeType.Digits, lexes.get(4).getType()); 54 | } 55 | 56 | @Test 57 | void bracket() { 58 | List lexes = Lexer.run("15*@(19.8-3.3)"); 59 | 60 | assertEquals(12, lexes.size()); 61 | assertEquals("15", lexes.get(0).getText()); 62 | assertEquals(LexemeType.Digits, lexes.get(0).getType()); 63 | assertEquals(LexemeType.Operator, lexes.get(1).getType()); 64 | assertEquals(LexemeType.Error, lexes.get(2).getType()); 65 | assertEquals(LexemeType.OpenBracket, lexes.get(3).getType()); 66 | assertEquals("19", lexes.get(4).getText()); 67 | assertEquals(LexemeType.Digits, lexes.get(4).getType()); 68 | assertEquals(LexemeType.Dot, lexes.get(5).getType()); 69 | assertEquals("8", lexes.get(6).getText()); 70 | assertEquals(LexemeType.Digits, lexes.get(6).getType()); 71 | assertEquals(LexemeType.Operator, lexes.get(7).getType()); 72 | assertEquals("3", lexes.get(8).getText()); 73 | assertEquals(LexemeType.Digits, lexes.get(8).getType()); 74 | assertEquals(LexemeType.Dot, lexes.get(9).getType()); 75 | assertEquals("3", lexes.get(10).getText()); 76 | assertEquals(LexemeType.Digits, lexes.get(10).getType()); 77 | assertEquals(LexemeType.CloseBracket, lexes.get(11).getType()); 78 | 79 | } 80 | 81 | @Test 82 | void X() { 83 | List lexes = Lexer.run("x * x"); 84 | 85 | assertEquals(5, lexes.size()); 86 | assertEquals(LexemeType.X, lexes.get(0).getType()); 87 | assertEquals(LexemeType.Space, lexes.get(1).getType()); 88 | assertEquals(LexemeType.Operator, lexes.get(2).getType()); 89 | assertEquals(LexemeType.Space, lexes.get(3).getType()); 90 | assertEquals(LexemeType.X, lexes.get(4).getType()); 91 | } 92 | 93 | @Test 94 | void Fun() { 95 | List lexes = Lexer.run("sin(x) + cos(x)"); 96 | 97 | assertEquals(11, lexes.size()); 98 | assertEquals(LexemeType.NameFunc, lexes.get(0).getType()); 99 | assertEquals("sin", lexes.get(0).getText()); 100 | assertEquals(LexemeType.OpenBracket, lexes.get(1).getType()); 101 | assertEquals(LexemeType.X, lexes.get(2).getType()); 102 | assertEquals(LexemeType.CloseBracket, lexes.get(3).getType()); 103 | assertEquals(LexemeType.Space, lexes.get(4).getType()); 104 | assertEquals(LexemeType.Operator, lexes.get(5).getType()); 105 | assertEquals(LexemeType.Space, lexes.get(6).getType()); 106 | assertEquals(LexemeType.NameFunc, lexes.get(7).getType()); 107 | assertEquals("cos", lexes.get(7).getText()); 108 | assertEquals(LexemeType.OpenBracket, lexes.get(8).getType()); 109 | assertEquals(LexemeType.X, lexes.get(9).getType()); 110 | assertEquals(LexemeType.CloseBracket, lexes.get(10).getType()); 111 | 112 | } 113 | 114 | @Test 115 | void If() { 116 | List lexes = Lexer.run("if(2 * 2 = 5, 3 + 7, 4 * 8)"); 117 | 118 | assertEquals(26, lexes.size()); 119 | assertEquals(LexemeType.NameFunc, lexes.get(0).getType()); 120 | assertEquals("if", lexes.get(0).getText()); 121 | assertEquals(LexemeType.OpenBracket, lexes.get(1).getType()); 122 | assertEquals(LexemeType.Digits, lexes.get(2).getType()); 123 | assertEquals(LexemeType.Space, lexes.get(3).getType()); 124 | assertEquals(LexemeType.Operator, lexes.get(4).getType()); 125 | assertEquals(LexemeType.Space, lexes.get(5).getType()); 126 | assertEquals(LexemeType.Digits, lexes.get(6).getType()); 127 | assertEquals(LexemeType.Space, lexes.get(7).getType()); 128 | assertEquals(LexemeType.CompareOperator, lexes.get(8).getType()); 129 | assertEquals("=", lexes.get(8).getText()); 130 | assertEquals(LexemeType.Space, lexes.get(9).getType()); 131 | assertEquals(LexemeType.Digits, lexes.get(10).getType()); 132 | assertEquals(LexemeType.Comma, lexes.get(11).getType()); 133 | 134 | 135 | // доделать 136 | 137 | 138 | assertEquals(LexemeType.CloseBracket, lexes.get(25).getType()); 139 | 140 | } 141 | 142 | } -------------------------------------------------------------------------------- /src/main/java/io/sancta/sanctorum/calculator/lexer/Lexer.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.lexer; 2 | 3 | import java.util.*; 4 | 5 | import calculator.dom.*; 6 | 7 | public class Lexer { 8 | 9 | private static final LexemeRule[] rules = new LexemeRule[] 10 | { 11 | new LexemeRule() { 12 | @Override 13 | public LexemeType getType() { 14 | return LexemeType.Digits; 15 | } 16 | 17 | @Override 18 | public int getSymbolCount(String text) { 19 | int i = 0; 20 | 21 | while (i < text.length() && Character.isDigit(text.charAt(i))) 22 | i++; 23 | 24 | return i; 25 | } 26 | }, 27 | new LexemeRule() { 28 | @Override 29 | public LexemeType getType() { 30 | return LexemeType.Dot; 31 | } 32 | 33 | @Override 34 | public int getSymbolCount(String text) { 35 | if (text.charAt(0) == '.') 36 | return 1; 37 | else return 0; 38 | } 39 | }, 40 | new LexemeRule() { 41 | @Override 42 | public LexemeType getType() { 43 | return LexemeType.Space; 44 | } 45 | 46 | @Override 47 | public int getSymbolCount(String text) { 48 | int i = 0; 49 | 50 | while (i < text.length() && Character.isSpaceChar(text.charAt(i))) 51 | i++; 52 | 53 | return i; 54 | } 55 | }, 56 | new LexemeRule() { 57 | @Override 58 | public LexemeType getType() { 59 | return LexemeType.Operator; 60 | } 61 | 62 | @Override 63 | public int getSymbolCount(String text) { 64 | return BinaryOperator.fromChar(text.charAt(0)) != null ? 1 : 0; 65 | } 66 | }, 67 | new LexemeRule() { 68 | @Override 69 | public LexemeType getType() { 70 | return LexemeType.CompareOperator; 71 | } 72 | 73 | @Override 74 | public int getSymbolCount(String text) { 75 | return BooleanOperator.fromChar(text.charAt(0)) != null ? 1 : 0; 76 | } 77 | }, 78 | new LexemeRule() { 79 | @Override 80 | public LexemeType getType() { 81 | return LexemeType.OpenBracket; 82 | } 83 | 84 | @Override 85 | public int getSymbolCount(String text) { 86 | if (text.charAt(0) == '(') 87 | return 1; 88 | else return 0; 89 | } 90 | }, 91 | new LexemeRule() { 92 | @Override 93 | public LexemeType getType() { 94 | return LexemeType.CloseBracket; 95 | } 96 | 97 | @Override 98 | public int getSymbolCount(String text) { 99 | if (text.charAt(0) == ')') 100 | return 1; 101 | else return 0; 102 | } 103 | }, 104 | 105 | new LexemeRule() { 106 | @Override 107 | public LexemeType getType() { 108 | return LexemeType.X; 109 | } 110 | 111 | @Override 112 | public int getSymbolCount(String text) { 113 | if (text.charAt(0) == 'x' || text.charAt(0) == 'X') 114 | return 1; 115 | return 0; 116 | } 117 | }, 118 | 119 | new LexemeRule() { 120 | @Override 121 | public LexemeType getType() { 122 | return LexemeType.NameFunc; 123 | } 124 | 125 | @Override 126 | public int getSymbolCount(String text) { 127 | for (FunctionType fun : FunctionType.values()) 128 | if (text.startsWith(fun.toString().toLowerCase())) 129 | return fun.toString().length(); 130 | return 0; 131 | } 132 | }, 133 | 134 | new LexemeRule() { 135 | @Override 136 | public LexemeType getType() { 137 | return LexemeType.Comma; 138 | } 139 | 140 | @Override 141 | public int getSymbolCount(String text) { 142 | if (text.charAt(0) == ',') 143 | return 1; 144 | return 0; 145 | } 146 | }, 147 | 148 | new LexemeRule() { 149 | @Override 150 | public LexemeType getType() { 151 | return LexemeType.Error; 152 | } 153 | 154 | @Override 155 | public int getSymbolCount(String text) { 156 | return 1; 157 | } 158 | } 159 | }; 160 | 161 | public static List run(String text) { 162 | List lexes = new ArrayList<>(); 163 | 164 | int position = 0; 165 | 166 | while (text.length() > 0) { 167 | for (LexemeRule rule : rules) { 168 | int length = rule.getSymbolCount(text); 169 | 170 | if (length > 0) { 171 | lexes.add(new Lexeme(text.substring(0, length), rule.getType(), position)); 172 | text = text.substring(length); 173 | position += length; 174 | break; 175 | } 176 | } 177 | } 178 | return lexes; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/test/java/io/sancta/sanctorum/calculator/dom/CalculateExpressionVisitorTest.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.dom; 2 | 3 | import io.sancta.sanctorum.calculator.dom.processing.CalculateExpressionVisitor; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.util.Arrays; 7 | 8 | class CalculateExpressionVisitorTest { 9 | 10 | @Test 11 | void minusMul(){ 12 | BinaryExpression e = new BinaryExpression(); 13 | BinaryExpression left = new BinaryExpression(); 14 | e.setLeft(left); 15 | left.setLeft(new NumberExpression(5)); 16 | left.setOperator(BinaryOperator.Minus); 17 | left.setRight(new NumberExpression(3)); 18 | e.setOperator(BinaryOperator.Mul); 19 | e.setRight(new NumberExpression(10)); 20 | 21 | assertEquals((5 - 3) * 10, e.accept(new CalculateExpressionVisitor(0))); 22 | } 23 | 24 | @Test 25 | void mulMinus (){ 26 | BinaryExpression e = new BinaryExpression(); 27 | BinaryExpression left = new BinaryExpression(); 28 | e.setLeft(left); 29 | left.setLeft(new NumberExpression(5)); 30 | left.setOperator(BinaryOperator.Mul); 31 | left.setRight(new NumberExpression(3)); 32 | e.setOperator(BinaryOperator.Minus); 33 | e.setRight(new NumberExpression(10)); 34 | 35 | assertEquals(5 * 3 - 10, e.accept(new CalculateExpressionVisitor(0))); 36 | 37 | } 38 | 39 | @Test 40 | void commutativeMinusMul (){ 41 | BinaryExpression e = new BinaryExpression(); 42 | BinaryExpression right = new BinaryExpression(); 43 | e.setRight(right); 44 | right.setLeft(new NumberExpression(5)); 45 | right.setOperator(BinaryOperator.Mul); 46 | right.setRight(new NumberExpression(3)); 47 | e.setOperator(BinaryOperator.Minus); 48 | e.setLeft(new NumberExpression(10)); 49 | assertEquals(10 - 5 * 3, e.accept(new CalculateExpressionVisitor(0))); 50 | } 51 | 52 | @Test 53 | void commutativeDivMul (){ 54 | BinaryExpression e = new BinaryExpression(); 55 | BinaryExpression right = new BinaryExpression(); 56 | e.setRight(right); 57 | right.setLeft(new NumberExpression(5)); 58 | right.setOperator(BinaryOperator.Mul); 59 | right.setRight(new NumberExpression(3)); 60 | e.setOperator(BinaryOperator.Div); 61 | e.setLeft(new NumberExpression(10)); 62 | assertEquals(10.0 / (5 * 3), e.accept(new CalculateExpressionVisitor(0))); 63 | } 64 | 65 | @Test 66 | void commutativeMinusPlus (){ 67 | BinaryExpression e = new BinaryExpression(); 68 | BinaryExpression right = new BinaryExpression(); 69 | e.setRight(right); 70 | right.setLeft(new NumberExpression(5)); 71 | right.setOperator(BinaryOperator.Plus); 72 | right.setRight(new NumberExpression(3)); 73 | e.setOperator(BinaryOperator.Minus); 74 | e.setLeft(new NumberExpression(10)); 75 | assertEquals(10 - (5 + 3), e.accept(new CalculateExpressionVisitor(0))); 76 | } 77 | 78 | @Test 79 | void X(){ 80 | BinaryExpression e = new BinaryExpression(); 81 | BinaryExpression right = new BinaryExpression(); 82 | e.setRight(right); 83 | right.setLeft(new NumberExpression(5)); 84 | right.setOperator(BinaryOperator.Plus); 85 | right.setRight(new NumberExpression(3)); 86 | e.setOperator(BinaryOperator.Minus); 87 | e.setLeft(new XExpression()); 88 | double x = 10; 89 | assertEquals( x - (5 + 3), e.accept(new CalculateExpressionVisitor(x))); 90 | } 91 | 92 | @Test 93 | void Fun(){ 94 | FunctionExpression e = new FunctionExpression(); 95 | e.setType(FunctionType.Log); 96 | e.getParameters().add(new NumberExpression(125)); 97 | e.getParameters().add(new NumberExpression(5)); 98 | assertEquals(3, e.accept(new CalculateExpressionVisitor(0)).intValue()); 99 | } 100 | 101 | @Test 102 | void FunAbs() { 103 | BinaryExpression e = new BinaryExpression(); 104 | e.setLeft(new NumberExpression(2.5)); 105 | e.setOperator(BinaryOperator.Div); 106 | BinaryExpression right = new BinaryExpression(); 107 | right.setOperator(BinaryOperator.Plus); 108 | right.setRight(new NumberExpression(1)); 109 | FunctionExpression f = new FunctionExpression(); 110 | f.setType(FunctionType.Abs); 111 | f.getParameters().add(new XExpression()); 112 | right.setLeft(f); 113 | e.setRight(right); 114 | assertEquals(2.5/4, e.accept(new CalculateExpressionVisitor(-3))); 115 | } 116 | 117 | @Test 118 | void Greater() { 119 | BooleanExpression b = new BooleanExpression(); 120 | b.setLeft(new NumberExpression(2.2)); 121 | b.setOperator(BooleanOperator.Greater); 122 | b.setRight(new NumberExpression(5.8)); 123 | assertEquals(0, b.accept(new CalculateExpressionVisitor())); 124 | } 125 | 126 | @Test 127 | void Less() { 128 | BooleanExpression b = new BooleanExpression(); 129 | b.setLeft(new NumberExpression(2.2)); 130 | b.setOperator(BooleanOperator.Less); 131 | b.setRight(new NumberExpression(5.8)); 132 | assertEquals(1, b.accept(new CalculateExpressionVisitor())); 133 | } 134 | 135 | @Test 136 | void MoreEquals() { 137 | BooleanExpression b = new BooleanExpression(); 138 | b.setLeft(new NumberExpression(2.2)); 139 | b.setOperator(BooleanOperator.MoreEquals); 140 | b.setRight(new NumberExpression(5.8)); 141 | assertEquals(0, b.accept(new CalculateExpressionVisitor())); 142 | } 143 | 144 | @Test 145 | void LessEquals() { 146 | BooleanExpression b = new BooleanExpression(); 147 | b.setLeft(new NumberExpression(2.2)); 148 | b.setOperator(BooleanOperator.LessEquals); 149 | b.setRight(new NumberExpression(5.8)); 150 | assertEquals(1, b.accept(new CalculateExpressionVisitor())); 151 | } 152 | 153 | @Test 154 | void Equals() { 155 | BooleanExpression b = new BooleanExpression(); 156 | b.setLeft(new NumberExpression(2.2)); 157 | b.setOperator(BooleanOperator.Equals); 158 | b.setRight(new NumberExpression(2.2)); 159 | assertEquals(1, b.accept(new CalculateExpressionVisitor())); 160 | } 161 | 162 | @Test 163 | void NotEquals() { 164 | BooleanExpression b = new BooleanExpression(); 165 | b.setLeft(new NumberExpression(2.2)); 166 | b.setOperator(BooleanOperator.NotEquals); 167 | b.setRight(new NumberExpression(5.8)); 168 | assertEquals(1, b.accept(new CalculateExpressionVisitor())); 169 | } 170 | 171 | @Test 172 | void If(){ 173 | 174 | FunctionExpression function = new FunctionExpression(); 175 | function.setType(FunctionType.If); 176 | 177 | BinaryExpression binaryPartOne = new BinaryExpression(); 178 | binaryPartOne.setLeft(new NumberExpression(2)); 179 | binaryPartOne.setOperator(BinaryOperator.Mul); 180 | binaryPartOne.setRight(new NumberExpression(2)); 181 | 182 | BooleanExpression booleanExpression = new BooleanExpression(); 183 | booleanExpression.setLeft(binaryPartOne); 184 | booleanExpression.setOperator(BooleanOperator.Equals); 185 | booleanExpression.setRight(new NumberExpression(5)); 186 | 187 | BinaryExpression binaryPartTwo = new BinaryExpression(); 188 | binaryPartTwo.setLeft(new NumberExpression(3)); 189 | binaryPartTwo.setOperator(BinaryOperator.Plus); 190 | binaryPartTwo.setRight(new NumberExpression(7)); 191 | 192 | BinaryExpression binaryPartThree = new BinaryExpression(); 193 | binaryPartThree.setLeft(new NumberExpression(4)); 194 | binaryPartThree.setOperator(BinaryOperator.Mul); 195 | binaryPartThree.setRight(new NumberExpression(8)); 196 | 197 | function.getParameters().addAll(Arrays.asList( 198 | booleanExpression, 199 | binaryPartTwo, 200 | binaryPartThree)); 201 | 202 | assertEquals(32, function.accept(new CalculateExpressionVisitor())); 203 | } 204 | 205 | } -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Apache Maven Wrapper startup batch script, version 3.2.0 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 28 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 29 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 30 | @REM e.g. to debug Maven itself, use 31 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 32 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 33 | @REM ---------------------------------------------------------------------------- 34 | 35 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 36 | @echo off 37 | @REM set title of command window 38 | title %0 39 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 40 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 41 | 42 | @REM set %HOME% to equivalent of $HOME 43 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 44 | 45 | @REM Execute a user defined script before this one 46 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 47 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 48 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 49 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 50 | :skipRcPre 51 | 52 | @setlocal 53 | 54 | set ERROR_CODE=0 55 | 56 | @REM To isolate internal variables from possible post scripts, we use another setlocal 57 | @setlocal 58 | 59 | @REM ==== START VALIDATION ==== 60 | if not "%JAVA_HOME%" == "" goto OkJHome 61 | 62 | echo. 63 | echo Error: JAVA_HOME not found in your environment. >&2 64 | echo Please set the JAVA_HOME variable in your environment to match the >&2 65 | echo location of your Java installation. >&2 66 | echo. 67 | goto error 68 | 69 | :OkJHome 70 | if exist "%JAVA_HOME%\bin\java.exe" goto init 71 | 72 | echo. 73 | echo Error: JAVA_HOME is set to an invalid directory. >&2 74 | echo JAVA_HOME = "%JAVA_HOME%" >&2 75 | echo Please set the JAVA_HOME variable in your environment to match the >&2 76 | echo location of your Java installation. >&2 77 | echo. 78 | goto error 79 | 80 | @REM ==== END VALIDATION ==== 81 | 82 | :init 83 | 84 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 85 | @REM Fallback to current working directory if not found. 86 | 87 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 88 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 89 | 90 | set EXEC_DIR=%CD% 91 | set WDIR=%EXEC_DIR% 92 | :findBaseDir 93 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 94 | cd .. 95 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 96 | set WDIR=%CD% 97 | goto findBaseDir 98 | 99 | :baseDirFound 100 | set MAVEN_PROJECTBASEDIR=%WDIR% 101 | cd "%EXEC_DIR%" 102 | goto endDetectBaseDir 103 | 104 | :baseDirNotFound 105 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 106 | cd "%EXEC_DIR%" 107 | 108 | :endDetectBaseDir 109 | 110 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 111 | 112 | @setlocal EnableExtensions EnableDelayedExpansion 113 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 114 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 115 | 116 | :endReadAdditionalConfig 117 | 118 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 123 | 124 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 125 | IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | if "%MVNW_VERBOSE%" == "true" ( 132 | echo Found %WRAPPER_JAR% 133 | ) 134 | ) else ( 135 | if not "%MVNW_REPOURL%" == "" ( 136 | SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 137 | ) 138 | if "%MVNW_VERBOSE%" == "true" ( 139 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 140 | echo Downloading from: %WRAPPER_URL% 141 | ) 142 | 143 | powershell -Command "&{"^ 144 | "$webclient = new-object System.Net.WebClient;"^ 145 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 146 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 147 | "}"^ 148 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ 149 | "}" 150 | if "%MVNW_VERBOSE%" == "true" ( 151 | echo Finished downloading %WRAPPER_JAR% 152 | ) 153 | ) 154 | @REM End of extension 155 | 156 | @REM If specified, validate the SHA-256 sum of the Maven wrapper jar file 157 | SET WRAPPER_SHA_256_SUM="" 158 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 159 | IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B 160 | ) 161 | IF NOT %WRAPPER_SHA_256_SUM%=="" ( 162 | powershell -Command "&{"^ 163 | "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ 164 | "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ 165 | " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ 166 | " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ 167 | " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ 168 | " exit 1;"^ 169 | "}"^ 170 | "}" 171 | if ERRORLEVEL 1 goto error 172 | ) 173 | 174 | @REM Provide a "standardized" way to retrieve the CLI args that will 175 | @REM work with both Windows and non-Windows executions. 176 | set MAVEN_CMD_LINE_ARGS=%* 177 | 178 | %MAVEN_JAVA_EXE% ^ 179 | %JVM_CONFIG_MAVEN_PROPS% ^ 180 | %MAVEN_OPTS% ^ 181 | %MAVEN_DEBUG_OPTS% ^ 182 | -classpath %WRAPPER_JAR% ^ 183 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 184 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 185 | if ERRORLEVEL 1 goto error 186 | goto end 187 | 188 | :error 189 | set ERROR_CODE=1 190 | 191 | :end 192 | @endlocal & set ERROR_CODE=%ERROR_CODE% 193 | 194 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 195 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 196 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 197 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 198 | :skipRcPost 199 | 200 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 201 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 202 | 203 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 204 | 205 | cmd /C exit /B %ERROR_CODE% 206 | -------------------------------------------------------------------------------- /src/test/java/io/sancta/sanctorum/calculator/dom/StringExpressionVisitorTest.java: -------------------------------------------------------------------------------- 1 | package io.sancta.sanctorum.calculator.dom; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.util.Arrays; 6 | 7 | class StringExpressionVisitorTest { 8 | 9 | @Test 10 | void integer() { 11 | NumberExpression ne = new NumberExpression(5); 12 | assertEquals("5", ne.toString(), "Expression String"); 13 | } 14 | 15 | @Test 16 | void intPlusInt(){ 17 | BinaryExpression e = new BinaryExpression(); 18 | e.setLeft(new NumberExpression(2)); 19 | e.setOperator(BinaryOperator.Mul); 20 | e.setRight(new NumberExpression(3)); 21 | 22 | assertEquals("2 * 3", e.toString()); 23 | } 24 | 25 | @Test 26 | void emptyBinary(){ 27 | BinaryExpression e = new BinaryExpression(); 28 | assertEquals("", e.toString()); 29 | } 30 | 31 | @Test 32 | void minusMul(){ 33 | BinaryExpression e = new BinaryExpression(); 34 | BinaryExpression left = new BinaryExpression(); 35 | e.setLeft(left); 36 | left.setLeft(new NumberExpression(5)); 37 | left.setOperator(BinaryOperator.Minus); 38 | left.setRight(new NumberExpression(3)); 39 | e.setOperator(BinaryOperator.Mul); 40 | e.setRight(new NumberExpression(10)); 41 | 42 | assertEquals("(5 - 3) * 10", e.toString()); 43 | } 44 | 45 | @Test 46 | void mulMinus (){ 47 | BinaryExpression e = new BinaryExpression(); 48 | BinaryExpression left = new BinaryExpression(); 49 | e.setLeft(left); 50 | left.setLeft(new NumberExpression(5)); 51 | left.setOperator(BinaryOperator.Mul); 52 | left.setRight(new NumberExpression(3)); 53 | e.setOperator(BinaryOperator.Minus); 54 | e.setRight(new NumberExpression(10)); 55 | 56 | assertEquals("5 * 3 - 10", e.toString()); 57 | 58 | } 59 | 60 | @Test 61 | void commutativeMinusMul (){ 62 | BinaryExpression e = new BinaryExpression(); 63 | BinaryExpression right = new BinaryExpression(); 64 | e.setRight(right); 65 | right.setLeft(new NumberExpression(5)); 66 | right.setOperator(BinaryOperator.Mul); 67 | right.setRight(new NumberExpression(3)); 68 | e.setOperator(BinaryOperator.Minus); 69 | e.setLeft(new NumberExpression(10)); 70 | assertEquals("10 - 5 * 3", e.toString()); 71 | } 72 | 73 | @Test 74 | void commutativeDivMul (){ 75 | BinaryExpression e = new BinaryExpression(); 76 | BinaryExpression right = new BinaryExpression(); 77 | e.setRight(right); 78 | right.setLeft(new NumberExpression(5)); 79 | right.setOperator(BinaryOperator.Mul); 80 | right.setRight(new NumberExpression(3)); 81 | e.setOperator(BinaryOperator.Div); 82 | e.setLeft(new NumberExpression(10)); 83 | assertEquals("10 / (5 * 3)", e.toString()); 84 | } 85 | 86 | @Test 87 | void commutativeMinusPlus (){ 88 | BinaryExpression e = new BinaryExpression(); 89 | BinaryExpression right = new BinaryExpression(); 90 | e.setRight(right); 91 | right.setLeft(new NumberExpression(5)); 92 | right.setOperator(BinaryOperator.Plus); 93 | right.setRight(new NumberExpression(3)); 94 | e.setOperator(BinaryOperator.Minus); 95 | e.setLeft(new NumberExpression(10)); 96 | assertEquals("10 - (5 + 3)", e.toString()); 97 | } 98 | 99 | @Test 100 | void X(){ 101 | BinaryExpression e = new BinaryExpression(); 102 | BinaryExpression right = new BinaryExpression(); 103 | e.setRight(right); 104 | right.setLeft(new NumberExpression(5)); 105 | right.setOperator(BinaryOperator.Plus); 106 | right.setRight(new NumberExpression(3)); 107 | e.setOperator(BinaryOperator.Minus); 108 | e.setLeft(new XExpression()); 109 | assertEquals("x - (5 + 3)", e.toString()); 110 | } 111 | 112 | @Test 113 | void Fun(){ 114 | FunctionExpression e = new FunctionExpression(); 115 | e.setType(FunctionType.Log); 116 | e.getParameters().add(new NumberExpression(25)); 117 | e.getParameters().add(new NumberExpression(5)); 118 | assertEquals("log(25, 5)", e.toString()); 119 | } 120 | 121 | @Test 122 | void FunAbs(){ 123 | BinaryExpression e = new BinaryExpression(); 124 | e.setLeft(new NumberExpression(2.5)); 125 | e.setOperator(BinaryOperator.Div); 126 | BinaryExpression right = new BinaryExpression(); 127 | right.setOperator(BinaryOperator.Plus); 128 | right.setRight(new NumberExpression(1)); 129 | FunctionExpression f = new FunctionExpression(); 130 | f.setType(FunctionType.Abs); 131 | f.getParameters().add(new XExpression()); 132 | right.setLeft(f); 133 | e.setRight(right); 134 | assertEquals("2.5 / (abs(x) + 1)", e.toString()); 135 | } 136 | 137 | @Test 138 | void Pi() { 139 | FunctionExpression e = new FunctionExpression(); 140 | e.setType(FunctionType.Pi); 141 | assertEquals("pi()", e.toString()); 142 | } 143 | 144 | @Test 145 | void E() { 146 | FunctionExpression e = new FunctionExpression(); 147 | e.setType(FunctionType.E); 148 | assertEquals("e()", e.toString()); 149 | } 150 | 151 | @Test 152 | void Greater() { 153 | BooleanExpression b = new BooleanExpression(); 154 | b.setLeft(new NumberExpression(2.2)); 155 | b.setOperator(BooleanOperator.Greater); 156 | b.setRight(new NumberExpression(5.8)); 157 | assertEquals("2.2 > 5.8", b.toString()); 158 | } 159 | 160 | @Test 161 | void Less() { 162 | BooleanExpression b = new BooleanExpression(); 163 | b.setLeft(new NumberExpression(2.2)); 164 | b.setOperator(BooleanOperator.Less); 165 | b.setRight(new NumberExpression(5.8)); 166 | assertEquals("2.2 < 5.8", b.toString()); 167 | } 168 | 169 | @Test 170 | void MoreEquals() { 171 | BooleanExpression b = new BooleanExpression(); 172 | b.setLeft(new NumberExpression(2.2)); 173 | b.setOperator(BooleanOperator.MoreEquals); 174 | b.setRight(new NumberExpression(5.8)); 175 | assertEquals("2.2 ≥ 5.8", b.toString()); 176 | } 177 | 178 | @Test 179 | void LessEquals() { 180 | BooleanExpression b = new BooleanExpression(); 181 | b.setLeft(new NumberExpression(2.2)); 182 | b.setOperator(BooleanOperator.LessEquals); 183 | b.setRight(new NumberExpression(5.8)); 184 | assertEquals("2.2 ≤ 5.8", b.toString()); 185 | } 186 | 187 | @Test 188 | void Equals() { 189 | BooleanExpression b = new BooleanExpression(); 190 | b.setLeft(new NumberExpression(2.2)); 191 | b.setOperator(BooleanOperator.Equals); 192 | b.setRight(new NumberExpression(2.2)); 193 | assertEquals("2.2 = 2.2", b.toString()); 194 | } 195 | 196 | @Test 197 | void NotEquals() { 198 | BooleanExpression b = new BooleanExpression(); 199 | b.setLeft(new NumberExpression(2.2)); 200 | b.setOperator(BooleanOperator.NotEquals); 201 | b.setRight(new NumberExpression(5.8)); 202 | assertEquals("2.2 ≠ 5.8", b.toString()); 203 | } 204 | 205 | @Test 206 | void If(){ 207 | FunctionExpression function = new FunctionExpression(); 208 | function.setType(FunctionType.If); 209 | 210 | BinaryExpression binaryPartOne = new BinaryExpression(); 211 | binaryPartOne.setLeft(new NumberExpression(2)); 212 | binaryPartOne.setOperator(BinaryOperator.Mul); 213 | binaryPartOne.setRight(new NumberExpression(2)); 214 | 215 | BooleanExpression booleanExpression = new BooleanExpression(); 216 | booleanExpression.setLeft(binaryPartOne); 217 | booleanExpression.setOperator(BooleanOperator.Equals); 218 | booleanExpression.setRight(new NumberExpression(5)); 219 | 220 | BinaryExpression binaryPartTwo = new BinaryExpression(); 221 | binaryPartTwo.setLeft(new NumberExpression(3)); 222 | binaryPartTwo.setOperator(BinaryOperator.Plus); 223 | binaryPartTwo.setRight(new NumberExpression(7)); 224 | 225 | BinaryExpression binaryPartThree = new BinaryExpression(); 226 | binaryPartThree.setLeft(new NumberExpression(4)); 227 | binaryPartThree.setOperator(BinaryOperator.Mul); 228 | binaryPartThree.setRight(new NumberExpression(8)); 229 | 230 | function.getParameters().addAll(Arrays.asList( 231 | booleanExpression, 232 | binaryPartTwo, 233 | binaryPartThree)); 234 | 235 | assertEquals("if(2 * 2 = 5, 3 + 7, 4 * 8)", function.toString()); 236 | } 237 | 238 | } -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Apache Maven Wrapper startup batch script, version 3.2.0 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | # e.g. to debug Maven itself, use 32 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | # ---------------------------------------------------------------------------- 35 | 36 | if [ -z "$MAVEN_SKIP_RC" ] ; then 37 | 38 | if [ -f /usr/local/etc/mavenrc ] ; then 39 | . /usr/local/etc/mavenrc 40 | fi 41 | 42 | if [ -f /etc/mavenrc ] ; then 43 | . /etc/mavenrc 44 | fi 45 | 46 | if [ -f "$HOME/.mavenrc" ] ; then 47 | . "$HOME/.mavenrc" 48 | fi 49 | 50 | fi 51 | 52 | # OS specific support. $var _must_ be set to either true or false. 53 | cygwin=false; 54 | darwin=false; 55 | mingw=false 56 | case "$(uname)" in 57 | CYGWIN*) cygwin=true ;; 58 | MINGW*) mingw=true;; 59 | Darwin*) darwin=true 60 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 61 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 62 | if [ -z "$JAVA_HOME" ]; then 63 | if [ -x "/usr/libexec/java_home" ]; then 64 | JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME 65 | else 66 | JAVA_HOME="/Library/Java/Home"; export JAVA_HOME 67 | fi 68 | fi 69 | ;; 70 | esac 71 | 72 | if [ -z "$JAVA_HOME" ] ; then 73 | if [ -r /etc/gentoo-release ] ; then 74 | JAVA_HOME=$(java-config --jre-home) 75 | fi 76 | fi 77 | 78 | # For Cygwin, ensure paths are in UNIX format before anything is touched 79 | if $cygwin ; then 80 | [ -n "$JAVA_HOME" ] && 81 | JAVA_HOME=$(cygpath --unix "$JAVA_HOME") 82 | [ -n "$CLASSPATH" ] && 83 | CLASSPATH=$(cygpath --path --unix "$CLASSPATH") 84 | fi 85 | 86 | # For Mingw, ensure paths are in UNIX format before anything is touched 87 | if $mingw ; then 88 | [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && 89 | JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" 90 | fi 91 | 92 | if [ -z "$JAVA_HOME" ]; then 93 | javaExecutable="$(which javac)" 94 | if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then 95 | # readlink(1) is not available as standard on Solaris 10. 96 | readLink=$(which readlink) 97 | if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then 98 | if $darwin ; then 99 | javaHome="$(dirname "\"$javaExecutable\"")" 100 | javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" 101 | else 102 | javaExecutable="$(readlink -f "\"$javaExecutable\"")" 103 | fi 104 | javaHome="$(dirname "\"$javaExecutable\"")" 105 | javaHome=$(expr "$javaHome" : '\(.*\)/bin') 106 | JAVA_HOME="$javaHome" 107 | export JAVA_HOME 108 | fi 109 | fi 110 | fi 111 | 112 | if [ -z "$JAVACMD" ] ; then 113 | if [ -n "$JAVA_HOME" ] ; then 114 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 115 | # IBM's JDK on AIX uses strange locations for the executables 116 | JAVACMD="$JAVA_HOME/jre/sh/java" 117 | else 118 | JAVACMD="$JAVA_HOME/bin/java" 119 | fi 120 | else 121 | JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" 122 | fi 123 | fi 124 | 125 | if [ ! -x "$JAVACMD" ] ; then 126 | echo "Error: JAVA_HOME is not defined correctly." >&2 127 | echo " We cannot execute $JAVACMD" >&2 128 | exit 1 129 | fi 130 | 131 | if [ -z "$JAVA_HOME" ] ; then 132 | echo "Warning: JAVA_HOME environment variable is not set." 133 | fi 134 | 135 | # traverses directory structure from process work directory to filesystem root 136 | # first directory with .mvn subdirectory is considered project base directory 137 | find_maven_basedir() { 138 | if [ -z "$1" ] 139 | then 140 | echo "Path not specified to find_maven_basedir" 141 | return 1 142 | fi 143 | 144 | basedir="$1" 145 | wdir="$1" 146 | while [ "$wdir" != '/' ] ; do 147 | if [ -d "$wdir"/.mvn ] ; then 148 | basedir=$wdir 149 | break 150 | fi 151 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 152 | if [ -d "${wdir}" ]; then 153 | wdir=$(cd "$wdir/.." || exit 1; pwd) 154 | fi 155 | # end of workaround 156 | done 157 | printf '%s' "$(cd "$basedir" || exit 1; pwd)" 158 | } 159 | 160 | # concatenates all lines of a file 161 | concat_lines() { 162 | if [ -f "$1" ]; then 163 | # Remove \r in case we run on Windows within Git Bash 164 | # and check out the repository with auto CRLF management 165 | # enabled. Otherwise, we may read lines that are delimited with 166 | # \r\n and produce $'-Xarg\r' rather than -Xarg due to word 167 | # splitting rules. 168 | tr -s '\r\n' ' ' < "$1" 169 | fi 170 | } 171 | 172 | log() { 173 | if [ "$MVNW_VERBOSE" = true ]; then 174 | printf '%s\n' "$1" 175 | fi 176 | } 177 | 178 | BASE_DIR=$(find_maven_basedir "$(dirname "$0")") 179 | if [ -z "$BASE_DIR" ]; then 180 | exit 1; 181 | fi 182 | 183 | MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR 184 | log "$MAVEN_PROJECTBASEDIR" 185 | 186 | ########################################################################################## 187 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 188 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 189 | ########################################################################################## 190 | wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" 191 | if [ -r "$wrapperJarPath" ]; then 192 | log "Found $wrapperJarPath" 193 | else 194 | log "Couldn't find $wrapperJarPath, downloading it ..." 195 | 196 | if [ -n "$MVNW_REPOURL" ]; then 197 | wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 198 | else 199 | wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 200 | fi 201 | while IFS="=" read -r key value; do 202 | # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) 203 | safeValue=$(echo "$value" | tr -d '\r') 204 | case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; 205 | esac 206 | done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" 207 | log "Downloading from: $wrapperUrl" 208 | 209 | if $cygwin; then 210 | wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") 211 | fi 212 | 213 | if command -v wget > /dev/null; then 214 | log "Found wget ... using wget" 215 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" 216 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 217 | wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 218 | else 219 | wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 220 | fi 221 | elif command -v curl > /dev/null; then 222 | log "Found curl ... using curl" 223 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" 224 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 225 | curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" 226 | else 227 | curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" 228 | fi 229 | else 230 | log "Falling back to using Java to download" 231 | javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" 232 | javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" 233 | # For Cygwin, switch paths to Windows format before running javac 234 | if $cygwin; then 235 | javaSource=$(cygpath --path --windows "$javaSource") 236 | javaClass=$(cygpath --path --windows "$javaClass") 237 | fi 238 | if [ -e "$javaSource" ]; then 239 | if [ ! -e "$javaClass" ]; then 240 | log " - Compiling MavenWrapperDownloader.java ..." 241 | ("$JAVA_HOME/bin/javac" "$javaSource") 242 | fi 243 | if [ -e "$javaClass" ]; then 244 | log " - Running MavenWrapperDownloader.java ..." 245 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" 246 | fi 247 | fi 248 | fi 249 | fi 250 | ########################################################################################## 251 | # End of extension 252 | ########################################################################################## 253 | 254 | # If specified, validate the SHA-256 sum of the Maven wrapper jar file 255 | wrapperSha256Sum="" 256 | while IFS="=" read -r key value; do 257 | case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; 258 | esac 259 | done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" 260 | if [ -n "$wrapperSha256Sum" ]; then 261 | wrapperSha256Result=false 262 | if command -v sha256sum > /dev/null; then 263 | if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then 264 | wrapperSha256Result=true 265 | fi 266 | elif command -v shasum > /dev/null; then 267 | if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then 268 | wrapperSha256Result=true 269 | fi 270 | else 271 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." 272 | echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." 273 | exit 1 274 | fi 275 | if [ $wrapperSha256Result = false ]; then 276 | echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 277 | echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 278 | echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 279 | exit 1 280 | fi 281 | fi 282 | 283 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 284 | 285 | # For Cygwin, switch paths to Windows format before running java 286 | if $cygwin; then 287 | [ -n "$JAVA_HOME" ] && 288 | JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") 289 | [ -n "$CLASSPATH" ] && 290 | CLASSPATH=$(cygpath --path --windows "$CLASSPATH") 291 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 292 | MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") 293 | fi 294 | 295 | # Provide a "standardized" way to retrieve the CLI args that will 296 | # work with both Windows and non-Windows executions. 297 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" 298 | export MAVEN_CMD_LINE_ARGS 299 | 300 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 301 | 302 | # shellcheck disable=SC2086 # safe args 303 | exec "$JAVACMD" \ 304 | $MAVEN_OPTS \ 305 | $MAVEN_DEBUG_OPTS \ 306 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 307 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 308 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 309 | --------------------------------------------------------------------------------