powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Java [игнор отключен] [закрыт для гостей] / Расширяемый парсер для интерпретатора выражений (калькулятор с функциями)
17 сообщений из 17, страница 1 из 1
Расширяемый парсер для интерпретатора выражений (калькулятор с функциями)
    #39379567
uid unique
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Рассмотрим такую задачу: нужно создать парсер для калькулятора с функциями (по умолчанию есть SUM(), SIN(), COS() к примеру);

Набор поддерживаемых функций может меняться через конфигурационный файл (имя функции и класс реализации).

каждая функция реализует интерфейс (pardon за ошибки)
Код: java
1.
Object eval(Object.. args); // массив объектов на входе



Как эффективно написать парсер чтобы расширение функционала (поддержка новых функций) было в run time без компиляции нового парсера?

PS Генераторы парсеров которые встречались ранее: ANTLR, JavaCC, SableCC.
Помню давным давно делал грамматику, генерил парсеры для сборки / разборки выражений (простые калькуляторы и SQL). Будем считать что все позабыл и возможно что-то свежее появилось, думаю это будет полезное упражнение для всех. Js контейнеры типа Rhino или в новой Java использовать не планируется.
...
Рейтинг: 0 / 0
Расширяемый парсер для интерпретатора выражений (калькулятор с функциями)
    #39379589
Фотография Usman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
uid uniqueJs контейнеры типа Rhino или в новой Java использовать не планируется.А как на счет Jython ?
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
package ru.sql.forum.java;

import org.python.core.PyFloat;
import org.python.core.PyInteger;
import org.python.core.PyLong;
import org.python.core.PyObject;
import org.python.util.PythonInterpreter;

public class Jython {
    
    static {
        System.setProperty("python.console.encoding", "UTF-8");
    }

    public static void main(String[] args) throws Exception {
        try (PythonInterpreter interp = new PythonInterpreter()) {
            interp.set("a", new PyInteger(2));
            interp.set("b", new PyFloat(2));
            interp.set("c", new PyLong(2));
            interp.exec("x = a + b * c");
            PyObject x = interp.get("x");

            System.out.println("x = " + x); // x = 6.0
        }
    }
}

Код: plaintext
1.
2.
3.
4.
5.
<dependency>
	<groupId>org.python</groupId>
	<artifactId>jython-standalone</artifactId>
	<version>2.7.1b3</version>
</dependency>
...
Рейтинг: 0 / 0
Расширяемый парсер для интерпретатора выражений (калькулятор с функциями)
    #39379596
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
uid unique, не забывай про безопасность. Всё что вводит пользователь - нужно запускать
в таком контексте чтобы не было возможности иметь прямой доступ к файловой системе
или сети.

Полностью согласен с другими ораторами по поводу интерпретаторов Python, JavaScript.
Какой из них лучше - дело вкуса.

P.S. Почему-то вспомнил золотую фразу

Any sufficiently complicated C or Fortran program contains an ad-hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.
...
Рейтинг: 0 / 0
Расширяемый парсер для интерпретатора выражений (калькулятор с функциями)
    #39379685
uid unique
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
UsmanА как на счет Jython ?
Спасибо за напоминание, забыл про него. Сталкивался в админ скриптах с ним но сам никогда не прикручивал.
Для калькулятора слишком могучая (и тяжелая) штука, но для расширений самое то. Не уверен насчет многопоточности интерпретатора - не придется их плодить по одному для каждого потока (а они наверное требуют много памяти)?

Нужен калькулятор с поддержкой функций, функции могут быть специфическими, к примеру залезть в базу, найти какой маппинг и тд и выдать результат который в свою очередь может обработаться другой бизнес функцией (в этом причина зачем нужна поддержка новых функций через конфиг), пример вычисления какой то надписи с заходом в базу и получением имя хоста (об имплементации этих фунций мы понятия не имеем, наше дело скормить им аргументы и получить результат):
Код: xml
1.
<label>CONCAT(GETUSERNAME({userId}), "_", GETHOSTNAME(), "_")</label>


где GETUSERNAME кастомная функция задаваемая через конфигурацую (id "GETUSERNAME", class name "come.package.impl.GetUserName") и к примеру она лезет в какую то базу данных в одном случае а в другом это будет маппинг через файл (да что угодно), GETHOSTNAME это другая фукция и тд. Пользователь добавляет новые функции которые затем можно использовать в выражениях в конфигурации.

или попроще пример:
Код: xml
1.
<label>1 + 3 + SIN({someVarName})</label>


Это на пальцах объяснение, в спешке написано.


Пока нашел старый пример калькулятора на JavaCC, JJTree класс позволяет делать обход дерева. Добавить стек, маппинг для переменных (имя в выражении / значение) и можно делать обход дерева и вычисления.

http://www.j-paine.org/dobbs/jjtree.html
http://www.idevelopment.info/data/Programming/java/JavaCC/SimpleCalc1.jj
https://github.com/jameswu/code-search/blob/master/javacc-example/src/main/javacc/Calculator.jj
https://www.ibm.com/developerworks/data/library/techarticle/dm-0401brereton/
...
Рейтинг: 0 / 0
Расширяемый парсер для интерпретатора выражений (калькулятор с функциями)
    #39379687
uid unique
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
maytonuid unique, не забывай про безопасность. Всё что вводит пользователь - нужно запускать
в таком контексте чтобы не было возможности иметь прямой доступ к файловой системе
или сети.
Да, это хорошая дыра - подключение поддержки скриптов в конфиге. Наверное можно локализовать доступ к ресурсам у интерпретаторов языков. По моему у Rhino была такая возможность (сделать песочницу без диступа во внешний мир).
...
Рейтинг: 0 / 0
Расширяемый парсер для интерпретатора выражений (калькулятор с функциями)
    #39379734
kealon(Ruslan)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
uid uniquemaytonuid unique, не забывай про безопасность. Всё что вводит пользователь - нужно запускать
в таком контексте чтобы не было возможности иметь прямой доступ к файловой системе
или сети.
Да, это хорошая дыра - подключение поддержки скриптов в конфиге. Наверное можно локализовать доступ к ресурсам у интерпретаторов языков. По моему у Rhino была такая возможность (сделать песочницу без диступа во внешний мир).
сначала сделать, а потом с этим бороться - очень современный тренд

uid unique,
если предполагается только работа с числами лучше ANTLR повторить и сделать простой калькулятор вида

compile(Code)
eval (CompiledCode, Map<varname, double>, Map<funcname, funcobj>)

примеров в сети полно, будет быстрее и в разратке и в скорости выполнения по сравнению с универсальными языками
...
Рейтинг: 0 / 0
Расширяемый парсер для интерпретатора выражений (калькулятор с функциями)
    #39379767
uid unique
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
kealon(Ruslan)uid unique,
если предполагается только работа с числами лучше ANTLR повторить и сделать простой калькулятор вида

compile(Code)
eval (CompiledCode, Map<varname, double>, Map<funcname, funcobj>)

примеров в сети полно, будет быстрее и в разратке и в скорости выполнения по сравнению с универсальными языками
Спасибо, сравню с тем что накопал по старинке из JavaCC. Смущает то что нужно писать грамматику, потом компилить ее и делать какие то пассы руками, было бы отлично компилить парсер на лету, модифицируя грамматику программным путем.
Пример json парсеров на ANTLR и JavaCC:
https://dzone.com/articles/antlr-and-javacc-parser-generators

К слову нашел пример парсера для калькулятора с фунциями, который можно расширить, так что проблема в принципе решена старыми методами (что не удивительно).

Наткнулся на интересный проект где парсер создается из кода а не компиляцией файла грамматики как в JavaCC / ANTLR, это может быть интересно:
https://github.com/jparsec/jparsec
https://github.com/jparsec/jparsec/wiki/Tutorial

Пример калькулятора
https://github.com/jparsec/jparsec/blob/master/jparsec-examples/src/main/java/org/jparsec/examples/calculator/Calculator.java
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
public final class Calculator {
  
  /** Parsers {@code source} and evaluates to an {@link Integer}. */
  public static int evaluate(String source) {
    return parser().parse(source);
  }
  
  static final Parser<Integer> NUMBER = Scanners.INTEGER.map(Integer::valueOf);
  
  static final BinaryOperator<Integer> PLUS = (a, b) -> a + b;
  
  static final BinaryOperator<Integer> MINUS = (a, b) -> a - b;
  
  static final BinaryOperator<Integer> MUL = (a, b) -> a * b;
  
  static final BinaryOperator<Integer> DIV = (a, b) -> a / b;
  
  static final BinaryOperator<Integer> MOD = (a, b) -> a % b;
  
  static final UnaryOperator<Integer> NEG = a -> -a;
  
  private static <T> Parser<T> op(char ch, T value) {
    return isChar(ch).retn(value);
  }
  
  static Parser<Integer> parser() {
    Parser.Reference<Integer> ref = Parser.newReference();
    Parser<Integer> term = ref.lazy().between(isChar('('), isChar(')')).or(NUMBER);
    Parser<Integer> parser = new OperatorTable<Integer>()
        .prefix(op('-', NEG), 100)
        .infixl(op('+', PLUS), 10)
        .infixl(op('-', MINUS), 10)
        .infixl(op('*', MUL), 20)
        .infixl(op('/', DIV), 20)
        .infixl(op('%', MOD), 20)
        .build(term);
    ref.set(parser);
    return parser;
  }
}


Пример SQL парсера:
https://github.com/jparsec/jparsec/tree/master/jparsec-examples/src/main/java/org/jparsec/examples/sql

Забавный проект, не находите?
...
Рейтинг: 0 / 0
Расширяемый парсер для интерпретатора выражений (калькулятор с функциями)
    #39379870
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
По статистике использований mvnrepository, ANTLR более распространен чем JavaCC.
...
Рейтинг: 0 / 0
Расширяемый парсер для интерпретатора выражений (калькулятор с функциями)
    #39379871
kealon(Ruslan)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
uid uniqueСмущает то что нужно писать грамматику, потом компилить ее и делать какие то пассы руками, было бы отлично компилить парсер на лету, модифицируя грамматику программным путем.

Код предгенерации парсера может быть очень большим и трудозатратным для выполнения - даже лексер не так прост как кажется, так что это логично
Гораздо больше людей смущают ошибки, которые выдают нагенерированные парсеры
uid uniqueНаткнулся на интересный проект где парсер создается из кода а не компиляцией файла грамматики как в JavaCC / ANTLR, это может быть интересно:
https://github.com/jparsec/jparsec
https://github.com/jparsec/jparsec/wiki/Tutorial
...
Забавный проект, не находите?
Таких проектов - вагон и маленькая тележка
Большинство из них страдают и по скорости работы (вплоть до O(exp(N)) ) и по трудозатратам на их поддержку
Более-менее как-то можно приспособить вариации алгоритма сортировочная станция . Но в целом это выходит сложнее для понимания и расширения, чем код на EBNF
...
Рейтинг: 0 / 0
Расширяемый парсер для интерпретатора выражений (калькулятор с функциями)
    #39379921
no56892
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Apache felix жеж
...
Рейтинг: 0 / 0
Расширяемый парсер для интерпретатора выражений (калькулятор с функциями)
    #39380022
uid unique
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
no56892Apache felix жеж
OSGi контейнер для динамически подключаемых/отключаемых сервисов но как это относится к парсеру.
...
Рейтинг: 0 / 0
Расширяемый парсер для интерпретатора выражений (калькулятор с функциями)
    #39380029
uid unique
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
kealon(Ruslan)Более-менее как-то можно приспособить вариации алгоритма сортировочная станция . Но в целом это выходит сложнее для понимания и расширения, чем код на EBNF
Самое простое получается сделать грамматику с токеном функции, потом сгенерить парсер на АNTRL или JavaCC, получить из выражения бинарное дерево и пройти его стековой машиной с нижнего правого листа до корня.
...
Рейтинг: 0 / 0
Расширяемый парсер для интерпретатора выражений (калькулятор с функциями)
    #39380106
no56892
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
uid uniqueno56892Apache felix жеж
OSGi контейнер для динамически подключаемых/отключаемых сервисов но как это относится к парсеру.
Добавляем бандл с новым оператором -> парсер в рантайме ловит это событие и записывает в свой мэп операторов. Бандл конечно нужно компилить и собирать это понятно, но вот перекомпилировать/перезапускать сам парсер не нужно.
...
Рейтинг: 0 / 0
Расширяемый парсер для интерпретатора выражений (калькулятор с функциями)
    #39380174
uid unique
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
no56892uid uniqueпропущено...

OSGi контейнер для динамически подключаемых/отключаемых сервисов но как это относится к парсеру.
Добавляем бандл с новым оператором -> парсер в рантайме ловит это событие и записывает в свой мэп операторов. Бандл конечно нужно компилить и собирать это понятно, но вот перекомпилировать/перезапускать сам парсер не нужно.
Конечно можно и OSGi использовать, это почти что (веб) сервисы - сериализация, интерфейсы в блюпринтах, свои плюсы но и свои минусы. Самое печальное когда OSGi сервис в большом приложении тихо падает и понять что именно сдохло можно только через лог или OSGi консоль.
...
Рейтинг: 0 / 0
Расширяемый парсер для интерпретатора выражений (калькулятор с функциями)
    #39380251
kealon(Ruslan)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
uid uniquekealon(Ruslan)Более-менее как-то можно приспособить вариации алгоритма сортировочная станция . Но в целом это выходит сложнее для понимания и расширения, чем код на EBNF
Самое простое получается сделать грамматику с токеном функции, потом сгенерить парсер на АNTRL или JavaCC, получить из выражения бинарное дерево и пройти его стековой машиной с нижнего правого листа до корня.
не всегда, стопроцентно потребуется ленивый if(expr, v_if, v_else) - его так не сделать
...
Рейтинг: 0 / 0
Расширяемый парсер для интерпретатора выражений (калькулятор с функциями)
    #39380289
Фотография Usman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
uid uniqueДля калькулятора слишком могучая (и тяжелая) штукаЭт точно
uid uniqueНужен калькулятор с поддержкой функций, функции могут быть специфическими, к примеру залезть в базу, найти какой маппинг и тд и выдать результат который в свою очередь может обработаться другой бизнес функцией (в этом причина зачем нужна поддержка новых функций через конфиг), пример вычисления какой то надписи с заходом в базу и получением имя хоста (об имплементации этих фунций мы понятия не имеем, наше дело скормить им аргументы и получить результат)SpEL - подходит под ваши требования: парсер выражений из коробки, имеется возможность вызова Java-функций:
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
package ru.sql.forum.java;

import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

public class SpEL {
    
    public static void main(String[] args) throws Exception {
        StandardEvaluationContext context = new StandardEvaluationContext();
        
        context.registerFunction("cos",
            Math.class.getDeclaredMethod("cos", new Class[] { double.class }));
        
        ExpressionParser parser = new SpelExpressionParser();
        
        int result = parser.parseExpression("2 + 2 * 2").getValue(int.class);
        
        double cos2Pi = 
            parser
                .parseExpression("#cos(2 * T(java.lang.Math).PI)")
                .getValue(context, double.class);        
        
        System.out.println(result);
        System.out.println(cos2Pi);
    }
}
...
Рейтинг: 0 / 0
Расширяемый парсер для интерпретатора выражений (калькулятор с функциями)
    #39380343
uid unique
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
UsmanSpEL - подходит под ваши требования: парсер выражений из коробки, имеется возможность вызова Java-функций:

Спасибо Usman за наводку, это "ядрен батон".
присмотрюсь внимательнее так как спринг пробегает почти во всех проектах хотя бы косвенно (в виде зависимости):

http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
10.5.12 Functions

You can extend SpEL by registering user defined functions that can be called within the expression string. The function is registered with the StandardEvaluationContext using the method.

public void registerFunction(String name, Method m)

This method is then registered with the evaluation context and can be used within an expression string.

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();

context.registerFunction("reverseString",
    StringUtils.class.getDeclaredMethod("reverseString", new Class[] { String.class }));

String helloWorldReversed = parser.parseExpression(
    "#reverseString('hello')").getValue(context, String.class);
...
Рейтинг: 0 / 0
17 сообщений из 17, страница 1 из 1
Форумы / Java [игнор отключен] [закрыт для гостей] / Расширяемый парсер для интерпретатора выражений (калькулятор с функциями)
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]