173 lines
5.0 KiB
Python
173 lines
5.0 KiB
Python
#!/usr/bin/env python3.9
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
JFinal Arith - Arithmetic Expression
|
|
"""
|
|
|
|
from .Expr import Expr
|
|
from decimal import Decimal, ROUND_HALF_UP
|
|
|
|
class Arith(Expr):
|
|
"""Arithmetic expression"""
|
|
|
|
ADD = "+"
|
|
SUB = "-"
|
|
MUL = "*"
|
|
DIV = "/"
|
|
MOD = "%"
|
|
|
|
_rounding_mode = ROUND_HALF_UP
|
|
|
|
def __init__(self, left: Expr, operator: str, right: Expr):
|
|
"""
|
|
Initialize arithmetic expression
|
|
|
|
Args:
|
|
left: Left expression
|
|
operator: Arithmetic operator
|
|
right: Right expression
|
|
"""
|
|
self._left = left
|
|
self._operator = operator
|
|
self._right = right
|
|
|
|
def eval(self, scope):
|
|
"""Evaluate arithmetic expression"""
|
|
left_val = self._left.eval(scope)
|
|
right_val = self._right.eval(scope)
|
|
|
|
# Handle None values
|
|
if left_val is None or right_val is None:
|
|
return None
|
|
|
|
# Convert to appropriate numeric types
|
|
left_num = self._to_number(left_val)
|
|
right_num = self._to_number(right_val)
|
|
|
|
if left_num is None or right_num is None:
|
|
return None
|
|
|
|
# Perform operation
|
|
if self._operator == Arith.ADD:
|
|
return left_num + right_num
|
|
elif self._operator == Arith.SUB:
|
|
return left_num - right_num
|
|
elif self._operator == Arith.MUL:
|
|
return left_num * right_num
|
|
elif self._operator == Arith.DIV:
|
|
if right_num == 0:
|
|
raise ZeroDivisionError("Division by zero")
|
|
return self._divide(left_num, right_num)
|
|
elif self._operator == Arith.MOD:
|
|
return left_num % right_num
|
|
else:
|
|
raise ValueError(f"Unknown operator: {self._operator}")
|
|
|
|
def _to_number(self, value):
|
|
"""Convert value to number"""
|
|
if isinstance(value, (int, float)):
|
|
return value
|
|
if isinstance(value, Decimal):
|
|
return value
|
|
if isinstance(value, str):
|
|
try:
|
|
if '.' in value:
|
|
return float(value)
|
|
else:
|
|
return int(value)
|
|
except ValueError:
|
|
return None
|
|
return None
|
|
|
|
def _divide(self, left, right):
|
|
"""Handle division with proper rounding"""
|
|
if isinstance(left, Decimal) or isinstance(right, Decimal):
|
|
result = Decimal(str(left)) / Decimal(str(right))
|
|
return result.quantize(Decimal('0.00000001'), rounding=self._rounding_mode)
|
|
else:
|
|
return left / right
|
|
|
|
@classmethod
|
|
def set_big_decimal_divide_rounding_mode(cls, rounding_mode):
|
|
"""Set rounding mode for decimal division"""
|
|
cls._rounding_mode = rounding_mode
|
|
|
|
def __repr__(self) -> str:
|
|
return f"Arith({self._left} {self._operator} {self._right})"
|
|
|
|
|
|
class Unary(Expr):
|
|
"""Unary expression (positive, negative, increment, decrement)"""
|
|
|
|
POSITIVE = "+"
|
|
NEGATIVE = "-"
|
|
NOT = "!"
|
|
|
|
def __init__(self, operator: str, expr: Expr):
|
|
"""
|
|
Initialize unary expression
|
|
|
|
Args:
|
|
operator: Unary operator
|
|
expr: Expression to operate on
|
|
"""
|
|
self._operator = operator
|
|
self._expr = expr
|
|
|
|
def eval(self, scope):
|
|
"""Evaluate unary expression"""
|
|
val = self._expr.eval(scope)
|
|
|
|
if self._operator == Unary.POSITIVE:
|
|
return +val if isinstance(val, (int, float)) else val
|
|
elif self._operator == Unary.NEGATIVE:
|
|
return -val if isinstance(val, (int, float)) else val
|
|
elif self._operator == Unary.NOT:
|
|
return not val
|
|
else:
|
|
raise ValueError(f"Unknown operator: {self._operator}")
|
|
|
|
def __repr__(self) -> str:
|
|
return f"Unary({self._operator}{self._expr})"
|
|
|
|
|
|
class IncDec(Expr):
|
|
"""Increment/Decrement expression"""
|
|
|
|
INC = "++"
|
|
DEC = "--"
|
|
|
|
def __init__(self, expr: Expr, operator: str, is_prefix: bool = True):
|
|
"""
|
|
Initialize increment/decrement
|
|
|
|
Args:
|
|
expr: Expression to increment/decrement
|
|
operator: INC or DEC
|
|
is_prefix: Whether this is prefix (++x) or postfix (x++)
|
|
"""
|
|
self._expr = expr
|
|
self._operator = operator
|
|
self._is_prefix = is_prefix
|
|
|
|
def eval(self, scope):
|
|
"""Evaluate increment/decrement"""
|
|
# This is a simplified implementation
|
|
# In a real implementation, this would modify the variable
|
|
current_val = self._expr.eval(scope)
|
|
|
|
if isinstance(current_val, int):
|
|
if self._operator == IncDec.INC:
|
|
return current_val + 1
|
|
else:
|
|
return current_val - 1
|
|
|
|
return current_val
|
|
|
|
def __repr__(self) -> str:
|
|
op_str = self._operator
|
|
if self._is_prefix:
|
|
return f"IncDec({op_str}{self._expr})"
|
|
else:
|
|
return f"IncDec({self._expr}{op_str})"
|