*/ protected array $parsedCache = []; public function __construct() { $this->language = new ExpressionLanguage(); } public function evaluate(string $expr, array $context = []) { $expr = trim($expr); if ($expr === '') { return null; } $names = array_keys($context); sort($names); $cacheKey = md5($expr . '|' . implode(',', $names)); if (!isset($this->parsedCache[$cacheKey])) { $this->parsedCache[$cacheKey] = $this->language->parse($expr, $names); } return $this->language->evaluate($this->parsedCache[$cacheKey], $context); } public function evaluateBoolean(?string $expr, array $context = []): bool { if ($expr === null || trim($expr) === '') { return true; } return (bool) $this->evaluate($expr, $context); } /** * Compile DSL expression to engine-compatible JSON structure. * * Supported DSL: * - if(condition ? action : action) * - sex('F'|'M') -> order["Sex"] == 'F' * - set_result(value) -> {"value": value} or {"valueExpr": "value"} * * @param string $expr The raw DSL expression * @return array The compiled structure with valueExpr * @throws \InvalidArgumentException If DSL is invalid */ public function compile(string $expr): array { $expr = trim($expr); if ($expr === '') { return []; } // Remove outer parentheses from if(...) if (preg_match('/^if\s*\(\s*(.+?)\s*\)$/s', $expr, $m)) { $expr = trim($m[1]); } // Parse: condition ? thenAction : elseAction if (!preg_match('/^(.+?)\s*\?\s*(.+?)\s*:\s*(.+)$/s', $expr, $parts)) { throw new \InvalidArgumentException('Invalid DSL: expected "if(condition ? action : action)" format'); } $condition = trim($parts[1]); $thenAction = trim($parts[2]); $elseAction = trim($parts[3]); // Compile condition $compiledCondition = $this->compileCondition($condition); // Compile actions $thenCompiled = $this->compileAction($thenAction); $elseCompiled = $this->compileAction($elseAction); // Build valueExpr combining condition and actions $thenValue = $thenCompiled['valueExpr'] ?? json_encode($thenCompiled['value'] ?? null); $elseValue = $elseCompiled['valueExpr'] ?? json_encode($elseCompiled['value'] ?? null); // Handle string vs numeric values if (is_string($thenCompiled['value'] ?? null)) { $thenValue = '"' . addslashes($thenCompiled['value']) . '"'; } if (is_string($elseCompiled['value'] ?? null)) { $elseValue = '"' . addslashes($elseCompiled['value']) . '"'; } $valueExpr = "({$compiledCondition}) ? {$thenValue} : {$elseValue}"; return [ 'conditionExpr' => $compiledCondition, 'valueExpr' => $valueExpr, 'then' => $thenCompiled, 'else' => $elseCompiled, ]; } /** * Compile DSL condition to ExpressionLanguage expression */ private function compileCondition(string $condition): string { $condition = trim($condition); // sex('F') -> order["Sex"] == 'F' if (preg_match("/^sex\s*\(\s*['\"]([MF])['\"]\s*\)$/i", $condition, $m)) { return 'order["Sex"] == "' . $m[1] . '"'; } // sex == 'F' (alternative syntax) if (preg_match('/^\s*sex\s*==\s*[\'"]([MF])[\'"]\s*$/i', $condition, $m)) { return 'order["Sex"] == "' . $m[1] . '"'; } // priority('S') -> order["Priority"] == 'S' if (preg_match("/^priority\s*\(\s*['\"]([SR])['\"]\s*\)$/i", $condition, $m)) { return 'order["Priority"] == "' . $m[1] . '"'; } // priority == 'S' (alternative syntax) if (preg_match('/^\s*priority\s*==\s*[\'"]([SR])[\'"]\s*$/i', $condition, $m)) { return 'order["Priority"] == "' . $m[1] . '"'; } // age > 18 -> patient["Age"] > 18 (if available) or order["Age"] > 18 if (preg_match('/^\s*age\s*([<>]=?)\s*(\d+)\s*$/i', $condition, $m)) { return 'order["Age"] ' . $m[1] . ' ' . $m[2]; } // If already valid ExpressionLanguage, return as-is return $condition; } /** * Compile DSL action to action params */ private function compileAction(string $action): array { $action = trim($action); // set_result(value) -> SET_RESULT action if (preg_match('/^set_result\s*\(\s*(.+?)\s*\)$/i', $action, $m)) { $value = trim($m[1]); // Check if it's a number if (is_numeric($value)) { return [ 'type' => 'SET_RESULT', 'value' => strpos($value, '.') !== false ? (float) $value : (int) $value, 'valueExpr' => $value, ]; } // Check if it's a quoted string if (preg_match('/^["\'](.+)["\']$/s', $value, $vm)) { return [ 'type' => 'SET_RESULT', 'value' => $vm[1], 'valueExpr' => '"' . addslashes($vm[1]) . '"', ]; } // Complex expression return [ 'type' => 'SET_RESULT', 'valueExpr' => $value, ]; } throw new \InvalidArgumentException('Unknown action: ' . $action); } }