diff --git a/src/autoscript/ASEngine.py b/src/autoscript/ASEngine.py index f73657b..352f4a8 100644 --- a/src/autoscript/ASEngine.py +++ b/src/autoscript/ASEngine.py @@ -33,7 +33,7 @@ from .ASTokenizer import ( ) -__all__ = ["execute", "addTargetVar"] +__all__ = ["execute", "addTargetVar", "splitTopLevel"] # Engine state @@ -69,7 +69,7 @@ _RE_CUR_DATE_OFFSET = re.compile(r"^CURRENT_DATE\s*\+\s*(\d+)$", re.IGNORECASE) _RE_CUR_TIME_OFFSET = re.compile(r"^CURRENT_TIME\s*\+\s*(\d+)$", re.IGNORECASE) -def _splitTopLevel( +def splitTopLevel( text: str, delimiter: str ) -> list: @@ -281,13 +281,13 @@ def _evaluateCondition( s = condition_str.strip() if not s: return False - or_parts = _splitTopLevel(s, ".OR.") + or_parts = splitTopLevel(s, ".OR.") if len(or_parts) > 1: return any( _evaluateCondition(p.strip(), target_data, line) for p in or_parts ) - and_parts = _splitTopLevel(s, ".AND.") + and_parts = splitTopLevel(s, ".AND.") if len(and_parts) > 1: return all( _evaluateCondition(p.strip(), target_data, line) @@ -466,12 +466,14 @@ class _EngineExecutor(NodeVisitor): return self._cur_line + def _incLine( self ): self._cur_line += 1 + def visitScript( self, _node: Script @@ -480,6 +482,7 @@ class _EngineExecutor(NodeVisitor): for child in _node.body: child.accept(self) + def visitIf( self, _node: IfNode @@ -506,6 +509,7 @@ class _EngineExecutor(NodeVisitor): for child in _node.else_body: child.accept(self) + def visitSet( self, _node: SetNode @@ -515,6 +519,7 @@ class _EngineExecutor(NodeVisitor): full_line = f"SET {_node.target} = {_node.value}" _executeSet(full_line, self._target_data, self._line) + def visitOp( self, _node: OpNode @@ -525,6 +530,7 @@ class _EngineExecutor(NodeVisitor): full_line = f"{_node.target} .{op_upper}. {_node.value}" _executeOperation(full_line, self._target_data, self._line) + def visitPass( self, _node: PassNode @@ -532,6 +538,7 @@ class _EngineExecutor(NodeVisitor): self._incLine() + def visitUnrecog( self, _node: UnrecogNode diff --git a/src/autoscript/ASOperator.py b/src/autoscript/ASOperator.py index 49facd7..60d6195 100644 --- a/src/autoscript/ASOperator.py +++ b/src/autoscript/ASOperator.py @@ -17,7 +17,7 @@ from datetime import ( from .ASObject import ASObject -__all__ = ["ASOperator"] +__all__ = ["ASOperator", "ARITH_TYPES", "COMPARISON_OPERATORS"] class ASOperator: @@ -96,12 +96,11 @@ class ASOperator: target_val = target.getValue(target_data) if target_val is None: raise ValueError(f"'{target.name}' 的值为空,无法进行运算") - if op == ".ADD.": - cls._arithAdd(target, target_val, operand, target_data) - elif op == ".SUB.": - cls._arithSub(target, target_val, operand, target_data) - else: + op_name = "ADD" if op == ".ADD." else "SUB" if op == ".SUB." else None + if op_name is None: raise ValueError(f"不支持的操作 '{op}'") + sign = 1 if op == ".ADD." else -1 + cls._arithBinary(target, target_val, operand, target_data, sign, op_name) @classmethod def _arithBinary( @@ -110,13 +109,13 @@ class ASOperator: target_val, operand: ASObject, target_data: dict, - sign: int + sign: int, + op_name: str = "" ): - """Apply ADD (sign=1) or SUB (sign=-1) per type.""" + """Apply arithmetic per type.""" tp = target.var_type raw_op = operand._value - op_name = "ADD" if sign == 1 else "SUB" if tp == "Date": if not isinstance(target_val, date): @@ -129,35 +128,13 @@ class ASOperator: dt = datetime.combine(datetime.today(), target_val) + delta new_val = dt.time() elif tp == "Int": - new_val = int(target_val) + int(raw_op) * sign + new_val = int(target_val) + int(raw_op)*sign elif tp == "Float": - new_val = float(target_val) + float(raw_op) * sign + new_val = float(target_val) + float(raw_op)*sign else: raise ValueError(f"'{tp}' 类型不支持 {op_name} 操作") target.setValue(new_val, target_data) - @classmethod - def _arithAdd( - cls, - target: ASObject, - target_val, - operand: ASObject, - target_data: dict - ): - """Dispatch ADD per type.""" - cls._arithBinary(target, target_val, operand, target_data, 1) - - @classmethod - def _arithSub( - cls, - target: ASObject, - target_val, - operand: ASObject, - target_data: dict - ): - """Dispatch SUB per type.""" - cls._arithBinary(target, target_val, operand, target_data, -1) - @classmethod def compare( cls, @@ -206,3 +183,9 @@ class ASOperator: f"无法比较 '{left.name}' ({left.var_type}) " f"与 '{right.name}' ({right.var_type})" ) + + +# Public constants +# may be used by the GUI orchestration dialog. +ARITH_TYPES = ASOperator._ARITH_TYPES +COMPARISON_OPERATORS = set(ASOperator._COMPARE.keys()) diff --git a/src/autoscript/__init__.py b/src/autoscript/__init__.py index c761e6d..c570fe5 100644 --- a/src/autoscript/__init__.py +++ b/src/autoscript/__init__.py @@ -14,13 +14,16 @@ from autoscript.ASTokenizer import ( from autoscript.ASEngine import ( execute, addTargetVar, + splitTopLevel, ) from autoscript.ASObject import _META_VARS as META_VARS from autoscript.ASObserver import ParsingObserver + __all__ = [ "execute", "addTargetVar", + "splitTopLevel", "registerDefaultTargetVars", "buildMockTargetData", "META_VARS", @@ -35,18 +38,6 @@ __all__ = [ "ParsingObserver", ] -# All variables available to scripts (display_name -> (name, type)). -# This mirrors the old AutoScriptEngine.VARIABLE_META for backward -# compatibility in the UI orchestration dialog. -ALL_VARIABLES: dict = { - "用户名": ("USERNAME", "String"), - "用户启用": ("USER_ENABLE", "Boolean"), - "预约日期": ("RESERVE_DATE", "Date"), - "预约开始时间": ("RESERVE_BEGIN_TIME", "Time"), - "预约结束时间": ("RESERVE_END_TIME", "Time"), - "当前时间": ("CURRENT_TIME", "Time"), - "当前日期": ("CURRENT_DATE", "Date"), -} # Key paths into target_data dict for each target variable. # (name, type, key_path, display_name) @@ -57,6 +48,15 @@ _TARGET_VAR_DEFS = [ ("RESERVE_BEGIN_TIME", "Time", ["reserve_info", "begin_time", "time"], "预约开始时间"), ("RESERVE_END_TIME", "Time", ["reserve_info", "end_time", "time"], "预约结束时间"), ] + +# All variables (display_name -> (name, type)), derived from target vars + meta vars. +ALL_VARIABLES = { + display_name: (name, var_type) + for name, var_type, _, display_name in _TARGET_VAR_DEFS +} | { + obj.display_name: (obj.name, obj.var_type) + for obj in META_VARS.values() +} _MOCK_TYPE_VALUES = { "String": "__mock__", "Boolean": True,