Определите тип возвращаемого значения выражения Java в строке во время выполнения

Во время выполнения в моей Java-программе, учитывая строку, я хотел бы знать тип возвращаемого значения. Например:

  • 1 + 1 возвращается int
  • 1L + 1L возвращается long
  • 1L + 1 возвращается long
  • 1 + 1.5 возвращается double
  • 1 + 2 - 3 * 4 / 5 возвращается int
  • 1 / 0 возвращается int
  • 1 + Math.nextInt() возвращается int
  • 1.5 + Math.nextInt() возвращается double
  • Color.RED возвращается java.awt.Color
  • При условии a это int: a + 1 возвращается int
  • При условии a это int: a + 1.5 возвращается double

Нет необходимости фактически оценивать код: мне просто нужен тип возвращаемого значения. Как я могу сделать это с помощью JDK-компилятора времени выполнения, ECJ JDT или любой другой чистой Java-зависимости?


Подробный код: Вот упрощенный модульный тест псевдокода для этого кода:

public static void ExpressionTyper {
    public String determineType(String expression, Map<String, String> variableTypes) {
       ... // How do I implement this?
    }
}
public void ExpressionTyperTest {
    @Test public void determineType() {
        assertEquals("int", ExpressionTyper.determineType("1 + 1", emptyMap());
        assertEquals("long", ExpressionTyper.determineType("1 + 1L", emptyMap());
        assertEquals("double", ExpressionTyper.determineType("1 + 1.5", emptyMap());
        assertEquals("int", ExpressionTyper.determineType("a + 1", mapOf({"a", "int"}));
        assertEquals("int", ExpressionTyper.determineType("a + b", mapOf({"a", "int"}, {"b", "int"}));
        assertEquals("double", ExpressionTyper.determineType("a + b", mapOf({"a", "double"}, {"b", "int"}));
    }
}

3 ответа

Решение

Я думаю, что это зависит от диапазона ввода, который вы хотите обработать.

Видите ли, в конце вы спрашиваете: как мне вычислить строковые выражения во время выполнения.

Итак, короткий ответ: вам нужна какая-то реализация интерпретатора / REPL; или, по крайней мере, "части" этого.

Другой подход может состоять в том, чтобы использовать компилятор javax, чтобы просто скомпилировать объект, а затем вывести тип, как здесь.

Другие варианты будут соответствовать определенным темам "компиляции", таким как постоянное сворачивание.

Я не пробовал это...

  1. Оберните свое выражение в коде так:

      public class Test {
          private Test xxx = <<insert expression>>;
      }
    
  2. Скомпилируйте код.

  3. Разобрать сообщение об ошибке компиляции, чтобы извлечь представление компилятора о типе RHS.

Проблема в том, что выражение как Math.nextInt() может потребоваться, чтобы импорт был компилируемым, и я сомневаюсь, что существует надежный способ сделать вывод о том, каким должен быть импорт. Тем не менее, это должно работать для полезного набора выражений.

Этот подход также хрупок и непереносим, ​​поскольку он зависит от точной формы сообщения об ошибке компиляции, которое может зависеть от компилятора / версии.


Лучшее решение (но больше работы) было бы реализовать синтаксический анализатор и проверку типов для вашего языка выражений подмножества Java.

Чтобы сделать это для произвольных выражений, вам нужен полный интерфейс Java, который будет анализировать строку и определять ее тип. По сути, вам нужно то, что делает компилятор.

Eclipse JDT может предложить решение; Я не очень знаком с этим.

Это можно сделать с помощью нашего набора инструментов для реинжиниринга программного обеспечения DMS с полным интерфейсом Java DMS. DMS анализирует источник в соответствии с внешним интерфейсом, который он использует, а затем может вызывать внешние службы для анализа этого кода.

Интерфейс Java обеспечивает синтаксический анализ Java в различных версиях и различных способов синтаксического анализа (файл, поток, строка), а затем создает дерево для проанализированного текста. Для распознавателя имен Java, встроенного во внешний интерфейс, может быть предложено вычислить тип произвольного выражения в соответствии с правилами области действия, действующими в конкретной точке кода.

Для OP это может быть не то, что он хочет, так как он настаивает на ответе на основе Java, а DMS не на основе Java (он просил "... любую другую зависимость"). Самый близкий из них - это вызвать DMS как подпроцесс и попросить DMS напечатать тип выражения. Если его выражения имеют чрезвычайно малое количество типов, как показано в его примере, это, вероятно, сработает. Если нет, ему понадобится сложный механизм для чтения типов выражений, который во всей своей красе (например, в форме с разрешением на пакеты, в шаблонной форме) может быть довольно сложным.

Если OP хочет строго ограничить класс выражений, которые ему нужно обработать, простой арифметикой, он может создать собственный синтаксический анализатор выражений и сам определить тип. Смотрите мой SO-ответ о том, как вручную создать синтаксический анализатор рекурсивного спуска: /questions/2016396/est-li-alternativa-flexbison-kotoruyu-mozhno-ispolzovat-v-8-bitnyih-vstroennyih-sistemah/2016399#2016399 этой же ветке ответов также показано, как построить дерево и "оценить" его; он может оценить его по типу, а не по вычисленному результату.

В противном случае ему понадобится большой молоток.

Другие вопросы по тегам