2

我正在为游戏构建 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) 算法,每秒 20 次迭代超过 100 个块,为 30 多个玩家在一个已经几乎没有吱吱声的廉价服务器上......或者,不可避免地影响数千个块的单个大规模爆炸导致可怕的滞后峰值。因此,后端类型检查或任何形式的鸭式打字都不会削减它(尽管我非常渴望它atm。)在这种特殊情况下,低级别的好处是必要的,尽管它是痛苦的。

4

2 回答 2

1

您可以通过向;添加一个方法Class<T> getType()来获得两全其美的效果。IResolve它的实现者应该简单地返回适当的Class对象。(如果实现者本身是通用的,则需要在构造函数或其他东西中获取对该对象的引用。)

然后你可以做left.getType().equals(Double.class),等等。

这与您是否应该使用静态类型构建自己的解析器的问题完全不同,这是非常值得一问的。

于 2012-06-26T05:25:03.257 回答
0

正如一些人在评论中所建议的那样,我要采用的解决方案是将解析和键入分成单独的阶段,以及使用enum我最初认为应该的类型来表示类型。

虽然我很欣赏 Taymon 的回答,但如果我希望将来支持用户定义的类,我就不能使用它。

如果有人有更好的解决方案,我会非常乐意接受!

于 2012-07-05T21:11:26.290 回答