/
javapractice
/
java-practice-antlr4
Обзор
Документация
Войти
/
javapractice
/
java-practice-antlr4
Код
Запросы
0
Задачи
Пакеты
0
Релизы
0
CI/CD
Аналитика
master
src/main/java/com/example/calc/Calculator.java
83 строки
4 KB
Кузьма Даждъбогин
Javadoc на классах, методах и unit-тестах
08 авг 2026, 11:37
08 авг 2026, 11:37
e717695
Код
Авторство
О чём код?
package com.example.calc; import org.antlr.v4.runtime.BaseErrorListener; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.Recognizer; /** * Фасад вычисления выражений: собирает лексер, парсер и {@link EvalVisitor}, * транслирует все ошибки в {@link CalcException}. * * <p>Стандартные error listener'ы ANTLR снимаются и у лексера, и у парсера — * вместо печати ошибки в stderr и разбора повреждённого дерева бросается * исключение с позицией символа. Класс не зависит от консоли и целиком * покрывается unit-тестами.</p> */ public class Calculator { /** Слушатель, превращающий синтаксическую ошибку в {@link CalcException}. */ private static final BaseErrorListener THROWING_LISTENER = new BaseErrorListener() { @Override public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { throw new CalcException("Syntax error at position " + charPositionInLine + ": " + msg); } }; /** * Вычисляет арифметическое выражение. * * <p>Контракт: выражение разбирается целиком до конца строки (якорь EOF * в грамматике), результат всегда конечное число.</p> * * @param expression строка с выражением, например {@code "2 + 3 * 4"} * @return значение выражения * @throws CalcException при {@code null} или пустой строке, ошибке * разбора либо небесконечном результате вычисления * (деление на ноль, переполнение) */ public double evaluate(String expression) { if (expression == null) { throw new CalcException("Expression must not be null"); } CalcLexer lexer = new CalcLexer(CharStreams.fromString(expression)); lexer.removeErrorListeners(); lexer.addErrorListener(THROWING_LISTENER); CalcParser parser = new CalcParser(new CommonTokenStream(lexer)); parser.removeErrorListeners(); parser.addErrorListener(THROWING_LISTENER); CalcParser.ProgContext tree = parser.prog(); double result = new EvalVisitor().visit(tree); if (!Double.isFinite(result)) { throw new CalcException("Evaluation error: result is not a finite number"); } return result; } /** * Форматирует результат для вывода пользователю. * * <p>Правила: конечное значение, равное своему округлению * ({@link Math#rint}) и лежащее в диапазоне {@code long}, печатается * без дробной части ({@code 5}, а не {@code 5.0}); {@code -0.0} * нормализуется в {@code 0}; остальные значения печатаются через * {@link Double#toString} — разделитель всегда точка, независимо * от системной локали.</p> * * @param value значение для печати * @return строковое представление результата */ public static String format(double value) { double normalized = value + 0.0; // -0.0 -> 0.0 if (normalized == Math.rint(normalized) && normalized >= Long.MIN_VALUE && normalized <= Long.MAX_VALUE) { return Long.toString((long) normalized); } return Double.toString(normalized); } }