ediprolog-2.2/0000755000175000017500000000000014326320200013163 5ustar dogslegdogslegediprolog-2.2/README-elpa0000644000175000017500000000706614326320200014773 0ustar dogslegdogsleg# Introduction *ediprolog* lets you interact with Prolog in all Emacs buffers. You can consult Prolog programs and evaluate embedded queries. **Project page**: [**https://www.metalevel.at/ediprolog/**](https://www.metalevel.at/ediprolog/) **Video**: [https://www.metalevel.at/prolog/videos/ediprolog](https://www.metalevel.at/prolog/videos/ediprolog) See also [PceProlog](https://www.metalevel.at/pceprolog/). # Installation With Emacs≥24.1, the simplest way to install ediprolog is to use Emacs's built-in *package manager* via the key sequence: M-x package-install RET ediprolog RET Alternatively, copy [ediprolog.el](ediprolog.el) to your `load-path` and add the following form to your `.emacs`, then evaluate the form or restart Emacs: (require 'ediprolog) After you have installed ediprolog, you can customize it with: M-x customize-group RET ediprolog RET The two most important configuration options are: - `ediprolog-system`, either `scryer` (default) or `swi` - `ediprolog-program`, the path of the Prolog executable. # Usage The central function is `ediprolog-dwim` (Do What I Mean). I recommend to bind it to the function key F10 by adding the following form to your `.emacs` and evaluating it: (global-set-key [f10] 'ediprolog-dwim) In the following, I assume that you have also done this. Depending on the content at point, `ediprolog-dwim` does the "appropriate" thing: If point is on a *query*, it sends the query to a Prolog process, and you interact with the process in the current buffer as on a terminal. Queries start with "?-" or ":-", possibly preceded by "%" and whitespace. An example of a query is: %?- member(X, "abc"). If you press F10 when point is on that query, you get: %?- member(X, "abc"). %@ X = a %@ ; X = b %@ ; X = c %@ ; false. When waiting for output of the Prolog process, you can press C-g to unblock Emacs and continue with other work. To resume interaction with the Prolog process, use **M-x ediprolog-toplevel RET**. If you press F10 when point is *not* on a query, the buffer content is consulted in the Prolog process, and point is moved to the first error (if any). You do *not* need to *save* the file beforehand, since the *buffer content* (not the file) is consulted. For convenience, the most recent interactions with the Prolog process are logged in the buffer `*ediprolog-history*`. Use **M-x ediprolog-localize RET** to make any Prolog process started in the current buffer buffer-local. This way, you can run distinct processes simultaneously. Revert with **M-x ediprolog-unlocalize RET**. `ediprolog-dwim` with prefix arguments has special meanings: | Key Sequence | Meaning | |--------------|----------------------------------------------------------| | C-0 F10 | kill Prolog process | | C-1 F10 | always consult buffer (even when point is on a query) | | C-2 F10 | always consult buffer, using a new process | | C-7 F10 | equivalent to `ediprolog-toplevel' | | C-u F10 | first consult buffer, then evaluate query (if any) | | C-u C-u F10 | like C-u F10, with a new process | Tested with Scryer Prolog 0.8.119 and SWI-Prolog 8.1.24, using Emacs versions 26.1 and 27.0.50. # Screenshot Here is a sample interaction, using [CLP(ℤ) constraints](https://www.metalevel.at/prolog/clpz) to relate a number to its factorial: ![Factorial](factorial.png) ediprolog-2.2/ediprolog-pkg.el0000644000175000017500000000060714326320200016253 0ustar dogslegdogsleg;; Generated package description from ediprolog.el -*- no-byte-compile: t -*- (define-package "ediprolog" "2.2" "Emacs Does Interactive Prolog" 'nil :commit "cfcdf9e42821d246b7fbc84877aa4ecacc184a1c" :authors '(("Markus Triska" . "triska@metalevel.at")) :maintainer '("Markus Triska" . "triska@metalevel.at") :keywords '("languages" "processes") :url "https://www.metalevel.at/ediprolog/") ediprolog-2.2/ediprolog.el0000644000175000017500000006407714326320177015524 0ustar dogslegdogsleg;;; ediprolog.el --- Emacs Does Interactive Prolog ;; Copyright (C) 2006-2022 Free Software Foundation, Inc. ;; Author: Markus Triska ;; Keywords: languages, processes ;; Version: 2.2 ;; Homepage: https://www.metalevel.at/ediprolog/ ;; This file is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 3, or (at your option) ;; any later version. ;; This file is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see . ;;; Commentary: ;; These definitions let you interact with Prolog in all buffers. ;; You can consult Prolog programs and evaluate embedded queries. ;; Installation ;; ============ ;; ;; Copy ediprolog.el to your load-path and add to your .emacs: ;; ;; (require 'ediprolog) ;; (global-set-key [f10] 'ediprolog-dwim) ;; ;; Restart Emacs and customize ediprolog with ;; ;; M-x customize-group RET ediprolog RET ;; ;; The two most important configuration options are: ;; ;; - `ediprolog-system', either 'scryer (default) or 'swi ;; - `ediprolog-program', the path of the Prolog executable. ;; Usage ;; ===== ;; ;; The central function is `ediprolog-dwim' (Do What I Mean), which is ;; bound to F10 by the snippet above. Depending on the content at ;; point, `ediprolog-dwim' does the "appropriate" thing: If point is ;; on a query, F10 sends the query to a Prolog process, and you ;; interact with the process in the current buffer as on a terminal. ;; Queries start with "?-" or ":-", possibly preceded by "%" and ;; whitespace. An example of a query is (without leading ";;"): ;; ;; %?- member(X, "abc"). ;; ;; If you press F10 when point is on that query, you get: ;; ;; %?- member(X, "abc"). ;; %@ X = a ;; %@ ; X = b ;; %@ ; X = c ;; %@ ; false. ;; ;; When waiting for output of the Prolog process, you can press C-g to ;; unblock Emacs and continue with other work. To resume interaction ;; with the Prolog process, use M-x ediprolog-toplevel RET. ;; If you press F10 when point is *not* on a query, the buffer content ;; is consulted in the Prolog process, and point is moved to the first ;; error (if any). In transient mark mode, if the region is active, ;; only the text in the region is consulted. ;; For convenience, the most recent interactions with the Prolog ;; process are logged in the buffer "*ediprolog-history*". ;; Use M-x ediprolog-localize RET to make any Prolog process started ;; in the current buffer buffer-local. This way, you can run distinct ;; processes simultaneously. Revert with M-x ediprolog-unlocalize RET. ;; `ediprolog-dwim' with prefix arguments has special meanings: ;; ;; C-0 F10 kill Prolog process ;; C-1 F10 always consult buffer (even when point is on a query) ;; C-2 F10 always consult buffer, using a new process ;; C-7 F10 equivalent to `ediprolog-toplevel' ;; C-u F10 first consult buffer, then evaluate query (if any) ;; C-u C-u F10 like C-u F10, with a new process ;; Tested with Scryer Prolog 0.8.119 and SWI-Prolog 8.1.24, ;; using Emacs versions 26.1 and 27.0.50. ;;; Code: (defconst ediprolog-version "2.2") (defgroup ediprolog nil "Transparent interaction with Prolog." :group 'languages :group 'processes) (defcustom ediprolog-system 'scryer "Prolog system that is used for interaction." :group 'ediprolog :type '(choice (const :tag "Scryer Prolog" :value scryer) (const :tag "SWI Prolog" :value swi))) (defcustom ediprolog-program (or (executable-find "scryer-prolog") (executable-find "swipl") "scryer-prolog") "Program name of the Prolog executable." :group 'ediprolog :type 'string) (defcustom ediprolog-program-switches nil "List of switches passed to the Prolog process. Example: '(\"-G128M\" \"-O\")" :group 'ediprolog :type '(repeat string)) (defcustom ediprolog-prefix "%@ " "String to prepend when inserting output from the Prolog process into the buffer." :group 'ediprolog :type 'string) (defcustom ediprolog-max-history 80000 "Maximal size of history buffers storing recent interactions, or nil to never truncate the history." :group 'ediprolog :type 'sexp) (defvar ediprolog-process nil "A Prolog process.") (defvar ediprolog-temp-buffer nil "Buffer that temporarily saves process output ") (defvar ediprolog-seen-prompt nil "Whether a prompt was (recently) emitted by the Prolog process.") (defvar ediprolog-read-term nil "Whether the Prolog process waits for the user to enter a term.") (defvar ediprolog-indent-prefix "" "Any whitespace occurring before the most recently executed query.") (defvar ediprolog-temp-file nil "File name of a temporary file used for consulting the buffer.") (defvar ediprolog-consult-buffer "*ediprolog-consult*" "Buffer used to display consult output.") (defvar ediprolog-consult-window nil "Window used to show consult output.") (defvar ediprolog-history-buffer nil "Buffer that stores recent interactions.") (defvar ediprolog-interrupted nil "True iff waiting for the previous query was interrupted with C-g.") (defun ediprolog-prompt () "Prompt used in the Prolog session." (cond ((eq ediprolog-system 'swi) "?ediprolog- ") (t "?- "))) (defmacro ediprolog-wait-for-prompt-after (&rest forms) "Evaluate FORMS and wait for prompt." `(progn (setq ediprolog-seen-prompt nil) (ediprolog-ensure-buffer "temp") (with-current-buffer ediprolog-temp-buffer (let (buffer-read-only) (erase-buffer))) ;; execute forms with default-directory etc. from invocation buffer ,@forms (while (not ediprolog-seen-prompt) ;; Wait for output/sentinel and update consult window, if any. ;; As `accept-process-output' does not run the sentinel in ;; Emacs <= 23.1, we use `sit-for' to do both. However, ;; `sit-for' returns immediately if keyboard input is ;; available, so we must discard input. (discard-input) (sit-for 0.1)))) (defmacro ediprolog-remember-interruption (form) "Set `ediprolog-interrupted' if evaluation of FORM was interrupted." `(condition-case nil ,form (quit (setq ediprolog-interrupted t)))) ;; Only the sentinel can reliably detect if no more output follows - ;; even if process-status is 'exit, further output can still follow. (defun ediprolog-sentinel (proc str) (when (buffer-live-p (process-buffer proc)) (with-current-buffer (process-buffer proc) (let ((status (with-temp-buffer (insert str) (while (search-backward "\n" nil t) (replace-match "")) (buffer-string)))) (ediprolog-log (format "%s: %s.\n" (substring (current-time-string) 4 -5) status) "green" t)) (when (string-match "^\\(?:finished\n\\|exited abnormally\\|killed\n\\)" str) (setq ediprolog-seen-prompt t))))) (defun ediprolog-ensure-buffer (name) (let ((str (format "*ediprolog-%s*" name)) (var (intern (format "ediprolog-%s-buffer" name)))) (unless (buffer-live-p (symbol-value var)) (set var (generate-new-buffer str)) (with-current-buffer (symbol-value var) (buffer-disable-undo) (setq buffer-read-only t))))) (defun ediprolog-log (str &optional col nl) (ediprolog-ensure-buffer "history") (with-current-buffer ediprolog-history-buffer (let (buffer-read-only) (goto-char (point-max)) (let ((s (format "%s%s" (if (and nl (not (bolp))) "\n" "") str))) (insert (if col (propertize s 'face `(:background ,col)) s))) (let ((size (- (point-max) (point-min)))) (when (and ediprolog-max-history (> size ediprolog-max-history)) ;; delete older half of the (possibly narrowed) history (delete-region (point-min) (+ (point-min) (/ size 2)))))))) (defun ediprolog-run-prolog () "Start a Prolog process." (let ((args (cons ediprolog-program ediprolog-program-switches))) (ediprolog-log (format "%s: starting: %S\n" (substring (current-time-string) 4 -5) args) "green" t) (condition-case nil (ediprolog-wait-for-prompt-after (setq ediprolog-process (apply #'start-file-process "ediprolog" (current-buffer) args)) (set-process-sentinel ediprolog-process 'ediprolog-sentinel) (set-process-filter ediprolog-process 'ediprolog-wait-for-prompt-filter) (when (eq ediprolog-system 'swi) (ediprolog-send-string (format "set_prolog_flag(color_term, false), \ set_prolog_flag(toplevel_prompt, '%s').\n" (ediprolog-prompt))))) ((error quit) (ediprolog-log "No prompt found." "red" t) (error "No prompt from: %s" ediprolog-program))))) (defun ediprolog-kill-prolog () "Kill the Prolog process and run the process sentinel." (when (ediprolog-running) (delete-process ediprolog-process))) (defun ediprolog-show-consult-output (str) (with-current-buffer (get-buffer-create ediprolog-consult-buffer) (setq buffer-read-only t) (let (buffer-read-only) (erase-buffer) (insert str) (goto-char (point-min)) ;; remove normal consult status lines, which start with "%" (while (re-search-forward "^[\t ]*%.*\n" nil t) (delete-region (match-beginning 0) (match-end 0)))) (setq str (buffer-string))) ;; show consult output in a separate window unless it is a prefix of ;; success (i.e., consulted without errors), or still an incomplete ;; line that starts with a comment character (unless (or (string-match "^[\t ]*\\(?:%.*\\)?\\'" str) (string-prefix-p str "true.") ;; newer versions of Scryer Prolog prepend 3 spaces to "true." (string-prefix-p str " true.")) (setq ediprolog-consult-window (display-buffer ediprolog-consult-buffer)) (set-window-dedicated-p ediprolog-consult-window t) (fit-window-to-buffer ediprolog-consult-window (/ (frame-height) 2)))) (defun ediprolog-consult-filter (proc str) "Filter used when consulting a file, showing consult output." (with-current-buffer (ediprolog-temp-buffer proc) (goto-char (point-max)) (let (buffer-read-only) (insert str)) (with-current-buffer (process-buffer proc) (ediprolog-log str)) (when (re-search-backward (format "^%s" (regexp-quote (ediprolog-prompt))) nil t) (with-current-buffer (process-buffer proc) (setq ediprolog-seen-prompt t))) (skip-chars-backward "\n") (ediprolog-show-consult-output (buffer-substring (point-min) (point))))) (defun ediprolog-wait-for-prompt-filter (proc str) "Filter that only waits until prompt appears." (with-current-buffer (ediprolog-temp-buffer proc) (goto-char (point-max)) (let (buffer-read-only) (insert str)) (with-current-buffer (process-buffer proc) (ediprolog-log str)) (when (re-search-backward (format "^%s" (regexp-quote (ediprolog-prompt))) nil t) (with-current-buffer (process-buffer proc) (setq ediprolog-seen-prompt t))))) ;;;###autoload (defun ediprolog-dwim (&optional arg) "Load current buffer into Prolog or post query (Do What I Mean). If invoked on a line starting with `:-' or `?-', possibly preceded by `%' and whitespace, call `ediprolog-interact' with the query as argument. Otherwise, call `ediprolog-consult'. With prefix argument 0, kill the Prolog process. With prefix 1, equivalent to `ediprolog-consult'. With prefix 2, equivalent to `ediprolog-consult' with a new Prolog process. With prefix 7, equivalent to `ediprolog-toplevel'. With just C-u, first call `ediprolog-consult' and then, if point is on a query, call `ediprolog-interact' with it as argument. Analogously, C-u C-u for `ediprolog-consult' with a new process. With other prefix arguments, equivalent to `ediprolog-remove-interactions'." (interactive "P") (cond ((eq arg 0) (unless (ediprolog-running) (error "No Prolog process running")) (ediprolog-kill-prolog) (message "Prolog process killed.")) ((eq arg 1) (ediprolog-consult)) ((eq arg 2) (ediprolog-consult t)) ((eq arg 7) (unless (ediprolog-more-solutions) (error "No query in progress")) (ediprolog-toplevel)) ((equal arg '(4)) (ediprolog-consult) (ediprolog-query)) ((equal arg '(16)) (ediprolog-consult t) (ediprolog-query)) ((null arg) (unless (ediprolog-query) (ediprolog-consult))) (t (ediprolog-remove-interactions)))) (defun ediprolog-process-ready () "Error if the previous query is still in progress." (when (and ediprolog-interrupted (ediprolog-running) (ediprolog-more-solutions)) (error "Previous query still in progress, see `ediprolog-toplevel'")) (setq ediprolog-interrupted nil)) (defun ediprolog-query () "If point is on a query, send it to the process and start interaction." (ediprolog-process-ready) (when (and (not (and transient-mark-mode mark-active)) (save-excursion (beginning-of-line) (looking-at "\\([\t ]*\\)%*[\t ]*[:?]- *"))) ;; whitespace preceding the query is the indentation level (setq ediprolog-indent-prefix (match-string 1)) (let* ((from (goto-char (match-end 0))) (to (if (re-search-forward "\\.[\t ]*\\(?:%.*\\)?$" nil t) ;; omit trailing whitespace (+ (point) (skip-chars-backward "\t ")) (error "Missing `.' at the end of this query"))) (query (buffer-substring-no-properties from to)) (handle (and (fboundp 'prepare-change-group) (fboundp 'undo-amalgamate-change-group) (cons t (prepare-change-group))))) (end-of-line) (insert "\n" ediprolog-indent-prefix ediprolog-prefix) (ediprolog-interact (format "%s\n" (if (eq ediprolog-system 'scryer) ;; Scryer Prolog emits no additional indicators ;; when a query spans multiple lines, so we send ;; the query verbatim. query ;; For other Prolog systems, we merge the query into ;; a single line. The drawback of this approach is ;; that single-line comments at the end of a line are ;; not handled correctly. (mapconcat #'identity ;; `%' can precede each query line (split-string query "\n[ \t%]*") " ")))) (when handle (undo-amalgamate-change-group (cdr handle)))) t)) ;;;###autoload (defun ediprolog-interact (query) "Send QUERY to Prolog process and interact as on a terminal. You can use \\[keyboard-quit] to unblock Emacs in the case of longer-running queries. When the query completes and the toplevel asks for input, use \\[ediprolog-toplevel] to resume interaction with the Prolog process." (unless (ediprolog-running) (ediprolog-run-prolog)) (set-marker (process-mark ediprolog-process) (point)) (set-process-buffer ediprolog-process (current-buffer)) (set-process-filter ediprolog-process 'ediprolog-interact-filter) (ediprolog-ensure-buffer "temp") (with-current-buffer ediprolog-temp-buffer (let (buffer-read-only) (erase-buffer))) (setq ediprolog-seen-prompt nil ediprolog-read-term nil) (ediprolog-send-string query) (ediprolog-toplevel)) (defun ediprolog-send-string (str) "Send string to Prolog process and log it." (ediprolog-log str "cyan") (process-send-string ediprolog-process str)) (defun ediprolog-toplevel () "Start or resume Prolog toplevel interaction in the buffer. You can use this function if you have previously quit (with \\[keyboard-quit]) waiting for a longer-running query and now want to resume interaction with the toplevel." (interactive) (when ediprolog-process (select-window (display-buffer (process-buffer ediprolog-process)))) (ediprolog-remember-interruption (while (ediprolog-more-solutions) (let (str char) ;; poll for user input; meanwhile, process output can arrive (while (and (ediprolog-more-solutions) (null str)) (goto-char (process-mark ediprolog-process)) (if ediprolog-read-term (progn (setq str (concat (read-string "Input: ") "\n")) (ediprolog-insert-at-marker str ediprolog-indent-prefix ediprolog-prefix) (setq ediprolog-read-term nil)) (condition-case nil (when (setq char (if (>= emacs-major-version 22) (read-char nil nil 0.1) (with-timeout (0.1 nil) (read-char)))) ;; char-to-string might still yield an error (C-0 etc.) (setq str (char-to-string char))) (error (message "Non-character key") ;; non-character keys must not remain in the input ;; buffer, lest `read-char' return immediately (discard-input))))) (when (ediprolog-more-solutions) (if (eq char ?\C-c) ; char can be nil too ;; sending C-c directly yields strange SWI buffering (interrupt-process ediprolog-process) (ediprolog-send-string str))))))) ;;;###autoload (defun ediprolog-remove-interactions () "Remove all lines starting with `ediprolog-prefix' from buffer. In transient mark mode, if the region is active, the function operates on the region." (interactive) (save-excursion (save-restriction (when (and transient-mark-mode mark-active) (narrow-to-region (region-beginning) (region-end))) (goto-char (point-min)) (flush-lines (concat "^[\t ]*" (regexp-quote ediprolog-prefix))))) (message "Interactions removed.")) ;;;###autoload (defun ediprolog-consult (&optional new-process) "Buffer is loaded into a Prolog process. If NEW-PROCESS is non-nil, start a new process. Otherwise use the existing process, if any. In case of errors, point is moved to the position of the first error, and the mark is left at the previous position. In transient mark mode, if the region is active, the function operates on the region." (interactive) (when (string= (buffer-name) ediprolog-consult-buffer) (error "Cannot consult the consult buffer")) (when (window-live-p ediprolog-consult-window) (condition-case nil ;; deleting the window can still raise an error, if the window ;; was the only window in the frame and the consult buffer was ;; killed (and it thus displays a different buffer now) (delete-window ediprolog-consult-window) (error nil))) (when (buffer-live-p ediprolog-consult-buffer) (bury-buffer ediprolog-consult-buffer)) (when new-process (ediprolog-kill-prolog)) (unless (ediprolog-running) (ediprolog-run-prolog)) (ediprolog-process-ready) (set-process-buffer ediprolog-process (current-buffer)) (when (or (null ediprolog-temp-file) (and buffer-file-name (not (equal (file-remote-p ediprolog-temp-file) (file-remote-p buffer-file-name))))) (setq ediprolog-temp-file (funcall (if (fboundp 'make-nearby-temp-file) 'make-nearby-temp-file 'make-temp-file) "ediprolog"))) (let ((start (if (and transient-mark-mode mark-active) (region-beginning) (point-min))) (end (if (and transient-mark-mode mark-active) (region-end) (point-max)))) (write-region start end ediprolog-temp-file nil 'silent)) (set-process-filter ediprolog-process 'ediprolog-consult-filter) (ediprolog-remember-interruption (ediprolog-wait-for-prompt-after (ediprolog-send-string (format "['%s'].\n" (if (fboundp 'file-local-name) (file-local-name ediprolog-temp-file) ediprolog-temp-file))))) (message "%s consulted." (if (and transient-mark-mode mark-active) "Region" "Buffer")) ;; go to line of the first error, if any (let ((line (with-current-buffer ediprolog-temp-buffer (when (save-excursion (goto-char (point-min)) (re-search-forward "^ERROR.*?:\\([0-9]+\\)" nil t)) (string-to-number (match-string 1)))))) (when line (let ((p (point))) (goto-char (if (and transient-mark-mode mark-active) (region-beginning) (point-min))) ;; doing this first would affect (region-beginning) (push-mark p)) (forward-line (1- line))))) (defun ediprolog-running () "True iff `ediprolog-process' is a running process." (and (processp ediprolog-process) (eq (process-status ediprolog-process) 'run))) (defun ediprolog-more-solutions () "True iff there could be more solutions from the process." (not ediprolog-seen-prompt)) (defun ediprolog-interact-filter (proc string) "Insert output from the process and update the state." (when (and (buffer-live-p (ediprolog-temp-buffer proc)) (buffer-live-p (process-buffer proc))) (let (str) (with-current-buffer (ediprolog-temp-buffer proc) (goto-char (point-max)) (let (buffer-read-only) (insert string)) (with-current-buffer (process-buffer proc) (ediprolog-log string)) ;; read a term from the user? (when (re-search-backward "^|: $" nil t) (with-current-buffer (process-buffer proc) (setq ediprolog-read-term t)) (setq str (buffer-string)) (let (buffer-read-only) (erase-buffer))) ;; check for prompt (goto-char (point-max)) (when (re-search-backward (format "^%s" (regexp-quote (ediprolog-prompt))) nil t) (with-current-buffer (process-buffer proc) (setq ediprolog-seen-prompt t)) ;; ignore further output due to accidental user input (C-j, ;; C-m, etc.) while the query was running (set-process-filter proc 'ediprolog-ignore-filter) (skip-chars-backward "\n") (setq str (buffer-substring (point-min) (point)))) (unless str (goto-char (point-max)) ;; delay final line if it can still be completed to prompt (let ((l (buffer-substring (line-beginning-position) (point)))) (when (and (<= (length l) (length (ediprolog-prompt))) (string= l (substring (ediprolog-prompt) 0 (length l)))) (goto-char (line-beginning-position)))) ;; delay emitting newlines until we are sure no prompt ;; follows; one or two newlines can precede a prompt (let ((d (abs (skip-chars-backward "\n")))) (when (> d 2) (forward-char (- d 2)))) (setq str (buffer-substring (point-min) (point))) (let (buffer-read-only) (delete-region (point-min) (point)))) (when str (with-temp-buffer ;; precede each line with ediprolog prefices (insert str) (goto-char (point-min)) (while (search-forward "\n" nil t) (replace-match (format "\n%s%s" (buffer-local-value 'ediprolog-indent-prefix (process-buffer proc)) (buffer-local-value 'ediprolog-prefix (process-buffer proc))))) (setq str (buffer-string))) (with-current-buffer (process-buffer proc) (let ((near (<= (abs (- (point) (process-mark proc))) 1))) (ediprolog-insert-at-marker str) (when near ;; catch up with output if point was reasonably close (goto-char (process-mark proc)))))))))) (defun ediprolog-insert-at-marker (&rest args) "Insert strings ARGS at marker and update the marker." (save-excursion (goto-char (process-mark ediprolog-process)) (end-of-line) (apply #'insert args) (set-marker (process-mark ediprolog-process) (point)))) (defun ediprolog-ignore-filter (proc str) "Log and then ignore all process output." (with-current-buffer (process-buffer proc) (ediprolog-log str "gray"))) (defun ediprolog-temp-buffer (proc) (with-current-buffer (process-buffer proc) ;; temp buffer can be buffer local ediprolog-temp-buffer)) (defun ediprolog-map-variables (func) "Call FUNC with all ediprolog variables that can become buffer-local." (mapc func '(ediprolog-process ediprolog-system ediprolog-program ediprolog-program-switches ediprolog-temp-buffer ediprolog-history-buffer ediprolog-seen-prompt ediprolog-interrupted ediprolog-read-term ediprolog-indent-prefix ediprolog-prefix ediprolog-temp-file))) ;;;###autoload (defun ediprolog-localize () "After `ediprolog-localize', any Prolog process started from this buffer becomes buffer-local." (interactive) (unless (local-variable-p 'ediprolog-process) (ediprolog-map-variables #'make-local-variable) (setq ediprolog-temp-file nil ediprolog-process nil ediprolog-history-buffer nil ediprolog-temp-buffer nil))) (defun ediprolog-unlocalize () "Revert the effect of `ediprolog-localize'." (interactive) (when (local-variable-p 'ediprolog-process) (ediprolog-kill-prolog) (ediprolog-map-variables #'kill-local-variable))) (provide 'ediprolog) ;;; ediprolog.el ends here ediprolog-2.2/README.md0000644000175000017500000000706614326320177014470 0ustar dogslegdogsleg# Introduction *ediprolog* lets you interact with Prolog in all Emacs buffers. You can consult Prolog programs and evaluate embedded queries. **Project page**: [**https://www.metalevel.at/ediprolog/**](https://www.metalevel.at/ediprolog/) **Video**: [https://www.metalevel.at/prolog/videos/ediprolog](https://www.metalevel.at/prolog/videos/ediprolog) See also [PceProlog](https://www.metalevel.at/pceprolog/). # Installation With Emacs≥24.1, the simplest way to install ediprolog is to use Emacs's built-in *package manager* via the key sequence: M-x package-install RET ediprolog RET Alternatively, copy [ediprolog.el](ediprolog.el) to your `load-path` and add the following form to your `.emacs`, then evaluate the form or restart Emacs: (require 'ediprolog) After you have installed ediprolog, you can customize it with: M-x customize-group RET ediprolog RET The two most important configuration options are: - `ediprolog-system`, either `scryer` (default) or `swi` - `ediprolog-program`, the path of the Prolog executable. # Usage The central function is `ediprolog-dwim` (Do What I Mean). I recommend to bind it to the function key F10 by adding the following form to your `.emacs` and evaluating it: (global-set-key [f10] 'ediprolog-dwim) In the following, I assume that you have also done this. Depending on the content at point, `ediprolog-dwim` does the "appropriate" thing: If point is on a *query*, it sends the query to a Prolog process, and you interact with the process in the current buffer as on a terminal. Queries start with "?-" or ":-", possibly preceded by "%" and whitespace. An example of a query is: %?- member(X, "abc"). If you press F10 when point is on that query, you get: %?- member(X, "abc"). %@ X = a %@ ; X = b %@ ; X = c %@ ; false. When waiting for output of the Prolog process, you can press C-g to unblock Emacs and continue with other work. To resume interaction with the Prolog process, use **M-x ediprolog-toplevel RET**. If you press F10 when point is *not* on a query, the buffer content is consulted in the Prolog process, and point is moved to the first error (if any). You do *not* need to *save* the file beforehand, since the *buffer content* (not the file) is consulted. For convenience, the most recent interactions with the Prolog process are logged in the buffer `*ediprolog-history*`. Use **M-x ediprolog-localize RET** to make any Prolog process started in the current buffer buffer-local. This way, you can run distinct processes simultaneously. Revert with **M-x ediprolog-unlocalize RET**. `ediprolog-dwim` with prefix arguments has special meanings: | Key Sequence | Meaning | |--------------|----------------------------------------------------------| | C-0 F10 | kill Prolog process | | C-1 F10 | always consult buffer (even when point is on a query) | | C-2 F10 | always consult buffer, using a new process | | C-7 F10 | equivalent to `ediprolog-toplevel' | | C-u F10 | first consult buffer, then evaluate query (if any) | | C-u C-u F10 | like C-u F10, with a new process | Tested with Scryer Prolog 0.8.119 and SWI-Prolog 8.1.24, using Emacs versions 26.1 and 27.0.50. # Screenshot Here is a sample interaction, using [CLP(ℤ) constraints](https://www.metalevel.at/prolog/clpz) to relate a number to its factorial: ![Factorial](factorial.png) ediprolog-2.2/factorial.png0000644000175000017500000011443714326320177015664 0ustar dogslegdogslegPNG  IHDR|iCCPICC Profile(c``*I,(aa``+) rwRR` ` \\À|/JyӦ|6rV%:wJjq2#R d:E%@ [>dd! vV dKIa[)@.ERבI90;@œ r0x00(030X228V:Teg(8C6U9?$HG3/YOGg?Mg;_`܃K})<~km gB_fla810$@$wikiTXtXML:com.adobe.xmp 868 974 1 Owz@IDATx} 99IΊ EE((L?}{>DTbV@0 "Yd$Ñs#޿Wlݽ UtnWWK(G>1رc|雊Av%E@P@.Hy{%E@PDLAL>APѳ"( 00y>E@PQ@B TE S# @ vVPDG@ ̐@P%!]IPE &ƀk`bcK%E@PDA@$%R_("`A@$$t @z(@"!`UȽ0DB(@# L@V4C}$Gƒh/tc7hAw}Ŧ #Ǫ;./-ZDr?#;C\r1{l|nfݻfپ}yޥK!ve@{?zn P&00 !fA)~xꩧ(%%itji"߽{w[.=qϲfS t%E6]noB|]]n}/4sLz')Og\'0bqׇr;d]DBh~H`&NkvgY`3 ҁ2U)))zzaZm nݺQ>'*4DRHYG)F= t E[=cѯ-mޥG~A~'8q#/֮E/??ztNڽEF|4zhbG3ߥGN2zi=1ozߗ&O~_VzC¹sḧԧ'Թ;Cci=^$]k=}tWG.u=z:!_ .n:Dڵ184c gkz-Z|9ORNБ#G/P0… 4j(b/^lnJ.y0Ќ$PP<@oe#ib(oO>}jQٳg髯:uP6m(yO>G}ځ~4p@[o'~!!ݼ q-jǎԩS'1$͛@H=@&!/~༁^wV3HM.jQZet l tߠ7's8{kfBE^%* !m :Dh2Bn> p$1'{:5gUZtσlZ<9)p.m>re^C::uƜ!E<Txs~x+<;CAPhM^L\fM4k {xw@sE*oA_3yk?=;ky ڿ cͪΟUyx d8Qd++™6BMo^]Ɩoihq^6yRϳ_(.<^\%3o a/}(ȃ|3>EeʺԼ ǐI99_պi߬koo,@tu*V2~;f}Q-mu8u՞k,S]9MdlB O0 `4n><Нøpb`=og0ʂ* 0;/y<@>j2oex/ɼ{.]>tv/ctx 44k@a#qGY^oM v@} e52$ M]<-/rnPićDIb(xٜ9seA3<VXљ$fbtf lk׮m'8T?Rh*Ch|}]I |}/L D(`63f ,{amEx|_R3H R<'$,x&C@MVv=Hߣ($KH&^%1oS 7+Lb0cL\7dҚ\_ۡ.Ԛgp͓'cPt-<@P{G VsQy1go02$+o)uu/]^#`@uC/!6jg8 j0'T1XlWe& NX  02w0cЃ6+ TSXq>}e}&/[ha[ƭVLA@hpԾ}{V;vgU^$ce|6>a|=GI&  6F9n ,ܟh LYupȒk„ a7@F 0X, wtງrYD,Aoݺu^#F}P? P ~tYJQ@=]  c)+(+j"8 BI eQl$BAc u]x"5 l +Ci7U H956y,J#G) |G&x6 n7;0 ,Cv׭+@+bbEx0`56}]$@%&A@%;hi^E@D* fa.m"(@SI%E@PQAbP%]"ߊ"STE@PQMj$J SPBL@09'YE@P010CZAt%E@PCuƐ`phwE@HlD"[x %6*{E@P }Ԟ?"(`  ^"("@Pɑ@0hWE@P` tE3(@""`UA Z*ҕE@P D:Pf`?"(VDRC3APRE LzL!~ _E@Par(#MIPE C3H@*)8pOE@P 00w$ vVPE 8 Z%ACϊ"$V e![]UE@"!@$ GIPE AIg0PAPRE Iҁ<$]("xG▊nKz@]TE@d3aO8+("83#5Hr$ vy'Q ,ڰlcȘljTuLvOsg{l{e/ZrEnbŊQѢE뮣y̟;Fƍ7v o_ͧXG@xA#3TI1IwM4v,C+f?SBwB ,~W:z(aP6miӆf͚M׿Eݻw1c ֭KO?4edF@iD@~"):&ǏD5km®R+_>b%?:0)W.<<<@?sLz')ODhzN>Mz,ݻKgϞ%< I[n4j(BGz6K $!0H<'NC@@F:Yb۷;Լ9Qժ:K/KNIII>/-Y*UD}-[J.M~!ҪU"Fz׍ GI>ͧ`PA}$H j,t5Cow(AT8lɀIGu8}NqO֬!b5jDT-ZԶxICNMQŎti￘_ *DԬ[k9aUx%T!uKD/xM6`Xc`WX?ӵ^sp|.](o޼΂!UtX(RtMFuXnM04㚇<'}N]jgRD.hת`űv-Ϡa"}k:bEbՆ3;ww}cB{!ڸh gֻCڵ#ڼّ&zּ^'rM>_n:AY3I 1вeK?~Qb+*'ٙnJM6FTL$yܟ}3L0"ڦC%'i<#H|C?6n`D1;o^yI A+Y.6̴qHT>PY/NJ={bIPoAjtY=g+09s]5fgm58شig:ڱcb= g^;ʬWٔ.},]6գ*م$5m) A^QquDc =QZĆv0$3g-#bu C硞!9Ylhj'"aޞf`ʗwT?S9_v';gx# mnm" Tfk@%wBnr:6kKQTlkт#+r p]|B@\Je_XeҜUΟ?\KCIc4E+p#f gN/M ?$:r1C}k6gH]=H-%ڮ1ޚTq\o,%._IˣJc0E`ڀ @Zaz ͆!6?Aa;A $ou 4-7/r5OO%Mx]|-7g'Xg. LU , Ij^w8m {4[8&ƀk`HK¢6[o.;bJ9￟U;UA"F2P:,1}Xo};Þ >Y'.xQeE0]ve0bj֏pͱ a348"H*}&Ǥ/>S"\C$Ki[W>g=pǡ{ Ϯ]gȎqJO=x6ukzw+V8w kddwQqQTF;ҿ 22z|G=5܍}$ ^|g/Q{[;wY />c˖->笇]X%Yϊ@TɀN?Ըڵ :NnAW {#Gnx`A&1+jbo8@-.$o`D,8@0=B$*[];bwQI}._<{]u$,bh_~X\ 1X΢^.VL0ۆm:dn߾=ah@P@MT|$QY DG+)2e ѣ>/gonC!6!v}G&L[7TR0|ae C)0"J!#> W+Rj0WOKRÆ )"l>/)"BaEIG>e◌ qB+o%u&uA$Чoum /Q(C׷D0!%y0<("yDRJ"( 0?5RU`dE@aD2kh0n6MPE ; gԁk}vԫe*" >PE"(@"FT0$,bNndfJ0ApVt rP--Z1r؟Սf۟FumH{4\[<>q쾙:vLxs{y3mʞF-_.$tԁ"39*g@x_p,mOc?;oW>~h qE@qCQ׈5Jݿ& '=_3grfΕ'OwhٞS#WI {5a5̅;Bz/@*]C$AY̐G$eYz|5>2|pD={4J+}[@ 3tؒ :iD}:%&)uH<C{9m mXca6gh BdڶxzJd7.ֻxbZ$Qlu)8cZ%qƢ"KtGE#۫3;ww}c=mH4h3!kGy#8ʴۚ7kZ{>>?}3V7Ô(s8-[~3pv 8q5kYن'Hx'0֗ح c u@Q.Yl"}1 qys샰~=DH9&%',#O Ƙ8A^|178\=$@>9˶x׀, qXYY#&ִiS9\k^׍Ο=CfĆyKx>ڽ9jM30h+) rFqng7fm6,GxF?NT:`-4vuh lÑi@xav#}glYz {G'`/g#¶8{\8>ԻF&0H5w=xmCs(#iN3@4@V&H p]|B[t C];ҢQos}@4B+UB@@$\KM ?$msvNPuc7h^)߮cm/pC2+ (iitXÈ[;U;g>`C<.Lq fÐ  L͝ 4jD7ϲE~oHo/]$M?ώ{X to ΚOSaa. LAg18)ێ~m"f<l !AoEU "rkWoofOZ0HKd;wJs+,aZ`L^QJԦӽ<I_l\R,E)`O}!n:wMb/8C9>[nGX`9TN$nsK'WpX]^axEGm(g>PB['|-'x[b톔F獮s9sx{7&)5v[ӵALH Pŵ=OkaXY |N!=HHG-c̙:rʬ^m:R| ŠieE8ڷon͙"kۤ+vxG0x2|TgՐcP!,WR!0;tGem^$@̡qE>c.}ф xm˭QnV05)0 JaAM6ݵ֭[7R:w+'ҥKYE:(\Q볾 9 %E FA7e5jTXC!J! ]p792 `:æPb :i 1Wí> tMn"(@8 fH 0 +)"$ F![*0J"0"-![B cMSE@D@$ 3P#<E@HP*#0hE@HL ȊfHLD׳ovݣ =}떛gӇ>M:1D` c /[d-Z#W㠇E](`\c㳪x" s 1@Y/ ZpĿ+Eq#tL PzUUa%5,J߇˪amM6c,9-BFP] :u9"GL@ɽ@ocydڳjQڃ0= k@@FO>Cڈ#>h H qCb>sqARGSRag!\ h9cuapOd,P;tqYq޸3ݥwƊw0Pj)О={"--HPS5$y̍~(- @BgB6[i@ @!JF:> EfX7NZz5=VQNf hŊ-_ܤI (Y" TH"1, "4C! 3&8fyᇩPB n*YSm]Մ0cؚ+>ȜǏO[n%`j4qD)m;H6Y 3T} +QJԦ#DRR)oƌԶm[UT9XTOӾ}g7p04C*a\ƍif3TF~c3$2V?C}c̘1޲h"@J_I4=5xHf0 kA8 ЭZ pIMII hf0>xIE d9HP!)Hm082Y,LA!)R5*HOE… zCIP<#Ga> QR|`k>TR~֭۬7.^T"H0,j$9"Ln` 0`JWa q5TH AԹsgjذavD7F Ŷ6)^ [oё#GL`}-&|Ud0ڷT3TtYGt-8N7„{YM3\yOQ%[N?E]Cyי@,# #)@RI~ZjF!ޒ5._I<0γS'h4y=g͒q6mP(-DH `9"h!p|CB&=֭MqE ?DϞ7KKƼK@?Dߞ.a{MÆ  t Fl76߹;z`b`¿J `$WX1{-2]2ӁMk`rYjKTxQxp)[oH{-sTR&+B?sFuhf_MRr J+HD@Pp9/ի= ~ܫJvF/ڼ=a$ L6F@<=& 8 .]j2V7% ۠HB@$%9;XE;Uk>j4cF'鸆%/Hn?W5] WAM)/=@68vZ4:6l0[p~ᇦ 1s~w=[=43fݡR =}숩@d+i$nN#.:@8 n(tb^pGӧK?@bILs2ȸpFn'<@7g|9,u%Q uǃi2re2"k! "rlL)6PfChĈ>o޼ tyqxJ:r *G>BGxa=$u֦PuEF}},>RR00IDv֫eV7YmUi֎0{Bۛ+J3T@<s̡ŋ;-[f? @ }UTC3B^-!N$FJs:a_ttDYJTe$G8N86] ` P CR!^I0÷#{y!+iI`p>q?4cܿ;~i9|鳾$DR@Dmd |i=JN>zi n`̝<;&>:u*UTj׮m^}͚5Zj|J@E  j# Vɴw#wR> v͸pg3Y_uӧu]g;{%\.DcOqiz:l)r~'lY@@GbS DGYAR[;epW t*Tuh T*RMo޼tqz}4gͯ493sNo NF:>ip.ZXںΤI aNM `8x`%2=E $+)bP8cGenT`:A$qLM 8NCs4*#lbRtж" q۶mڞhP?|ݻwa3fLwG!@bKsP` lpJ4yѦ?7iM@XLga}+={P۵kg^ظq#E%ޑ{x]ם:u2Moi]tFVequtFtTUFǞop%0hT/y{/4H a rI!>Pڲk.: () "(Gq>6~!d?*R()@,"o–wN6jbH 8dE3Gr^kE"0@J0`@hFE L*$0HAPR, 5kuܙ6 q qжG=Y()@"},tC8lD:Pf/DWE  @e ^G8FR$AIPDC@$02D%hE@`z4%E@P@?G8 @bA?E@H($ޑ'*;3*T 0r 9of cٳmh"7ެwӨ it&~}떛gӇ>WAhlhAS^z]/ hHGfm_m @IDAT?ã&2BVIAг x:vX .#|sW?jz)JII!|wq6{W{ׯ_M;Υqv-OC{,f`߽{w[.=&oLv,L2$ TSF9ӦM3j"5H!v>}rJCǏ?h0)W\eq<=J~W9s wM|g]?;_]<җfcTs4rc]GS=ۆ҄`I Rĉ]i7"!@$ rZ?! }vvͩjժ:KH:|p¼gϞ~:+v\7ӁMk`rYjKTx2/ZZ<8Dm뢣Ḟ1 (@|A Umpc6V+C@bCo>;DTxq۷/ k7-6̽4kG.vjvʅsPڏ7tns} WDt O$ $M1y9 %u0,BO"k`űvZ֕Vj䮻/ҚlTXz pB{= 4{Mk׎6olq_ۚ7kZ{ӧ~(+JbFn}t)_QL;Ukx<,QI<b Al[lIǏ~Q ? QKoAѢEiժUtQf֙ʡ+{vᙻM^zӧ Il7$~j?l033?q>1vT_䑿XILp7ݫ.J.Ҷ` `9DR2.nޛS{Aq)S n`D1;oܸqzYKPoA 3m\BP G(ѣ=s΢ނ>33g}z2g*VINMMgyƚ]|lW¦MUJ*"zZ -siɃ{R;tx3?sQ浐(&~R$(v%۪F5V~0x CkԨaÇ%)";v)SVV;IF̙3l2ygHN o3xV^HE|f>6v@o:mxVwʝ$ y#%Hy/JQg=7lP/oeqPHP!AG343tfʑ" xP[ۃz\5KخF Oٲe3+2 5kZBO(5 Rj뎴hԛ\;<^W!1:r *G>̃n |CҶ`7l`C] [SDm_H WhA90 ?ȑ#fP{t`۶mxxQ0.ʆ[Jv"W!@!JF:>)aP4֦e3g!m_lcKao0?L+pW|6l`: C.r007w4UH ~cIIHe*TO?.`I1?cm D5\%Y Drr0Ii THBuTs[~`my7P/6mT]``PA !wr"`,j[U "kQ3?{; R1A"Fz27n) c h[@ߵkWofKZ0HCdLM 8NCs# <^|lv*V)J'NѠPڏ >̟?_?VT{ b囉v``&L1hc(C=W<a-t=Y- 7Tϡr% vm^z, [\F!l >mD H8Dz$ ߄G޽[3TCb톔a=Dn^T)T VI~7FfHۖ5ma[aV\~ kWr*ۃTGjO`Gvڄ5`e5t:u2x@!VnڼS`Azjm?3Sp < DGڇulsx3og0&x]aQz랖ڦ#W=ܗՈn<*4`J %o/JE|nE(\۷oo&.jv ɠ"ptΆ!#:3E@ p СY裏FR`%O>iB@oI}G&L %s* S(VAT(()96muJԹsg}ҥFvS/ԏPgBEH;9D}$6@A?Gx5jTA0ؘӒ;*G!> 1WU}H nj5J"@R3ҕE@P qCEk``9Wa`_*XkE@HbKsP )MSE ]f ⵋ"xCLA )"P Ȋfȡ]n)"BLLe0H0> APRE S -J"( H "(3HvWP+")@BH>bE@H4D2S^+SH_WPF@ HSRE@H0Ќ$J SPBL@}BUE@p &2BVIAг"( HlYȖ@0hWE@PHP!Ā0QRE@HDRTG>bE@H4DRt O$I4=+"$2#ޑےhE@PtJ=+"$ p 5TPE LA޻/>}R#8 -U+hR'M?ծMgt)_g |%=osG8O&G΁?x Ħ5*, ?E?LGTki]wѩ;fPZ꫄O8@ΜM˞}6|wҧ9Dfvlh#_+TR%|m>zYg^ᦛ3>/)#O,A|#F A~ rۻ?@W^Io*D?/[v(a{%ΥwaY?|8]8GGN}sPj?s&uڰ:mHvy GX&JRy֌p0}=X.ۗ.-s&s%%֯|Ŕ_|`sh%r$Sz<ׄA@ ̐@P%!=0D4Ogd1ӨS] 7lHy 6/㔾{rU]7%7+2Ҟ戮l3rC:u۟r]G'l,1+Fʝ??mx킶; *0FJ~v?DeXOLx+ \ETQ#[D Sl_>BzJTi%3'"4Q@]@HCZ,Al[Rʼn43"jQUƺҭk'Rwt#mf{Rz6v}3fVJXiJ;&M#Gu 6EjԠjzۚÇҚw%0A%ϛGy $ >uX+_>ZγNKZ oTeK]iyӾJ) ~miׯcG!!X<Y|2jꞵoEKy_fMjG*Ư|p5F0Az4e0S#V96EN94;*aq|fMb|<`uPUֵ ֗/_NKY;ug{,9Byܶͤ~SVL֖_']r`&E+pIv]f 'L3y0UobZ&MhɴUUn=mfj#T=AU}{a}gw13ʚTMc|@3h~0~M_2q)3R7 FMy6QѺu#jzl+Y];ҽ@L0$#5e-i&A83;x'\PdWc^ Vl\2ټEϓ@8eISy trvGz S#0ef^CX2(3W@' gVd%x05OaR2S8{$E4T6{g͢S;w҅ӧ]ꅚM!@le<3l.31JY% Wv[5v-V̚rM?W޼~fCxb2mIw;jޜTH` !K:N{/e03c+ͫ6Z6}uPL[ȟ:.d&릭6(/t +;n o+U<;iNOHugzeMg*6=5Ňƚ?Y7q1DA``BE`THY^ !蠡Ě+٠ϋXpI0[ѣۀc͇kW= rQ C,RWa û7i \[$,2t'a\>fu]|\ds]8"7lTH7B=گ  `" ZҡW¸_vr_o֎S T7}mcK؃*K-#mO7`%7mjbuvKIXCˤ$ϢH` 렙VvbQW,zW10xIJaueUWOZIc)1t 5b1?^%و2XB`c;Uۛ+W b~\{Q6܋$)/HP)1D`rϗJ"E0{ٻ>J1ayl'*jG0"\mSH\(9ذ7%`> 9=&Gz쬠{mD~QAuP@>#FB0LB(GPXB@$Db4ZmQE@p@u#/pMRH ,zVE@H) H 0h7E@P rF -WRE@ Y F5(y.ʫ2%[@/'@%d"u2}cfNfpKOW0%KX0q{H|֋`e{5v㿳g :ˑIF}#.Oj$Jʒ0-ݸ^6cԖeLK7s`2DUCvQޜ>OBFciF9Hڜȟv,+V23p<ӳVcI*QB@H ۂfm0bIx졀Xk_ttjo_*‹vslD07~6ob6~eڴ1zH0ܶaP%];aŜFpG/Zאخ` r35v* ovHxJx5o2dr澾9{{!;"j"K)H|`` 3h]H+U26 !ǔ䀁ḁ84L]  ݁:sxG10l QH4EP LAFP#9"O 38wjL3NJY c&]Dtas'YraCakPjը/¶L::)^"WOH+[ԉбj{V f#PRfI׳"/` `Bb fu@{7A +y*\ԯ]Ń $r<b\7;mv$tؿtlZfoV83G8Kq673L$D%7vɇʹyf`'Fi|~.:c3?6P ,iA%zlIigz151{ IvXƄWIPrp9 -hۂ6DJM[ Ʊ?OuxU'T+XߞөxFh7P$tc ^KXݎvfE3/0X]$EkM6%seoiVD[u$>k{wD"[CH p)g`WHx%a2\S!=@uzxش=b!C)'L*{Xgޒ熼Fq|oSx_„6<P ]q, /Ls*aFUm'jXL!X Oݛ6})Ux"!QKV-YGhСtW:abKvE4\ZՆ/E:K Xr+"> *Q"|+jcFI…&JQ(@%>$(@:>#.[IPE F$ԍ$F(m"(@LTF8FV("d`V_6IPF@Q-_PE FH@*)8pOE@P K*0@Tt=FEyj },;~lRsB8n݌fSvˍUB쳍zc ;R7*M=fW_#|J8鱆g` 0,[EHRᇩ1ᅩE[9ѼG!& ڋ>)}PNlÙ&:?|U<59۪xדWT!1d![l0B+W AC0gsDo_*‹sTT?:w8!(RޟĖ-f_c::(A 3~츶#{H)VTFLH 8 3-L6(}^Ǜ`i1 as*vqca eFTլ y7Έ f"h<5@1V3&mT&*[FtߣJ0?>7~Y 7XrGa=4tB^@};piTl7@=U ;wͽGh'ԵB Jc18w DS{{#l$ۈz{+-􅋦'L;`7̠uk!`ΤbL:$ ̠r}HJlgw.唛 fxÚKr .E@)1@$G08K1){0/SFni3-[:UIx#l|yBzƍw!-,g+5-.CDS8Gg(E}7 HP!%OO7Ꟶx#}gˍ5{2?ue֭œGsE6C:{"(H `")Zl ~ۛ;~ J~Bwtǿc*EU<?Ԯ]09xy/9 ۊ7Lؼ]wHUn1O@@$B:YBkrW%0̌[q(ٲfM)3oTQNy ™3f׹A"- >,! Jx[kJ\~9m'o7g0]cS^% ַ4%seoIt W!PEw73P(uొ/cO_tE T{0ʇ)\JY_;&M"x%ar6m >xb&֪-d5b(d[GB݈6*1UhC F$'p^T|ymC*ȁ Oݛ6})UkDBܣZ5 5u:K Xr)\c :JJN\DgZ,^+|Fs!xc\sQkHMlsB+xRdP#֦Q j$H FXf79f`ec|ΎX @,%|l76DUD}$6@:[敟}F{yÖOx/ɝթCGvY`>p QCt+E?xkm@P||@}jvl%E@PE#10CZQZ$"(@b L@Al |("(D 0ek?E@pC@$$ 3V#[z("C)c3{a1Tm"(@v#T0Y Ս(siiTW ߟGɁ'?@:a Bgwff[n,:=HL㟠\Fg5( ^\U$GLJ,Rf 5^0&DVyb+Jj] UB妣rU@q"3t|홦ge޷9}sy{}t7ovt=Ɲ|nܹz8-oxzMw^ڧjkמnb/E<4@!.B3 =Flu7E7Юj#i㏻ej#qn]LJAYnB (SŷM#Õ۵dIRQ,*v,@D]E(,:Fp^8ah"ϧ{]ƨ+*j3ض~{z,uW\pÃ+ܚ{qø%kq\)9J XY)vMWcխͮ^$<#'> T9yM]U^cQE@9Z.Buuԫà=M|+rb^YL R@1$gbȗA@gr'jC7/[7NF˯j+$:r`;|bUT2G0籎HO-7ٿe;^1_nPRkxaӆBPX H(d;.k>}9+0~|b/*^V,:L4 wLО=a^S#yl_L[^!C̲,. Qᩣ ɮ(Y(FƵgoZ>ܱ#q́Zߢ'xۊB"ۄr(ϫ-V]V^hxb-v^KUЦڃHAX9=_BaB  E1]{]ѷ]^nEK?up-%=䎨RT \*I4UkiZb2IP:+ޠS`ׯzFQNYJix0f`Xf7RqcAʠWm[6 >Z'+WIcng4rE P(H &eN~)׼t"BS{)?PU{*߂F-MEo;!/DRZM>Y!G:!`@"G u%zvY!`B2}((&!` `O `ʠv C"R jm0 CW X Ʈ0 C 'BJjpFB9d-tAݨٟ55U wݎ>&Gx;ݎEgGU8t+}6{rKQH-vի=[JNV~ gE7zta߬v)zoկE|/0کr_SCWCX p#!YY <uK7[rQ 7jrW-2 9]UQ(<|jF鱓x/8Jé${׬q󦛼ӮCk#|tEYhHq|VvZV|NDK豺2hl6jI!PlxP ,3BAetv!}n}IϬF4/[,F6m{G X!2:3AƉXnGer{sw>{kݵ3[}{>D:Kfu3=AVrb/u44z>O T)b(A[,5(4h σ8!0XuCuq}?)`eelzQ+WwK.7PƕV/z|Sڟ t!+`w-|%qr 'g߾tyRS Mĺ:+g˴OmQofyMh21J!P(aXCU gP`=N7,3_w]UʛczA3E)W&@j=d!C!aԨz7CzM67bE (GSB+=  JR@rJxZDMp% l޽䲀{ʭ[s9D?VWp ǏS8Ejg~*ge.*Ӹ%G y) io[whYISf?[a;!,.$X(\Hz޸ZIkufi!G:!`@",\Hb`;0 C!\EQ L >M C0J`)` 憀!`32BXM`4 CsO(R[0 C f7R:]P)LԌ5jpw;zE;t;rz}DAiDWJ;WZD߼Fk:}ʬv)z!B]7; PSNB7j\O=:bF X!7.$\I GnQߢnh}H-S2a]LJAYBs7!@] `[wA)Cw.)LbgBǶ`)r-d<p/ 0hSHaY޺fW1Jß~>z%Z m}c)@F,OOot˖JX 8  >ZVpky=[1ٕqe`)uB\!K5#@q7(T1#G (6r<@95 h Z)Aӧ'\I(zB24!P^uk>c`-Uzt88!@ *\HB8LO2@rrQ)|/2 Ќ(p#!Yz1:Pf aڵW^v9xO+,~0U}{t'{[S P5Jb}VU|`Ů|*iB_? E:6ER% .$Mn5~|P\,![!aw\`)P (&\I!ƠK&o wQ boϗʹX1 d#8 JA U7K"qdeg}T<ls!Ō}2^8c/XawmSN`)` AdO8c_ 6( \հanRC9s\rOjDߣD\CZe)xQ+RAKIZ}!wD*bne`5PltFZYoB)d\Cd(L @8Bh}\Ej(^nW%\ fjԹ͛W{fFόm5JG(\H(,B3 iz5/]3p` 3;Nx22HwE޽joQ&"`Nj(xh:I@M}@!G:!`@"K!X!D/.0 CP p#}!` 2@)DM)ed@ 9;!`@ bQ0 n@4%ŀ!+ 肺Q?kjr5>}L"Νw?aVP.PZn RՑSk3Ğy㍞59i2]{}/٥/_~w&>1Eď_}moh;E'_x5wZdS_;hdTzQ6TjoIy ]UQ(׌FnzT\i8Rܤ{#>|@{iwF,&˿f۾wz['ްnҦ .mn@HEeC َ9yz !=<-Z䗓az]ƨ+*襗j3ض݅X'b5**y-.i I!HȭVt +1>AԆt-+Vx1[(c~X:Wq=z4p6mT@) <(e?.~餵ٳM*3o[ U#k#|tEYIE qג%ZIUО|M;ڧ׾ >gV^QzH~Sd~w_t24U`M/Φ,tv/GkJ$:n'`In)C R:} D&ѼZk4&ZDvWvL_!֯wW2T>AW[ )W \3gɓ&z.^SsS䠈G$1p Ú/IĔ=ӵ3C|K!퓼 |? CVB STZVt}!Ov|q ʷ?ߛok]+Iqp喜"NNa&Ŏ/k)J( <@;RmO.N<]C뷊8ī@Z:#d;0g? _@0.`P|;#q=vSNIlk4}z•@d\Cpor 2@c?iWg|{} X ,BFdq'_/fdaR٠4=ɘ8sgb_I (JRo_=꽖JOx믿(@3dHJh T响In —Z:`ic*B|d9*SiB_xf2X9 *m Sjr#'uVFY\7 yJȨ?;C\HAH5 GC X (GwdMxcuP5YH>p MbC_{?d{M%ke]x;w!CW4Y Y#"8y ܶ{"G M7Lf}$+iF8d`=:7ϏxH\>C~ l*}=kܺ4|aR]JLab]NP<)9ؙoяڭ 6H%,K| 2Nu?8S'$L t`)::}S - 6( \M|D9s\rOW*H(G% ˭r$.x|Zg)]WUG;Z]tXQi%$O\d.uGfKjj-* -&I(^̖ёJab%O)ʂ?xVO!BY,FQN8NbLEo;!b &@P]T~m|.D#+WIcng4rE BL7UJ];JZ䧞r"!gf2?r-!8{*Qnx0f ,UNԄ?h&G?/H61 C(Qp3n%^ C0! ŀ"`B9M HU11 B X!r0PZߑ]!`!,N bNd@i!u Jjj C0 @PQ:1]P};6ٯ(m5Y}v2 C(=P ((,Prܹnfo}p/FIQZz㏻eqn]܎^裶0 C@"RYߡlyh)HCv-Ztp\znuE̳dq*c6W!`@`u2hwdgH5lmnQ#cFyMM3O*WL C0C X (L@.rHi Ҽl;w_\/QpI6 X=-!`Ā@ P 嬔-z'7œ'ff}`ۺkg䭷:!֯|/dɥfzRB1ihpyC0*O7r C aJ7=썯\wS.oFi Uʷ?']b!f&,$+K_°2QKX,UUn8z*Pu>go C0A"b W"$eD{lւH >=Jb@q*;֕[t !` 娻,"cHʠ=E#]P܉}nGlǓ/Ad58C0 @)B2Bs 5$o_ɢäA\?Dd'hϞ61GY!`ď@pyP^QT!ŋ]O6)mvT״p/lz3@oyuos\Q_~yxksC0 "(&,`=h󒪯>D?Q(#oX{~:U8Kf )۟{._q:"[;{#7 #, d"a]sMI)%YIT( )h@wAC'iTiQJ~3I*;+ޠS5 =N67*Ѝrq6),܆htбm@qeq# WDʠWm[x~{j̻zKeyǹy r/vPC0* > Gp!aAL:\ҥnsߵgʅPU{*߂쳻r!`>:Qw FS2VmRBXn@p`u+ f C0 p: !),p)lgC$Vf Jk2 C P ReY C0 @8v+`1"Bӎi!PFP&!`> 1HY GqWC0(APAyn0 \I71 CfG XfB9M C0 ,O > IENDB`