diff --git a/src/multi_swarm/protocol/validator.py b/src/multi_swarm/protocol/validator.py index 6802d13..0e7ade7 100644 --- a/src/multi_swarm/protocol/validator.py +++ b/src/multi_swarm/protocol/validator.py @@ -6,6 +6,17 @@ from .parser import Node, Strategy KNOWN_INDICATORS: frozenset[str] = frozenset({"sma", "rsi", "atr", "macd", "realized_vol"}) KNOWN_FEATURES: frozenset[str] = frozenset({"open", "high", "low", "close", "volume"}) +# Numero di parametri numerici accettati dopo il nome dell'indicatore. +# La tupla (min, max) include solo i numeri (gli argomenti di tipo Node sono +# proibiti dal compiler - gli indicatori non sono annidabili in Phase 1). +INDICATOR_ARITY: dict[str, tuple[int, int]] = { + "sma": (1, 1), # length + "rsi": (1, 1), # length + "atr": (1, 1), # length + "realized_vol": (1, 1), # window + "macd": (0, 3), # fast, slow, signal (tutti opzionali con default) +} + class ValidationError(Exception): """Raised when an AST violates Phase 1 protocol semantics.""" @@ -55,12 +66,25 @@ def _validate_node(node: Node, _expect_bool: bool) -> None: return if node.kind == "indicator": - if len(node.args) < 2: - raise ValidationError("'indicator' needs >=2 args (name, length)") + if len(node.args) < 1: + raise ValidationError("'indicator' needs >=1 args (name [, params...])") name_node = node.args[0] ind_name = name_node.kind if isinstance(name_node, Node) else str(name_node) if ind_name not in KNOWN_INDICATORS: raise ValidationError(f"unknown indicator: {ind_name}") + # Gli indicatori non accettano Node come params (no-nesting in Phase 1). + for a in node.args[1:]: + if isinstance(a, Node): + raise ValidationError( + f"indicator '{ind_name}' does not accept nested expressions; " + f"only numeric literals (got node {a.kind})" + ) + n_params = len(node.args) - 1 + min_p, max_p = INDICATOR_ARITY[ind_name] + if not (min_p <= n_params <= max_p): + raise ValidationError( + f"indicator '{ind_name}' arity {n_params} out of [{min_p},{max_p}]" + ) return if node.kind == "feature":