1
1
mirror of https://github.com/KenanZhu/AutoLibrary.git synced 2026-06-18 23:43:02 +08:00

refactor(autoscript): 使用观察者模式解耦解析与预检查/编排流程

This commit is contained in:
2026-05-17 01:48:25 +08:00
parent 9bdc9a3de9
commit 2843300cf9
3 changed files with 191 additions and 39 deletions
+109 -26
View File
@@ -326,12 +326,27 @@ class ASTokenizer:
"""
Tokenizer / parser for the AutoScript DSL.
Provides three entry points:
Main class-level entry points (engine-facing):
- classifyLine(line) — single-line classifier.
- tokenize(script) — flat Stmt list.
- parse(script) — structured AST (Script root).
Observer-enabled API (used by pre-check & orchestration):
>>> obs = ScriptPrecheckObserver()
>>> stmts = ASTokenizer.tokenizeWithObservers(script, [obs])
"""
@classmethod
def _notifyObservers(
cls,
observers: list,
method: str,
*args
):
for obs in observers:
getattr(obs, method)(*args)
@classmethod
def _matchLine(
cls,
@@ -345,18 +360,29 @@ class ASTokenizer:
return (None, None)
@classmethod
def classifyLine(
def _buildStmt(
cls,
stripped: str
):
stripped: str,
kind: str | None,
data
) -> Stmt:
kind, data = cls._matchLine(stripped)
if kind is None or kind == K_PASS:
return None
return (kind, data)
stmt = Stmt(kind=kind, raw_line=stripped)
if kind == K_IF or kind == K_ELSE_IF:
stmt.condition = data
elif kind == K_SET:
stmt.target, stmt.value = data
stmt.op_type = OP_SET
elif kind == K_ADD:
stmt.target, stmt.value = data
stmt.op_type = OP_ADD
elif kind == K_SUB:
stmt.target, stmt.value = data
stmt.op_type = OP_SUB
return stmt
@classmethod
def tokenize(
def _tokenizeImpl(
cls,
script: str
) -> list:
@@ -367,29 +393,15 @@ class ASTokenizer:
if not stripped:
continue
kind, data = cls._matchLine(stripped)
stmt = Stmt(kind=kind, raw_line=stripped)
if kind == K_IF or kind == K_ELSE_IF:
stmt.condition = data
elif kind == K_SET:
stmt.target, stmt.value = data
stmt.op_type = OP_SET
elif kind == K_ADD:
stmt.target, stmt.value = data
stmt.op_type = OP_ADD
elif kind == K_SUB:
stmt.target, stmt.value = data
stmt.op_type = OP_SUB
statements.append(stmt)
statements.append(cls._buildStmt(stripped, kind, data))
return statements
@classmethod
def parse(
def _parseTokens(
cls,
script: str
tokens: list
) -> Script:
tokens = cls.tokenize(script)
body = []
i = 0
while i < len(tokens):
@@ -420,6 +432,77 @@ class ASTokenizer:
i += 1
return Script(body=body)
@classmethod
def classifyLine(
cls,
stripped: str
):
kind, data = cls._matchLine(stripped)
if kind is None or kind == K_PASS:
return None
return (kind, data)
@classmethod
def tokenize(
cls,
script: str
) -> list:
return cls._tokenizeImpl(script)
@classmethod
def parse(
cls,
script: str
) -> Script:
return cls._parseTokens(cls._tokenizeImpl(script))
@classmethod
def tokenizeWithObservers(
cls,
script: str,
observers: list
) -> list:
"""
Tokenize and notify observers for each classified line.
Fires onParseStart, onTokenParsed, and onParseComplete
events to each observer. This is the single tokenization
pipeline shared by pre-check and orchestration modules.
"""
cls._notifyObservers(observers, "onParseStart", script)
statements = []
for i, raw_line in enumerate(script.split("\n"), 1):
stripped = raw_line.strip()
if not stripped:
continue
kind, data = cls._matchLine(stripped)
cls._notifyObservers(observers, "onTokenParsed", kind, data, i, stripped)
statements.append(cls._buildStmt(stripped, kind, data))
cls._notifyObservers(observers, "onParseComplete", statements)
return statements
@classmethod
def parseWithObservers(
cls,
script: str,
observers: list
) -> Script:
"""
Parse and notify observers throughout the pipeline.
Calls tokenizeWithObservers (which fires per-token events),
then builds the AST and fires onASTReady.
"""
tokens = cls.tokenizeWithObservers(script, observers)
ast = cls._parseTokens(tokens)
cls._notifyObservers(observers, "onASTReady", ast)
return ast
@classmethod
def _parseIfBlock(
cls,