在Java中静态键入脚本语言

时间:2012-06-26 04:13:35

标签: java parsing interpreter scripting-language static-typing

我正在用Java构建一个游戏脚本语言,而我正在研究解析器。玩家/模组/我自己使用该语言来创建自定义法术和效果。但是,我很难想象如何在当前系统中平滑地实现静态类型(由性能需求驱动的痛苦必要性)。如果编译速度很快,我并不在乎,但实际执行需要尽可能快(至少在合理范围内。我希望很快能完成这项工作。)

因此,解析器具有next()peek()方法来迭代标记流。它目前由层次结构方法构建,这些方法以保留类型优先级的方式相互调用(“最底层”方法返回常量,变量等)。每个方法都返回IResolve,其{“1}}具有”解析“到的通用类型<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 mod)并且使用他们需要快速。想象一个脚本,它是一个O(n ^ 2)算法,每秒二十次迭代超过100个块,对于一个廉价服务器上的30多个玩家来说,已经几乎没有吱吱作响...或者,一个单一的,大规模的爆炸影响了数千块,不可避免地造成了可怕的滞后飙升。因此,后端类型检查或任何形式的鸭子打字都不会削减它(虽然我迫不及待地想要它。)在这种特殊情况下,低水平的好处是必要的,尽管它很痛苦。

2 个答案:

答案 0 :(得分:1)

通过向Class<T> getType()添加方法IResolve,您可以充分利用这两个方面。它的实现者应该只返回适当的Class对象。 (如果实现者本身是通用的,则需要在构造函数中获取对该对象的引用。)

然后,您可以执行left.getType().equals(Double.class)等等。

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

答案 1 :(得分:0)

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

虽然我很欣赏Taymon的答案,但如果我希望将来支持用户定义的课程,我就无法使用它。

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