;;;; cl-scopes/core - here comes the real action (defpackage :scopes/core (:use :common-lisp) (:local-nicknames (:config :scopes/config) (:message :scopes/core/message)) (:export #:*root* #:setup #:find-service #:config #:service-config #:name #:start #:actions #:context #:add-action #:add-service #:send #:action-spec #:printer)) (in-package :scopes/core) ;;;; config (defclass config (config:root) ()) (defclass service-config (config:base) ((name :reader name :initarg :name) (start :reader start :initarg :start :initform nil) (actions :reader actions :initarg :actions :initform nil))) ;;;; actions (defclass action-spec () ((pattern :reader pattern :initarg :pattern :initform nil) (handlers :reader handlers :initarg :handlers))) (defun select (msg acts) (let ((h (message:head-as-list msg)) (hdlrs nil)) (dolist (a acts) (if (match (pattern a) h) (dolist (hdlr (handlers a)) (push hdlr hdlrs)))) hdlrs)) ;(nreverse hdlrs))) (defun match (pat h) (dolist (item pat) (if (and item (not (eq item (pop h)))) (return-from match nil))) t) (defun find-action (pat acts) (dolist (a acts) (if (equal (pattern a) pat) (return-from find-action a)))) ;;;; context (defvar *root* nil) (defclass context () ((config :reader config :initarg :config) (actions :accessor actions :initform nil) (services :accessor services :initform nil))) (defun find-service (name) (with-slots (services) *root* (when services (gethash name services)))) (defun setup (cfg) (setf *root* (make-instance 'context :config cfg)) (dolist (c (config:children cfg)) (add-service *root* c))) (defun add-action (ctx pat hdlr) (let* ((acts (actions ctx)) (act (find-action pat acts))) (if act (push hdlr (handlers act)) (push (make-instance 'action-spec :pattern pat :handlers (list hdlr)) (actions ctx))))) (defun add-service (ctx cfg) (with-slots (services) ctx (unless services (setf services (make-hash-table))) ;(let ((child (make-instance 'context :config cfg))) (let ((child (funcall (start cfg) cfg))) (dolist (a (actions cfg)) (add-action child (car a) (cadr a))) (setf (gethash (name cfg) services) child)))) (defgeneric send (rcvr msg) (:method ((rcvr context) msg) (let* ((acts (actions rcvr)) (hdlrs (select msg acts))) (if (null hdlrs) (log:warn "no action selected for ~s" msg) (dolist (hdlr hdlrs) (funcall hdlr rcvr msg)))))) ;;;; simple printer service (defun do-print (ctx msg) (declare (ignore ctx)) (format t "~&~s~%" msg)) (defclass printer (context) ((actions :initform (list (make-instance 'action-spec :handlers (list #'do-print)))))) (defun printer (name) (make-instance 'printer :name name))