Exemple #1
0
def parse_abs_op(assignment):
    assert isinstance(assignment.value, expression.AbsOp)
    actions = parse_actionD(ParameterAssignment(assignment.param, assignment.value.expr))
    cond_block = conditional.Conditional(
        nmlop.CMP_LT(assignment.value.expr, 0),
        [ParameterAssignment(assignment.param, nmlop.SUB(0, assignment.value.expr))],
        None,
    )
    actions.extend(conditional.ConditionalList([cond_block]).get_action_list())
    return actions
Exemple #2
0
    def preprocess_binop(self, expr):
        """
        Several nml operators are not directly support by nfo so we have to work
        around that by implementing those operators in terms of others.

        @return: A pre-processed version of the expression.
        @rtype:  L{Expression}
        """
        assert isinstance(expr, expression.BinOp)
        if expr.op == nmlop.CMP_LT:
            # return value is 0, 1 or 2, we want to map 0 to 1 and the others to 0
            expr = nmlop.VACT2_CMP(expr.expr1, expr.expr2)
            # reduce the problem to 0/1
            expr = nmlop.MIN(expr, 1)
            # and invert the result
            expr = nmlop.XOR(expr, 1)
        elif expr.op == nmlop.CMP_GT:
            # return value is 0, 1 or 2, we want to map 2 to 1 and the others to 0
            expr = nmlop.VACT2_CMP(expr.expr1, expr.expr2)
            # subtract one
            expr = nmlop.SUB(expr, 1)
            # map -1 and 0 to 0
            expr = nmlop.MAX(expr, 0)
        elif expr.op == nmlop.CMP_LE:
            # return value is 0, 1 or 2, we want to map 2 to 0 and the others to 1
            expr = nmlop.VACT2_CMP(expr.expr1, expr.expr2)
            # swap 0 and 2
            expr = nmlop.XOR(expr, 2)
            # map 1/2 to 1
            expr = nmlop.MIN(expr, 1)
        elif expr.op == nmlop.CMP_GE:
            # return value is 0, 1 or 2, we want to map 1/2 to 1
            expr = nmlop.VACT2_CMP(expr.expr1, expr.expr2)
            expr = nmlop.MIN(expr, 1)
        elif expr.op == nmlop.CMP_EQ:
            # return value is 0, 1 or 2, we want to map 1 to 1, other to 0
            expr = nmlop.VACT2_CMP(expr.expr1, expr.expr2)
            expr = nmlop.AND(expr, 1)
        elif expr.op == nmlop.CMP_NEQ:
            # same as CMP_EQ but invert the result
            expr = nmlop.VACT2_CMP(expr.expr1, expr.expr2)
            expr = nmlop.AND(expr, 1)
            expr = nmlop.XOR(expr, 1)

        elif expr.op == nmlop.HASBIT:
            # hasbit(x, n) ==> (x >> n) & 1
            expr = nmlop.SHIFTU_RIGHT(expr.expr1, expr.expr2)
            expr = nmlop.AND(expr, 1)
        elif expr.op == nmlop.NOTHASBIT:
            # !hasbit(x, n) ==> ((x >> n) & 1) ^ 1
            expr = nmlop.SHIFTU_RIGHT(expr.expr1, expr.expr2)
            expr = nmlop.AND(expr, 1)
            expr = nmlop.XOR(expr, 1)

        return expr.reduce()
Exemple #3
0
def builtin_date(name, args, pos):
    """
    date(year, month, day) builtin function.

    @return Days since 1 jan 1 of the given date.
    """
    days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    if len(args) != 3:
        raise generic.ScriptError("date() requires exactly 3 arguments", pos)
    identifier.ignore_all_invalid_ids = True
    year = args[0].reduce(global_constants.const_list)
    identifier.ignore_all_invalid_ids = False
    try:
        month = args[1].reduce_constant().value
        day = args[2].reduce_constant().value
    except generic.ConstError:
        raise generic.ScriptError(
            "Month and day parameters of date() should be compile-time constants",
            pos)
    generic.check_range(month, 1, 12, "month", args[1].pos)
    generic.check_range(day, 1, days_in_month[month - 1], "day", args[2].pos)

    if not isinstance(year, ConstantNumeric):
        if month != 1 or day != 1:
            raise generic.ScriptError(
                "when the year parameter of date() is not a compile time constant month and day should be 1",
                pos)
        # num_days = year*365 + year/4 - year/100 + year/400
        part1 = nmlop.MUL(year, 365)
        part2 = nmlop.DIV(year, 4)
        part3 = nmlop.DIV(year, 100)
        part4 = nmlop.DIV(year, 400)
        res = nmlop.ADD(part1, part2)
        res = nmlop.SUB(res, part3)
        res = nmlop.ADD(res, part4)
        return res

    generic.check_range(year.value, 0, 5000000, "year", year.pos)
    day_in_year = 0
    for i in range(month - 1):
        day_in_year += days_in_month[i]
    day_in_year += day
    if month >= 3 and (year.value % 4 == 0) and ((not year.value % 100 == 0) or
                                                 (year.value % 400 == 0)):
        day_in_year += 1
    return ConstantNumeric(
        year.value * 365 + calendar.leapdays(0, year.value) + day_in_year - 1,
        pos)
Exemple #4
0
def parse_actionD(assignment):
    assignment.value.supported_by_actionD(True)

    if isinstance(assignment.param, expression.SpecialParameter):
        assignment.param, assignment.value = assignment.param.to_assignment(assignment.value)
    elif isinstance(assignment.param, expression.Identifier):
        if global_constants.identifier_refcount[assignment.param.value] == 0:
            # Named parameter is not referenced, ignoring
            return []
        assignment.param = expression.Parameter(
            expression.ConstantNumeric(global_constants.named_parameters[assignment.param.value]), assignment.param.pos
        )
    assert isinstance(assignment.param, expression.Parameter)

    if isinstance(assignment.value, expression.SpecialParameter):
        assignment.value = assignment.value.to_reading()

    if isinstance(assignment.value, expression.TernaryOp):
        return parse_ternary_op(assignment)

    if isinstance(assignment.value, expression.AbsOp):
        return parse_abs_op(assignment)

    if isinstance(assignment.value, expression.SpecialCheck):
        return parse_special_check(assignment)

    if isinstance(assignment.value, expression.GRMOp):
        return parse_grm(assignment)

    if isinstance(assignment.value, expression.BinOp):
        op = assignment.value.op
        if op == nmlop.HASBIT or op == nmlop.NOTHASBIT:
            return parse_hasbit(assignment)
        elif op == nmlop.MIN or op == nmlop.MAX:
            return parse_min_max(assignment)

    if isinstance(assignment.value, expression.Boolean):
        return parse_boolean(assignment)

    if isinstance(assignment.value, expression.Not):
        expr = nmlop.SUB(1, assignment.value.expr)
        assignment = ParameterAssignment(assignment.param, expr)

    if isinstance(assignment.value, expression.BinNot):
        expr = nmlop.SUB(0xFFFFFFFF, assignment.value.expr)
        assignment = ParameterAssignment(assignment.param, expr)

    action6.free_parameters.save()
    action_list = []
    act6 = action6.Action6()
    assert isinstance(assignment.param, expression.Parameter)
    target = assignment.param.num
    if isinstance(target, expression.Parameter) and isinstance(target.num, expression.ConstantNumeric):
        act6.modify_bytes(target.num.value, 1, 1)
        target = expression.ConstantNumeric(0)
    elif not isinstance(target, expression.ConstantNumeric):
        tmp_param, tmp_param_actions = get_tmp_parameter(target)
        act6.modify_bytes(tmp_param, 1, 1)
        target = expression.ConstantNumeric(0)
        action_list.extend(tmp_param_actions)

    data = None
    # print assignment.value
    if isinstance(assignment.value, expression.ConstantNumeric):
        op = nmlop.ASSIGN
        param1 = expression.ConstantNumeric(0xFF)
        param2 = expression.ConstantNumeric(0)
        data = assignment.value
    elif isinstance(assignment.value, expression.Parameter):
        if isinstance(assignment.value.num, expression.ConstantNumeric):
            op = nmlop.ASSIGN
            param1 = assignment.value.num
        else:
            tmp_param, tmp_param_actions = get_tmp_parameter(assignment.value.num)
            act6.modify_bytes(tmp_param, 1, 3)
            action_list.extend(tmp_param_actions)
            op = nmlop.ASSIGN
            param1 = expression.ConstantNumeric(0)
        param2 = expression.ConstantNumeric(0)
    elif isinstance(assignment.value, expression.OtherGRFParameter):
        op = nmlop.ASSIGN
        if isinstance(assignment.value.num, expression.ConstantNumeric):
            param1 = assignment.value.num
        else:
            tmp_param, tmp_param_actions = get_tmp_parameter(assignment.value.num)
            act6.modify_bytes(tmp_param, 1, 3)
            action_list.extend(tmp_param_actions)
            param1 = expression.ConstantNumeric(0)
        param2 = expression.ConstantNumeric(0xFE)
        data = expression.ConstantNumeric(expression.parse_string_to_dword(assignment.value.grfid))
    elif isinstance(assignment.value, expression.PatchVariable):
        op = nmlop.ASSIGN
        param1 = expression.ConstantNumeric(assignment.value.num)
        param2 = expression.ConstantNumeric(0xFE)
        data = expression.ConstantNumeric(0xFFFF)
    elif isinstance(assignment.value, expression.BinOp):
        op, expr1, expr2, extra_actions = transform_bin_op(assignment)
        action_list.extend(extra_actions)

        if isinstance(expr1, expression.ConstantNumeric):
            param1 = expression.ConstantNumeric(0xFF)
            data = expr1
        elif isinstance(expr1, expression.Parameter) and isinstance(expr1.num, expression.ConstantNumeric):
            param1 = expr1.num
        else:
            tmp_param, tmp_param_actions = get_tmp_parameter(expr1)
            action_list.extend(tmp_param_actions)
            param1 = expression.ConstantNumeric(tmp_param)

        # We can use the data only for one for the parameters.
        # If the first parameter uses "data" we need a temp parameter for this one
        if isinstance(expr2, expression.ConstantNumeric) and data is None:
            param2 = expression.ConstantNumeric(0xFF)
            data = expr2
        elif isinstance(expr2, expression.Parameter) and isinstance(expr2.num, expression.ConstantNumeric):
            param2 = expr2.num
        else:
            tmp_param, tmp_param_actions = get_tmp_parameter(expr2)
            action_list.extend(tmp_param_actions)
            param2 = expression.ConstantNumeric(tmp_param)

    else:
        raise generic.ScriptError("Invalid expression in argument assignment", assignment.value.pos)

    if len(act6.modifications) > 0:
        action_list.append(act6)

    action_list.append(ActionD(target, param1, op, param2, data))
    action6.free_parameters.restore()
    return action_list
Exemple #5
0
def transform_bin_op(assignment):
    op = assignment.value.op
    expr1 = assignment.value.expr1
    expr2 = assignment.value.expr2
    extra_actions = []

    if op == nmlop.CMP_GE:
        expr1, expr2 = expr2, expr1
        op = nmlop.CMP_LE

    if op == nmlop.CMP_LE:
        extra_actions.extend(parse_actionD(ParameterAssignment(assignment.param, nmlop.SUB(expr1, expr2))))
        op = nmlop.CMP_LT
        expr1 = assignment.param
        expr2 = expression.ConstantNumeric(1)

    if op == nmlop.CMP_GT:
        expr1, expr2 = expr2, expr1
        op = nmlop.CMP_LT

    if op == nmlop.CMP_LT:
        extra_actions.extend(parse_actionD(ParameterAssignment(assignment.param, nmlop.SUB(expr1, expr2))))
        op = nmlop.SHIFTU_LEFT  # shift left by negative number = shift right
        expr1 = assignment.param
        expr2 = expression.ConstantNumeric(-31)

    elif op == nmlop.CMP_NEQ:
        extra_actions.extend(parse_actionD(ParameterAssignment(assignment.param, nmlop.SUB(expr1, expr2))))
        op = nmlop.DIV
        # We rely here on the (ondocumented) behavior of both OpenTTD and TTDPatch
        # that expr/0==expr. What we do is compute A/A, which will result in 1 if
        # A != 0 and in 0 if A == 0
        expr1 = assignment.param
        expr2 = assignment.param

    elif op == nmlop.CMP_EQ:
        # We compute A==B by doing not(A - B) which will result in a value != 0
        # if A is equal to B
        extra_actions.extend(parse_actionD(ParameterAssignment(assignment.param, nmlop.SUB(expr1, expr2))))
        # Clamp the value to 0/1, see above for details
        extra_actions.extend(
            parse_actionD(ParameterAssignment(assignment.param, nmlop.DIV(assignment.param, assignment.param)))
        )
        op = nmlop.SUB
        expr1 = expression.ConstantNumeric(1)
        expr2 = assignment.param

    if op == nmlop.SHIFT_RIGHT or op == nmlop.SHIFTU_RIGHT:
        if isinstance(expr2, expression.ConstantNumeric):
            expr2.value *= -1
        else:
            expr2 = nmlop.SUB(0, expr2)
        op = nmlop.SHIFT_LEFT if op == nmlop.SHIFT_RIGHT else nmlop.SHIFTU_LEFT

    elif op == nmlop.XOR:
        # a ^ b ==> (a | b) - (a & b)
        expr1 = parse_subexpression(expr1, extra_actions)
        expr2 = parse_subexpression(expr2, extra_actions)
        tmp_param1, tmp_action_list1 = get_tmp_parameter(nmlop.OR(expr1, expr2))
        tmp_param2, tmp_action_list2 = get_tmp_parameter(nmlop.AND(expr1, expr2))
        extra_actions.extend(tmp_action_list1)
        extra_actions.extend(tmp_action_list2)
        expr1 = expression.Parameter(expression.ConstantNumeric(tmp_param1))
        expr2 = expression.Parameter(expression.ConstantNumeric(tmp_param2))
        op = nmlop.SUB

    return op, expr1, expr2, extra_actions
Exemple #6
0
def builtin_abs(name, args, pos):
    if len(args) != 1:
        raise generic.ScriptError(name + "() must have 1 parameter", pos)
    guard = nmlop.CMP_LT(args[0], 0)
    return TernaryOp(guard, nmlop.SUB(0, args[0]), args[0], args[0].pos).reduce()
Exemple #7
0
 def p_unary_minus(self, t):
     "expression : MINUS expression"
     t[0] = nmlop.SUB(0, t[2], t.lineno(1))
Exemple #8
0
def value_sign_extend(var, info):
    #r = (x ^ m) - m; with m being (1 << (num_bits -1))
    m = expression.ConstantNumeric(1 << (info['size'] - 1))
    return nmlop.SUB(nmlop.XOR(var, m), m)
Exemple #9
0
def vehicle_length(value):
    return nmlop.SUB(8, value)
Exemple #10
0
    def reduce(self, id_dicts=None, unknown_id_fatal=True):
        # Reducing a BinOp expression is done in several phases:
        # - Reduce both subexpressions.
        # - If both subexpressions are constant, compute the result and return it.
        # - If the operator allows it and the second expression is more complex than
        #   the first one swap them.
        # - If the operation is a no-op, delete it.
        # - Variables (as used in action2var) can have some computations attached to
        #   them, do that if possible.
        # - Try to merge multiple additions/subtractions with constant numbers

        # - Reduce both subexpressions.
        expr1 = self.expr1.reduce(id_dicts)
        expr2 = self.expr2.reduce(id_dicts)

        # Make sure the combination of operands / operator is valid
        if self.op.validate_func is not None:
            self.op.validate_func(expr1, expr2, self.pos)

        # - If both subexpressions are constant, compute the result and return it.
        if (
            isinstance(expr1, ConstantNumeric)
            and isinstance(expr2, ConstantNumeric)
            and self.op.compiletime_func is not None
        ):
            return ConstantNumeric(self.op.compiletime_func(expr1.value, expr2.value), self.pos)

        if isinstance(expr1, StringLiteral) and isinstance(expr2, StringLiteral):
            assert self.op == nmlop.ADD
            return StringLiteral(expr1.value + expr2.value, expr1.pos)

        if (
            isinstance(expr1, (ConstantNumeric, ConstantFloat))
            and isinstance(expr2, (ConstantNumeric, ConstantFloat))
            and self.op.compiletime_func is not None
        ):
            return ConstantFloat(self.op.compiletime_func(expr1.value, expr2.value), self.pos)

        # - If the operator allows it and the second expression is more complex than
        #   the first one swap them.
        op = self.op
        if op.commutative or op in (nmlop.CMP_LT, nmlop.CMP_GT):
            prio1 = self.get_priority(expr1)
            prio2 = self.get_priority(expr2)
            if prio2 < prio1:
                expr1, expr2 = expr2, expr1
                if op == nmlop.CMP_LT:
                    op = nmlop.CMP_GT
                elif op == nmlop.CMP_GT:
                    op = nmlop.CMP_LT

        # - If the operation is a no-op, delete it.
        if op == nmlop.AND and isinstance(expr2, ConstantNumeric) and (expr2.value == -1 or expr2.value == 0xFFFFFFFF):
            return expr1

        if op in (nmlop.DIV, nmlop.DIVU, nmlop.MUL) and isinstance(expr2, ConstantNumeric) and expr2.value == 1:
            return expr1

        if op in (nmlop.ADD, nmlop.SUB) and isinstance(expr2, ConstantNumeric) and expr2.value == 0:
            return expr1

        # - Variables (as used in action2var) can have some computations attached to
        #   them, do that if possible.
        if isinstance(expr1, Variable) and expr2.supported_by_actionD(False):
            # An action2 Variable has some special fields (mask, add, div and mod) that can be used
            # to perform some operations on the value. These operations are faster than a normal
            # advanced varaction2 operator so we try to use them whenever we can.
            if op == nmlop.AND and expr1.add is None:
                expr1.mask = nmlop.AND(expr1.mask, expr2, self.pos).reduce(id_dicts)
                return expr1
            if op == nmlop.ADD and expr1.div is None and expr1.mod is None:
                if expr1.add is None:
                    expr1.add = expr2
                else:
                    expr1.add = nmlop.ADD(expr1.add, expr2, self.pos).reduce(id_dicts)
                return expr1
            if op == nmlop.SUB and expr1.div is None and expr1.mod is None:
                if expr1.add is None:
                    expr1.add = ConstantNumeric(0)
                expr1.add = nmlop.SUB(expr1.add, expr2, self.pos).reduce(id_dicts)
                return expr1
            # The div and mod fields cannot be used at the same time. Also whenever either of those
            # two are used the add field has to be set, so we change it to zero when it's not yet set.
            if op == nmlop.DIV and expr1.div is None and expr1.mod is None:
                if expr1.add is None:
                    expr1.add = ConstantNumeric(0)
                expr1.div = expr2
                return expr1
            if op == nmlop.MOD and expr1.div is None and expr1.mod is None:
                if expr1.add is None:
                    expr1.add = ConstantNumeric(0)
                expr1.mod = expr2
                return expr1
            # Since we have a lot of nml-variables that are in fact only the high bits of an nfo
            # variable it can happen that we want to shift back the variable to the left.
            # Don't use any extra opcodes but just reduce the shift-right in that case.
            if (
                op == nmlop.SHIFT_LEFT
                and isinstance(expr2, ConstantNumeric)
                and expr1.add is None
                and expr2.value < expr1.shift.value
            ):
                expr1.shift.value -= expr2.value
                expr1.mask = nmlop.SHIFT_LEFT(expr1.mask, expr2).reduce()
                return expr1

        # - Try to merge multiple additions/subtractions with constant numbers
        if (
            op in (nmlop.ADD, nmlop.SUB)
            and isinstance(expr2, ConstantNumeric)
            and isinstance(expr1, BinOp)
            and expr1.op in (nmlop.ADD, nmlop.SUB)
            and isinstance(expr1.expr2, ConstantNumeric)
        ):
            val = expr2.value if op == nmlop.ADD else -expr2.value
            if expr1.op == nmlop.ADD:
                return nmlop.ADD(expr1.expr1, (expr1.expr2.value + val), self.pos).reduce()
            if expr1.op == nmlop.SUB:
                return nmlop.SUB(expr1.expr1, (expr1.expr2.value - val), self.pos).reduce()

        if op == nmlop.OR and isinstance(expr1, Boolean) and isinstance(expr2, Boolean):
            return Boolean(nmlop.OR(expr1.expr, expr2.expr, self.pos)).reduce(id_dicts)

        return BinOp(op, expr1, expr2, self.pos)