;;; -*- Mode: Common-Lisp; Package: itsdb; Encoding: utf-8; -*- (in-package :itsdb) (defparameter *www-port* 8010) (defparameter *www-log* nil) (defparameter *www-interrupt* nil) (defparameter *www-logon-css* "logon") (defparameter *www-logon-js* "logon") (defparameter *www-custom-js* "noen") (defparameter *www-alttxt-js* "alttxt") (defparameter *www-scriptaculous-js* '("builder" "controls" "dragdrop" "effects" "prototype" "scriptaculous" "slider")) (defparameter *www-title* :noen) (defparameter *www-disclaimer* nil) (defparameter *www-introduction* "noen") (defparameter *www-sample* "Bergensområdet er tett befolket fremdeles.") (defparameter *www-roots* nil) (defparameter *www-generics* nil) (defparameter *www-urls* '((:parse "http://www.ling.uib.no/~victoria/NorGram/") (:generate "http://www.delph-in.net/erg"))) (defparameter *www-icon* nil) (defparameter *www-1x20* nil) (defparameter *www-comparisons* '((:smt "http://www.isi.edu/publications/licensed-sw/pharaoh/") (:google "http://www.google.com/translate_t?sl=no&tl=en") (:visl "http://www.gramtrans.com/?pair=nor2eng") (:it "http://www.tranexp.com:2000/Translate/result.shtml"))) (defparameter *www-maximal-number-of-edges* 20000) (defparameter *www-maximal-number-of-results* 5) (defparameter *www-capabilities* nil) (defvar %www-clients% 0) (defvar %www-item-id% 0) (defvar %www-object-counter% 0) (defvar %www-attic% (make-array 512)) (defvar %www-request% nil) (defvar %www-entity% nil) (defvar %www-item% nil) (defun www-initialize (&key (port *www-port*) pattern) (setf *www-port* port) (let ((interrupt (format nil "/tmp/.aserve.~a.~a" (current-user) port))) (when (keywordp *www-title*) (when (null *www-log*) (setf *www-log* (format nil "www.~(~a~).~a.~a.log" *www-title* *www-port* (current-user)))) (setf *www-title* (case *www-title* (:noen "Norwegian-English LOGON On-Line Demonstrator") (:deen "German-English LOGON On-Line Demonstrator") (:ende "English-German LOGON On-Line Demonstrator") (:jaen "Japanese-English LOGON On-Line Demonstrator") (:enja "English-Japanese LOGON On-Line Demonstrator") (:erg "English Resource Grammar (ERG) LOGON On-Line Demonstrator") (:gg "German Grammar (GG) LOGON On-Line Demonstrator") (:jacy "JACY LOGON On-Line Demonstrator") (:srg "Spanish Resource Grammar (SRG) LOGON On-Line Demonstrator") (:cst "CST Danish Grammar (CST) LOGON On-Line Demonstrator") (:hag "Hausa Grammar (HAG) LOGON On-Line Demonstrator") (:krg "Korean Resource Grammar (KRG) LOGON On-Line Demonstrator") (t (format nil "~a LOGON On-Line Demonstrator" *www-title*))))) (when (null *www-log*) (setf *www-log* (format nil "www.~a.~a.log" *www-port* (current-user)))) (unless *www-icon* (setf *www-icon* (make-pathname :directory (pathname-directory (dir-append (get-sources-dir "tsdb") '(:relative "tsdb" "html"))) :name "logon.gif"))) (unless *www-1x20* (setf *www-1x20* (make-pathname :directory (pathname-directory (dir-append (get-sources-dir "tsdb") '(:relative "tsdb" "html"))) :name "1x20.jpg"))) (sleep 2) (setf %www-clients% 0) (setf %www-item-id% 0) (setf %www-object-counter% 0) (setf %www-attic% (make-array 512)) ;; ;; a first attempt at `session management': if we fail to grab the port we ;; need, attempt to shut down the competing process (assuming it is a web ;; server that unterstands our interrupt protocol), wait long enough for ;; the interrupt handler to take effect, and try again. ;; (unless (ignore-errors (start :port port :external-format (excl:crlf-base-ef :utf-8))) (format t "initialize(): unable to bind port to ~d; attempting interrupt.~%" port) (force-output t) (with-open-file (foo interrupt :direction :output :if-exists :supersede)) (sleep 10) (start :port port :external-format (excl:crlf-base-ef :utf-8))) (unless (mp:process-p *www-interrupt*) (flet ((check-interrupt () (loop (when (probe-file interrupt) (format t "check-interrupt(): exiting for `~a'~%" interrupt) (force-output t) (delete-file interrupt) (excl:exit)) (sleep 5)))) (setf *www-interrupt* (mp:process-run-function '(:name "aserve interrupt handler") #'check-interrupt))))) (unless *www-capabilities* (when (loop for client in *pvm-clients* for cpu = (pvm:client-cpu client) thereis (smember :parse (pvm:cpu-task cpu))) (pushnew :parse *www-capabilities*)) (when (loop for client in *pvm-clients* for cpu = (pvm:client-cpu client) thereis (smember :transfer (pvm:cpu-task cpu))) (pushnew :transfer *www-capabilities*)) (when (loop for client in *pvm-clients* for cpu = (pvm:client-cpu client) thereis (smember :generate (pvm:cpu-task cpu))) (pushnew :generate *www-capabilities*)) (when (and (smember :parse *www-capabilities*) (smember :transfer *www-capabilities*) (smember :generate *www-capabilities*)) (pushnew :translate *www-capabilities*))) (let ((css (pathname-directory (dir-append (get-sources-dir "tsdb") '(:relative "tsdb" "css")))) (js (pathname-directory (dir-append (get-sources-dir "tsdb") '(:relative "tsdb" "js"))))) (publish-file :path "/logon.css" :file (make-pathname :directory css :name *www-logon-css* :type "css")) (publish-file :path "/logon.js" :file (make-pathname :directory js :name *www-logon-js* :type "js")) (let ((file (if (pathnamep *www-custom-js*) *www-custom-js* (make-pathname :directory js :name *www-custom-js* :type "js")))) (publish-file :path "/custom.js" :file file)) (publish-file :path "/alttxt.js" :file (make-pathname :directory js :name *www-alttxt-js* :type "js"))) ;; ;; _fix_me_ ;; for the run-time binaries, we need to recompute these paths (and maybe a ;; few others too). (1-dec-08; oe) ;; (publish-file :path "/icon.gif" :file *www-icon*) (publish-file :path "/1x20.jpg" :file *www-1x20*) (publish :path "/compare" :content-type "text/html" :function #'(lambda (request entity) (www-compare request entity))) (publish :path "/fetch" :content-type "text/html" :function #'(lambda (request entity) (www-fetch request entity))) (loop with directory = (pathname-directory (dir-append (get-sources-dir "tsdb") '(:relative "tsdb" "js"))) for name in *www-scriptaculous-js* for file = (make-pathname :directory directory :name name :type "js") when (probe-file file) do (publish-file :path (format nil "/~a.js" name) :file file)) (publish :path "/logon" :content-type "text/html" :function #'(lambda (request entity) (www-logon request entity))) (publish :path "/browse" :content-type "text/html" :function #'(lambda (request entity) (www-browse request entity))) (publish :path "/view" :content-type "text/html" :function #'(lambda (request entity) (www-view request entity))) (publish :path "/podium" :content-type "text/html" :function #'(lambda (request entity) (www-podium request entity :pattern pattern))) (publish :path "/itsdb" :content-type "text/html" :function #'(lambda (request entity) (www-itsdb request entity)))) (defun www-logon (request entity) (setf %www-request% request %www-entity% entity) (let* ((method (request-method request)) (body (when (eq method :post) (get-request-body request))) (query (and body (form-urlencoded-to-query body))) (task (lookup-form-value "task" query)) (input (or (lookup-form-value "input" query) *www-sample*)) (exhaustivep (let ((foo (lookup-form-value "exhaustivep" query))) (string-equal foo "all"))) (output (lookup-form-value "output" query)) (treep (or (null body) (if (stringp output) (equal output "tree") (member "tree" output :test #'equal)))) (mrsp (or (null body) (if (stringp output) (equal output "mrs") (member "mrs" output :test #'equal)))) (nresults (or (lookup-form-value "nresults" query) "5")) (roots (let ((foo (lookup-form-value "roots" query))) (if (stringp foo) (list foo) foo))) (genericsp (let ((foo (lookup-form-value "genericsp" query))) (or (null query) (string-equal foo "yes")))) (*www-maximal-number-of-results* (cond ((equal nresults "1") 1) ((equal nresults "5") 5) ((equal nresults "10") 10) ((equal nresults "50") 50) ((equal nresults "100") 100) ((equal nresults "500") 500) ((equal nresults "all") nil) (t *www-maximal-number-of-results*)))) (with-http-response (request entity) (with-http-body (request entity :external-format (excl:crlf-base-ef :utf-8)) (www-doctype *html-stream*) (html (:html (www-header *html-stream* (format nil "~a~@[ (~a)~]" *www-title* (cond ((string-equal task "analyze") "Analysis") ((string-equal task "translate") "Translation")))) ((:body :onload "messenger()") :newline (:center (unless (eq method :post) (www-output *www-introduction* :stream *html-stream* :absolutep (pathnamep *www-introduction*))) ((:form :action "/logon" :method "post" :id "main" :onsubmit "submitter('main')" :accept-charset "utf-8" :target "_self") :newline ((:input :type "button" :class "bright" :value "Sample" :onclick "showSample('main', 'input');")) " " ((:input :type "button" :class "bright" :value "Reset" :onclick "clearElement('main', 'input');")) " " ((:input :type "text" :name "input" :class "bright" :value (or input "") :size "70")) " " ((:input :type "submit" :name "task" :class "bright" :id "analyze" :value "Analyze" :disabled '||' :onclick "setTarget('main', '_self');")) " " ((:input :type "submit" :name "task" :class "bright" :id "translate" :value "Translate" :disabled '||' :onclick "setTarget('main', '_self');")) :br :newline (when (or *www-roots* *www-generics*) (format *html-stream* "~%~%") (when *www-roots* (html ((:td :class "buttons") "allow:") (loop for root in *www-roots* for name = (first root) for active = (if roots (member name roots :test #'equal) (fourth root)) do (html ((:td :class "buttons") ((:input :type "checkbox" :name "roots" :value name :if* active :checked '||))) ((:td :class "buttons") (format *html-stream* "~a" name)))) :newline)) (when *www-generics* (format *html-stream* "" *www-roots*) (html ((:td :class "buttons") ((:input :type "checkbox" :name "genericsp" :value "yes" :if* genericsp :checked '||))))) (format *html-stream* "
~ ~@[~*  |  ~]unknown words:~
~%")) ((:table :border 0 :cellspacing 0) (:tr ((:td :class "buttons") "search:") ((:td :class "buttons") ((:input :type :radio :name "exhaustivep" :value "all" :if* exhaustivep :checked '||))) ((:td :class "buttons") "all") ((:td :class "buttons") ((:input :type :radio :name "exhaustivep" :value "best" :if* (not exhaustivep) :checked '||))) ((:td :class "buttons") "best" ) ((:td :class "buttons") "  |  output:") ((:td :class "buttons") ((:input :type "checkbox" :name "output" :value "tree" :if* treep :checked '||))) ((:td :class "buttons") "tree") ((:td :class "buttons") ((:input :type "checkbox" :name "output" :value "mrs" :if* mrsp :checked '||))) ((:td :class "buttons") "mrs") ((:td :class "buttons") "  |  show: ") ((:td :class "buttons") ((:select :size 1 :name "nresults") ((:option :value "1" :if* (equal nresults "1") :selected '||) "1") ((:option :value "5" :if* (equal nresults "5") :selected '||) "5") ((:option :value "10" :if* (equal nresults "10") :selected '||) "10") ((:option :value "50" :if* (equal nresults "50") :selected '||) "50") ((:option :value "100") "100") ((:option :value "500") "500") ((:option :value "all") "all"))) ((:td :class "buttons") " results"))) :newline)) (cond ((and (string-equal task "analyze") input) (www-parse input :exhaustivep exhaustivep :treep treep :mrsp mrsp :roots roots :genericsp genericsp :request request :stream *html-stream*)) ((and (string-equal task "translate") input) (www-translate input :exhaustivep exhaustivep :request request :stream *html-stream*)) (t (www-version *html-stream*)))))))))) (defun www-parse (input &key exhaustivep treep mrsp roots genericsp request stream) (let* ((item (pairlis '(:i-id :parse-id :i-input :edges) (list (incf %www-item-id%) 0 input *www-maximal-number-of-edges*))) (nresults (or *www-maximal-number-of-results* 0)) (nanalyses (if exhaustivep 0 nresults)) (roots (loop for root in roots for match = (find root *www-roots* :key #'first :test #'equal) when match collect (list (second match) (third match)))) (flags (when *www-generics* (list :generics (and genericsp t)))) (item (setf %www-item% (pvm-process item :parse :exhaustive exhaustivep :roots roots :flags flags :nanalyses nanalyses :nresults nresults))) (readings (get-field :readings item)) (nresults (or *www-maximal-number-of-results* readings)) (time (get-field :tcpu item)) (time (and (numberp time) (/ time 1000))) (pedges (get-field :pedges item)) (results (get-field :results item)) (unique (length results)) (rawp nil) (error (get-field :error item)) (error (unless (and (numberp readings) (> readings 0) results) (or (loop with end = 0 with start with starts with ends with result while end do (setf start end) (multiple-value-setq (start end starts ends) (ppcre::scan "Word `([^']*)' is not in lexicon." error :start start)) (when (and starts ends) (pushnew (subseq error (aref starts 0) (aref ends 0)) result :test #'equal)) finally (return (nreverse result))) (when (search "invalid SEM-I predicates" error) (setf rawp t) error) (when (search "no lexicon entries for" error) (loop with end = 0 with start = end with starts with ends with result while end do (setf start end) (multiple-value-setq (start end starts ends) (ppcre::scan "\"([^\"]*)\"" error :start start)) (when (and starts ends) (pushnew (subseq error (aref starts 0) (aref ends 0)) result :test #'equal)) finally (return (nreverse result)))) (multiple-value-bind (foo bar) (ppcre::scan-to-strings "edge limit \\(([0-9]+)\\)" error) (declare (ignore foo)) (when bar (ignore-errors (read-from-string (aref bar 0) nil nil)))) (multiple-value-bind (foo bar) (ppcre::scan-to-strings "edge limit exhausted \\(([0-9]+)" error) (declare (ignore foo)) (when bar (ignore-errors (read-from-string (aref bar 0) nil nil)))) error)))) (when request (www-log request input readings time pedges error)) (format stream "
~%") (cond ((null error) (format stream "
~ [~d of ~d~@[ (of ~a)~] ~:[analyses~;analysis~]~ ~@[; processing time: ~,2f seconds~]~ ~@[; ~a edges~]]
~%~
~%" (if (numberp *www-maximal-number-of-results*) (min unique readings *www-maximal-number-of-results*) (min unique readings)) (min unique readings) (and (not (= readings unique)) readings) (= readings 1) time pedges pedges) (loop with *reconstruct-cache* = (make-hash-table :test #'eql) with mrs::*mrs-relations-per-row* = 5 with mrs::*lnkp* = :characters initially (format stream "
~%~ ~% ~ ~% ~ ~% ~ ~% ~
~% ~ ~% ~ ~% ~ ~% ~   |  ~% ~ ~% ~ ~% ~  ~% ~  ~% ~
~%~%" (www-store-object nil item) (www-store-object nil results) *www-maximal-number-of-results* (not (smember :transfer *www-capabilities*)) (not (smember :generate *www-capabilities*))) finally (format stream "
~%") for i from 0 for result in results for derivation = (get-field :derivation result) for mrs = (mrs::read-mrs-from-string (get-field :mrs result)) for edge = (when (or treep (and mrsp (null mrs))) (or (get-field :edge result) (when derivation (let ((edge (reconstruct derivation))) (setf (lkb::edge-mrs edge) mrs) edge)))) while (< i nresults) do (when edge (nconc result (acons :edge edge nil))) (format stream "~%~% ~ ~% ~ ~% ~ ~% ~
~%~
# ~a
~ ~
~%" i i) when (and treep edge) do (format stream "~%") (lkb::html-tree edge :stream stream :indentation 4) (format stream "~%") when (and mrsp (or mrs edge)) do (format stream "~%") (when (null mrs) (setf mrs (mrs::extract-mrs edge)) (let ((mrs (with-output-to-string (stream) (mrs::output-mrs1 mrs 'mrs::simple stream)))) (nconc result (acons :mrs mrs nil)))) (mrs::output-mrs1 mrs 'mrs::html stream i) (format stream "~%") do (format stream ""))) ((or (null error) (equal error "")) (format stream "
~

No result(s) were found for this input.  ~ Is it well-formed?

~%~
~%")) ((integerp error) (format stream "
~

The parser exhausted its search space limit ~ (of ~d passive edge~p);
~% ~ try non-exhaustive parsing or a shorter (or less ambiguous) ~ sentence.

~%
~%" error error)) ((consp error) (format stream "
~ The following input tokens were not found in the lexicon:
~% ~ ~{‘~a’~^ ~}.~%
~%" error)) ((and rawp (stringp error)) (format stream "
~a.~%
~%" (string-right-trim '(#\. #\? #\!) error))) (t (format stream "
~ The server encountered an (unexpected) error:
~% ~ ‘~a’.~%
~%" (string-right-trim '(#\. #\? #\!) error)))) (format stream "
~%") (www-version stream))) (defun www-translate (input &key exhaustivep request stream) (format stream "
~%") (let* ((n (if exhaustivep *www-maximal-number-of-results* 1)) (comparisons (loop for comparison in *www-comparisons* for id = (first comparison) for task = (background #'www-translate-item input :engine id) collect (pairlis '(:task :id :url) (list task id (second comparison))))) result) ;; ;; to give the background threads the opportunity to send out all requests ;; (sleep 0.1) ;; ;; _fix_me_ ;; we should possibly use translate-item() instead, if only to have correct ;; statistics available for logging below. however, then (by default) the ;; individual results would no longer be available, i.e. we might have to ;; require that translate-string() does the object storage already (which ;; would eliminate the potential for id mismatches). (10-mar-07; oe) ;; (setf result (setf %www-item% (translate-string input :stream stream :format :html :nanalyses (format nil "~ax~ax~ax50" n n n) :index %www-object-counter%))) ;; ;; at this point, we rely on translate-string() to have arranged for items ;; to be rendered with anchors using object ids in the order corresponding ;; to those assigned by the www-store-object() calls below. this mainly ;; serves to increase modularity, i.e. spare translate-string() from having ;; to do the actual object storage. ;; (let* ((www (get-field :www result)) (id (and (numberp www) (www-store-object nil result))) (time (get-field :total result))) (unless (= www id) (www-warn request (format nil "www-translate(): object id mismatch (~a != ~a)" www id))) (loop for transfer in (get-field :transfers result) for www = (get-field :www transfer) for id = (and (numberp www)(www-store-object nil transfer)) for realizations = (get-field :realizations transfer) unless (= www id) do (www-warn request (format nil "www-translate(): object id mismatch (~a != ~a)" www id)) do (loop for realization in realizations for www = (get-field :www realization) for id = (and (numberp www) (www-store-object nil realization)) unless (= www id) do (www-warn request (format nil "www-translate(): object id mismatch (~a != ~a)" www id)) do (incf time (get-field :total realization))) (incf time (or (get-field :total transfer) 0))) (when request (let ((readings (length (get-field :translations result))) (error (get-field :error result))) (www-log request input readings time -1 error)))) (when comparisons (format stream "~%") (format stream "~%") (sleep 0.1) (loop for comparison in comparisons for task = (get-field :task comparison) for results = (get-field :results (background-status task)) for output = (get-field :surface (first results)) do (nconc comparison (acons :output output nil)) (format stream "~ ~%" (get-field :id comparison) (get-field :id comparison) (get-field :url comparison) (or output " "))) (format stream "
Other Translations (Scraped off the Internet)
~ ~a
~%")) (format stream "
~%") (www-version stream))) (defun www-browse (request entity &key results) (setf %www-request% request %www-entity% entity) (let* ((method (request-method request)) (body (when (eq method :post) (get-request-body request))) (query (and body (form-urlencoded-to-query body))) (action (lookup-form-value "action" query)) (results (or results (if query (lookup-form-value "results" query) (request-query-value "results" request :post nil)))) (results (typecase results (string (ignore-errors (parse-integer results))) (number results))) (set (lookup-form-value "set" query)) (selection (lookup-form-value "selection" query))) (cond ((string-equal action "latex") (when (and selection (string-equal set "active")) (loop with all = (www-retrieve-object nil results) with active = nil for foo in (if (listp selection) selection (list selection)) for i = (ignore-errors (parse-integer foo)) for edge = (and i (nth i all)) when results do (push edge active) finally (setf results (www-store-object nil active)))) (www-latex request entity :results results)) ((string-equal action "compare") (when (and selection (string-equal set "active")) (loop with all = (www-retrieve-object nil results) with active = nil for foo in (if (listp selection) selection (list selection)) for i = (ignore-errors (parse-integer foo)) for edge = (and i (nth i all)) when results do (push edge active) finally (setf results (www-store-object nil active)))) (www-compare request entity :results results)) ((or (string-equal action "transfer") (string-equal action "generate")) (when (and selection (string-equal set "active")) ;; ;; for transfer or generation, we can only take in one result at a time ;; (loop with all = (www-retrieve-object nil results) with active = nil for foo in (if (listp selection) selection (list selection)) for i = (ignore-errors (parse-integer foo)) for edge = (and i (nth i all)) when results do (push edge active) finally (setf results (www-store-object nil active)))) (www-process request entity :results results :type (if (string-equal action "transfer") :transfer :generate)))))) (defun www-process (request entity &key type results (wait 5)) (setf %www-request% request %www-entity% entity) (let* ((method (request-method request)) (body (when (eq method :post) (get-request-body request))) (query (and body (form-urlencoded-to-query body))) (item (if query (lookup-form-value "item" query) (request-query-value "item" request :post nil))) (item (typecase item (string (ignore-errors (parse-integer item))) (number item))) (item (www-retrieve-object nil item)) (results (or results (if query (lookup-form-value "results" query) (request-query-value "results" request :post nil)))) (results (typecase results (string (ignore-errors (parse-integer results))) (number results))) (results (www-retrieve-object nil results)) (results (stable-sort results #'< :key #'(lambda (foo) (get-field :result-id foo)))) (item (acons :ranks (loop for i from 1 for result in results unless (get-field :mrs result) do ;; ;; if need be, say if earlier we only visualized the tree ;; structure, or on results returned from the generator, ;; attempt to fill in the MRS for this .result. ;; (let* ((derivation (get-field :derivation result)) (edge (or (get-field :edge result) (and derivation (reconstruct derivation)))) (mrs (and edge (mrs::extract-mrs edge)))) (when mrs (let ((mrs (with-output-to-string (stream) (mrs::output-mrs1 mrs 'mrs::simple stream)))) (nconc result (acons :mrs mrs nil))))) collect (acons :rank i result)) item)) (exhaustivep (let ((foo (lookup-form-value "exhaustivep" query))) (string-equal foo "all"))) (nresults (lookup-form-value "nresults" query)) (nresults (cond ((equal nresults "1") 1) ((equal nresults "5") 5) ((equal nresults "10") 10) ((equal nresults "50") 50) ((equal nresults "100") 100) ((equal nresults "500") 500) ((equal nresults "all") nil) (t *www-maximal-number-of-results*))) (nanalyses (if exhaustivep 0 nresults)) (hook (and (eq type :generate) "mrs::get-mrs-string")) (item (setf %www-item% (pvm-process item type :wait wait :exhaustive exhaustivep :nanalyses nanalyses :nresults nresults :semantix-hook hook))) (readings (get-field :readings item)) (time (get-field :tcpu item)) (time (and (numberp time) (/ time 1000))) (pedges (get-field :pedges item)) (results (get-field :results item)) (rawp nil) (error (get-field :error item)) (error (unless (and (numberp readings) (> readings 0) results) (or (loop with end = 0 with start with starts with ends with result while end do (setf start end) (multiple-value-setq (start end starts ends) (ppcre::scan "Word `([^']*)' is not in lexicon." error :start start)) (when (and starts ends) (pushnew (subseq error (aref starts 0) (aref ends 0)) result :test #'equal)) finally (return (nreverse result))) (when (search "no lexicon entries for" error) (loop with end = 0 with start = end with starts with ends with result while end do (setf start end) (multiple-value-setq (start end starts ends) (ppcre::scan "\"([^\"]*)\"" error :start start)) (when (and starts ends) (pushnew (subseq error (aref starts 0) (aref ends 0)) result :test #'equal)) finally (return (nreverse result)))) (when (or (search "invalid SEM-I predicates" error) (search "invalid transfer predicates" error) (search "invalid predicates" error) (search "unknown input relation" error)) (setf rawp t) error) (multiple-value-bind (foo bar) (ppcre::scan-to-strings "edge limit \\(([0-9]+)\\)" error) (declare (ignore foo)) (when bar (ignore-errors (read-from-string (aref bar 0) nil nil)))) (multiple-value-bind (foo bar) (ppcre::scan-to-strings "edge limit exhausted \\(([0-9]+)" error) (declare (ignore foo)) (when bar (ignore-errors (read-from-string (aref bar 0) nil nil)))) error)))) (when request (www-log request (get-field :i-input item) readings time pedges error)) (with-http-response (request entity) (with-http-body (request entity :external-format (excl:crlf-base-ef :utf-8)) (www-doctype *html-stream*) (html (:html (www-header *html-stream* (format nil "~a~@[ (~a)~]" *www-title* (case type (:transfer "Transfer") (:generate "Generation"))) (case type (:transfer "transfer") (:generate "generate"))) ((:body :onload "messenger()") (:center (unless (eq method :post) (www-output *www-introduction* :stream *html-stream* :absolutep (pathnamep *www-introduction*))) ((:form :action "/browse" :method "post" :id "browse" :target "_blank" :accept-charset "utf-8") :newline (:center (cond ((null error) (format *html-stream* "
~ [~d of ~d ~:[analyses~;analysis~]~ ~@[; processing time: ~,2f seconds~]~ ~@[; ~a edges~]]
~%~
~%" (if (numberp *www-maximal-number-of-results*) (min readings *www-maximal-number-of-results*) readings) readings (= readings 1) time pedges pedges) (loop with *reconstruct-cache* = (make-hash-table :test #'eql) with mrs::*mrs-relations-per-row* = 5 with mrs::*lnkp* = :characters initially (format *html-stream* "~% ~ ~% ~
~% ~   ~ ~% ~ ~% ~ ~% ~   |  ~% ~ ~% ~ ~% ~ ~@[~* ~% ~]~  ~% ~   |  show: ~%~ ~%  results~% ~
~%" (www-store-object nil item) (www-store-object nil results) (not (smember :transfer *www-capabilities*)) (not (smember :generate *www-capabilities*)) (not (eq type :transfer))) (when (and (eq type :generate) (> readings 0)) (format *html-stream* "
~ ~%") (loop for i from 0 for result in results for tree = (get-field :tree result) for class = (determine-string-class tree) for score = (get-field :score result) when (stringp tree) do (format *html-stream* "~ ~ ~ ~ ~%" i class class i tree score)) (format *html-stream* "
~ (~a)  ~ ~a~ ~@[  [~,1f]~]
~%")) (format *html-stream* "~%") finally (format *html-stream* "
~%") for i from 0 for result in results for derivation = (get-field :derivation result) for mrs = (mrs::read-mrs-from-string (get-field :mrs result)) for edge = (or (get-field :edge result) (and derivation (reconstruct derivation))) for tree = (get-field :tree result) while (< i nresults) do (when edge (nconc result (acons :edge edge nil))) when (or mrs edge (and tree (eq type :transfer))) do (format *html-stream* "~%~% ~ ~% ~ ~% ~ ~% ~
~%~
# ~a
~
~ ~
~%" i i i) when (and edge (not (eq type :transfer))) do (format *html-stream* "~%") (lkb::html-tree edge :stream *html-stream* :indentation 4) (format *html-stream* "~%") when (and tree (eq type :transfer)) do (format *html-stream* "~%") (format *html-stream* "~%") #+:mt (loop for derivation = (mt::read-derivation-from-string tree) then (mt::edge-daughter derivation) while (and (mt::edge-p derivation) (mt::edge-daughter derivation)) do (format *html-stream* "~%" (mt::edge-rule derivation) (mt::edge-id derivation))) (format *html-stream* "
~ ~(~a~)  [~a]
~%") when (or mrs edge) do (format *html-stream* "~%") (when (null mrs) (setf mrs (mrs::extract-mrs edge)) (let ((mrs (with-output-to-string (stream) (mrs::output-mrs1 mrs 'mrs::simple stream)))) (nconc result (acons :mrs mrs nil)))) (mrs::output-mrs1 mrs 'mrs::html *html-stream* i) (format *html-stream* "~%") do (format *html-stream* ""))) ((or (null error) (equal error "")) (format *html-stream* "
~

No result(s) were found for this input.  ~ Is it grammatical?

~%~
~%")) ((integerp error) (format *html-stream* "
~

The processor exhausted its search space limit ~ (of ~d passive edge~p);
~ try non-exhaustive processing or a shorter ~ (or less ambiguous) ~ input.

~%
~%" error error)) ((consp error) (format *html-stream* "
~ The following input tokens were ~ not recognized by the processor:
~% ~ ~{‘~(~a~)’~^ ~}.~%
~%" error)) ((and rawp (stringp error)) (format *html-stream* "
~a.~%
~%" (string-right-trim '(#\. #\? #\!) error))) (t (format *html-stream* "
~ The server encountered an (unexpected) error:
~% ~ ‘~a’.~%
~%" (string-right-trim '(#\. #\? #\!) error)))) (www-version *html-stream*))))))))))) (defun www-view (request entity &key type item nresults) (setf %www-request% request %www-entity% entity) (let* ((method (request-method request)) (body (when (eq method :post) (get-request-body request))) (query (and body (form-urlencoded-to-query body))) (item (or item (let* ((item (if query (lookup-form-value "item" query) (request-query-value "item" request :post nil))) (item (typecase item (string (ignore-errors (parse-integer item))) (number item)))) (www-retrieve-object nil item)))) (nresults (or nresults (lookup-form-value "nresults" query))) (nresults (cond ((equal nresults "1") 1) ((equal nresults "5") 5) ((equal nresults "10") 10) ((equal nresults "50") 50) ((equal nresults "100") 100) ((equal nresults "500") 500) ((equal nresults "all") nil) (t *www-maximal-number-of-results*))) (type (or type (cond ((null item) :unknown) ((get-field :transfers item) :parse) ((get-field :realizations item) :transfer) (t :generate)))) (readings (get-field :readings item)) (time (get-field :tcpu item)) (time (and (numberp time) (/ time 1000))) (pedges (get-field :pedges item)) (results (get-field :results item)) (rawp nil) (error (get-field :error item)) (error (unless (and (numberp readings) (> readings 0)) (or (loop with end = 0 with start with starts with ends with result while end do (setf start end) (multiple-value-setq (start end starts ends) (ppcre::scan "Word `([^']*)' is not in lexicon." error :start start)) (when (and starts ends) (pushnew (subseq error (aref starts 0) (aref ends 0)) result :test #'equal)) finally (return (nreverse result))) (when (search "no lexicon entries for" error) (loop with end = 0 with start = end with starts with ends with result while end do (setf start end) (multiple-value-setq (start end starts ends) (ppcre::scan "\"([^\"]*)\"" error :start start)) (when (and starts ends) (pushnew (subseq error (aref starts 0) (aref ends 0)) result :test #'equal)) finally (return (nreverse result)))) (when (or (search "invalid SEM-I predicates" error) (search "invalid transfer predicates" error) (search "invalid predicates" error) (search "unknown input relation" error)) (setf rawp t) error) (multiple-value-bind (foo bar) (ppcre::scan-to-strings "edge limit \\(([0-9]+)\\)" error) (declare (ignore foo)) (when bar (ignore-errors (read-from-string (aref bar 0) nil nil)))) (multiple-value-bind (foo bar) (ppcre::scan-to-strings "edge limit exhausted \\(([0-9]+)" error) (declare (ignore foo)) (when bar (ignore-errors (read-from-string (aref bar 0) nil nil)))) error)))) (when request (www-log request (get-field :i-input item) readings time pedges error)) (with-http-response (request entity) (with-http-body (request entity :external-format (excl:crlf-base-ef :utf-8)) (www-doctype *html-stream*) (html (:html (www-header *html-stream* (format nil "~a~@[ (~a)~]" *www-title* (case type (:parse "Analysis") (:transfer "Transfer") (:generate "Generation"))) ;; ;; in case we were called as a call-back from the fan-out HTML, ;; then all viewing targets a new window. ;; (if (null query) (gensym "") (case type (:parse "parse") (:transfer "transfer") (:generate "generate") (t (gensym ""))))) ((:body :onload "messenger()") (:center ((:form :action "/browse" :method "post" :id "browse" :target "_blank" :onsubmit "submitter('main')" :accept-charset "utf-8") :newline (:center (cond ((null error) (format *html-stream* "
~ [~d of ~d ~:[analyses~;analysis~]~ ~@[; processing time: ~,2f seconds~]~ ~@[; ~a edges~]]
~%~
~%" (if (numberp *www-maximal-number-of-results*) (min readings *www-maximal-number-of-results*) readings) readings (= readings 1) time pedges pedges) (loop with *reconstruct-cache* = (make-hash-table :test #'eql) with mrs::*mrs-relations-per-row* = 5 initially (format *html-stream* "~% ~ ~% ~
~% ~   ~ ~% ~ ~% ~ ~% ~   |  ~% ~ ~% ~ ~% ~ ~@[~* ~% ~]~  ~% ~   |  show: ~%~ ~%  results~% ~
~%" (www-store-object nil item) (www-store-object nil results) (not (eq type :transfer))) (when (and (eq type :generate) (> readings 0)) (format *html-stream* "
~ ~%") (loop for i from 0 for result in results for tree = (get-field :tree result) for class = (determine-string-class tree) for score = (get-field :score result) when (stringp tree) do (format *html-stream* "~ ~ ~ ~ ~%" i class class i tree score)) (format *html-stream* "
~ (~a)  ~ ~a~ ~@[  [~,1f]~]
~%")) (format *html-stream* "~%") finally (format *html-stream* "
~%") for i from 0 for result in results for derivation = (get-field :derivation result) for mrs = (mrs::read-mrs-from-string (get-field :mrs result)) for edge = (or (get-field :edge result) (and derivation (reconstruct derivation))) for tree = (get-field :tree result) while (< i nresults) do (when edge (nconc result (acons :edge edge nil))) when (or mrs edge (and tree (eq type :transfer))) do (format *html-stream* "~%~% ~ ~% ~ ~% ~ ~% ~
~%~
# ~a
~
~ ~
~%" i i i) when (and edge (not (eq type :transfer))) do (format *html-stream* "~%") (lkb::html-tree edge :stream *html-stream* :indentation 4) (format *html-stream* "~%") when (and tree (eq type :transfer)) do (format *html-stream* "~%") (format *html-stream* "~%") #+:mt (loop for derivation = (mt::read-derivation-from-string tree) then (mt::edge-daughter derivation) while (and (mt::edge-p derivation) (mt::edge-daughter derivation)) do (format *html-stream* "~%" (mt::edge-rule derivation) (mt::edge-id derivation))) (format *html-stream* "
~ ~(~a~)  [~a]
~%") when (or mrs edge) do (format *html-stream* "~%") (mrs::output-mrs1 (or mrs (mrs::extract-mrs edge)) 'mrs::html *html-stream* i) (format *html-stream* "~%") do (format *html-stream* ""))) ((or (null error) (equal error "")) (format *html-stream* "
~

No result(s) were found for this input.  ~ Is it grammatical?

~%~
~%")) ((integerp error) (format *html-stream* "
~

The processor exhausted its search space limit ~ (of ~d passive edge~p);
~ try non-exhaustive processing or a shorter ~ (or less ambiguous) ~ input.

~%
~%" error error)) ((consp error) (format *html-stream* "
~ The following input tokens were ~ not recognized by the processor:
~% ~ ~{‘~(~a~)’~^ ~}.~%
~%" error)) ((and rawp (stringp error)) (format *html-stream* "
~a.~%
~%" (string-right-trim '(#\. #\? #\!) error))) (t (format *html-stream* "
~ The server encountered an (unexpected) error:
~% ~ ‘~a’.~%
~%" (string-right-trim '(#\. #\? #\!) error)))) (www-version *html-stream*))))))))))) (defun determine-string-class (string) (cond ((search " || /" string) :token) ((search "|| " string) :fragment))) (defun www-compare (request entity &key data results) (setf %www-request% request %www-entity% entity) (let* ((method (request-method request)) (body (when (eq method :post) (get-request-body request))) (query (and body (form-urlencoded-to-query body))) (index (if query (lookup-form-value "frame" query) (request-query-value "frame" request :post nil))) (frame (when (stringp index) (ignore-errors (parse-integer index)))) (frame (when (integerp frame) (www-retrieve-object nil frame))) (results (or results (if query (lookup-form-value "results" query) (request-query-value "results" request :post nil)))) (results (typecase results (string (ignore-errors (parse-integer results))) (number results))) (data (or data (if query (lookup-form-value "data" query) (request-query-value "data" request :post nil)))) (action (lookup-form-value "action" query)) (mode (lookup-form-value "mode" query)) (mode (and mode (intern (string-upcase mode) :keyword))) (display (lookup-form-value "display" query)) (display (and display (intern (string-upcase display) :keyword))) classicp concisep orderedp fullp) ;; ;; there are quite a few different ways for this function to be called ... ;; (cond ;; ;; first-time entry for browsing a (Redwoods-type) profile: construct a ;; comparison frame, store it in the attic, and initialize everything. ;; ((and (null frame) data) (setf frame (browse-trees data :runp nil)) (setf index (www-store-object nil frame)) (browse-tree data (first (lkb::compare-frame-ids frame)) frame :runp nil)) ;; ;; interactive parse comparison from set of results: again, construct a ;; new comparison frame, store it in the attic, and initialize everything. ;; go into `modern' discriminant mode, mostly for advertising purposes ... ;; ((and (null frame) (integerp results)) (let* ((results (www-retrieve-object nil results)) (*reconstruct-cache* (make-hash-table :test #'eql)) (edges (loop for result in results for derivation = (get-field :derivation result) for mrs = (let ((mrs (get-field :mrs result))) (mrs::read-mrs-from-string mrs)) for edge = (or (get-field :edge result) (let ((edge (if derivation (reconstruct derivation) (lkb::make-edge :from 0 :to 0)))) (nconc result (acons :edge edge nil)) (setf (lkb::edge-mrs edge) mrs) edge)) collect edge)) (lkb::*tree-discriminants-mode* :modern) (lkb::*tree-display-threshold* 10)) (when edges (setf frame (lkb::compare edges :runp nil)) (setf index (www-store-object nil frame))))) ;; ;; call-back from comparison form: perform whatever action was requested ;; and update the comparison frame and our local variables accordingly. ;; (frame (cond ;; ;; while browsing a profile, move to previous or following item: from ;; the list of identifiers in the frame, find the appropriate position ;; and re-initialize the compare frame ;; ((member action '("previous" "next") :test #'string-equal) (let ((nextp (string-equal action "next")) (current (lkb::compare-frame-item frame))) (loop with status = nil for ids on (if nextp (lkb::compare-frame-ids frame) (reverse (lkb::compare-frame-ids frame))) for next = (or (when (eql current (first ids)) (second ids)) (when (eq :null (get-field :status status)) (second ids))) when next do (setf status (browse-tree data next frame :runp nil)) unless (or (null next) (eq :null (get-field :status status))) return next))) ((and mode (not (eq mode (lkb::compare-frame-mode frame)))) (setf (lkb::compare-frame-mode frame) mode) (lkb::set-up-compare-frame frame (lkb::compare-frame-edges frame))) ((and display (not (eq display (lkb::compare-frame-display frame)))) (setf (lkb::compare-frame-display frame) display) (lkb::update-trees frame)) ((string-equal action "clear") (lkb::reset-discriminants frame)) (t (loop with discriminants = (lkb::compare-frame-discriminants frame) with decisions = nil for i from 0 to (length (lkb::compare-frame-discriminants frame)) for key = (format nil "~a" i) for value = (lookup-form-value key query) when (and value (not (equal value "?"))) do (let ((value (when (equal value "+") t))) (push (cons i value) decisions)) finally (loop for (i . value) in decisions for discriminant = (nth i discriminants) do (setf (lkb::discriminant-toggle discriminant) value) (setf (lkb::discriminant-state discriminant) value)) (lkb::recompute-in-and-out frame) (lkb::update-trees frame t)))))) (setf classicp (eq (lkb::compare-frame-mode frame) :classic)) (setf concisep (eq (lkb::compare-frame-display frame) :concise)) (setf orderedp (eq (lkb::compare-frame-display frame) :ordered)) (setf fullp (eq (lkb::compare-frame-display frame) :full)) #+:debug (setf lkb::%frame% frame) (with-http-response (request entity) (with-http-body (request entity :external-format (excl:crlf-base-ef :utf-8)) (www-doctype *html-stream*) (html (:html (www-header *html-stream* "Redwoods Tree Comparison") ((:body :onload "messenger()") (:center ((:form :action "/compare" :method "post" :accept-charset "utf-8") ((:table :class "compareNavigation") (:span (:td ((:input :type "button" :name "close" :value "close" :onClick "window.close()"))) (:td " ") (:td ((:input :type "button" :name "save" :value "save" :disabled '||))) (when data (html (:td " ") (:td ((:input :type "submit" :name "action" :value "previous"))) (:td " ") (:td ((:input :type "submit" :name "action" :value "next"))))) (:td " ") (:td ((:input :type "submit" :name "action" :value "clear"))) (:td "  |  mode:") (:td (;; ;; _fix_me_ ;; originally, i had :disabled '|| on the mode selection; ;; if we were to enable comparison on transfer outputs, i ;; imagine only :modern should be allowed (as there would ;; be no sensible derivations to discriminate), but just ;; now i fail to think of other situations where :classic ;; could go wrong. (26-apr-08; oe) ;; (:select :size 1 :name "mode" :onChange "this.form.submit()") ((:option :value "classic" :if* classicp :selected :if* classicp '||) "classic") ((:option :value "modern" :if* (not classicp) :selected :if* (not classicp) '||) "modern"))) (:td "  |  display:") (:td ((:select :size 1 :name "display" :onChange "this.form.submit()") ((:option :value "concise" :if* concisep :selected :if* concisep '||) "concise") ((:option :value "ordered" :if* orderedp :selected :if* orderedp '||) "ordered") ((:option :value "full" :if* fullp :selected :if* fullp '||) "full"))))) :newline (when data (html ((:input :type "hidden" :name "data" :value data)))) ((:input :type "hidden" :name "frame" :value index)) :newline (when frame (lkb::html-compare frame :stream *html-stream*)) (www-version *html-stream*)))))))))) (defun www-latex (request entity &key results) (setf %www-request% request %www-entity% entity) (let* ((method (request-method request)) (body (when (eq method :post) (get-request-body request))) (query (and body (form-urlencoded-to-query body))) (item (if query (lookup-form-value "item" query) (request-query-value "item" request :post nil))) (item (typecase item (string (ignore-errors (parse-integer item))) (number item))) (item (www-retrieve-object nil item)) (results (or results (if query (lookup-form-value "results" query) (request-query-value "results" request :post nil)))) (results (typecase results (string (ignore-errors (parse-integer results))) (number results))) (results (www-retrieve-object nil results)) (results (stable-sort results #'< :key #'(lambda (foo) (get-field :result-id foo))))) (with-http-response (request entity :format :text :content-type "text/plain; charset=UTF-8;") (with-http-body (request entity :external-format (excl:crlf-base-ef :utf-8)) (loop with *reconstruct-cache* = (make-hash-table :test #'eql) with mrs::*lnkp* = :characters initially (format *html-stream* "%~%% LaTeX result(s) for `~a'~%~ % [~a; ~a]~%%~%~%~%" (get-field :i-input item) *www-title* (current-time :long :pretty)) for result in results for derivation = (get-field :derivation result) for mrs = (mrs::read-mrs-from-string (get-field :mrs result)) for edge = (or (get-field :edge result) (let ((edge (and derivation (reconstruct derivation)))) (when edge (nconc result (acons :edge edge nil))) edge)) when (or mrs edge) do (format *html-stream* "%~%% result # ~a~%%~%" (get-field :result-id result)) when edge do (ignore-errors (lkb::latex-tree edge :stream *html-stream* :format :derivation)) (ignore-errors (lkb::latex-tree edge :stream *html-stream* :format :syntax)) when (or mrs edge) do (when (null mrs) (setf mrs (mrs::extract-mrs edge)) (let ((mrs (with-output-to-string (stream) (mrs::output-mrs1 mrs 'mrs::simple stream)))) (nconc result (acons :mrs mrs nil)))) (mrs::output-mrs1 mrs 'mrs::latex *html-stream*) (terpri *html-stream*)))))) (defun www-fetch (request entity) #+:debug (setf %www-request% request %www-entity% entity) (let* ((method (request-method request)) (body (when (eq method :post) (get-request-body request))) (query (and body (form-urlencoded-to-query body))) (id (if query (lookup-form-value "id" query) (request-query-value "id" request :post nil))) (id (when (stringp id) (ignore-errors (parse-integer id)))) (object (when id (www-retrieve-object nil id))) (value (background-status object)) (value (get-field :surface (first (get-field :results value))))) (with-http-response (request entity :content-type "text/plain") (with-http-body (request entity :external-format (excl:crlf-base-ef :utf-8)) (html (when value (format *html-stream* value))))))) (defun www-podium (request entity &key pattern) (setf %www-request% request %www-entity% entity) (let* ((method (request-method request)) (body (when (eq method :post) (get-request-body request))) (query (and body (form-urlencoded-to-query body)))) (declare (ignore query)) (with-http-response (request entity) (with-http-body (request entity :external-format (excl:crlf-base-ef :utf-8)) (www-doctype *html-stream*) (html (:html (www-header *html-stream* "[incr tsdb()] Redwoods Treebanks") ((:body :onload "messenger()") (:center ((:form :action "/itsdb" :method "post" :target "_blank" :accept-charset "utf-8") :newline ((:table :border 0 :cellspacing 0) (:tr ((:td :class "buttons") (:i "where ")) ((:td :class "buttons") ((:input :type "text" :name "where" :value "" :size "40"))) ((:td :class "buttons") " ") ((:td :class "buttons") ((:input :type "submit" :name "action" :value "summarize"))) ((:td :class "buttons") " ") ((:td :class "buttons") ((:input :type "submit" :name "action" :value "browse"))) ((:td :class "buttons") " ") ((:td :class "buttons") ((:input :type "submit" :name "action" :value "Errors" :disabled '||))))) :br :newline ((:div :class "profiles") (tsdb-do-list *tsdb-home* :pattern pattern :stream *html-stream* :format :html))) (www-version *html-stream*))))))))) (defun www-itsdb (request entity) (setf %www-request% request %www-entity% entity) (let* ((method (request-method request)) (body (when (eq method :post) (get-request-body request))) (query (and body (form-urlencoded-to-query body))) (action (lookup-form-value "action" query)) (data (lookup-form-value "data" query)) (condition (lookup-form-value "condition" query))) (cond ((equal action "browse") (www-compare request entity :data data)) ((equal action "summarize") (with-http-response (request entity) (with-http-body (request entity :external-format (excl:crlf-base-ef :utf-8)) (www-doctype *html-stream*) (html (:html (www-header *html-stream* "Redwoods Annotation Summary") ((:body :onload "messenger()") (:center (analyze-trees data :file *html-stream* :condition condition :format :html) (www-version *html-stream*))))))))))) (defun www-doctype (stream) (format stream "~%")) (defun www-header (stream title &optional (name "default")) (let ((*html-stream* stream)) (html (:head ((:meta :http-equiv "Content-Type" :content "text/html; charset=utf-8")) (:title (format stream "~a" title)) :newline ((:link :type "text/css" :rel "stylesheet" :href "/logon.css")) :newline ((:link :type "image/gif" :rel "icon" :href "/icon.gif")) :newline #+:null (format *html-stream* "~% ~ ~%") ((:script :src "/logon.js" :language "javascript" :type "text/javascript")) ((:script :src "/custom.js" :language "javascript" :type "text/javascript")) ((:script :src "/prototype.js" :language "javascript" :type "text/javascript")) ((:script :src "/scriptaculous.js" :language "javascript" :type "text/javascript")) :newline ((:script :src "/alttxt.js" :language "javascript" :type "text/javascript")) :newline (format stream "~%" (smember :transfer *www-capabilities*) (smember :generate *www-capabilities*) (smember :translate *www-capabilities*)) (when name (format stream "~%" name))) :newline))) (defun www-version (stream) (format stream "
~%
[~ LOGON (~a)" (subseq mt::*version* 7 32)) (loop for task in '(:parse :transfer :generate) for grammar = (loop for client in *pvm-clients* for cpu = (pvm:client-cpu client) when (smember task (pvm:cpu-task cpu)) return (pvm:cpu-grammar cpu) finally (return "unknown")) for url = (second (assoc task *www-urls*)) when (smember task *www-capabilities*) do (format stream " — ~@[~]~a~@[~*~]" url grammar url)) (format stream "]
~%") (when (stringp *www-disclaimer*) (write-string *www-disclaimer* stream)) (when (functionp *www-disclaimer*) (funcall *www-disclaimer* stream)) (format stream "~%
")) (let ((lock (mp:make-process-lock))) (defun www-log (request input readings time edges error) (mp:with-process-lock (lock) (with-open-file (stream *www-log* :direction :output :if-does-not-exist :create :if-exists :append) (let* ((headers (net.aserve::request-headers request)) (forwarded (rest (assoc :x-forwarded-for headers))) (socket (request-socket request)) (address (or forwarded (socket:remote-host socket))) (host (socket:ipaddr-to-hostname address))) (format stream "[~a] www-log(): [~a] `~a' --- ~a~@[ (~,2f)~]~@[ <~a>~]~ ~@[ error: `~a'~].~%" (current-time :long :pretty) (or host address) input readings time edges (unless (equal error "") error)))))) (defun www-warn (request string) (mp:with-process-lock (lock) (with-open-file (stream *www-log* :direction :output :if-does-not-exist :create :if-exists :append) (let* ((socket (request-socket request)) (address (socket:remote-host socket)) (host (socket:ipaddr-to-hostname address))) (format stream "[~a] www-warn(): [~a] ~a.~%" (current-time :long :pretty) (or host address) string)))))) (let ((lock (mp:make-process-lock))) (defun www-store-object (id object &key globalp) (mp:with-process-lock (lock) (let ((n %www-object-counter%)) (setf (aref %www-attic% n) (cons (if globalp -1 id) object)) (incf %www-object-counter%) (when (>= %www-object-counter% (array-total-size %www-attic%)) (setf %www-attic% (adjust-array %www-attic% (* %www-object-counter% 2)))) n))) (defun www-retrieve-object (id n) (when (and (numberp n) (>= n 0) (< n (array-total-size %www-attic%))) (mp:with-process-lock (lock) (let ((bucket (aref %www-attic% n))) (when (or (equal (first bucket) -1) (equal (first bucket) id)) (rest bucket))))))) (defun lookup-form-value (name query) (loop with result = nil for (key . value) in query when (string-equal key name) do (push value result) finally (return (if (rest result) result (first result))))) (defun www-output (file &key (stream t) (absolutep t) values) (let ((file (if absolutep file (merge-pathnames (make-pathname :directory (pathname-directory (dir-append (get-sources-dir "tsdb") '(:relative "tsdb" "html")))) (make-pathname :name file :type "html"))))) (when (probe-file file) (loop with buffer = (make-array 4096 :element-type 'character :adjustable t :fill-pointer 0) with in = (open file :direction :input) for c = (read-char in nil nil) while c do (vector-push-extend c buffer) finally (close in) (apply #'format stream buffer values)))))