Статическая типизация языка сценариев в Java

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

Так что парсер имеет next() а также peek() методы итерации по потоку токенов. В настоящее время он построен из методов иерархии, которые вызывают друг друга таким образом, что сохраняет приоритет типа (самый нижний метод, возвращающий константу, переменную и т. Д.). Каждый метод возвращает IResolve имеет общий тип <T> это "решает", чтобы. Например, вот метод, который обрабатывает "или" выражения, причем "и" более тесно связаны:

protected final IResolve checkGrammar_Or() throws ParseException
{
    IResolve left = checkGrammar_And();

    if (left == null)
        return null;

    if (peek().type != TokenType.IDENTIFIER || !"or".equals((String)peek().value))
        return left;

    next();

    IResolve right = checkGrammar_Or();

    if (right == null)
        throwExpressionException();

    return new BinaryOperation(left, right, new LogicOr());
}

Проблема в том, когда мне нужно реализовать функцию, которая зависит от типа. Как вы, наверное, заметили, универсальный тип не определяется синтаксическим анализатором и является частью проблемы проектирования. В этой функции я надеялся сделать что-то вроде следующего (хотя это не сработало бы из-за стирания универсальных типов...)

protected final IResolve checkGrammar_Comparison() throws ParseException
{
    IResolve left = checkGrammer_Term();

    if (left == null)
        return null;

    IBinaryOperationType op;

    switch (peek().type)
    {
    default:
        return left;

    case LOGIC_LT:

        //This ain't gonna work because of erasure
        if (left instanceof IResolve<Double>)
            op = new LogicLessThanDouble();

        break;

    //And the same for these
    case LOGIC_LT_OR_EQUAL:
    case LOGIC_GT:
    case LOGIC_GT_OR_EQUAL:
    }

    next();

    IResolve right = checkGrammar_Comparison();

    if (right == null)
        throwExpressionException();

    return new BinaryOperation(left, right, op);
}

Проблемное место, где я хотел бы установить соединение, находится в операторе switch. Я уже уверен, что мне нужно сделать IResolve неуниверсальным и дать ему метод "getType()", который возвращает int или что-то, особенно если я хочу поддерживать определяемые пользователем классы в будущем.

Вопрос в том:

Какой лучший способ добиться статической типизации, учитывая мою текущую структуру и желание смешанного наследования (пользовательские классы и интерфейсы, такие как Java и C#)? Если нет хорошего способа, как я могу изменить или даже перестроить свою структуру, чтобы достичь ее?

Примечание: я не претендую на то, чтобы иметь представление о том, во что ввязался, конструктивная критика приветствуется. Если мне нужно что-то уточнить, дайте мне знать!

Еще одно замечание: я знаю, что вы думаете "зачем использовать статическую типизацию?", И обычно я бы с вами согласился, однако игровой мир состоит из вокселей (точнее, мод для Minecraft), и работа с ними требует будь быстрым Представьте себе сценарий, который представляет собой алгоритм O(n^2), повторяющийся более 100 блоков двадцать раз в секунду, для 30+ игроков на дешевом сервере, который уже почти не скрипит... или один огромный взрыв, производящий тысячи блоков, неизбежно вызывая ужасный скачок задержки. Следовательно, проверка типа бэкэнда или любая форма утки не приведут к ее сокращению (хотя я отчаянно жажду этого). Преимущества низкого уровня являются необходимостью в данном конкретном случае, хотя и болезненными.

2 ответа

Решение

Как я уже говорил в комментариях, решение, с которым я столкнулся, заключалось в том, чтобы разделить синтаксический анализ и ввод в отдельные этапы, наряду с использованием enum представлять тип, как я изначально чувствовал, что должен.

Хотя я ценю ответ Тэймона, я не могу его использовать, если надеюсь на поддержку пользовательских классов в будущем.

Если у кого-то есть лучшее решение, я был бы более чем счастлив принять его!

Вы можете получить лучшее из обоих миров, добавив метод Class<T> getType() в IResolve; его разработчики должны просто вернуть соответствующий Class объект. (Если сами реализации являются общими, вам нужно получить ссылку на этот объект в конструкторе или что-то в этом роде.)

Вы можете сделать left.getType().equals(Double.class), так далее.

Это полностью отделено от вопроса о том, стоит ли вам создавать собственный синтаксический анализатор со статической типизацией, который очень стоит задать.

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