;;;### File "ezlist.lisp"
;;; Copyright Notice
;;;
;;; This software is distributed for non-profit and research purposes only.
;;; Non-profit redistribution of the current version or parts of the current
;;; version is permitted if this copyright notice is included unchanged.
;;; We give no warranty of any kind for this prototype. It will be further
;;; improved as time permits.
;;;
;;; Michael Herfert, Harold Boley (boley@informatik.uni-kl.de)
;;; History (see individual modules for details):
;;; Feb. 93: First version [MH]
;;; Feb. 96: CLiCCable version [WG,UH,MP,MS]
;;; June 96: mod -> md [HB,MS]
;;; Aug. 96: ":-&" -> ":- &" [HB]
;;; Feb. 97: New PROLOG-like clause syntax (newsyn, part 1) implemented [HB]
;;; Mar. 97: New PROLOG-like is syntax (newsyn, part 2) implemented [HB]
;;; Mar. 97: ":&-3" scanned as ":& -3"; ":-&" scanned as ":- &" [HB]
;;; Sep. 97: New valued-conjunction top-level [MP]
;;; Nov. 97: Corrected newsyn for printing iso-prolog is in rfp2pl/rf2pl [MP]
;;; Apr. 98: gensyms to be seen by the user replaced by gentemps [HB]
;; ---------------- global variables and constants ---------------------------
(defconstant the-non-printing-char (code-char 0))
;; Easy life for the editor:
(defconstant sqr-left #\[)
(defconstant sqr-right #\])
(defconstant round-left #\()
(defconstant round-right #\))
(defconstant comma #\,)
(defconstant bar #\|)
(defconstant eof-char (code-char 4))
(defvar *style* 'prolog) ; pre-defvarred
(defvar *rfi-standard-output* nil) ; only for CLiCC
(defvar *print-like-prolog* nil
"<==> the pretty-printer uses the syntax of Prolog, not the prologish
syntax of Relfun
")
;; -------------------------- M a c r o s -----------------------------------
(defmacro accept-token-type (tok-type)
;; Signals an error, if the wrong token is found,
;; otherwise it reads in the next token.
`(if (eq (funcall scanner 'last-token-type)
,tok-type)
(funcall scanner)
(signal-error scanner
(format nil "Expected: ~A~%Found: "
(tok-type->string ,tok-type) )
(funcall scanner 'last-token) )))
(defmacro stringcat (&rest strings)
`(concatenate 'string
,@strings ))
(defmacro append* (var-sym a-list)
;; easy to type
`(setq ,var-sym (append ,var-sym ,a-list)) )
(defmacro let-assoc ((fct-id item assoc-list) then-form else-form)
;; assoc-list = ( (key-1 . fct-1) (key-2 . fct-2) .. )
;; If item is in assoc-list then the corresponding function is called
;; with then-args as arguments.
;; Else then-args are not evaluated and else-form is executed.
;; Example:
;; (let-assoc (g symbol *extra-parsing-fcts*)
;; (g scanner nil)
;; (parse-round-list scanner nil) )
`(let ((pair (assoc ,item ,assoc-list)))
(if (null pair)
,else-form
(flet ((,fct-id (&rest args)
(apply (symbol-function (cdr pair))
args)))
,then-form ))))
#|
----------------------------- e z l i s t -----------------------------------
This section defines four macros to access parts of a list by
providing a simple kind of pattern matching.
let+ is an extension of let.
Syntax:
LET+ ({var | (var value) | (pattern value)}*) {form}*
let*+ is an extension of let*.
Syntax:
LET*+ ({var | (var value) | (pattern value)}*) {form}*
get-el replacement for car, cdr, c..r
Syntax:
GET-EL pattern form
set-el replacement for rplacar, rplacdr, setf
(set-el replaces only the list-acces facilities of setf).
Syntax:
SET-EL {pattern form value}*
Pattern is a s-expr made of strings, symbols and patterns:
string | symbol | (pattern [. pattern])
Example 1:
==========
(let*+ ((a 1)
((el1 el2) (g a))
(b 2) )
)
expands to to:
(let* ((a 1)
(#:|let-161| (g a))
(el1 (nth 0 #:|let-161|))
(el2 (nth 1 #:|let-161|))
(b 2))
)
Example 2:
==========
(let*+ (((hn-or-ft (functor . arguments) . factors)
clause ))
)
expands to to:
(let* ((#:|let-162| clause)
(hn-or-ft (nth 0 #:|let-162|))
(functor (nth 0 (nth 1 #:|let-162|)))
(arguments (nthcdr 1 (nth 1 #:|let-162|)))
(factors (nthcdr 2 #:|let-162|)))
)
Example 3:
==========
A string can be used to disable the generation of an access function:
(let*+ ((a 1)
(("el1" el2) (g a))
(b 2) )
)
expands to to:
(let* ((a 1)
(#:|let-161| (g a))
(el2 (nth 1 #:|let-161|)) ; compare with first example
(b 2))
)
Example 4:
==========
(get-el ("tag" (head-functor . "head-args") . "body") clause)
expands to to:
(nth 0 (nth 1 clause))
The pattern must contain exact one symbol.
Example 5:
==========
(set-el ("tag" (head-functor . "head-args") . "body")
clause
new-head-functor )
expands to to:
(setf (nth 0 (nth 1 clause)) new-head-functor)
The pattern must contain exact one symbol.
Emacs users loading cl-indent.el may place the following s-expr
in .emacs to format the new macros correctly (tested with Emacs 18.59):
(let ((l '((let+ ((&whole 4 &rest (&whole 1 1 2)) &body))
(let*+ . let+) )))
(while l
(put (car (car l)) 'common-lisp-indent-hook
(if (symbolp (cdr (car l)))
(get (cdr (car l)) 'common-lisp-indent-hook)
(car (cdr (car l)))))
(setq l (cdr l)) ))
|#
(defmacro let*+ (variables &rest body-exprs)
(list* 'let* (let*0 variables) body-exprs) )
(defmacro let+ (variables &rest body-exprs)
(let0 variables body-exprs) )
(defun let*0 (variables)
(if (null variables)
nil
(let ((item (car variables)))
(append
(cond ((atom item) (list item))
((atom (car item)) (list item))
(t (let*1 (first item) (second item))) )
(let*0 (rest variables)) ))))
(defun let*1 (symbols value)
(if (null value)
(error (concatenate 'string
"While expanding let*+:~%"
" Value form is NIL (resp. not supplied) !~%"
" Pattern: ~S~%" )
symbols )
(let ((temp-var (gensym "let-")))
(cons (list temp-var value)
(gen-list-access-functions symbols temp-var) ))))
(defun let0 (list-of-tuples body-exprs)
(let (body-prefix new-lot)
(do* ((lot list-of-tuples (rest lot))
(sym-value (first lot) (first lot))
new-sym-value
temp-sym
)
((null lot)
(setq new-lot (reverse new-lot)) )
(cond ((or (atom sym-value)
(atom (first sym-value)) )
;; it's a normal clause of let:
(setq new-lot (cons sym-value new-lot)) )
;; it's a pattern:
((null (second sym-value))
;; no value form provided:
(error (concatenate 'string
"While expanding let+:~%"
" Value form is NIL (resp. not supplied) !~%"
" Pattern: ~S~%" )
(first sym-value) ))
(t
;; process pattern:
(setq temp-sym (gensym "let-")
new-sym-value (list temp-sym (second sym-value))
body-prefix (cons (list (first sym-value) temp-sym)
body-prefix )
new-lot (append (tree2symbol-list (first sym-value))
(list new-sym-value) new-lot) ))))
(list* 'let new-lot (gen-setq body-prefix) body-exprs) ))
(defun gen-setq (body-prefix)
;; body-prefix = (( ) ..)
;; Value: (setq ..)
(let (l)
(mapc #'(lambda (pattern-and-temp-sym)
(setq l
(append l (gen-list-access-functions
(first pattern-and-temp-sym)
(second pattern-and-temp-sym) ))))
body-prefix )
(unless (null l) ; return nil if l = nil
(cons 'setq (reduce #'append l)) )))
(defun tree2symbol-list (tree)
"Returns list of symbols used in tree"
(cond ((null tree)
nil )
((stringp tree)
nil )
((atom tree)
(list tree) )
(t
(append (tree2symbol-list (car tree))
(tree2symbol-list (cdr tree)) ))))
(defun gen-list-access-functions (pattern value-form)
(gen-list-access-functions-aux pattern value-form 0) )
(defun gen-list-access-functions-aux (symbols value-form n)
;; Example: (gen-list-access-functions-aux '(a b) 'v 0)
;; ==>
;; ((a (nth 0 v))
;; (b (nth 1 v)) )
(cond ((null symbols)
nil )
((stringp symbols)
nil )
((symbolp symbols)
(list (list symbols (list 'nthcdr n value-form))) )
((stringp (first symbols))
(gen-list-access-functions-aux (rest symbols) value-form (1+ n)) )
((atom (first symbols))
(cons (list (first symbols)
(list 'nth n value-form) )
(gen-list-access-functions-aux (rest symbols) value-form (1+ n))
))
(t
(append (gen-list-access-functions-aux (first symbols)
(list 'nth n value-form)
0 )
(gen-list-access-functions-aux (rest symbols)
value-form
(1+ n))
))))
(defmacro get-el (pattern a-list)
(let ((acc-functions (gen-list-access-functions pattern a-list)))
(if (/= 1 (length acc-functions))
(error "~A~A :~%~A~%"
"While expanding "
(list 'get-el pattern a-list)
"There must be exact one symbol in pattern." )
(cadar acc-functions) )))
(defmacro set-el (&rest tripels)
(if (= 0 (mod (length tripels) 3))
(set-el1 tripels nil) ; nil: result of expansion
(error "~A~A :~%~A~%"
"While expanding "
(cons 'set-el tripels)
"Number of arguments is not a multiple of 3." )))
(defun set-el1 (tripels result)
(if (null tripels)
`(setf ,@result)
(let* ((pattern (first tripels))
(form (second tripels))
(new-value (third tripels))
(acc-functions (gen-list-access-functions pattern form))
(acc-function (cadar acc-functions)) )
(if (/= 1 (length acc-functions))
(error "~A~A :~%~A~%"
"While expanding "
(list 'set-el pattern form new-value)
"There must be exact one symbol in pattern." )
(set-el1 (cdddr tripels)
(append result (list acc-function new-value)) )))))
(pushnew :rf-ez-list *features*)
#| --------------------------- T e s t s ------------------------------------
(load "xpp.lsp") ; xlisp pretty printer
(progn
(xpp
(macroexpand '(let*+ (x
(n 10)
((a b c) (f n))
(m 20)
)
(setq xx 10)
(fac 10)
)))
(let*+ (((hn-or-ft (functor . arguments) . body)
'(ft (facrel n r) (* 10 n)) )
((a b c . d) '(1 2 3 4 5 6 7))
)
(format t "hn-or-ft: ~A~%functor: ~A~%arguments: ~A~%body: ~A~%a: ~A~%b: ~A~%c: ~A~%d: ~A~%" hn-or-ft functor arguments body a b c d))
)
(progn
(xpp
(macroexpand '(let*+ ((a 1)
((el1 el2) (g a))
(b 2) ))))
(xpp
(macroexpand '(let+ (x
(n 10)
((a (b . c)) (f 4))
(m 20)
((x "y" z) (g 8))
)
(setq xx 10)
(fac 10)
)))
(xpp
(macroexpand '(let+ ((x 20)
((a b) (f 20))
y
)
(g 30) )))
(let+ ((x "This is x")
((a1 (a2 a3 . a4) (a5)) '(1 (2 3 . 4) (5)))
(y "This is y") )
(format t "x: ~A~%a1: ~A~%a2: ~A~%a3: ~A~%a4: ~A~%a5: ~A~%y: ~A~%"
x a1 a2 a3 a4 a5 y) )
)
;; An error:
(xpp (macroexpand (quote
(let*+ ((x "This is x")
((a1 (a2 a3 . a4) (a5))) ; <== error
(y "This is y") )
(format t "x: ~A~%a1: ~A~%a2: ~A~%a3: ~A~%a4: ~A~%a5: ~A~%y: ~A~%"
x a1 a2 a3 a4 a5 y) ))))
(progn
(gen-list-access-functions '(a b c) 'v)
(gen-list-access-functions '(a (x y) c) 'v)
(gen-list-access-functions '(a . b) 'v)
(gen-list-access-functions '(a (x y . z) c) 'v)
)
(macroexpand '(get-el
("tag" (head-functor . "head-args") . "body") clause))
(macroexpand '(set-el ("tag" (head-functor . "head-args") . "body")
clause
new-head-functor ))
(get-el ("a" ("b" el) "c") '(1 (2 444) 5))
(get-el ("a" ("b" el) "c") nil) ; an error
(get-el ("a" ("b" el) "c")) ; an error
(progn ; an error
(setq l '(1 (2 555) 4))
(set-el ("a" ("b" el) "c") l)
l )
(progn
(setq l '(1 (2 555) 4))
(set-el ("a" ("b" el) "c") l 3)
l )
(progn
(setq l '(1 (2 888) (4 999 6) 7))
(set-el ("a" ("b" v) . "dont-care") l 3
("a" "b" ("c" v "d") . "dont-care") l 5)
l )
|#
;; ----------------------- e z l i s t E n d -------------------------------
;;;### eof
;;;### File "tracer.lisp"
;;; Copyright Notice
;;;
;;; This software is distributed for non-profit and research purposes only.
;;; Non-profit redistribution of the current version or parts of the
;;; current version is permitted if this copyright notice is included unchanged.
;;; I give no warranty of any kind for this prototype. It will be further
;;; improved as time permits.
;;;
;;; Michael Herfert, 1993 (herfert@dfki.uni-kl.de)
;;; History:
;;; Feb. 93: First version [mh]
;;; Nov. 94: *tracebase* (defined in rfi.lisp)
;;; as name for the database used by the tracer
;;; Aug. 95: (get-sysbase 'tracebase) to get the database used by the tracer
;;; because introduction of modules
;;; Oct. 95: trace -all, trace -rest [SA]
;;; Nov. 95: keyword-prefix - [SA]
;; ------------------------- T r a c e r ------------------------------------
;;
;; Tracer based on the box model of PROLOG.
;; Implemented by horizontal compilation.
(defstruct tracing
;; level-now is incremented when entering a traced clause,
;; decremented when exiting.
(level-now 0)
;; level-max is used for correct indentation after a more-request.
;; It is compared against level-now everytime a traced clause has succeeded:
(level-max 0)
;; first factor in a traced body:
(id '+tracer+)
;; id for the print-fail clause added by the tracer:
(fail-id '+tracer-fail+)
;; heads =
;; ((head-functor-1 redo-functor-1 org-clauses-1) ... )
(heads nil)
)
(defun rf-keywordp (symbol)
;; if prefix is `-' then true (prefix `:' unsuitable because of sort meaning)
(if (symbolp symbol)
(char= #\- (char (string symbol) 0))))
(defvar tracer (make-tracing) "The state of the tracer.")
(defun tracer-reset-to-zero ()
;; (princ "tracer-reset-to-zero")
(setf (tracing-level-now tracer) 0
(tracing-level-max tracer) 0 ))
(defun tracer-reset-to-max ()
;; (princ "tracer-reset-to-max")
(setf (tracing-level-now tracer) (tracing-level-max tracer)
(tracing-level-max tracer) 0 ))
(defun tracer-increment-level ()
(incf (tracing-level-now tracer)) )
(defun tracer-decrement-level ()
(decf (tracing-level-now tracer)) )
(defun tracer-check-max ()
(when (> (tracing-level-now tracer) (tracing-level-max tracer))
(setf (tracing-level-max tracer) (tracing-level-now tracer)) )
t )
(defun tracer-print-status ()
(let ((headlist (tracing-heads tracer)))
(if (null headlist) ;; no procedures are traced, SA
(rf-princ-like-lisp "No procedures traced")
(dolist (triple headlist t)
(let+ (((functor "redo-functor" "org-clauses") triple))
(when (traced-p functor)
;; the procedure still exists and is traced:
(pp functor) ))))))
(defun traced-p (head-functor)
;; Value: nil, if head-functor is not traced.
;; redo-functor, if head-functor is traced and redo-functor
;; is used for redo-detection.
;;
(let*+ ((("head-func" redo-functor "org-clauses")
(car (member head-functor (tracing-heads tracer)
:key #'car :test #'equal )))
(clause (car (member head-functor (get-database)
:key #'caadr :test #'equal )))
(("tag" "head" body-1 . "body-rest") clause))
;; check a clause for tracing (maybe database was destroyed):
(and (eq (tracing-id tracer) body-1)
redo-functor )))
(defun tracer-book-keeping (head-functor redo-functor org-clauses)
;; store head-functor, ..
(let ((l
(car (member head-functor (tracing-heads tracer)
:key #'car :test #'equal))))
(if l
;; was traced, replace old values destr.:
(set-el ("head-functor" redo-functor "org-clauses") l redo-functor
("head-functor" "redo-functor" org-clauses) l org-clauses )
;; was not traced, insert values:
(setf
(tracing-heads tracer)
(cons (list head-functor redo-functor org-clauses)
(tracing-heads tracer) )))))
(defun tracer-get-org-clauses (head-functor)
;; value: list of untraced clauses
(get-el (("head-functor" "redo-functor" org-clauses))
(member head-functor (tracing-heads tracer) :key #'car :test #'equal)))
(defun tracer-get-redo-functor (head-functor)
;; value: the redo functor/SA
(get-el (("head-functor" redo-functor "org-clauses"))
(member head-functor (tracing-heads tracer) :key #'car :test #'equal)))
(defun trace-or-retrace-procedure (functor incl excl print-p print-f)
;; Destruct. modifies the database
(let ((redo-sym (traced-p functor)))
(if redo-sym
(retrace-procedure functor redo-sym incl excl print-p print-f)
(trace-procedure functor incl excl print-p print-f) )))
(defun trace-procedure (functor incl excl print-p print-f)
;; Destruct. modifies the database.
;; Replaces old clauses of procedure by clauses
;; modified for tracing.
;; Condition on entry: is not traced.
;; Value: list of clauses (unmodified) (or nil on error).
(let ((default-action (get-default-action incl excl)))
(if (null default-action)
(progn
(rf-princ-like-lisp (format nil
"Do not use both of -incl and -excl." ))
nil )
(do* ((rest-clauses (member functor (get-database)
:key #'caadr :test #'equal )
(member functor (cdr rest-clauses)
:key #'caadr :test #'equal ))
(traced-clauses nil (cons clause traced-clauses))
(clause (car rest-clauses) (car rest-clauses))
(i 1 (1+ i))
(action (or (get-incl-or-excl i incl excl) default-action)
(or (get-incl-or-excl i incl excl) default-action) )
(redo-sym (create-redo-functor functor))
)
((null rest-clauses)
(if (null traced-clauses)
;; functor not found:
(progn
(rf-princ-like-lisp "Procedure not found: ")
(pp functor)
nil )
(let ((org-clauses (reverse traced-clauses)))
(tracer-book-keeping functor redo-sym org-clauses)
(append-redo-and-fail-clauses
(create-redo-clauses org-clauses redo-sym)
functor
redo-sym )
org-clauses )))
(setf
(car rest-clauses)
(create-traced-clause clause clause i redo-sym
action print-p print-f ))))))
(defun retrace-procedure (functor redo-sym incl excl print-p print-f)
;; destruct. modifies the database.
;; Replaces old clauses of procedure by clauses
;; modified for tracing.
;; Condition on entry: functor is traced.
;; Value: void
(do* ((rest-clauses (member functor (get-database)
:key #'caadr :test #'equal )
(member functor (cdr rest-clauses)
:key #'caadr :test #'equal ))
(clause (car rest-clauses) (car rest-clauses))
(org-clauses (tracer-get-org-clauses functor) (cdr org-clauses))
(org-clause (car org-clauses) (car org-clauses))
(i 1 (1+ i))
)
((null rest-clauses))
(setf (car rest-clauses)
(create-traced-clause clause org-clause i redo-sym
(get-incl-or-excl i incl excl) print-p print-f
))))
(defun untrace-procedure (functor)
(remove-redo-and-fail-clauses functor)
(do* ((rest-clauses (member functor (get-database)
:key #'caadr :test #'equal )
(member functor (cdr rest-clauses)
:key #'caadr :test #'equal ))
(clause (car rest-clauses) (car rest-clauses))
(org-clauses (tracer-get-org-clauses functor)
(cdr org-clauses) )
(org-clause (car org-clauses) (car org-clauses))
;; may be the database has been destroyed:
(still-traced-p (eq (tracing-id tracer)
(get-el ("hn-or-ft" "head" body-1 . "body-rest")
clause )))
)
((or (null rest-clauses) (null org-clauses) (not still-traced-p))
(setf (tracing-heads tracer)
(remove functor (tracing-heads tracer)
:key #'car :test #'equal ))
t )
;; replace a traced clause:
(setf (car rest-clauses) org-clause) ))
(defun create-traced-clause
(db-clause org-clause clause-n redo-sym incl-excl-nil print-p print-f)
;; returns a traced version of org-clause or db-clause
(if (eq (tracing-fail-id tracer)
(get-el ("tag" "head" body-1 . "body-rest") db-clause) )
;; found an old fail clause:
db-clause
(case incl-excl-nil
((nil) db-clause)
(incl
(create-incl-clause org-clause clause-n redo-sym print-p print-f) )
(excl (create-excl-clause org-clause))
(t (error "Internal error in create-traced-clause.")) )))
(defun create-excl-clause (clause)
;; returns a traced version of clause (printing disabled)
(let+ (((tag head . body) clause))
(list* tag head (tracing-id tracer) body) ))
(defun create-incl-clause (clause clause-n redo-sym print-p print-f)
;; returns a traced version of clause (printing enabled)
(let*+ (((hn-or-ft (functor . arguments) . body) clause)
(bar-in-head-p (member '\| arguments))
(arity (if bar-in-head-p (1- (length arguments)) (length arguments)))
((vari-foot vari-head vari-redo-p
vari-print-flag . new-head-arguments)
(create-variables (+ 4 arity ) '(1) (collect-variables clause)) )
(new-head (if bar-in-head-p
`(,functor
,@(butlast new-head-arguments)
\|
,@(last new-head-arguments) )
`(,functor ,@new-head-arguments) ))
(vari-foot-or-nil (if (eq 'hn hn-or-ft) nil (list vari-foot)))
(the-tracing-id-line (tracing-id tracer))
(the-redo-line `(is ,vari-redo-p
(once (tracer_get_redo_p
,clause-n
,(inst-functor-for-call redo-sym)
(inst ,new-head) ))))
(the-unification-line
`(is ,vari-head (is ,new-head (inst (,functor ,@arguments)))) )
(the-print-flag-line (if (or (eq 'true print-p))
;; no print-predicate given:
`(is ,vari-print-flag ,print-p)
;; print-predicate given:
`(tracer_get_print_flag
,vari-head
,print-p
,vari-print-flag )))
(the-print-function (if (eq 'true print-f)
;; use default print-function:
'tracer_default_printer
print-f ))
(the-print-enter-line
`(tracer_print_enter
,vari-head ,clause-n
,vari-redo-p
,vari-print-flag
,the-print-function
,hn-or-ft ))
(the-body-line (prepare-body hn-or-ft
vari-head
body
clause-n
vari-foot
vari-print-flag
the-print-function ))
(the-print-exit-line
`(tracer_print_exit
,vari-head
,clause-n
,vari-print-flag
,the-print-function
,@(if (eq 'hn hn-or-ft)
nil
`((inst (the_foot ,@vari-foot-or-nil))) )
))
)
`(,hn-or-ft
,new-head
,the-tracing-id-line
,the-redo-line
,the-unification-line
,the-print-flag-line
,the-print-enter-line
,@the-body-line
,the-print-exit-line
,@vari-foot-or-nil
)))
(defun create-variables (n varnuml oldvars)
"Returns a list of new variables (not contained in oldvars)."
(if (zerop n)
nil
(let ((var-as-number (car (nextnew varnuml oldvars))))
(cons (legnumvar (list var-as-number))
(create-variables (1- n) (list (1+ var-as-number)) oldvars) ))))
(defun create-redo-functor (functor)
"Returns functor used for redo-clauses."
(if (symbolp functor)
(make-symbol (stringcat (princ-to-string functor) "_redo_p" ))
(let ((head-symbol (get-symbol-of-call functor)))
;; make-symbol returns an uninterned symbol (Steele p.244)
(subst (make-symbol (stringcat (princ-to-string head-symbol)
"_redo_p" ))
head-symbol
functor
:test #'eq ))))
(defun create-redo-clauses (list-of-clauses redo-sym)
;; list-of-clauses contains all clauses of a procedure.
;; Value: list of redo-clauses.
(let* ((clause-n 0)
(list-of-redo-clauses
(mapcar #'(lambda (clause)
(let* ((new-variables
(create-variables 1
'(1)
(collect-variables clause) ))
(vari-clause-n (first new-variables))
)
(incf clause-n)
`(hn ; (cadr clause) = head
(,redo-sym ,vari-clause-n ,(cadr clause))
;(rf-print "--------------->")
;(rf-print ,clause-n)
(> ,vari-clause-n ,clause-n) )))
list-of-clauses )))
(append list-of-redo-clauses
`((ft (,redo-sym id id) false)) )))
(defun get-symbol-of-call (call)
"Returns the symbol that would be printed at first
by the PROLOG-pretty-printer."
(if (symbolp call)
call
(get-symbol-of-call (car call)) ))
(defun prepare-body
(hn-or-ft vari-head factors clause-n vari-foot print-flag print-f)
;; insert a message to be displayed when running from the right over a cut.
;; Save the foot in a variable.
(let ((body nil)
cut-at-the-end-p
)
(dolist (factor factors)
(if (eq '\! factor)
(setq
body
(list*
`(tracer_cut_message ,vari-head ,clause-n ,print-flag ,print-f)
'! ; body will be reversed
body)
cut-at-the-end-p t )
(setq body (cons factor body)
cut-at-the-end-p nil )))
(when (eq 'ft hn-or-ft)
;; prepare foot:
(if cut-at-the-end-p
(setf (third body) `(is ,vari-foot ,(third body))) ; destructive
(setf (first body) `(is ,vari-foot ,(first body))) ))
(reverse body) ))
(defun get-default-action (incl excl)
;; default action if for first call of trace:
(cond ((and (null incl) (null excl))
'incl )
((and incl excl)
nil ) ; this is an error
(incl
'excl )
(t
'incl )))
(defun get-incl-or-excl (clause-n incl excl)
;; Value is one of {incl, excl, nil}
(cond ((membership clause-n excl) 'excl)
((membership clause-n incl) 'incl)
(t 'nil) ))
(defun membership (item list-or-all)
(if (eq 'all list-or-all)
t
(member item list-or-all) ))
(defun inst-functor-for-call (functor)
(if (symbolp functor)
functor
(list 'inst functor) ))
(defun append-redo-and-fail-clauses (redo-clauses functor redo-sym)
;; Extends the database by appending the redo-clauses and
;; appending a clause with head matching all calls.
(let ((vari-head-args
(if (symbolp functor)
(legnumvar '(1))
(first (create-variables 1 '(1) (collect-variables functor))) ))
(big-number (length redo-clauses)) ) ; = 1 + nr of clauses of functor
(set-database
(append (get-database)
redo-clauses
`((hn ; the fail clause
(,functor \| ,vari-head-args)
;; args of tracer_print_fail:
;; functor redo-p print-p print-f
,(tracing-fail-id tracer)
(tracer_print_fail
(inst (,functor \| ,vari-head-args))
;;t
(once (,(inst-functor-for-call redo-sym)
,big-number (inst (,functor \| ,vari-head-args))) )
true
tracer_default_printer)
unknown
)) ))))
(defun remove-redo-and-fail-clauses (head-functor)
;; removes the redo- and fail-clauses for head-functor from database.
;; It does not matter if head-functor is not a traced procedure.
(let ((redo-functor (traced-p head-functor)))
(when redo-functor
;; it is a traced procedure:
(set-database
(remove-if
#'(lambda (clause)
(let+ ((("tag" (head-f . "head-args") body-1 . "body-rest")
clause ))
(or (equal head-f redo-functor) ; rem. redo-clause
(and (equal head-f head-functor) ; rem. fail-clause
(eq body-1 (tracing-fail-id tracer)) ))))
(get-database) )))))
(defun tracer-cps (term)
;; copies a structure
;;(get-el ("inst" head) (tracer-copy-vars-aux term))
(tracer-copy-vars-aux term) )
(defun tracer-copy-vars-aux (term)
;; Returns a copy of term with fresh vars.
(cond ((atom term)
term )
((vari-t term)
(list 'vari (gentemp) 1) )
(t
(cons (tracer-copy-vars-aux (car term))
(tracer-copy-vars-aux (cdr term))) )))
;; ----------------------------------------------------------------------------
(defconstant tracer-min-space 30 "stop indenting if less space then this")
(defun tracer-print-heading (string)
;; Example: (tracer-print-heading "> ")
;; Returns the cursor-pos.
;; Indentation is controlled by the trace level
(let ((spaces-n (tracer-get-indent))
indent-as-str )
(if (< (- *rf-print-width* spaces-n) tracer-min-space)
;; no further indenting because of missing space:
(progn
(setq spaces-n (- *rf-print-width* tracer-min-space))
(setq indent-as-str (format nil "[~D]" (tracing-level-now tracer)))
(rf-princ-like-lisp indent-as-str)
(spaces (- spaces-n (length indent-as-str))) )
(spaces spaces-n) )
(rf-princ-like-lisp string)
(+ spaces-n (length string)) ))
(defun tracer-print-hn-or-ft (hn-or-ft-or-ft-exit cursor-pos)
;; Prints ":-" or ":- &" or ":-&"
;; Returns new cursor position
(let* ((str (case hn-or-ft-or-ft-exit
(hn " :- ")
(ft " :- & ")
(ft-exit " :-& ")
(hn-exit " :- ")
(t (error "Error in tracer.lsp (tracer-print-hn-or-ft).")) ))
(len (length str)) )
(rf-princ-like-lisp str)
(+ cursor-pos len) ))
(defun tracer-print-head (indent head &optional clause-n)
;; Value: cursor-pos
(when clause-n
(incf indent 4)
(rf-princ-like-lisp clause-n)
(rf-princ-like-lisp " ") )
(if (eq *style* 'lisp)
(progn (pp-init indent)
(pp-expr head)
(1+ pp-currentpos*) )
(pro-print-head head indent) ))
(defun tracer-print-foot (cursor-pos foot)
(if (eq *style* 'lisp)
(tracer-print-foot-lsyn (tracer-get-indent) cursor-pos foot )
(tracer-print-foot-psyn (tracer-get-indent) cursor-pos foot ))
(rf-terpri) )
(defun tracer-print-foot-lsyn (left-margin cursor-pos foot)
(let ((rest-col (- *rf-print-width* cursor-pos 3)) ;3: safety
(cursor-start cursor-pos) )
(when (and (< rest-col tracer-min-space)
(> (flatsize foot) rest-col) )
(setq cursor-start (+ 5 left-margin))
(pro-newline-and-indent cursor-start) )
(pp-init cursor-start)
(pp-expr foot) ))
(defun tracer-print-foot-psyn (left-margin cursor-pos foot)
(pro-print* (list 'inst foot) cursor-pos (+ 5 left-margin) tracer-min-space))
(defun tracer-get-indent () (* 2 (1- (tracing-level-now tracer))))
;; ---------------------------- r f i - c o m m a n d s ------------------
(defun rfi-cmd-trace (userline)
;; userline = (trace ..)
;; Transformation from P-syntax to L-syntax has been done.
(when (null (get-sysbase 'tracebase))
(rfi-load-tracer) )
(unless (null (get-sysbase 'tracebase))
(if (= 1 (length userline))
(tracer-print-status)
(cond ((eq '-rest (cadr userline)) (rfi-cmd-trace-rest userline))
;; trace those procedures not yet traced at all, SA
((eq '-all (cadr userline)) (rfi-cmd-trace-all userline))
;; trace all clauses of all procedures in rfi-database, SA
(t (rfi-cmd-trace-many-defs (cdr userline)))))))
(defun rfi-cmd-trace-rest (userline)
;; trace those procedures not yet traced at all, SA
(unless (null (cddr userline))
(rf-princ-like-lisp
"After `-rest' no more arguments are considered"))
(let* ((all-procs (remove-if #'vari-t (operators)))
(redo-functors (mapcar #'tracer-get-redo-functor all-procs)))
(do* ( (all-procs all-procs (cdr all-procs))
(clause (car (member (car all-procs) (get-database)
:key #'caadr :test #'equal ))
(car (member (car all-procs) (get-database)
:key #'caadr :test #'equal )))
(body-1 (third clause) (third clause)))
((null all-procs) nil)
(unless (or (eq (tracing-id tracer) body-1)
(eq (tracing-fail-id tracer) body-1)
(member (car all-procs) redo-functors))
(trace-procedure (car all-procs) 'all nil 'true 'true)))))
(defun rfi-cmd-trace-all (userline)
;; trace all clauses of all procedures in rfi-database, SA
(unless (null (cddr userline))
(rf-princ-like-lisp
"After `-all' no more arguments are considered"))
;; untrace all:
(mapc #'untrace-procedure
(mapcar #'car (tracing-heads tracer)) )
;; trace all:
(do ((heads (remove-if #'vari-t (operators)) (cdr heads)))
((null heads) nil)
(trace-procedure (car heads) 'all nil 'true 'true)))
(defun rfi-cmd-trace-many-defs (many-defs)
(let ((first-proc (rfi-cmd-trace-0 many-defs t)))
(if (null first-proc)
nil
(progn
(rfi-cmd-trace-one-def first-proc)
(rfi-cmd-trace-many-defs (nthcdr (length first-proc)
many-defs ))))))
(defun rfi-cmd-trace-one-def (one-def)
;; e.g. one-def = (facrel -incl 1 2 -excl 3 4)
(let ((incl (rfi-cmd-trace-1 one-def '-incl nil))
(excl (rfi-cmd-trace-1 one-def '-excl nil))
(print-p (rfi-cmd-trace-1 one-def '-print-p 'true))
(print-f (rfi-cmd-trace-1 one-def '-print-f 'true)) )
(if (and (null incl) (null excl))
(trace-or-retrace-procedure (car one-def) 'all nil print-p print-f)
(trace-or-retrace-procedure (car one-def) incl excl print-p print-f))))
(defun rfi-cmd-trace-0 (many-defs last-arg-was-keyword-p)
;; many-defs = {proc-1 ..}
;; Value: list for first procedure
(cond ((null many-defs)
nil )
((rf-keywordp (car many-defs))
(cons (car many-defs)
(rfi-cmd-trace-0 (cdr many-defs) t) ))
(last-arg-was-keyword-p
(cons (car many-defs)
(rfi-cmd-trace-0 (cdr many-defs) nil)) )
((numberp (car many-defs))
(cons (car many-defs)
(rfi-cmd-trace-0 (cdr many-defs) nil)) )
(t
;; start of a new proc.
nil )))
(defun rfi-cmd-trace-1 (one-def keyword default)
(let ((m (member keyword one-def))) ; e.g. m = (-incl 1 2 3 )
(if m
(case (car m)
((-incl -excl)
(rfi-cmd-trace-2 (cdr m)) )
((-print-p -print-f)
(second m) )
(t
(error "Unknown keyword in rfi-cmd-trace-1: " keyword) ))
default )))
(defun rfi-cmd-trace-2 (a-list)
;; collect a list of integers (or the symbol ALL)
(cond ((null a-list)
nil )
((eq 'all (car a-list))
'all )
((numberp (car a-list))
(cons (car a-list) (rfi-cmd-trace-2 (cdr a-list))) )
(t nil) ))
(defun rfi-cmd-untrace (userline)
;; userline = untrace {head-functor}
;; Transformation to P-syntax has been done.
(if (null (cdr userline))
;; untrace all:
(mapc #'untrace-procedure
(mapcar #'car (tracing-heads tracer)) )
;; untrace selected:
(mapc #'untrace-procedure (cdr userline)) ))
(pushnew :rf-tracer *features*)
;; ----------------------------------------------------------------------------
#|
(load "/home/herfert/lisp/tracer.lsp")
(defun ppp (list-of-clauses) ; pretty print prolog, for testing only
(rf-terpri)
(let ((*style* 'prolog))
(dolist (clause list-of-clauses 'okay)
;;(rf-terpri)
(pp clause)
)))
(pro-print (create-traced-clause
'(hn (p (f (vari x)) (g (vari y))) b1 b2)
1
'p_redo
'true
'true ))
(progn
(mapc #'(lambda (clause)
(terpri)
(pro-print clause) )
(create-redo-clauses
'((hn (p (f (vari x)) (g (vari y))) b1 b2)
(hn (p (h (vari z)) (vari r)))
)
'tracer_p_redo ))
'okay )
(progn
(set-database (copy-tree rfi-db))
(ppp (trace-procedure 'facrel))
(ppp (get-database))
)
|#
;;;### eof
;;;### File "comment.lisp"
;;; Copyright Notice
;;;
;;; This software is distributed for non-profit and research purposes only.
;;; Non-profit redistribution of the current version or parts of the
;;; current version is permitted if this copyright notice is included unchanged.
;;; I give no warranty of any kind for this prototype. It will be further
;;; improved as time permits.
;;;
;;; Michael Herfert, 1992
;;; --------------------------------------------------------------------------
;;;
;;; Module comment.lsp.
;;;
;;; Contains functions to read and print comments
;;; of Relfun-programs.
;;; If L-syntax is active then comments are recognized by two
;;; read-macros.
;;; If P-syntax is active then comments are recognized by the scanner.
(defvar *comment-style* nil
"Controls reading and printing.
If nil then comments are ignored.
If not nil then it is a list describing how to print comments:
*comment-style* = (
)
is in the range 0..*rfi-print-width*.
is a symbol describing how to format
end-of-line-comments:
RAW no special formatting
COMMENT-COLUMN align to .
COMMENT-COLUMN-OR-MARGIN align to . If not
possible align to right margin
MARGIN align to right margin
is a symbol describing how to format
begin-of-line-comments:
BREAK break lines if longer then *rfi-print-width*
NO-BREAK never break lines even if a line
exceeds the right margin.
See also function translate-file.
")
(defvar *default-comment-style*
'(40 comment-column-or-margin break))
(defconstant lsyn-comment-lead-in #\;)
(defconstant psyn-comment-lead-in #\%)
(defconstant com-max-depth 2)
(defstruct com-cell
lisp ; expr to print before the comment
(pre-txt "") ; comments before lisp (seldom used)
(post-txt "") ; comments after lisp (the usual case)
;; If post-txt begins with a #\newline then it's a begin-of-line-comment,
;; otherwise the first substring (up to the first #\newline) is a
;; end-of-line-comment.
;; If post-txt is not the empty string it is terminated by #\newline.
;; post-txt is printed after lisp.
internal-p ; lisp contains a coment
)
(defmacro look-for-comment ()
;; Checks if the scanner holds a comment.
`(make-com-cell
:post-txt (if (funcall scanner 'comment-p)
(funcall scanner 'comment)
""))
)
(defun comment-p (sexpr)
;; <==> sexpr is a com-cell containing a non-empty comment.
(or (and (com-cell-p sexpr)
(or (string/= "" (com-cell-pre-txt sexpr))
(string/= "" (com-cell-post-txt sexpr)) ))
(and (pro-expr-p sexpr)
(or (string/= "" (pro-expr-pre-txt sexpr))
(string/= "" (pro-expr-post-txt sexpr)) ))))
(defun get-comment-lead-in ()
(if (eq *style* 'lisp)
lsyn-comment-lead-in
psyn-comment-lead-in ))
;;; -------------------------------------------------------------------------
;;;
;;; Reading Commments
;;;
;;; -------------------------------------------------------------------------
(defun eat-comment-lead-ins (the-input-stream com-lead-in)
;; reads all comment-lead-in characters from the input stream.
;; On exit (read-char the-input-stream) returns the first char
;; after the last com-lead-in.
;; No value.
(do ((ch (peek-char nil the-input-stream nil eof-char)
(peek-char nil the-input-stream nil eof-char) ))
((char/= com-lead-in ch))
(read-char the-input-stream) ; consume the com-lead-in
))
(defun read-until-newline (the-input-stream)
"Reads the rest of the line including the newline-char.
On exit (read-char the-input-stream) returns the first char after
#\newline.
"
(do* ((ch (read-char the-input-stream nil #\newline)
(read-char the-input-stream nil #\newline) )
(txt (string ch)
(stringcat txt (string ch)) )
)
((char= ch #\newline)
txt )
)
)
(defun read-until-non-white (the-input-stream)
"Read until the first non-white-space char or eof-char in the current line.
#\newline is not a whitespace.
On entry the (imag.) cursor is in the first column.
Returns a list:
( )
On exit the next call of (read-char the-input-stream) returns the
non-white char.
"
(do ((ch (peek-char nil the-input-stream nil eof-char)
(peek-char nil the-input-stream nil eof-char)
;; peek-char
;; (Steele p574)
)
(column 1 (1+ column))
)
((case ch
((#\space #\tab)
nil ) ; continue reading
(t t)) ; non-white found
(list ch column)
)
(read-char the-input-stream) ; consume the white-space
))
(defun read-until-black (the-input-stream)
"Reads until a non-white-space. #\newline is a whitespace.
On exit (read-char the-input-stream) returns the non-white char.
Value is a list: ( )
str is a string containing a #\newline for every read #\newline.
"
(do ((non-white-char (first (read-until-non-white the-input-stream))
(first (read-until-non-white the-input-stream)) )
(str "" (stringcat str (string #\newline)))
)
((char/= #\newline non-white-char)
(list non-white-char str) )
(read-char the-input-stream) ; consume the #\newline
))
(defun read-comments (com-lead-in
type-of-first-com ; dom = {1, 2}
the-input-stream )
"Read in comments until a non-comment. Empty lines are parts of the comment.
Returns a list:
( )
If type-of-first-com = 2 then has a leading #\newline.
On entry (read-char the-input-stream) returns the first char after
the (first) comment lead in.
On exit the next call of (read-char the-input-stream) returns
the first char that is not part of a comment resp. eof.
"
(eat-comment-lead-ins the-input-stream com-lead-in)
(do* ((txt (read-until-newline the-input-stream)
(stringcat txt (read-until-newline the-input-stream)) )
(list-2 (read-until-non-white the-input-stream)
(read-until-non-white the-input-stream) )
(non-white-char (first list-2) (first list-2))
(column (second list-2) (second list-2))
(nr-of-newlines 1 (1+ nr-of-newlines))
)
((and (char/= non-white-char com-lead-in)
(char/= non-white-char #\newline) )
(list (if (= 2 type-of-first-com)
(stringcat (string #\newline) txt)
txt)
nr-of-newlines
column )
)
(when (char= non-white-char com-lead-in)
;; consume the com-lead-in:
(eat-comment-lead-ins the-input-stream com-lead-in))
))
(defun lsyn-comment-type-1-reader (stream comment-lead-in)
"Reader-macro.
Called for lines of the format:
;
Reads the comment and all comments immedeatly following this line.
"
(declare (ignore comment-lead-in))
(make-com-cell
:post-txt (first (read-comments lsyn-comment-lead-in 1 stream)) ))
(defun lsyn-comment-type-2-reader (stream the-newline-char)
"Reader-macro.
Called at the end of the line to check if the next line
begins with a comment.
If so it reads the comments and all comments immedeatly following
this line.
"
(declare (ignore the-newline-char))
(let ((non-white-char (first (read-until-non-white stream))))
(cond ((char= lsyn-comment-lead-in non-white-char)
;; comment detected:
(read-char stream) ; consume the comment-lead-in
(make-com-cell
:post-txt (first (read-comments lsyn-comment-lead-in 2 stream))))
;;((char= #\newline non-white-char)
;; empty line without comment-leadin detected:
;;(read-char stream) ; consume the comment-lead-in
;;#\newline )
(t
;; no comment:
(values) ; return no values
))))
;; -------------------------------------------------------------------------
(defun hide-comment-at-first (the-list)
;; the-list begins with a comment.
;; Returns a list.
(let* ((el-1 (first the-list))
(el-2 (second the-list))
;(el-3 (third the-list))
(result-of-2 (hide-comments el-2))
)
(if (com-cell-p result-of-2)
;; el-2 has an internal comment:
(setf (com-cell-lisp el-1) (com-cell-lisp result-of-2)
(com-cell-internal-p el-1) t )
;; el-2 has no internal comment:
(setf (com-cell-lisp el-1) result-of-2) )
(setf (com-cell-pre-txt el-1) (com-cell-post-txt el-1)
(com-cell-post-txt el-1) "" )
(cons el-1 (hide-comments-in-list (cddr the-list))) ))
(defun hide-comments-in-list (the-list &aux (the-car (car the-list)))
;; the-list can begin with a comment but not with a tag.
;; Returns a list.
;; Example: ( )
;; ==> ( )
;; is now in the lisp-field of .
;;(terpri) (princ "hide-comments-in-list: the-list = ") (princ the-list)
(cond ((null the-list)
nil )
((com-cell-p the-car)
;; the-list begins with a comment:
(hide-comment-at-first the-list) )
(;;(or (eq 'hn the-car) (eq 'ft the-car) (eq '\| the-car) )
(member the-car '(hn ft uc \| !))
;; never comment a tag or a bar:
(cons the-car (hide-comments-in-list (rest the-list))) )
(t
;; no comment, no tag:
(let ((el-2 (second the-list)))
(if (com-cell-p el-2)
;; comment on second position:
(let ((res-of-first (hide-comments (first the-list))))
(if (com-cell-p res-of-first)
;; the car of the-list has an internal comment:
(setf (com-cell-lisp el-2)
(com-cell-lisp res-of-first)
(com-cell-internal-p el-2)
t )
;; the car of the list has no internal comment:
(setf (com-cell-lisp el-2)
res-of-first ))
(cons (second the-list)
(hide-comments-in-list (cddr the-list)) ))
;; no comment on second position:
(cons (hide-comments (first the-list))
(hide-comments-in-list (cdr the-list)) )
)))))
(defun hide-comments (expr)
"Merges sexpr-s and comments.
If expr is a list and one of its element is a comment,
the whole expr will be packed in a com-cell."
(if (atom expr)
expr
;; expr is a list:
(let ((result (hide-comments-in-list expr)))
(if (any-true-p result #'com-cell-p)
;; at least one element has a comment:
(lift-comments (make-com-cell :internal-p t :lisp result))
;; no element contains a comment:
result ))))
(defun pack-comments (expr)
"Sets up the internal-p flag and creates com-cells in expressions
containing com-cells."
(cond ((com-cell-p expr)
(let ((result (pack-comments (com-cell-lisp expr))))
(make-com-cell
:lisp result
:pre-txt (com-cell-pre-txt expr)
:post-txt (com-cell-post-txt expr)
:internal-p (com-cell-p result) )))
((consp expr)
(let ((result (mapcar #'pack-comments expr)))
(if (any-true-p result #'com-cell-p)
;; at least one element has a comment:
(make-com-cell :internal-p t :lisp result)
;; no element contains a comment:
result )))
(t
expr )))
;;; -------------------------------------------------------------------------
;;;
;;; Printing Commments
;;;
;;; -------------------------------------------------------------------------
(defun gen-n-spaces (n)
(do ((i 0 (1+ i))
(spaces "" (stringcat spaces " "))
)
((= i n)
spaces
)))
(defun gen-n-newlines (n)
(do ((i 0 (1+ i))
(newlines "" (stringcat newlines (string #\newline)))
)
((= i n)
newlines
)))
(defun find-newline (str start-pos)
;; Returns the position of the first #\newline.
;; Starts search at start-pos.
;; There must be a newline.
(do ((i start-pos (1+ i))
)
((char= #\newline (char str i))
i
)))
(defun get-next-word (str start-pos)
;; Returns a list of two integers:
;; ( )
;; If there is no word after start-pos or a #\newline is detected
;; then nil is returned.
;; str must contain at least one #\newline.
;; Words are delimited by blanks.
;; start-pos is numbered from zero.
(if (>= start-pos (length str))
nil
(let* ((start-of-word (do ((i start-pos (1+ i))) ; find non-white-char
((case (char str i)
((#\space #\tab)
nil ) ; continue loop
(#\newline
(return -1) ) ; abort loop, no word
(t
(return i) )))
)) ; abort loop, word found
)
(if (= start-of-word -1)
nil ; no word
(do ((end-of-word start-of-word (1+ end-of-word)))
((case (char str (1+ end-of-word))
((#\space #\tab #\newline)
;; abort loop:
t )
(t
;; continue loop:
nil ))
(list start-of-word end-of-word)
))))))
(defun print-one-line-of-comment (str
leading-spaces
beg-end-of-first-word
cursor-col
indent-col
com-lead-in )
;; Prints str (str must be terminated by #\newline), possibly on multiple
;; lines.
;; Every new line begins with a number of leading spaces, determined
;; by leading-spaces.
;; Prints at least the first word of str.
;; There must be at least one word in str.
;; On exit the cursor is after the last printed character.
;; No value.
(do* ((beg-end-of-word beg-end-of-first-word)
(beg-of-word (first beg-end-of-first-word)
(first beg-end-of-word))
(spaces-before-word leading-spaces
(subseq str (1+ end-of-word) beg-of-word))
(end-of-word (second beg-end-of-first-word)
(second beg-end-of-word))
(the-word (stringcat spaces-before-word
(subseq str beg-of-word (1+ end-of-word)) )
(stringcat spaces-before-word
(subseq str beg-of-word (1+ end-of-word)) ))
(new-cursor-col (+ cursor-col (length the-word))
(+ new-cursor-col (length the-word)) )
)
((and (> new-cursor-col *rf-print-width*) ; last column reserved
; first word extra:
(not (eq beg-end-of-word beg-end-of-first-word)) )
;; no space on this line but words to print left:
(pro-newline-and-indent indent-col)
(rf-princ-like-lisp com-lead-in)
;; print the rest words:
(print-one-line-of-comment str
leading-spaces
beg-end-of-word
(+ 1 indent-col) ; 2 = len(";")
indent-col
com-lead-in )
)
(rf-princ-like-lisp the-word)
(setq beg-end-of-word (get-next-word str (1+ end-of-word)))
(when (null beg-end-of-word)
;; no more words:
(return)
)))
(defun print-one-line-of-comment-wo-break (str str-start-pos)
;; Prints str from str-start-pos up to the first #\newline (must exist)
;; even if it exceeds *rfi-print-width*
(rf-princ-like-lisp (subseq str
str-start-pos
(find-newline str str-start-pos))))
(defun print-comment-type-1 (str-wo-newline lead-in-char cursor-col indent-col)
;; Try to print the comment at the current line.
;; If this is not possible print as type 2.
;; On exit the cursor is on the right of the last printed character.
;; No value.
(let* ((len-of-com (length str-wo-newline))
(space-on-line (- *rf-print-width* cursor-col))
(real-space-after-indent-col (- *rf-print-width*
(max (first *comment-style*)
cursor-col )))
)
(if (<= space-on-line (+ 2 len-of-com)) ; 2 = len("; ")
;; does not fit on line:
(progn
(pro-newline-and-indent indent-col)
(print-comment-type-2 (stringcat str-wo-newline (string #\newline))
0 ; str-start-pos
lead-in-char
indent-col) )
;; comment fits on line:
(case (second *comment-style*)
(raw
;; no alignment at all.
(rf-princ-like-lisp " ")
(rf-princ-like-lisp lead-in-char)
(rf-princ-like-lisp str-wo-newline) )
(comment-column
;; indent to comment-column, if possible.
(if (> real-space-after-indent-col len-of-com)
;; alignment possible ==> go to indent-column
(spaces (- (first *comment-style*) cursor-col))
;; no alignment poss.:
(rf-princ-like-lisp " ") )
(rf-princ-like-lisp lead-in-char)
(rf-princ-like-lisp str-wo-newline) )
(comment-column-or-margin
;; indent to indent-column, if possible, else align to right.
(if (> real-space-after-indent-col len-of-com)
;; align to indent-column:
(spaces (- (first *comment-style*) cursor-col))
;; align to right margin:
(spaces (- *rf-print-width* cursor-col len-of-com 1)) )
(rf-princ-like-lisp lead-in-char)
(rf-princ-like-lisp str-wo-newline) )
(margin
;; always align to right:
(spaces (- *rf-print-width* cursor-col len-of-com 1))
(rf-princ-like-lisp lead-in-char)
(rf-princ-like-lisp str-wo-newline) )
(t
(error "Internal error in ") )))))
(defun print-comment-type-2 (str str-start-pos com-lead-in indent-col)
"Prints str (from str-start-pos) up to the first #\newline (must exist).
On entry cursor is in indent-col.
On exit cursor is on the right of the last printed character.
"
(let* ((beg-end-of-first-word (get-next-word str str-start-pos))
(beg-of-first-word (first beg-end-of-first-word))
;;(end-of-first-word (second beg-end-of-first-word))
)
(unless (null beg-end-of-first-word)
;; comment not empty:
(rf-princ-like-lisp com-lead-in)
(if (eq 'break (third *comment-style*))
;; break comment-lines if longer then *rfi-print-width*:
(print-one-line-of-comment str
(gen-n-spaces (- beg-of-first-word
str-start-pos ))
beg-end-of-first-word
(+ 1 indent-col) ; 1 = len(";")
indent-col
com-lead-in )
(print-one-line-of-comment-wo-break str str-start-pos) ))))
(defun print-comment (str lead-in-char cursor-col indent-col)
"str contains comments each terminated by a #\newline.
Prints all comments. The last #\newline is not printed.
On exit cursor is on the right of the last printed character.
"
(let ((start-of-type-2 1) ; pos. 0: #\newline
(len-of-str (length str))
)
(unless (char= #\newline (char str 0))
;; starts with a end-of-line-comment:
(print-comment-type-1 (subseq str 0 (find-newline str 0))
lead-in-char
cursor-col
indent-col )
(setq start-of-type-2 (1+ (find-newline str 0))) )
;; all other comments are of type 2:
(do ((i start-of-type-2 (1+ (find-newline str i)))
)
((= i len-of-str)
;; all comments printed:
indent-col
)
;; print one line of comment:
(pro-newline-and-indent indent-col)
(print-comment-type-2 str i lead-in-char indent-col) )))
(defun lsyn-comment-printer (com cursor-col indent-col &optional clause-p)
;; type(com) = com-cell
;; clause-p <==> called from pp-clause
;; On exit cursor is on the right of the last printed character.
(unless (string= "" (com-cell-pre-txt com))
;; print a leading comment (always type 2):
(print-comment-type-2
(com-cell-pre-txt com)
(if (char= #\newline (char (com-cell-pre-txt com) 0)) 1 0)
lsyn-comment-lead-in
cursor-col )
(pro-newline-and-indent indent-col) )
(if (com-cell-internal-p com)
;; Comment inside lisp-part:
(if clause-p
(pp-big-clause (com-cell-lisp com))
(pp-broken-list (com-cell-lisp com)
round-left
round-right ))
;; no internal comment:
(if clause-p
(pp-clause (com-cell-lisp com))
(pp-expr (com-cell-lisp com)) ))
(unless (string= "" (com-cell-post-txt com))
;; print the comment:
;;(princ pp-currentpos*)
(print-comment (com-cell-post-txt com)
lsyn-comment-lead-in
(1+ pp-currentpos*)
indent-col )
;; After a comment a closing parenthese requires a new line:
(push 'comment-was-here pp-stack*) ; pp-push replaced by push (MS/MP)
))
;;; -------------------------------------------------------------------------
;;;
;;; Auxilliary functions
;;;
;;; -------------------------------------------------------------------------
(defun apply-and (list-of-bool)
"<==> all elements are non-nil"
(or (null list-of-bool)
(and (car list-of-bool)
(apply-and (cdr list-of-bool)) )))
;; rename to
(defun any-true-p (the-list &optional #-CLiCC (pred #'(lambda (x) x))
#+CLiCC (pred nil))
; needed for CLiCC optimizer problem
;; Returns nil, if all elements evaluate to nil under pred.
;; Else it returns the first element not evaluating to nil.
(do* ((rest-of-list the-list (rest rest-of-list))
(element (first rest-of-list) (first rest-of-list))
)
((null rest-of-list)
;; all nil
nil
)
(when (funcall pred element)
(return element) )))
(defun extract-atoms-by-predicate (tree pred)
"Returns a list of the atoms that satisfies the pred."
(cond ((null tree)
nil )
((consp tree)
(append (extract-atoms-by-predicate (car tree) pred)
(extract-atoms-by-predicate (cdr tree) pred) ))
;; tree is an atom :
((funcall pred tree)
(list tree) )
(t
nil )))
(defun setup-comment-readtable (a-readtable)
;; Sets read-macros for #\; and #\newline.
;; a-readtable is not destructively modified.
;; Value: The new readtable.
(let ((new-readtable (copy-readtable a-readtable)))
(set-macro-character lsyn-comment-lead-in
#'lsyn-comment-type-1-reader
nil ; non-terminating-p
new-readtable )
(set-macro-character #\newline
#'lsyn-comment-type-2-reader
nil ; non-terminating-p
new-readtable )
new-readtable
))
;;; -------------------------------------------------------------------------
;;;
;;; Functions to extract/insert comments.
;;;
;;; -------------------------------------------------------------------------
(defun rf-tag-p (symbol)
(or (member symbol '(hn ft uc is inst vari tup ! \|) )
(lisp-function-p symbol) ))
;;; Manage a table to associate symbols with values:
;;; ------------------------------------------------
;; Alternative implementation via hash tables is possible.
(defun com-set-field (table vari field value)
"Set the field of vari to the given value.
If vari is not in table it will be inserted.
Field is one of
{org-symbol, depth, comment, toplevel}.
Return the modified table."
(let* ((pair (assoc vari table))
(tuple (if pair
(cdr pair)
(list nil 0 "" "") ))
(new-table (if pair
table
(acons vari tuple table) )) )
;; tuple = (org-symbol depth comment)
(case field
(org-symbol (setf (nth 0 tuple) value))
(depth (setf (nth 1 tuple) value))
(comment (setf (nth 2 tuple) value))
(toplevel (setf (nth 3 tuple) value))
(t (error "Internal error in : unknown field.")) )
new-table ))
(defun com-get-field (table vari field &key (signal-errorp t))
"Get vari.field. See also com-set-field.
If signal-errorp = NIL it returns NIL if vari does not exist in table,
If signal-errorp = T it signals an error if vari does not exist in table."
(let* ((pair (assoc vari table))
(tuple (cdr pair)) )
(cond(pair
(case field
(org-symbol (nth 0 tuple))
(depth (nth 1 tuple))
(comment (nth 2 tuple))
(toplevel (nth 3 tuple))
(t (error "Internal error in : unknown field.")) ))
(signal-errorp
(error "Internal error in : unknown variable. "))
(t nil) )))
(defun com-get-fields (table vari)
"Lookup vari in table. Return a tuple if it exists, nil otherwise."
(cdr (assoc vari table)) )
;;; Extract comments:
;;; -----------------
(defun replace-user-symbol (symbol-or-number table tag-p)
"Generates a new symbol.
Records the new symbol and its old counterpart in the table.
Returns a list: ( ) "
(if (funcall tag-p symbol-or-number)
;; protect hn, ft etc.:
(list symbol-or-number table)
(let* ((str (if (numberp symbol-or-number)
(stringcat "number_" (princ-to-string symbol-or-number))
(princ-to-string symbol-or-number)) )
(new-symbol (gentemp (stringcat str "_"))) )
(list new-symbol
(com-set-field table
new-symbol
'org-symbol
symbol-or-number )))))
(defun replace-user-symbols (expr table tag-p)
"Replaces all user symbols in expr by symbols generated by gentemp.
table records the replacements.
Returns a list: ( )"
(cond ((com-cell-p expr)
(let+ (((e1 t1)
(replace-user-symbols (com-cell-lisp expr) table tag-p) ))
(list (make-com-cell
:lisp e1
:pre-txt (com-cell-pre-txt expr)
:post-txt (com-cell-post-txt expr) )
t1 )))
((atom expr)
(replace-user-symbol expr table tag-p) )
;; expr is a cons-cell:
((null (cdr expr))
;; do not replace a trailing NIL:
(let+ (((e1 t1) (replace-user-symbols (car expr) table tag-p)))
(list (list e1) t1) ))
(t
(let*+ (((e1 t1) (replace-user-symbols (car expr) table tag-p))
((e2 t2) (replace-user-symbols (cdr expr) t1 tag-p))
)
(list (cons e1 e2) t2) ))))
(defun assoc-expr-with-comment (expr comment depth table tag-p toplevelp)
"Associates comment with expr. This is done by finding the first symbol
in expr and associating it with comment .
The parameter toplevelp indicates a toplevel comment,
depth is not used in this case.
Returns an updated table."
(cond ((null expr)
nil )
((com-cell-p expr)
(assoc-expr-with-comment
(com-cell-lisp expr) comment depth tag-p table toplevelp))
((and (atom expr)
(funcall tag-p expr) )
;; protect tags like hn, ft etc.:
nil )
((and (atom expr) toplevelp)
;; it is a userdefined symbol:
(com-set-field table expr 'toplevel comment) )
((atom expr)
;; it is a userdefined symbol:
(com-set-field table expr 'comment comment)
(com-set-field table expr 'depth (min depth com-max-depth)) )
;; it is a cons-cell:
((assoc-expr-with-comment
(car expr) comment (1+ depth) table tag-p toplevelp))
(t
(assoc-expr-with-comment
(cdr expr) comment (1+ depth) table tag-p toplevelp))
))
(defun remove-com-cells (expr table tag-p)
"Removes all com-cells from expr.
Returns a list:
( )
Where updated-table associates symbols with tuples.
tuple = ( ) ."
(let*+ (((copy-of-expr table1) (replace-user-symbols expr table tag-p))
((com-free-expr table2 ) (remove-com-cells-aux copy-of-expr
table1
tag-p )))
(list com-free-expr table2) ))
(defun remove-com-cells-aux (expr table tag-p)
(cond ((consp expr)
(let*+ (((e1 t1) (remove-com-cells-aux (car expr) table tag-p))
((e2 t2) (remove-com-cells-aux (cdr expr) t1 tag-p)) )
(list (cons e1 e2) t2) ))
((not (com-cell-p expr))
(list expr table) )
;; it is a com-cell:
((string= "" (stringcat (com-cell-pre-txt expr) (com-cell-post-txt expr)))
;; the comment is intern:
(remove-com-cells-aux (com-cell-lisp expr) table tag-p) )
(t
(let*+ ((comment (stringcat (com-cell-pre-txt expr)
(com-cell-post-txt expr)))
(e1 (com-cell-lisp expr))
(t1 (assoc-expr-with-comment e1 comment 0 table tag-p nil)) )
(remove-com-cells-aux e1 t1 tag-p) ))))
(defun remove-comments-from-database (database-with-comments tag-p)
"Returns a list
( )
has no comment at all.
can be used to reinsert the comments (ref. insert-comments).
= ( ) records the first
and the last comment of the database."
(let (start-comment rest-db)
(if (stringp (car database-with-comments))
(setq start-comment (car database-with-comments)
rest-db (cdr database-with-comments) )
(setq start-comment ""
rest-db database-with-comments ))
(let*+ ((triple (remove-comments-from-database-aux rest-db nil tag-p))
((db-wo-comments table end-comment) triple) )
(list db-wo-comments table (list start-comment end-comment)) )))
(defun remove-comments-from-database-aux (db-w-comment table tag-p)
"Returns a triple:
( ) "
(cond ((null db-w-comment)
(list nil table "") )
;; it is a cons-cell:
((and (null (cdr db-w-comment)) (stringp (car db-w-comment)))
;; database ends with a comment:
(list nil table (car db-w-comment)) )
((stringp (car db-w-comment))
;; comment between clauses:
(let*+ (((d1 t1)
(remove-com-cells (second db-w-comment) table tag-p))
(t2
(assoc-expr-with-comment d1 (car db-w-comment) 0 t1 tag-p t))
((d3 t3 e3)
(remove-comments-from-database-aux (cddr db-w-comment)
t2
tag-p )))
(list (cons d1 d3) t3 e3) ))
(t
(let*+ (((d1 t1) (remove-com-cells (car db-w-comment) table tag-p))
((d2 t2 e2) (remove-comments-from-database-aux
(cdr db-w-comment) t1 tag-p) ))
(list (cons d1 d2) t2 e2) ))))
;;; Insert comments:
;;; ----------------
(defun get-toplevel-comments (symbols table)
"Returns a string with the toplevel comments of the symbols.
Condition on entry: Every symbol has a toplevel comment."
(if (null symbols)
""
(stringcat (com-get-field table (car symbols) 'toplevel)
(get-toplevel-comments (cdr symbols) table) )))
(defun re-assoc-comment (depth comment list-of-comments)
(let ((cons-cell (nthcdr depth list-of-comments)))
(setf (car cons-cell)
(stringcat (car cons-cell) comment) )))
(defun insert-comments-into-expr (expr table)
(insert-comments-into-expr-aux expr table nil) )
(defun insert-comments-into-expr-aux (expr table list-of-comments)
(cond ((symbolp expr)
(let*+ ((tuple (com-get-fields table expr))
((org-symbol0 depth comment) tuple)
(org-symbol (lisp-sym->pro-sym org-symbol0)) )
(cond ((null tuple)
;; expr is a new symbol
expr )
((string= "" comment)
;; no comment associated
org-symbol )
((= 0 depth)
(make-com-cell
:lisp org-symbol
:post-txt comment ))
(t
;; comment associated with x:
(re-assoc-comment (1- depth) comment list-of-comments)
org-symbol ))))
((consp expr)
(let* ((ext-list-of-comments (cons "" list-of-comments))
(expr-with-comments (mapcar
#'(lambda (x)
(insert-comments-into-expr-aux
x
table
ext-list-of-comments))
expr ))
(top-comment (car ext-list-of-comments)) )
(if (string= "" top-comment)
expr-with-comments
(make-com-cell
:lisp expr-with-comments
:post-txt top-comment ))))
(t
expr ) ))
(defun insert-comments-into-database (database table start-end-comments)
"Returns a database with comments,
printable by print-database-with-comments."
(let+ (((start-comment end-comment) start-end-comments))
(remove ""
(append (list start-comment)
(insert-comments-into-database-aux database table)
(list end-comment) )
:test #'equal )))
(defun insert-comments-into-database-aux (database table)
(if (null database)
nil
(let* ((expr (car database))
(symbols-with-toplevel-comments
(extract-atoms-by-predicate
expr
#'(lambda (x)
(and (symbolp x)
(let ((tuple (com-get-fields table x)))
(and tuple
(string/= "" (fourth tuple)) ))))))
(toplevel-comments (if symbols-with-toplevel-comments
(list (get-toplevel-comments
symbols-with-toplevel-comments
table ))
nil )))
(append toplevel-comments
(cons
(pack-comments (insert-comments-into-expr expr table))
(insert-comments-into-database-aux (cdr database) table))))))
(defun lift-comments (expr)
"Comments at the end of an expression are lifted to the
surrounding expression."
(if (and (com-cell-p expr)
(string= "" (com-cell-post-txt expr) )
(consp (com-cell-lisp expr)) )
(let* ((expr-1 (mapcar #'lift-comments (com-cell-lisp expr)))
(expr-but-last (butlast expr-1))
(expr-last (car (last expr-1))) )
(if (and (com-cell-p expr-last)
(string/= "" (com-cell-post-txt expr-last)) )
;; the last arg. of expr has a comment --> lift it:
(let ((internal-comment-p
(or (any-true-p expr-but-last #'com-cell-p)
(com-cell-internal-p expr-last) )))
(make-com-cell
:lisp (append expr-but-last
(list (com-cell-lisp expr-last)) )
:pre-txt (com-cell-pre-txt expr-last)
:post-txt (com-cell-post-txt expr-last)
:internal-p internal-comment-p ))
;; the last arg. has no comment --> do not lift:
(make-com-cell
:lisp expr-1
:pre-txt (com-cell-pre-txt expr ) ; no post-txt
:internal-p (com-cell-internal-p expr) )))
expr ))
;;; -------------------------------------------------------------------------
;;;
;;; Main functions
;;;
;;; -------------------------------------------------------------------------
(defun echo-empty-lines (i-stream comment-lead-in)
"Returns t iff at least one empty line read."
(do ((non-white-char (first (read-until-non-white i-stream))
(first (read-until-non-white i-stream)) )
(empty-lines-p nil t)
)
((or (char= eof-char non-white-char)
(char/= #\newline non-white-char) )
(when (and empty-lines-p (char/= comment-lead-in non-white-char))
(pro-newline-and-indent 1) )
empty-lines-p )
(read-char i-stream)
(when empty-lines-p (pro-newline-and-indent 1)) ))
(defun count-empty-lines (i-stream comment-lead-in)
"Returns the number of empty lines in the i-stream.
I.e. it returns the number of #\newlines read until a non-white-space
or eof is reached."
;;(format t "~%(count-empty-lines): ~a ~@C"
;; "Next char to read:"
;; (peek-char nil i-stream nil eof-char) )
(declare (ignore comment-lead-in))
(do ((non-white-char (first (read-until-non-white i-stream))
(first (read-until-non-white i-stream)) )
(empty-lines-counter 0 (1+ empty-lines-counter))
)
((or (char= eof-char non-white-char)
(char/= #\newline non-white-char) )
;;(when (and empty-lines-counter (char/= comment-lead-in non-white-char))
;; (incf empty-lines-counter ))
empty-lines-counter )
(read-char i-stream) ))
(defun glue-comments (database)
"database includes comments.
On return all consecutive comments on toplevel
are glued to a single string."
(cond ((null database)
nil )
((or (stringp (car database))
(numberp (car database)) )
(glue-comments-aux database "") )
(t
(cons (car database)
(glue-comments (cdr database)) ))))
(defun glue-comments-aux (database accu-string)
(cond ((null database)
(list accu-string) )
((stringp (car database))
(glue-comments-aux (cdr database)
(stringcat accu-string
(car database))) )
((numberp (car database))
(glue-comments-aux
(cdr database)
(stringcat accu-string
;; print-comment does not print the last newline:
(string #\newline)
(gen-n-newlines (car database))) ))
(t (cons accu-string (glue-comments database) ))))
(defun read-database-with-comments (input-stream input-style)
"Reads a database-stream. Returns a list:
Strings denote comments between clauses.
Clauses may be encapsulated within com-cells.
Input-style is one of {lisp, prolog, kif}."
(let (*style* db)
(case input-style
(lisp
(setq *style* 'lisp
db (read-database-with-comments-lsyn input-stream
*rfi-readtable* )))
(prolog
(setq *style* 'prolog
db (read-database-with-comments-psyn input-stream) ))
#|
(kif
(setq *style* 'lisp
db (read-database-with-comments-lsyn input-stream
*kif-readtable* )))
|#
(t (error "Internal error in read-database-with-comments.")) )
(glue-comments db) ))
(defun read-database-with-comments-lsyn (lsyn-stream a-readtable)
"See read-database-with-comments for documentation."
(let* ((*readtable* (setup-comment-readtable a-readtable))
)
(do* ((empty-lines-counter
(count-empty-lines lsyn-stream lsyn-comment-lead-in)
(count-empty-lines lsyn-stream lsyn-comment-lead-in) )
(non-white-char (peek-char nil lsyn-stream nil eof-char)
(peek-char nil lsyn-stream nil eof-char))
(type-of-comment 2 (if (zerop empty-lines-counter) 1 2))
(result)
)
((char= eof-char non-white-char)
(reverse result) )
(unless (zerop empty-lines-counter)
(setq result (cons empty-lines-counter result)) )
(if (char= lsyn-comment-lead-in non-white-char)
;; comment detected:
(progn
(read-char lsyn-stream) ; consume the com-lead-in
;; print-comment starts with a #\newline for type-2-comments
(setq result (cons (first (read-comments lsyn-comment-lead-in
type-of-comment
lsyn-stream ))
result )))
;; clause detected:
(progn
(setq result (cons (hide-comments (read lsyn-stream))
result ))
;; eat the #\newline if the rest of the line are whitespace chars.
(when (char= (first (read-until-non-white lsyn-stream))
#\newline )
(read-char lsyn-stream) )))
;;(format t "~%~a ~@C"
;; "Next char to read:"
;; (peek-char nil lsyn-stream nil eof-char) )
)))
(defun read-database-with-comments-psyn (psyn-stream)
"See read-database-with-comments for documentation."
(catch :pro-read-error-tag
(let ((*comment-style* t)) ; do not remove comments
(read-database-with-comments-psyn-aux (gen-scanner psyn-stream)))))
(defun read-database-with-comments-psyn-aux (scanner)
"See read-database-with-comments for documentation."
(cond ((funcall scanner 'comment-p)
;; comment detected:
(cons (funcall scanner 'comment)
(read-database-with-comments-psyn-aux scanner) ))
((funcall scanner 'str-of-newlines-p)
(let ((l (length (funcall scanner 'str-of-newlines))))
(if (> l 1)
;; no comment but empty lines after the last clause:
(cons (1- l)
(read-database-with-comments-psyn-aux scanner) )
(read-database-with-comments-psyn-aux scanner) )))
((eq 'empty (funcall scanner 'last-token-type))
nil )
(t
;; clause detected:
(cons (hide-comments (parse-clause scanner))
(read-database-with-comments-psyn-aux scanner) ))))
(defun print-database-with-comments (database output-stream comment-style)
"Prints a database produced by read-database-with-comments"
(let* ((*comment-style* comment-style)
(*rfi-standard-output* output-stream)
(comment-lead-in (if (eq *style* 'lisp)
lsyn-comment-lead-in
psyn-comment-lead-in ))
)
(do ((cursor-col 1)
(item (car database) (car rest-database))
(rest-database (cdr database) (cdr rest-database))
)
((null item)
t )
(cond ((numberp item)
(dotimes (i item) (pro-newline-and-indent 1))
(setq cursor-col 1) )
((stringp item)
(print-comment item comment-lead-in cursor-col 1)
)
(t
(pro-newline-and-indent 1)
(setq cursor-col (pp-clause item)) )))))
(defun generate-iso-prolog-text (database out-stream comment-style)
"Prints database in a style readable by ISO-Prolog."
(let*+ (((db-without-comments table start-end-comments)
(remove-comments-from-database database #'rf-tag-p ))
(*style* 'prolog)
(*print-like-prolog* t)
(db-flattened (rfp-db-2-pl-db
(extrarg-database
(passtup
;;pl-flatter/db
(flatten-database
db-without-comments) ))
table ))
(db-flattened-with-comments (insert-comments-into-database
db-flattened
table
start-end-comments )))
(print-database-with-comments db-flattened-with-comments
out-stream
comment-style)))
;; ---------------------------------------------------------------------------
;; rfx2rfx - functions
;; ---------------------------------------------------------------------------
(defun rf2rf (lsyn-in lsyn-out comment-style)
"Pretty prints a file written in L-syntax including the comments."
(let* ((*comment-style* comment-style)
(*style* 'lisp)
(*readtable* (setup-comment-readtable *rfi-readtable*))
(*rfi-standard-output* lsyn-out)
)
(do* ((empty-lines-p (echo-empty-lines lsyn-in lsyn-comment-lead-in)
(echo-empty-lines lsyn-in lsyn-comment-lead-in))
(cursor-col 1 (if empty-lines-p 1 cursor-col))
(non-white-char (peek-char nil lsyn-in nil eof-char)
(peek-char nil lsyn-in nil eof-char))
(type-of-comment 2 (if empty-lines-p 2 1))
)
((char= eof-char non-white-char) t)
(if (char= lsyn-comment-lead-in non-white-char)
;; comment detected:
(progn
(read-char lsyn-in) ; consume the com-lead-in
(print-comment
(first (read-comments lsyn-comment-lead-in
type-of-comment
lsyn-in ))
lsyn-comment-lead-in
cursor-col
1 ) ; 1 = indent-col.
(pro-newline-and-indent 1)
)
;; clause detected:
(progn
(setq cursor-col (pp-clause (hide-comments (read lsyn-in))))
)))))
(defun rf2rfp (lsyn-stream psyn-stream comment-style)
;; Transforms a file written in L-syntax to P-Syntax including the
;; comments.
(let* ((*comment-style* comment-style)
(*readtable* (setup-comment-readtable *rfi-readtable*))
(*rfi-standard-output* psyn-stream)
)
(do* ((empty-lines-p (echo-empty-lines lsyn-stream lsyn-comment-lead-in)
(echo-empty-lines lsyn-stream lsyn-comment-lead-in))
(cursor-col 1 (if empty-lines-p 1 cursor-col))
(non-white-char (peek-char nil lsyn-stream nil eof-char)
(peek-char nil lsyn-stream nil eof-char))
(type-of-comment 2 (if empty-lines-p 2 1))
)
((char= eof-char non-white-char) t)
(if (char= lsyn-comment-lead-in non-white-char)
;; comment detected:
(progn
(read-char lsyn-stream) ; consume the com-lead-in
;; print-comment starts with a #\newline for type-2-comments
(print-comment
(first (read-comments lsyn-comment-lead-in
type-of-comment
lsyn-stream ))
psyn-comment-lead-in
cursor-col
1 ) ; 1 = indent-col.
(pro-newline-and-indent 1)
)
;; clause detected:
(progn
(setq cursor-col (pro-print (hide-comments (read lsyn-stream))))
)))))
(defun rfp2rfp (psyn-in psyn-out comment-style)
;; Pretty print a file written in P-syntax including the comments.
(let* ((*comment-style* comment-style)
(*rfi-standard-output* psyn-out)
scanner
)
(catch :pro-read-error-tag
(do* ((empty-lines-p (echo-empty-lines psyn-in psyn-comment-lead-in)
(echo-empty-lines psyn-in psyn-comment-lead-in))
(cursor-col 1 (if empty-lines-p 1 cursor-col))
(non-white-char (peek-char nil psyn-in nil eof-char)
(peek-char nil psyn-in nil eof-char))
(type-of-comment 2 (if empty-lines-p 2 1))
)
((char= eof-char non-white-char) t)
(if (char= psyn-comment-lead-in non-white-char)
;; comment detected:
(progn
(read-char psyn-in) ; consume the com-lead-in
;; print-comment starts with a #\newline for type-2-comments
(print-comment
(first (read-comments psyn-comment-lead-in
type-of-comment
psyn-in ))
psyn-comment-lead-in
cursor-col
1 ) ; 1 = indent-col.
(pro-newline-and-indent 1)
)
;; clause detected:
(progn
(if scanner
nil
(setq scanner (gen-scanner psyn-in)) )
(setq cursor-col
(pro-print (hide-comments (parse-clause scanner))))
;(princ cursor-col)
(if (funcall scanner 'comment-p)
(progn
(print-comment (funcall scanner 'comment)
psyn-comment-lead-in
cursor-col
1 )
(pro-newline-and-indent 1))
(progn
(dotimes (i (length (funcall scanner 'str-of-newlines)))
(pro-newline-and-indent 1) )
))))))))
(defun rfp2rf (psyn-stream lsyn-stream comment-style)
;; Transforms a file written in P-syntax to L-Syntax including the
;; comments.
(let* ((*comment-style* comment-style)
(*style* 'lisp)
(*rfi-standard-output* lsyn-stream)
scanner
)
(catch :pro-read-error-tag
(do* ((empty-lines-p (echo-empty-lines psyn-stream psyn-comment-lead-in)
(echo-empty-lines psyn-stream psyn-comment-lead-in))
(cursor-col 1 (if empty-lines-p 1 cursor-col))
(non-white-char (peek-char nil psyn-stream nil eof-char)
(peek-char nil psyn-stream nil eof-char))
(type-of-comment 2 (if empty-lines-p 2 1))
)
((char= eof-char non-white-char) t)
(if (char= psyn-comment-lead-in non-white-char)
;; comment detected:
(progn
(read-char psyn-stream) ; consume the com-lead-in
(print-comment
(first (read-comments psyn-comment-lead-in
type-of-comment
psyn-stream ))
lsyn-comment-lead-in
cursor-col
1 ) ; 1 = indent-col.
(pro-newline-and-indent 1)
)
;; clause detected:
(progn
(if scanner
nil
(setq scanner (gen-scanner psyn-stream)) )
(setq cursor-col
(pp-clause (hide-comments (parse-clause scanner))))
;(princ cursor-col)
(if (funcall scanner 'comment-p)
(progn
(print-comment (funcall scanner 'comment)
lsyn-comment-lead-in
cursor-col
1 )
(pro-newline-and-indent 1))
(progn
(dotimes (i (length (funcall scanner 'str-of-newlines)))
(pro-newline-and-indent 1) )
))))))))
(defun rf2pl (in-stream out-stream comment-style)
"Transforms a stream written in Relfun's L-syntax to real Prolog-syntax"
(generate-iso-prolog-text
(read-database-with-comments in-stream 'lisp)
out-stream
comment-style ))
(defun rfp2pl (in-stream out-stream comment-style)
"Transforms a stream written in Relfun's P-syntax to real Prolog-syntax"
(generate-iso-prolog-text
(read-database-with-comments in-stream 'prolog)
out-stream
comment-style ))
;; ---------------------------------------------------------------------------
;; batches
;; ---------------------------------------------------------------------------
(defun bal2bap (lsyn-stream psyn-stream comment-style)
;; Transforms a batch-file written in L-syntax to P-syntax.
(let* ((*comment-style* comment-style)
(*readtable* (setup-comment-readtable *rfi-readtable*))
(*rfi-standard-output* psyn-stream)
goal-or-cmd
first-symbol
)
(do* ((empty-lines-p (echo-empty-lines lsyn-stream lsyn-comment-lead-in)
(echo-empty-lines lsyn-stream lsyn-comment-lead-in))
(cursor-col 1 (if empty-lines-p 1 cursor-col))
(non-white-char (peek-char nil lsyn-stream nil eof-char)
(peek-char nil lsyn-stream nil eof-char))
(type-of-comment 2 (if empty-lines-p 2 1))
)
((char= eof-char non-white-char) t)
(if (char= lsyn-comment-lead-in non-white-char)
;; comment detected:
(progn
(read-char lsyn-stream) ; consume the com-lead-in
;; print-comment starts with a #\newline for type-2-comments
(print-comment
(first (read-comments lsyn-comment-lead-in
type-of-comment
lsyn-stream ))
psyn-comment-lead-in
cursor-col
1 ) ; 1 = indent-col.
(pro-newline-and-indent 1)
)
;; command or goal detected:
(progn
(setq goal-or-cmd (read-goal-or-cmd-from-lsyn-batch lsyn-stream)
first-symbol (first goal-or-cmd)
cursor-col
(if (member first-symbol *rfi-commands*)
(let ((first-symbol-as-string
(stringcat (string-downcase
(princ-to-string first-symbol) )
" " )))
(case first-symbol
((l listing)
(cond ((null (cdr goal-or-cmd))
;; listing without argument:
(pro-print-string first-symbol-as-string 1) )
((null (cddr goal-or-cmd))
;; listing with symbol as argument:
(pro-print
(second goal-or-cmd)
(pro-print-string first-symbol-as-string 1)
))
(t
;; listing with head-pattern:
(pro-print-head
(second goal-or-cmd)
(pro-print-string first-symbol-as-string 1)
))))
(style
(cond ((null (cdr goal-or-cmd))
;; without argument:
(pro-print-string first-symbol-as-string 1) )
((eq 'lisp (second goal-or-cmd))
(pro-print-string "style prolog" 1) )
((eq 'prolog (second goal-or-cmd))
(pro-print-string "style lisp" 1) )
(t
)))
(sl
(pro-print-string "sp" 1) )
(sp
(pro-print-string "sl" 1) )
((a0 az rx)
(pro-print
(second goal-or-cmd)
(pro-print-string first-symbol-as-string 1)) )
(azhn
(pro-print
(cons 'hn (cdr goal-or-cmd))
(pro-print-string "az " 1)) )
(a0hn
(pro-print
(cons 'hn (cdr goal-or-cmd))
(pro-print-string "a0 " 1)) )
(azft
(pro-print
(cons 'ft (cdr goal-or-cmd))
(pro-print-string "az " 1)) )
(a0ft
(pro-print
(cons 'ft (cdr goal-or-cmd))
(pro-print-string "a0 " 1)) )
(rxft
(pro-print
(cons 'ft (cdr goal-or-cmd))
(pro-print-string "rx " 1)) )
(t
;; no special handling of the rfi-cmd:
(dolist (element goal-or-cmd)
(rf-princ-like-lisp element)
(rf-princ-like-lisp " ") )
50 )))
;; it's a goal:
(prog1
(pro-print-arg-list
(construct-measure-tree-of-arg-list goal-or-cmd
nil)
1 ; cursor col.
0
the-non-printing-char
the-non-printing-char )))))))))
(defun bap2bal (psyn-stream lsyn-stream comment-style)
;; Transforms a batch-file written in P-syntax to L-syntax.
(let* ((*comment-style* comment-style)
(*rfi-standard-output* lsyn-stream)
(*style* 'lisp) ; style when printing
)
(do* ((empty-lines-p (echo-empty-lines psyn-stream psyn-comment-lead-in)
(echo-empty-lines psyn-stream psyn-comment-lead-in))
(cursor-col 1 (if empty-lines-p 1 cursor-col))
(non-white-char (peek-char nil psyn-stream nil eof-char)
(peek-char nil psyn-stream nil eof-char))
(type-of-comment 2 (if empty-lines-p 2 1))
)
((char= eof-char non-white-char) t)
(if (char= psyn-comment-lead-in non-white-char)
;; comment detected:
(progn
(print-comment
(first (read-comments psyn-comment-lead-in
type-of-comment
psyn-stream ))
lsyn-comment-lead-in
cursor-col
1 ) ; 1 = indent-col.
(pro-newline-and-indent 1)
)
;; command or goal detected:
(setq cursor-col
(let* ((goal-or-cmd
(read-goal-or-cmd-from-psyn-batch psyn-stream))
(pair (pro-split-input goal-or-cmd))
(first-symbol (car pair))
(rest-of-line-as-string (cdr pair))
)
(if (member first-symbol *rfi-commands*)
;; it's a rfi-command:
(let* ((first-symbol-as-string
(stringcat (princ-to-string first-symbol) " " ))
(start-column (length first-symbol-as-string))
(user-line-as-list
(handle-rfi-cmd first-symbol
rest-of-line-as-string
goal-or-cmd )))
(rf-princ-like-lisp first-symbol-as-string)
(case first-symbol
((a0 az rx)
;; print as clause:
(pp-clause (second user-line-as-list)
start-column ) )
;; l and listing require no special printing:
(t
;; rfi-cmd requires no special printing:
(print-lsyn-enumeration (rest user-line-as-list)
start-column ))))
;; it's a goal (at least 1 element):
(print-lsyn-enumeration (pro-read-goal goal-or-cmd) 0)
)))))))
(defun read-goal-or-cmd-from-lsyn-batch (the-input-stream)
(do* ((goal-or-cmd (list (hide-comments (read the-input-stream)))
(cons (hide-comments (read the-input-stream))
goal-or-cmd ))
(non-white-char (car (read-until-non-white the-input-stream))
(car (read-until-non-white the-input-stream)) )
)
((case non-white-char
((#\newline #\;) t) ; abort loop
(t nil) ) ; continue
(reverse goal-or-cmd) )
))
(defun read-goal-or-cmd-from-psyn-batch (the-input-stream)
;; Returns the complete input (may be multiple lines) as a string.
(do* ((goal-or-cmd "" (stringcat goal-or-cmd (string ch)))
(ch (peek-char nil the-input-stream) (peek-char nil the-input-stream))
(*style* 'prolog)
)
(nil)
(case ch
(#\newline
;; end of input line but may be continued
(when (pro-complete-cmd-p goal-or-cmd)
(return goal-or-cmd) ))
(#\%
;; comment at end of line:
(if (pro-complete-cmd-p goal-or-cmd)
(return goal-or-cmd)
(let* ((str-stream (make-string-output-stream))
(echo-stream (make-echo-stream the-input-stream str-stream))
)
;; value does not care,
;; but input chars are accumulated in str-stream:
(read-comments psyn-comment-lead-in
1 ; type of comment
echo-stream )
(setq goal-or-cmd (stringcat
goal-or-cmd
(get-output-stream-string str-stream) ))
(close str-stream) )))
(t))
(setq ch (read-char the-input-stream)) ))
(defun print-lsyn-enumeration (enum start-column)
;; Example: enum = (a b c) => a b c
;; Returns the cursor-col.
;; left-margin: start-column = 0.
(pp-init start-column)
(if (com-cell-p enum)
;; internal comment (never end-of-line comment):
(pp-broken-list (com-cell-lisp enum) '|| '||)
(pp-list enum '|| '||) )
(1+ pp-currentpos*)
)
(defun translate-file (in-filename
out-filename
&key direction
(comment-style *default-comment-style*))
"Translates a file from one syntax to the other including the comments.
Filenames can be symbols or strings, if not supplied standard extensions
are added.
Empty or NULL out-filename means send to *standard-output*.
Possible values for direction (always required) are:
rf2rf, rf2rfp, rfp2rf, rfp2rfp,
rf2pl, rfp2pl, bal2bap, bap2bal
Example: (translate-file \"facfix.rf\" facfix :direction 'rf2rfp)
This is equivalent to:
(translate-file \"facfix.rf\" \"facfix.rfp\" :direction 'rf2rfp)
"
(let (error-p translator
in-extension out-extension
full-in-filename
(full-out-filename (if (and (stringp out-filename)
(string= "" out-filename) )
nil
out-filename ))
(comment-lead-in (if (eq 'prolog *style*)
psyn-comment-lead-in
lsyn-comment-lead-in ))
)
(case direction
(rf2rfp (setq in-extension ".rf"
out-extension ".rfp"
translator #'rf2rfp ) )
(rfp2rf (setq in-extension ".rfp"
out-extension ".rf"
translator #'rfp2rf ))
(rf2rf (setq in-extension ".rf"
out-extension ".rf"
translator #'rf2rf ))
(rfp2rfp (setq in-extension ".rfp"
out-extension ".rfp"
translator #'rfp2rfp ))
(rf2pl (setq in-extension ".rf"
out-extension ".pl"
translator #'rf2pl ))
(rfp2pl (setq in-extension ".rfp"
out-extension ".pl"
translator #'rfp2pl ))
(bal2bap (setq in-extension ".bat"
out-extension ".bat"
translator #'bal2bap ))
(bap2bal (setq in-extension ".bat"
out-extension ".bat"
translator #'bap2bal ))
(t (setq error-p t
in-extension ""
out-extension "") ))
(setq full-in-filename (rfi-extension in-filename in-extension))
(when full-out-filename
(setq full-out-filename (rfi-extension out-filename out-extension)) )
(cond (error-p
(rf-princ-like-lisp "Error: Illegal direction.") )
((equal full-in-filename full-out-filename)
(rf-princ-like-lisp "Error: Source = Destination")
(setq error-p t) )
((not (probe-file full-in-filename))
;; the in-file must exist:
(rf-princ-like-lisp (stringcat "Error: Source "
full-in-filename
" does not exist." ))
(setq error-p t) )
((and full-out-filename (probe-file full-out-filename))
;; outfile exists:
(if (rfi-yes-or-no-p "Destination already exists - overwrite? ")
(delete-file full-out-filename)
(setq error-p t) ))
(t) )
(unless (and (numberp (first comment-style))
(member (second comment-style)
'(raw comment-column comment-column-or-margin margin))
(member (third comment-style)
'(break no-break) ))
(setq error-p t)
(rf-princ-like-lisp "Error: Illegal comment-style.")
(rf-terpri)
(rf-princ-like-lisp " Domain of the list comment-style is ")
(rf-terpri)
(rf-princ-like-lisp
" ( ,")
(rf-terpri)
(rf-princ-like-lisp
" {COMMENT-COLUMN, COMMENT-COLUMN-OR-MARGIN, MARGIN},")
(rf-terpri)
(rf-princ-like-lisp
" {BREAK, NO-BREAK} )")
)
(unless error-p
(with-open-file (in-stream full-in-filename :direction :input)
(rf-princ-like-lisp
(format nil "~c Converting ~S " comment-lead-in full-in-filename) )
(if full-out-filename
;; send result to file:
(with-open-file (out-stream full-out-filename :direction :output)
(rf-princ-like-lisp
(format nil "to ~S .." full-out-filename) )
(rf-terpri)
(funcall translator in-stream out-stream comment-style) )
;; send result to terminal:
(progn
(rf-princ-like-lisp "..")
(rf-terpri)
(funcall
translator in-stream *rfi-standard-output* comment-style ))
)))))
(pushnew :rf-comment *features*)
;;; -------------------------------------------------------------------------
;;;
;;; End of module comment.lsp
;;;
;;; -------------------------------------------------------------------------
;;;### eof
;;;### File "lisp2pro.lisp"
;;; Copyright Notice
;;;
;;; This software is distributed for non-profit and research purposes only.
;;; Non-profit redistribution of the current version or parts of the
;;; current version is permitted if this copyright notice is included unchanged.
;;; I give no warranty of any kind for this prototype. It will be further
;;; improved as time permits.
;;;
;;; Michael Herfert, Harold Boley (boley@informatik.uni-kl.de)
; -----------------------------------------------------------------------------
; Module Lisp2Pro.Lsp: Transforms an expression given in the Lisp-like
; syntax into the Prolog-like syntax.
;
;
; 2/92: First Version
; 7/92: Fixed problem with "|"
; 9/92: Print of comments
; 2/93: miser-level, pro-print*
; 3/94: uc-tag, typ-tag, printing of infix-forms
; 7/94 Capability to produce ISO-Prolog code
; 2/97: newsyn for clauses via new type-of-clause's hn/ft-cutfact/rule
; 3/97: newsyn for is via simple ".=" substitution
; 11/97: corrected newsyn for printing correct iso-prolog-'is' in rfp2pl/rf2pl
;
;
;
;
; Exported Item:
;
; pro-print expr-or-clause-in-lisp-syntax [Function]
;
; Prints the argument in a pretty-print format in Prolog-like syntax.
;
(defvar *miser-level* 2
"Parameter of P-syntax pretty printer.
0: Use many lines to print an expression.
1: Never break a line if it can contain the expression.
2: Insert spaces only between factors of the body.
3: Never insert spaces.
(2) and (3) are prerelease versions.
")
(defmacro compact-mode-p () '(> *miser-level* 0))
;; Try at top level with miser-level 0 and 1:
;; [1, 2, 3, 4, [a, b, c, d, e, f, g, h, i], 5, 6, 7, 8]
(defmacro print-one-space-or-not ()
(quote
(if (or (<= *miser-level* 1)
(and (= 2 *miser-level*) body-p) )
(pro-print-char #\space new-x-cursor)
new-x-cursor )))
(defun inst-tup-t (x)
(and (inst-t x)
(tup-t (second x)) ))
(defun inst-vari-t (x)
(and (inst-t x)
(vari-t (second x)) ))
(defun variable-binding-t (x)
(and (consp x)
(= 3 (length x))
;(vari-t (first x))
(eq '= (second x)) ))
(defun cut-sym-t (x)
(and (not *print-like-prolog*) ; ignore cut while generating
(eq '! x) )) ; code for ISO-Prolog
;; The following constants are parameters of the pretty printer.
;; They can be changed here without side effects:
;; Small number ==> use many lines when printing:
(defconstant max-len-of-a-simple-arg 7)
;; Small number ==> use many lines when printing:
(defconstant max-args-per-line 3)
(defun internal-error (&rest msgs)
(mapcar #'rf-print msgs)
(error "Internal-Error.") )
;; The type pro-expr describes the Prolog-like representation of a RelFun-
;; expression.
;; The field len describes the space to print the whole expression
;; including commas, spaces and parentheses resp. brackets.
;; The contents of value depends on the value of type:
;; type = atomic (that means constant or variable)
;; value: the string-form of the atom.
;; type = argument-list
;; value: a list of triples: ( )
;; (seldom used) is e.g. #\|
;; is a pro-expr describing the argument
;; (often used) is e.g. #\,
;; type = round-application (e.g. f(x, y))
;; value: a list. First element: the functor,
;; Second element: the argument-list.
;; type = sqr-application (e.g. g[a,b])
;; value: a list. First element: the functor or nil if it is a tupel.
;; Second element: the argument-list.
;; type = infix (e.g. X is 5)
;; value: a list. 1st element: left side
;; 2nd connector
;; 3rd right-side
;; type in {hn-fact, hn-rule, ft-fact, ft-rule, uc-clause, file-clause}:
;; value: a list: 1st element: head
;; 2nd connector (e.g. " :- ")
;; 3rd body (body is of type arg-list)
(defstruct pro-expr
type ; dom(type) = {round-application, sqr-application,
; argument-list, arg-list-tail, is,
; variable-binding, atomic}
len ; dom(len) = integer
value ; dom(value) = STRING u pro-expr.
internal-p ; <==> comment inside value
(pre-txt "") ; dom = STRING
(post-txt "") ; dom = STRING
;; If comment begins with a #\newline then it's a begin-of-line-comment,
;; otherwise the first substring (upto the first #\newline) is a
;; end-of-line-comment.
;; If comment is not the empty string it is terminated by #\newline.
;; comment is printed after value.
)
(defun newline-indent (indent)
(rf-terpri)
(dotimes (i indent)
(rf-princ-like-lisp " ") ))
(defun pps (s &optional (indent 0) &key rem)
;; pretty-print-structure useful for debugging.
(if rem (progn (rf-terpri) (rf-princ-like-lisp rem) (rf-terpri)))
(cond ((pro-expr-p s)
(rf-princ-like-lisp " <*pro-expr*>")
(newline-indent indent)
(rf-princ-like-lisp "Type: ")
(rf-princ-like-lisp (pro-expr-type s))
(newline-indent indent)
(rf-princ-like-lisp "Len: ")
(rf-princ-like-lisp (pro-expr-len s))
(newline-indent indent)
(rf-princ-like-lisp "Val: ")
(pps (pro-expr-value s) (+ indent 6)) )
((consp s)
(rf-princ-like-lisp "[")
(mapcar #'(lambda (x)
(newline-indent (1+ indent))
(pps x (1+ indent))
)
s)
(newline-indent indent)
(rf-princ-like-lisp "]"))
(t
(rf-princ-like-lisp s) ))
s)
(defun construct-measure-tree (expr must-be-sqr-p &aux pair triple)
;; Returns a value of type pro-expr.
;; The parameter must-be-sqr-p signals that all round applications
;; have to be transformed to sqr-applications, because the call came
;; from inside a sqr argument list.
(cond ((com-cell-p expr)
(construct-measure-tree-of-com-cell expr must-be-sqr-p) )
((stringp expr)
(make-pro-expr :type 'atomic
:len (flatsize expr)
:value (prin1-to-string expr) ))
((eq 'id expr)
(make-pro-expr :type 'atomic
:len 1
:value "_" ))
;;((and (characterp expr) (char= expr the-non-printing-char))
;; used for empty lines within an expression
;;(make-pro-expr :type 'atomic
;; :len 0
;; :value "" ))
((atom expr)
(make-pro-expr :type 'atomic
:len (flatc expr)
;; :value (string-downcase (prin1-to-string expr)) ))
;; |1.2.3| ==> 1.2.3
:value (string-downcase (princ-to-string expr)) ))
((vari-t expr)
;; expr = (VARI symbol)
(let ((name-conc-level (pro-get-variable expr)))
(make-pro-expr :type 'atomic
:len (length name-conc-level)
:value name-conc-level )))
((eq 'typ (first expr))
;; expr = (TYP vari-or-symbol)
(let ((vari-or-symbol (construct-measure-tree (second expr) nil)))
(make-pro-expr
:type 'atomic
:len (1+ (pro-expr-len vari-or-symbol))
:value (stringcat "$" (pro-expr-value vari-or-symbol)) )))
((variable-binding-t expr)
;; expr = (variable = binding-value)
;; If the interpreter is active, then variable is a symbol with
;; leading "_" (e.g. _xyz).
;; If the emulator is active then, variable is a taged list
;; (e.g. (vari x))
(let ((var-as-str
(if (atom (first expr))
(lisp-var-sym->pro-var-sym (first expr))
(pro-get-variable (car expr)) )))
(setq triple (list (make-pro-expr
:type 'atomic
:len (length var-as-str)
:value var-as-str )
"="
(construct-measure-tree (third expr)
must-be-sqr-p ))))
(make-pro-expr
:type 'infix
:len (+ 3 ; 3 = length(" = ")
(pro-expr-len (first triple))
(pro-expr-len (third triple)) )
:value triple ))
((and *print-like-prolog* nil
(eq '= (car expr)) )
;; this is not realy necessary to generate ISO-PROLOG-Code,
;; but it looks better if '=' is printed as infix.
;; (= variable value) ==> (variable = value)
(construct-measure-tree (list (second expr) '= (third expr))
must-be-sqr-p ))
((pro-clause-t expr)
(construct-measure-tree-of-clause expr must-be-sqr-p) )
((pro-is-t expr)
;; expr = (IS left right)
;; impl. INST on the left side
(setq triple (list (construct-measure-tree (second expr)
t ) ;left
".="
;HB; "is"
(construct-measure-tree (third expr)
nil ) )) ;right
(make-pro-expr
:type 'infix
:len (+ 4 ; 4 = length(" is ")
(pro-expr-len (first triple))
(pro-expr-len (third triple)) )
:value triple ))
((typed-expr-t expr)
(setq triple (list (construct-measure-tree (typed-expr-term expr) nil)
":"
(construct-measure-tree (typed-expr-type expr) nil)))
(make-pro-expr
:type 'infix
:len (+ 3 ; 1 = length(" : ")
(pro-expr-len (first triple))
(pro-expr-len (third triple)) )
:value triple ))
((and must-be-sqr-p (pro-tup-t expr))
;; expr = (tup arg-1 .. arg-n)
;; inst outside --> suppress the tup-constructor
(setq pair (list ; constructor & argument-list
nil
(construct-measure-tree-of-arg-list (cdr expr)
t ) ))
(make-pro-expr
:type 'sqr-application
:len (pro-expr-len (second pair))
:value pair ))
(must-be-sqr-p
;; expr = (constructor arg-1 .. arg-n)
;; inst outside --> handle inst like any other constructor
(setq pair (list ; constructor & argument-list
(construct-measure-tree (first expr) t)
(construct-measure-tree-of-arg-list (cdr expr) t) ))
(make-pro-expr
;;:type 'sqr-application
:type (if *print-like-prolog* 'round-application 'sqr-application)
:len (+ (pro-expr-len (first pair))
(pro-expr-len (second pair)) )
:value pair ))
;; there was no inst outside:
;; By the following case (inst (vari x)) is printed as X if there
;; was no inst outside.
;; Remove this sexpression and (inst (vari x)) is printed as inst(X).
((inst-vari-t expr)
(construct-measure-tree (second expr) nil) )
((and (inst-t expr) (not (vari-t (second expr))))
;; expr = (INST (constructor arg-1 .. arg-n))
(construct-measure-tree (second expr) t) )
(t
;; expr = (functor arg-1 .. arg-n)
(setq pair (list
(construct-measure-tree (first expr) nil)
(construct-measure-tree-of-arg-list (cdr expr) nil)))
(make-pro-expr
:type 'round-application
:len (+ (pro-expr-len (first pair))
(pro-expr-len (second pair)) )
:value pair ))))
(defun construct-measure-tree-of-arg-list (arg-list must-be-sqr-p &key foot-p)
;;; Not only used for argument list but also used for body.
;; Returns a value of type pro-expr.
;; The len-field contains the whole length included parentheses
;; (or brackets), commas and spaces.
;; The type-field is of type ARGUMENT-LIST.
;; The value-field is a list of elements.
;; Each element is a triple:
;; ( )
(do* ((rest-args arg-list (rest rest-args))
(arg (first rest-args) (first rest-args))
(next-arg (second rest-args) (second rest-args))
(ignore-next-arg-p nil)
(total-length 2) ; len("()")
(triple nil (list pre-sep tree post-sep))
(pro-arg-list nil
(if tree (cons triple pro-arg-list) pro-arg-list))
(internal-comment-p nil
(or
internal-comment-p
(and tree (pro-expr-internal-p tree))
(and tree (string/= "" (pro-expr-pre-txt tree)))
(and tree (string/= "" (pro-expr-post-txt tree)))
))
(pre-sep)
(tree)
(post-sep)
)
((null rest-args)
;; end of do-loop:
(make-pro-expr
:type 'argument-list
:value (reverse pro-arg-list)
:internal-p internal-comment-p
:len total-length ))
(if ignore-next-arg-p
(setq ignore-next-arg-p nil
tree nil )
(cond ((cut-sym-t arg)
;; cut is the first el. in arg-list:
(cond ((null (cdr rest-args))
;; arg-list = (!)
(setq tree (construct-measure-tree '! must-be-sqr-p)
post-sep the-non-printing-char ))
((and foot-p
(null (cddr rest-args)) )
;; arg-list = (! )
(setq tree (construct-measure-tree next-arg
must-be-sqr-p )
pre-sep "!& "
post-sep the-non-printing-char
ignore-next-arg-p t ))
((null (cddr rest-args))
;; arg-list = (! )
(setq tree (construct-measure-tree next-arg
must-be-sqr-p)
pre-sep "! "
post-sep the-non-printing-char
ignore-next-arg-p t ))
(t
;; arg-list = (! .. )
(setq tree (construct-measure-tree next-arg
must-be-sqr-p)
pre-sep "! "
post-sep (if (and foot-p ; M.S. 11/1993
(= (length rest-args) 3))
" &"
#\,)
ignore-next-arg-p t )))
(if (stringp post-sep) ; M.S.
(incf total-length (1+ (length post-sep)))
(when (char/= the-non-printing-char post-sep)
(incf total-length 2))) ; 2=len(", ") (automatic space)
(when pre-sep
(incf total-length (length pre-sep)) )
(incf total-length (pro-expr-len tree)) )
((eq '|\|| arg)
;; arg-list = (\| )
(setq tree (construct-measure-tree next-arg must-be-sqr-p)
pre-sep #\|
post-sep the-non-printing-char
ignore-next-arg-p t)
(incf total-length (+ 2 (pro-expr-len tree))) ; 2=len("| ")
)
(t
(setq pre-sep nil)
(setq tree (construct-measure-tree arg must-be-sqr-p) )
(incf total-length (pro-expr-len tree))
(cond ((cut-sym-t next-arg)
(if (null (cddr rest-args))
;; cut at the end:
(setq post-sep " !" ; 2=len(" !")
total-length (+ total-length 2) )
;; cut not at the end:
;; rest-args = ( ! ..)
(if (and foot-p (null (cdddr rest-args)))
;; rest-args = ( ! )
(setq post-sep " !&" ; 4=len(" !& ")
total-length (+ total-length 4) )
;; no foot after cut:
(setq post-sep " !" ; =len(" ! ")
total-length (+ total-length 3) )))
(setq ignore-next-arg-p t) )
((eq '|\|| next-arg)
;; arg-list = ( .. \| )
(incf total-length 3) ; 3 = len(" | ")
;; in the second case the length is not correct: (!!!)
(setq post-sep (if (<= *miser-level* 1) " |" "|"))
(setq ignore-next-arg-p t) )
((null (rest rest-args))
;; last element:
(setq post-sep the-non-printing-char) )
((or (and foot-p
(null (cddr rest-args)))
(and foot-p
(null (cdddr rest-args))
(cut-sym-t (caddr rest-args)) ))
;; two versions of footed rule:
;; rest-args = ( )
;; or rest-args = ( !)
(incf total-length 3) ; 3=len(" & ")
(if (= 3 *miser-level*)
(setq post-sep " & ") ; prerelease
(setq post-sep " &") ))
(t
(incf total-length 2) ; 2 = len(", ")
(setq post-sep #\,) )) )))))
(defun construct-measure-tree-of-head (head)
(cond ((com-cell-p head)
;; the head has a comment:
(let ((pro-head
(construct-measure-tree-of-head (com-cell-lisp head))))
(setf (pro-expr-pre-txt pro-head) (com-cell-pre-txt head)
(pro-expr-post-txt pro-head) (com-cell-post-txt head) )
pro-head ))
((consp head)
;; head = (functor arg-1 arg-2 .. )
(let ((pair (list
;; functor & argument-list
(construct-measure-tree (first head) t)
(construct-measure-tree-of-arg-list (cdr head) t) )))
(make-pro-expr
:type 'round-application
:len (+ (pro-expr-len (first pair))
(pro-expr-len (second pair)) )
:value pair )))
(t
;; head = ATOM
(construct-measure-tree head nil) )))
(defun construct-measure-tree-of-clause (expr must-be-sqr-p)
(declare (ignore must-be-sqr-p))
(let* ((type-of-clause
(cond ((uc-t expr) 'uc-clause)
((mod-sub-module-t expr) 'file-clause)
((type-tag-eq expr 'hn)
;; hornish-clause:
(cond ((= 2 (length expr))
'hn-fact) ; (HN )
((cut-sym-t (third expr))
(if (= 3 (length expr))
'hn-cutfact ; (HN !) own neck
'hn-cutrule));(HN ! .. ) own neck
(t 'hn-rule ))) ; (HN .. )
;; footed-clause:
((type-tag-eq expr 'ft)
(cond ((or (= 3 (length expr))
(and (= 4 (length expr))
(cut-sym-t (fourth expr)) ))
'ft-fact) ; (FT [!])
((cut-sym-t (third expr))
(if (= 4 (length expr))
'ft-cutfact ; (FT ! ) own neck
'ft-cutrule));(FT ! .. ) own neck
(t 'ft-rule ))) ; (FT .. )
(t
(error ": unknown tag.")) ))
(head (construct-measure-tree-of-head (second expr)))
(connector (case type-of-clause
(hn-fact "") ; shortens " :-"
(hn-cutfact "!") ; shortens " !-"
(hn-rule " :- ")
((hn-cutrule ft-cutrule) " !- ") ; shortens " :- ! "
;; (ft-fact " :- & ")
(ft-fact " :& ") ; shortens " :- & "
(ft-cutfact " !& ") ; shortens " :& ! "
(ft-rule " :- " )
;; (uc-clause " :-# ")
(uc-clause " :# ") ; newsyn
(file-clause "")
(t (error
"Internal error: "
))))
(body
(construct-measure-tree-of-arg-list (if (or (eq 'hn-cutfact ;There's
type-of-clause) ; a
(eq 'hn-cutrule ;cut
type-of-clause) ;in
(eq 'ft-cutfact ;connector
type-of-clause);(neck)
(eq 'ft-cutrule
type-of-clause)) ;thus
(cdddr expr) ;here without cut
(cddr expr))
nil ; must-be-sqr
:foot-p (or (eq 'ft-rule
type-of-clause)
(eq 'ft-cutrule
type-of-clause)) ))
)
(decf (pro-expr-len body) 1) ; -1 = len(".") - len("()")
(make-pro-expr
:type type-of-clause
:value (list head connector body)
:internal-p (or (pro-expr-internal-p head)
(pro-expr-internal-p body) )
:len (+ (pro-expr-len head)
(pro-expr-len body)
(length connector) ))))
(defun construct-measure-tree-of-com-cell (expr must-be-sqr-p)
;; expr is a com-cell.
(let ((tree (construct-measure-tree (com-cell-lisp expr)
must-be-sqr-p )))
(setf (pro-expr-pre-txt tree) (com-cell-pre-txt expr)
(pro-expr-post-txt tree) (com-cell-post-txt expr)
(pro-expr-internal-p tree) (com-cell-internal-p expr) )
tree
))
(defun pro-print (expr &optional (x-cursor 1))
; Prints expr in the Prolog-style syntax.
; Starts at x-cursor.
; Returns no value
(pro-print-tree (construct-measure-tree expr nil) x-cursor))
(defun pro-print*
(expr x-cursor left-margin min-rest-space &optional continuation-indicator)
;; Prints expr in the Prolog-style syntax.
;; Assumes that cursor is in column x-cursor.
;; If expr does not fit on line and rest space is less than
;; min-rest-space then it begins a newline in column left-margin.
;; The continuation-indicator is printed at cursor-position if
;; expr starts at a new line.
;; Returns no value
(let ((tree (construct-measure-tree expr nil))
(rest-col (- *rf-print-width* x-cursor))
(start-col x-cursor) )
(when (and (< rest-col min-rest-space)
(> (pro-expr-len tree) rest-col) )
(when continuation-indicator (rf-princ-like-lisp continuation-indicator))
(setq start-col (pro-newline-and-indent left-margin)) )
(pro-print-tree tree start-col) ))
;; All functions named pro-print-... return the position of the
;; cursor after printing the expression.
(defun pro-print-clause (expr x-cursor)
;; Printing of a clause:
;; 1.: .. .
;; 2.:
;; _________ .. . (_ means space)
;; 3.:
;; ____ .. .
(let* ((value (pro-expr-value expr))
(head (first value))
(connector (second value))
(body (third value))
(comment-after-clause "")
(cursor-pos-after-dot 0)
)
(setq cursor-pos-after-dot
(pro-print-char
#\.
(if (and (space-enough-p (pro-expr-len expr) x-cursor)
(not (pro-expr-internal-p expr)) )
;; fits in one line:
(pro-print-arg-list
body
(pro-print-string
connector
(pro-print-tree head x-cursor :try-single-line-p t))
0
the-non-printing-char
the-non-printing-char
:try-single-line-p t
:body-p t )
;; not on a single line:
(let (new-cursor)
(cond ((string= "" connector)
;; hn-fact ==> no connector
(when (string/= (pro-expr-post-txt head) "")
(setq comment-after-clause (pro-expr-post-txt head))
(setf (pro-expr-post-txt head) ""))
(setq new-cursor
(pro-print-tree head x-cursor
:try-single-line-p t) ))
((space-enough-p head x-cursor (length connector))
;; print as:
(pro-print-tree head
x-cursor
:try-single-line-p t
:post-script connector )
(setq new-cursor
(pro-newline-and-indent (+ 9 x-cursor))))
(t
;; print as:
;; ____ ..
(pro-print-tree head x-cursor :try-single-line-p t)
(setq new-cursor
(pro-print-string
connector
(pro-newline-and-indent (+ x-cursor 4)) ))))
(pro-print-arg-list body
new-cursor
0 ; don't go left
the-non-printing-char
the-non-printing-char
:try-single-line-p t
:body-p t )))))
(if (string= "" comment-after-clause)
cursor-pos-after-dot
(print-comment comment-after-clause
(if (eq *style* 'prolog)
psyn-comment-lead-in
lsyn-comment-lead-in )
cursor-pos-after-dot
0 ))))
(defun pro-print-head (head &optional (x-cursor 1))
(pro-print-tree (construct-measure-tree-of-head head) x-cursor) )
(defun pro-print-tree (tree x-cursor &key post-script try-single-line-p)
;; post-script (e.g. #\,) is printed after tree but before
;; a possibly existing comment.
(unless (string= "" (pro-expr-pre-txt tree))
;; print a leading comment (always type 2):
(print-comment-type-2
(pro-expr-pre-txt tree)
(if (char= #\newline (char (pro-expr-pre-txt tree) 0)) 1 0)
psyn-comment-lead-in
x-cursor )
(pro-newline-and-indent x-cursor) )
(let ((new-x-cursor
(case (pro-expr-type tree)
(atomic
(pro-print-atom tree x-cursor) )
((round-application sqr-application)
(pro-print-application tree
x-cursor
:try-single-line-p
(or (compact-mode-p) try-single-line-p) ))
(infix
(pro-print-infix tree x-cursor) )
((hn-fact hn-rule hn-cutfact hn-cutrule ft-fact ft-rule ft-cutfact ft-cutrule uc-clause file-clause)
(pro-print-clause tree x-cursor) )
(t
(internal-error "Unknown tree-type in "
(pro-expr-type tree) )))))
(when post-script
(setq new-x-cursor (pro-print-txt post-script new-x-cursor)) )
(if (string= (pro-expr-post-txt tree) "")
;; no comment:
new-x-cursor
;; print the comment:
(print-comment (pro-expr-post-txt tree)
psyn-comment-lead-in
new-x-cursor
x-cursor ))))
(defun pro-print-atom (tree x-cursor)
(if (space-enough-p tree x-cursor)
(progn (rf-princ-like-lisp (pro-expr-value tree))
(+ x-cursor
(pro-expr-len tree) ))
(progn (rf-terpri)
(rf-princ-like-lisp (pro-expr-value tree))
(pro-expr-len tree) )))
(defun pro-print-application (tree x-cursor &key try-single-line-p)
; type = round-application or sqr-application
; value = (functor arg-list)
(let ((functor (first (pro-expr-value tree)))
(arg-list (second (pro-expr-value tree)))
(x x-cursor)
(xx nil)
(open-char nil)
(close-char nil) )
(case (pro-expr-type tree)
(round-application (setq open-char round-left)
(setq close-char round-right) )
(sqr-application (setq open-char sqr-left)
(setq close-char sqr-right) )
(t (error "in pro-print-application.")) )
(if functor
; it's not a list:
(progn (if (space-enough-p functor x)
; never break a functor
(setq x x-cursor)
(setq x (pro-newline-and-indent 1)) )
(setq xx (pro-print-tree functor
x
:try-single-line-p
(or (compact-mode-p)
try-single-line-p )))
(when (string/= "" (pro-expr-post-txt functor))
;; functor has a comment ==> args require a fresh line:
(setq xx (pro-newline-and-indent (+ x 2)))) )
; it's a list:
(setq xx x) )
(pro-print-arg-list arg-list
xx
(+ x 2)
open-char
close-char
:try-single-line-p (or try-single-line-p
(compact-mode-p) ))))
(defun pro-print-arg-list
(arg-list x-cursor x-cursor-left open-char close-char
&key try-single-line-p body-p)
(let* ((elements (pro-expr-value arg-list))
(arg-list-info (get-arg-list-info elements))
(nr-of-el (first arg-list-info))
(max-len (second arg-list-info))
(simple-args-p (third arg-list-info))
(internal-comment-p (pro-expr-internal-p arg-list))
)
(cond ((and (or (<= nr-of-el max-args-per-line)
simple-args-p
try-single-line-p )
(space-enough-p (pro-expr-len arg-list) x-cursor)
(not internal-comment-p) )
;; functor(arg-1, arg2)
(pro-print-char close-char
(pro-print-arg-list-elements
elements
(pro-print-char open-char x-cursor)
nil ; no extra-lines
:try-single-line-p
(or (compact-mode-p) try-single-line-p)
:body-p body-p
)))
((or (space-enough-p max-len x-cursor 2) ; 2 = length(sqr-left",")
(<= x-cursor x-cursor-left)
try-single-line-p )
;; functor( arg-1,
;; arg-2 )
(pro-print-char close-char
(pro-print-arg-list-elements
elements
(pro-print-char open-char x-cursor)
t ; print on separated lines
:try-single-line-p (compact-mode-p) ; (miser)
)))
(t
;; functor(
;; arg-1,
;; arg-2)
(pro-print-char open-char x-cursor)
(pro-print-char close-char
(pro-print-arg-list-elements
elements
(pro-newline-and-indent x-cursor-left)
t ; print on separated lines
:try-single-line-p (compact-mode-p) ; (miser)
))))))
(defun pro-print-arg-list-elements
(elements x-cursor extra-lines-p &key try-single-line-p body-p)
;; each element is a triple: ( )
;; is usualy not used (that means NIL)
;; is usualy #\,
;; it is the-non-printing-char for the last element.
;; body-p <==> it's the body of a clause
(let ((new-x-cursor x-cursor)
last-element-has-comment-p
)
(mapl #'(lambda (rest-elements &aux (triple (car rest-elements)))
(unless (null (first triple))
(setq new-x-cursor
(pro-print-txt (first triple) new-x-cursor) ))
(setq new-x-cursor
(pro-print-tree (second triple)
new-x-cursor
:post-script (third triple)
:try-single-line-p
(or (compact-mode-p) try-single-line-p) ))
(cond ((null (rest rest-elements))
;; all elements printed ==> no space behind post-sep:
(setq last-element-has-comment-p
(string/= "" (pro-expr-post-txt (second triple)))))
(t
(setq new-x-cursor
(if extra-lines-p
(pro-newline-and-indent x-cursor)
;;(pro-print-char #\space new-x-cursor)
(print-one-space-or-not)
)))))
elements )
(if last-element-has-comment-p
;; last item was a comment ==> closing-char requires a fresh line:
(pro-newline-and-indent x-cursor)
new-x-cursor )))
(defun pro-print-infix (tree x-cursor)
;; type = infix
;; value = (left-side connector right-side)
(let*+ ((x x-cursor)
((left-side connector right-side) (pro-expr-value tree))
(space-or-no-space (if (or (zerop *miser-level*)
(string= ".=" connector) ;HB; .=+(3,3) not
; currently parsed either, so pp .= +(3,3)
(alpha-char-p (character
(elt connector 0) )))
" "
"" ))
)
(if (and *print-like-prolog* (string= ".=" connector))
(setf connector "is")) ; MP 27.11.97 for correct prolog output
(setq x (pro-print-tree left-side x))
(if (comment-p left-side)
;; last item was a comment, print as:
;;
;; is
(pro-print-tree
right-side
(pro-print-string (stringcat connector " ")
x
(pro-newline-and-indent x-cursor) ))
(pro-print-tree
right-side
(pro-print-string (stringcat space-or-no-space
connector
space-or-no-space )
x
x-cursor )))))
(defun pro-print-txt (char-or-string x-cursor)
(if (characterp char-or-string)
(pro-print-char char-or-string x-cursor)
(pro-print-string char-or-string x-cursor) ))
(defun pro-print-char (char x-cursor)
(if (char= char the-non-printing-char)
x-cursor
(case char
((#\. #\, #\space)
(rf-princ-like-lisp char)
(1+ x-cursor) )
(t
(if (space-enough-p 1 x-cursor)
(progn (rf-princ-like-lisp char)
(1+ x-cursor) )
(progn (pro-newline-and-indent 1)
(rf-princ-like-lisp char)
2 )))))) ; 2 = Cursor column
(defun pro-print-string (str x-cursor &optional (x-cursor-left 1))
; Tries to print str on the current line.
; If not possible print on the next line at column x-cursor-left.
(let ((str-wo-blank ""))
(cond ((space-enough-p (length str) x-cursor)
; fits on line
(rf-princ-like-lisp str)
(+ x-cursor
(length str) ))
((space-enough-p (length str) x-cursor-left)
; next line with indentation
(pro-newline-and-indent x-cursor-left)
(if (char= #\Space
(char str 0))
(setq str-wo-blank (subseq str 1))
(setq str-wo-blank str) )
(rf-princ-like-lisp str-wo-blank)
(+ x-cursor-left
(length str-wo-blank) ))
(t
; next line without indentation
(pro-newline-and-indent 1)
(if (char= #\Space
(char str 0))
(setq str-wo-blank (subseq str 1))
(setq str-wo-blank str) )
(rf-princ-like-lisp str-wo-blank)
(+ x-cursor-left
(length str-wo-blank) )))))
(defun pro-newline-and-indent (x)
(rf-terpri)
(dotimes (i (1- x) x) (rf-princ-like-lisp " ")) )
(defun space-enough-p (tree-or-number x-cursor &optional (extra-space 0))
; Note: the last column is reserved for special characters, which
; never should be printed at the begin of a line (e.g comma,
; point).
(>= (- *rf-print-width* x-cursor extra-space)
(if (pro-expr-p tree-or-number)
(pro-expr-len tree-or-number)
tree-or-number )))
(defun pro-get-variable (var)
; var = (VARI name level)
; where the level-field is optional.
; Example: var = (VARI time 127) ==> "Time:127"
(let ((level (level-of var))
(name (string-upcase
(string-downcase (princ-to-string
(second var)) )
:start 0
:end 1 )))
(if (not (null level))
(setq name (stringcat name "*" (princ-to-string level))) )
(if (digit-char-p (character (subseq name 0 1)))
(stringcat "_" name)
name )))
(defun lisp-var-sym->pro-var-sym (lisp-var-sym)
; Example: input: _xyz
; output: Xyz
(let ((pro-name
(nstring-upcase (nstring-downcase
(subseq (princ-to-string lisp-var-sym) 1))
:start 0
:end 1 )))
(if (digit-char-p (character (subseq pro-name 0 1)))
(stringcat "_" pro-name)
pro-name )))
(defun lisp-sym->pro-sym (symbol-or-string)
"Replaces dashes by underscores in symbols.
E.g. expr-type ==> expr_type
Used to generate ISO-Prolog code."
(if (symbolp symbol-or-string)
(let* ((old-str (princ-to-string symbol-or-string))
(new-str (substitute #\_ #\- old-str :test #'char=)) )
(when (or (position #\? new-str :test #'char=)
(position #\+ new-str :test #'char=)
(position #\* new-str :test #'char=)
(position #\/ new-str :test #'char=) )
(setq new-str (stringcat "'" new-str "'")) )
(intern new-str) )
symbol-or-string ))
(defun get-arg-list-info (list-of-triples)
;; Returns a list. 1.: No. of elements
;; 2.: Length of biggest element.
;; 3.: simple-arg-p
(do* ((n 0 (1+ n))
(maxi 0 (max maxi
(pro-expr-len element) ))
(simple-arg-p t (and simple-arg-p
(or (eq 'atomic
(pro-expr-type element) )
(<= (pro-expr-len element)
max-len-of-a-simple-arg))))
(l list-of-triples (cdr l))
(triple (car l) (car l))
(element (second triple) (second triple) )
)
((null l)
(list n maxi simple-arg-p) )))
(defun type-tag-eq (expr tag-of-car)
;; <==> (car expr) is tag-of-car
;; Works with com-cells too.
(cond ((com-cell-p expr)
(type-tag-eq (com-cell-lisp expr) tag-of-car) )
((consp expr)
(eq (car expr) tag-of-car) )
(t
nil )))
(defun pro-is-t (expr)
;; <==> IS is the car of expr.
;; Works with com-cells too.
(type-tag-eq expr 'is) )
(defun pro-tup-t (expr)
;; <==> TUP is the car of expr.
;; Works with com-cells too.
(type-tag-eq expr 'tup) )
(defun pro-clause-t (expr)
;; <==> HN or FT is the car of expr.
;; Works with com-cells too.
(or (type-tag-eq expr 'hn)
(type-tag-eq expr 'ft)
(uc-t expr)
(mod-sub-module-t expr)
))
(defun prolog-toplevel (flattened-clause table)
"Replaces some occurences of RELFUNs is-primitive with PROLOGs = .
Needed to generate real-PROLOG text."
(declare (ignore table))
(mapcar #'(lambda (literal)
(cond ((is-t literal)
(let ((replacement
(list '= (second literal) (third literal))))
;; literal = (IS vari expr):
(cond ((not (consp (third literal)))
;; expr is an atom:
replacement)
;; expr is not an atom:
((lisp-function-p (car (third literal)))
;; PROLOG has to evaluate, do not change is:
literal )
(t
replacement ))))
((and (consp literal)
(null (cdr literal)))
;; call without arguments:
;; PROLOG does not like empty parenthesis
(car literal) )
(t
literal )))
flattened-clause ))
(defun rfp-db-2-pl-db (rfp-db table)
(mapcar #'(lambda (flattened-clause)
(prolog-toplevel flattened-clause table) )
rfp-db ))
(pushnew :rf-lisp2pro *features*)
;; ----------------------------------------------------------------------------
;; End of Module Lisp2Pro.Lsp
;; ----------------------------------------------------------------------------
;;;### eof
;;;### File "pro2lisp.lisp"
;;; Copyright Notice
;;;
;;; This software is distributed for non-profit and research purposes only.
;;; Non-profit redistribution of the current version or parts of the
;;; current version is permitted if this copyright notice is included unchanged.
;;; I give no warranty of any kind for this prototype. It will be further
;;; improved as time permits.
;;;
;;; Michael Herfert, Harold Boley (boley@informatik.uni-kl.de)
; ----------------------------------------------------------------------------
;
; Module Pro2Lisp.Lsp: Contains functions to read goals und clauses
; given in the Prolog-like syntax of Relfun.
; The input can be read from a string or from a
; file of chararacters.
; The output contains the equivalent form in
; the Lisp-like syntax as a symbolic expression.
;
; If an error is detected a message is displayed
; an the value NIL is returned.
;
;
; First Version by Michael Herfert, 2/92
; For earlier history see lisp2pro.lisp
; 2/97: newsyn for clauses via new impliesepsilon
; 3/97: newsyn for is via point as special-token
; 3/97: new scan-special-item separates after max. special-token
; 9/97: new valued-conjunction top-level implemented by parse-query change [MP]
;
;
;
; Exported items:
;
;
; pro-read-data-base filename [Function]
;
; Assumes that the file contains clauses.
; Returns a list of the clauses in Lisp-like syntax.
;
;
; pro-read-goal string [Function]
;
; Reads in a single goal from a string.
; Note that there is no point at the end of a goal.
;
;
; pro-read-clause string [Function]
;
; Reads in a single clause from the string.
;
;
; pro-split-input string [Function]
;
; Splits the given string in the first symbol and the rest
; of it. Useful for recognizing system-commands.
; Returns a pair.
; Examples:
; (pro-split-input "consult my-base.rfp")
; ==> (consult . "my-base.rfp")
;
; (pro-split-input "[a,b,c]")
; ==> (nil . "")
#| #% replaced by (look-for-comment) (for CLiCC) [MS/MP]
;; #. causes read-time-evaluation, important for the compiler.
;; (Steele p. 531/534)
#.
(set-dispatch-macro-character ; Steele, p. 546ff
;; #% sends a message to the scanner to look for a comment.
#\#
#\%
#'(lambda (stream subchar integer-arg)
(declare (ignore subchar integer-arg)) ; subchar is #\%
(list 'look-for-comment) ))
|#
(defun single-char-reader (stream char)
(declare (ignore stream))
char )
(defun set-syntax-pro (a-readtable)
;; reading lists:
;; (#'... not allowed on boards!!!)
(set-macro-character #\| #+genera 'single-char-reader
#-genera #'single-char-reader
nil a-readtable)
(set-macro-character #\, #+genera 'single-char-reader
#-genera #'single-char-reader
nil a-readtable)
(set-macro-character #\' #+genera 'single-char-reader
#-genera #'single-char-reader
nil a-readtable)
(set-macro-character #\` #+genera 'single-char-reader
#-genera #'single-char-reader
nil a-readtable)
(set-syntax-from-char #\_ #\A a-readtable)
a-readtable )
(defvar *rfi-readtable-pro*
; this is a read-only variable
(set-syntax-pro (copy-readtable nil)) ) ; nil --> copy of std. readtable
;;; Functions in *extra-parsing-fcts* would have to be additionally
;;; declared (declare-funcallable ...) for CLiCC
(defvar *extra-parsing-fcts*
nil
;; prepared for future release
;;(list (cons 'inst 'parse-inst))
"Assoc-list of symbols requiring special parsing.
Special parsing implies these symbols can not be used for meta-calls.
The functions have always this arg-list: (scanner &optional auto-inst-p)
"
)
;; ----------------------------------------------------------------------------
(defun signal-error (scanner &rest msg-s)
; Prints all arguments and the error-position via the scanner.
(pro-print-error scanner msg-s)
(throw :pro-read-error-tag nil) ) ; nil signals an error
(defun pro-print-error (scanner msg-s)
; msg-s = (msg-1 msg-2 ..)
(rf-terpri)
(let ((x (token-x-pos (funcall scanner 'last-token)))
(y (token-y-pos (funcall scanner 'last-token)))
(last-line (funcall scanner 'last-line))
(act-line (funcall scanner 'act-line))
)
(rf-princ-like-lisp (format nil
"Error near line ~A, column ~A.~%"
y
x ))
(mapcar #'(lambda (msg)
(if (token-p msg)
(if (token-value msg)
(rf-princ-like-lisp (token-value msg))
(rf-princ-like-lisp (tok-type->string
(token-type msg) )))
(rf-princ-like-lisp msg) ))
msg-s )
(rf-terpri)
(if (> y 1)
(rf-princ-like-lisp last-line) )
(rf-terpri)
(rf-princ-like-lisp act-line)
(rf-terpri)
(rf-terpri) ))
(defun tok-type->string (tok-type) ;HB; why without "!" etc.?
(case tok-type
(round-left (string round-left))
(round-right (string round-right))
(sqr-left (string sqr-left))
(sqr-right (string sqr-right))
(comma (string comma))
(bar (string bar))
(ampersand "&")
(implies ":-")
(point ".")
(is ".=")
;HB; (is "is")
(type "type")
(colon "colon")
(empty "End of input")
(t (princ-to-string tok-type)) ))
;; The scanner returns a value of this type:
(defstruct token
type ; dom = {constant, variable, type,
; round-left, round-right, sqr-left, square-right
;HB; point, comma, ampersand, implies, implies-uc, impliesepsilon
;HB; cut, point, is, colon, empty}
value ; used with number, constant, variable.
x-pos ; to report an error-position
y-pos
)
(defun not-equal (x y) (not (eq x y)) )
(defun gen-scanner (the-input-stream)
"Returns a scanner-function.
The scanner could be seen as an object in the view of OOP.
A very big function, but there are many local functions defined by LABELS.
"
(let ((x-pos 0)
(y-pos 1)
(char-pos -1)
(last-ch #\Space)
(last-token (make-token :x-pos 1 :y-pos 1 :type 'empty))
(last-line "")
(comment "")
(act-line "1: ")
(str-of-newlines "")
(stacked-tokens nil) ; used e.g. for expanding "!-" to ":-" and "!"
(special-tokens
;; special-tokens = (triple-1 .. triple-n)
;; triple = (token-as-string token-type stacked-tokens)
'((":-" implies nil)
;; (":-#" implies-uc nil)
(":#" implies-uc nil) ; newsyn
;; (":-&" implies (ampersand))
(":&" implies (ampersand)) ; newsyn
;;; (":-!&" implies (cut ampersand))
;;; (":-!" implies (cut))
;HB; ("!&" cut (ampersand))
("!&" impliesepsilon (cut ampersand)) ;implies (neck) or empty (body)
;; ("&!" cut (ampersand)) ; newsyn (abandoned)
("!-" implies (cut))
;;; ("!-&" implies (cut ampersand))
("!" cut nil)
("!." cut (point)) ; newsyn for is via next comment requires this
("!-." implies (cut point)) ; and this
("&" ampersand nil)
(":" colon nil)
(".=" is nil) ; newsyn for is requires:
("." point nil) ; point special-token instead of ...
))
;; terminating characters:
;; (cons )
(terminating-chars (list (cons sqr-left 'sqr-left)
(cons sqr-right 'sqr-right)
(cons round-left 'round-left)
(cons round-right 'round-right)
;(cons #\. 'point) ;... terminating character
(cons #\, 'comma)
(cons #\| 'bar)
(cons #\$ nil)
;;"!" is not a terminating char ("!-")
;(cons #\! 'cut)
;(cons #\& 'ampersand)
)))
(labels
((get-ch ()
(if (char/= last-ch eof-char)
(progn
(setq last-ch (read-char the-input-stream
nil
eof-char ))
(setq char-pos (1+ char-pos))
(cond ((char= last-ch #\Newline)
(setq x-pos 1)
(setq y-pos (1+ y-pos))
(setq last-line act-line)
(setq act-line (stringcat (princ-to-string y-pos)
": " )))
(t
(setq act-line (stringcat act-line (string last-ch)))
(setq x-pos (1+ x-pos))) )))
last-ch )
(lookahead ()
(peek-char nil the-input-stream nil eof-char) )
(scan-item (continue-p &optional (start-str ""))
;; returns a string starting with start-str.
(loop (if (and (char/= eof-char last-ch)
(funcall continue-p last-ch) )
(setq start-str
(stringcat start-str (string last-ch)))
(return start-str) )
(get-ch) ))
(scan-special-item (&optional (start-str "")) ;HB; stops after...
;; returns a string initialized with start-str = "".
(loop (if (and (char/= eof-char last-ch)
(special-char-p last-ch)
(or (not (member start-str ; ...max. special
special-tokens ; tokens
:test #'string= ; such as
:key #'car )) ; ":&"["-3"],
(member (stringcat start-str ; ".="[..],
(string last-ch)) ;or
special-tokens ; any others,
:test #'string= ; even within
:key #'car )) ) ; ":-!&" etc.
(setq start-str
(stringcat start-str (string last-ch)))
(return start-str) )
(get-ch) ))
(psyn-comment-type-1-reader ()
;; reads in the comment and all comments immedeatly following.
(let ((triple (read-comments psyn-comment-lead-in
1 ; type of first comment
the-input-stream)))
(setq comment (stringcat comment (first triple))
y-pos (+ y-pos (second triple))
x-pos (third triple) )))
(psyn-comment-type-2-reader ()
;; called at the end of a line to check if the next line
;; begins with a comment.
;; On entry (read-char the-input-stream) returns the first
;; char after #\newline.
(unread-char last-ch the-input-stream)
(let* ((tuple (read-until-black the-input-stream))
(non-white-char (first tuple))
(triple (if (char= psyn-comment-lead-in non-white-char)
(read-comments psyn-comment-lead-in
2
the-input-stream)
nil ))
)
(setq str-of-newlines (second tuple))
(if triple
;; comment detected:
(progn
;(princ "non-w:") (princ non-white-char) (terpri)
(setq comment
(stringcat str-of-newlines
(subseq (first triple) 1) )
str-of-newlines "" ) ; [new]
;(princ "comment=") (princ comment) (princ"<") (terpri)
)
;; no comment at begin of line:
(setq x-pos 0) )
))
(id-char-p (ch)
(or (id-char-aux-p ch)
(char= #\= ch) ;HB;newsyn for is requires: #\= in constants ...
(and (char= #\. ch)
(id-char-aux-p (lookahead)) )))
(id-char-aux-p (ch)
(cond ((both-case-p ch) t)
((digit-char-p ch) t)
(t (member ch '(#\+ #\- #\* #\/ #\_ #\< #\>
;HB; #\= ; ... only if not immediately after #\.
)
:test #'char=)) ))
(special-char-p (ch)
(cond ((assoc ch terminating-chars :test #'char=)
nil )
((member ch '(#\; #\' #\`) :test #'char=)
nil )
((char<= ch #\Space) nil)
((char> ch (code-char 127)) nil)
((digit-char-p ch) nil)
((both-case-p ch) nil)
(t t) ))
(string-constituent-p (ch)
(and (not (char= #\" ch))
(not (char= #\Newline ch)) ))
(scan-whitespace ()
(do ((abort nil))
(abort)
(case last-ch
(#\Space (get-ch))
(#\Newline (if (null *comment-style*)
nil
(psyn-comment-type-2-reader) )
(get-ch) ; last-ch:= non-comment-char
)
(#\Tab (get-ch))
(#\%
(if (null *comment-style*)
;; ignore until end of line:
(do ((abort nil)) ; comment
(abort)
(setq abort (char=
last-ch
#\Newline))
(get-ch) )
;; read in the type-1-comment:
(progn
;; problem: comment at the start of the file
;; is of type-2 without a leading #\Newline
(when (and (= 1 x-pos) (= 1 y-pos))
;; file starts with a comment
(setq comment (string #\Newline) ))
(psyn-comment-type-1-reader)
(get-ch) ; last-ch := non-comment-char
)))
(t (setq abort t)))))
(scan-digits ()
;; digits := digit {digit}
;; returns a string of digits
(if (digit-char-p last-ch)
(scan-item #'digit-char-p)
(signal-error #'scanner
(format nil
"Expected: digit~%Found: ~C"
last-ch ))))
(scan-integer ()
;; integer := ["+"l"-"] digits
;; returns a string
(let ((sign (case last-ch
(#\+ (get-ch)
"+")
(#\- (get-ch)
"-")
(t "" ))))
(stringcat sign (scan-digits)) ))
(scan-number-or-silly-symbol ()
;; scans reals, rationals and symbols like 1.2.3.
;; returns a number resp. a symbol.
;; real := integer ["." digits]
;; ["E" integer]
(let* ((left-side (scan-integer))
(right-side (if (and (char= #\. last-ch)
(digit-char-p (peek-char
nil
the-input-stream
nil
eof-char)))
(progn (get-ch)
(stringcat "." (scan-integer)) )
"" ))
(exponent (case last-ch
((#\E)
(get-ch)
(stringcat "E" (scan-integer)) )
(t "") )))
(cond ((and (id-char-p last-ch)
(char/= #\/ last-ch) )
;; a symbol starting with a digit (e.g. 1.2.3 or 12-May)
(read-from-string
(scan-item #'id-char-p (stringcat left-side
exponent
right-side ))))
((and (string= "" right-side)
(string= "" exponent)
(rational-p) )
;; it's a rational:
(scan-rational left-side) )
(t
(read-from-string (stringcat left-side
right-side
exponent ))))))
(rational-p ()
;; called after an integer has been detected.
;; Value <==> next non-white is a slash
(scan-whitespace)
(char= #\/ last-ch) )
(scan-rational (numerator-as-string)
;; Condition on entry: last-ch = slash
(get-ch) ; eat the slash
(scan-whitespace)
(read-from-string (stringcat numerator-as-string
"/"
(scan-integer) )))
(scan-type ()
;; On entry: last-ch = char. after "$"
(let ((vari-or-symbol (next-token)))
(if (eq 'variable (token-type vari-or-symbol))
(list 'typ (list 'vari (token-value vari-or-symbol)))
(list 'typ (token-value vari-or-symbol)) )))
(next-token ()
(let ((r nil)
(*readtable* *rfi-readtable-pro*) ) ; dynamic variable
(if stacked-tokens
(setq r (make-token :type (car stacked-tokens)
:x-pos x-pos
:y-pos y-pos )
stacked-tokens (cdr stacked-tokens) )
(progn
(scan-whitespace)
(setq r (make-token :x-pos x-pos :y-pos y-pos))
(cond ((digit-char-p last-ch)
(setf (token-type r) 'constant)
(if (char= #\1 last-ch)
;; check for functors 1- and 1+
(case (peek-char nil the-input-stream
nil eof-char)
(#\+ (setf (token-value r) '1+)
(get-ch) (get-ch) )
(#\- (setf (token-value r) '1-)
(get-ch) (get-ch) )
(t
(setf (token-value r)
(scan-number-or-silly-symbol))))
(setf (token-value r)
(scan-number-or-silly-symbol) )))
((or (lower-case-p last-ch)
(member last-ch '(#\* #\/)) )
(setf (token-value r)
(read-from-string (scan-item
#'id-char-p)) )
;HB; old is (if (eq 'is (token-value r))
;HB; forbidden (setf (token-type r) 'is
;HB; with ".=" (token-value r) nil )
(setf (token-type r) 'constant)
;HB; )
)
((upper-case-p last-ch)
(setf (token-type r) 'variable)
(setf (token-value r)
(read-from-string (scan-item
#'id-char-p)) ))
((char= #\_ last-ch)
(get-ch) ; read the underscore
(cond ((digit-char-p last-ch)
(setf (token-type r)
'variable
(token-value r)
(intern (scan-digits))))
((lower-case-p last-ch)
(setf (token-type r)
'variable
(token-value r)
(read-from-string (scan-item
#'id-char-p ))))
((upper-case-p last-ch)
(signal-error
#'scanner
"Uppercase-letter not allowed after _"))
(t ; anonymous variable
(setf (token-type r)
'variable
(token-value r)
'_ ))))
((char= #\$ last-ch)
(if (id-char-p (get-ch))
(setf (token-type r)
'type
(token-value r)
(scan-type) )
(signal-error #'scanner
(format
nil
"symbol or variable expected" ))))
((char= #\" last-ch)
;;(setf (token-type r) 'string)
(setf (token-type r) 'constant)
(get-ch) ; read opening quote
(setf (token-value r)
(scan-item #'string-constituent-p) )
(if (char= #\" last-ch)
(get-ch) ; read closing quote
(signal-error #'scanner
(format nil
"Expected: \"~%Found: ~C"
last-ch ))))
#|
;; Problems with keywords in the tracer !
((and (char= #\: last-ch)
(lower-case-p (lookahead)) )
;; look for a keyword (e.g. :incl)
(get-ch) ; read the colon
(setf (token-value r)
(read-from-string (stringcat ":"
(scan-item
#'id-char-p )))
(token-type r) 'constant) )
|#
((or (char= #\+ last-ch)
(char= #\- last-ch) )
(cond ((digit-char-p (peek-char
nil the-input-stream
nil eof-char))
;;(setf (token-type r) 'number)
(setf (token-type r)
'constant
(token-value r)
(scan-number-or-silly-symbol) ))
(t
(setf (token-type r) 'constant
(token-value r) (read-from-string
(scan-item
#'id-char-p))))))
((special-char-p last-ch)
;; read in a max. sequence of special-chars
(let* ((str (scan-special-item)) ;HB; see def.
(triple (car (member str
special-tokens
:test #'string=
:key #'car ))))
(if triple
(setf (token-type r) (second triple)
stacked-tokens (third triple) )
(setf (token-type r) 'constant
(token-value r) (read-from-string str) )
)))
((char= eof-char last-ch)
(setf (token-type r) 'empty) )
(t (let ((pair-of-char-and-type
(assoc last-ch terminating-chars) ))
(if pair-of-char-and-type
(progn
(setf (token-type r)
(cdr pair-of-char-and-type))
(get-ch) )
(signal-error #'scanner
(format
nil
"Illegal character: ~C"
last-ch ))))))))
(setq last-token r)
r ))
(continue-after-error ()
;; searches for the next token after a point.
(setq stacked-tokens nil)
(when *comment-style*
(setq last-ch (peek-char nil the-input-stream nil eof-char)) )
(loop
(case (token-type last-token)
(point (return (next-token)))
(empty (return last-token))
(t (next-token)) ))
)
(scanner (&optional (message 'next-token))
(case message
(next-token (next-token))
(last-token last-token)
(last-token-type (token-type last-token))
(last-token-value (token-value last-token))
(comment-p (string/= "" comment))
(comment (prog1 comment (setq comment "")))
(str-of-newlines-p (string/= "" str-of-newlines))
(str-of-newlines (prog1 str-of-newlines
(setq str-of-newlines "") ))
(x-pos x-pos)
(y-pos y-pos)
(last-line last-line)
(act-line act-line)
(last-char last-ch)
(continue-after-error (continue-after-error))
(pos-of-first-non-white (scan-whitespace)
char-pos )
(t (internal-error "unknown message in scanner"
message)) )))
; init variables:
(get-ch)
(next-token)
#'scanner ; value of gen-scanner-from-fct
)))
;; Following two meta-rules to obtain a clear structure:
(defun parse-general-loop ( scanner syntax-rule
first-of-syntax-rule
&optional auto-inst-p )
; Parses a construct of the form:
; syntax-rule {syntax-rule}
; where first(syntax-rule) = first-of-syntax-rule.
; syntax-rule is a parser-function, first-of-syntax-rule is a token-type.
; Returns a list of the results
(do ((l (list (funcall syntax-rule scanner auto-inst-p))
(append l (list (funcall syntax-rule scanner auto-inst-p))) ))
((not-equal first-of-syntax-rule
(funcall scanner 'last-token-type) )
l) ))
(defun parse-general-enumeration (scanner syntax-rule &optional auto-inst-p)
;; Parses a construct of the form:
;; syntax-rule {"," syntax-rule}
;; where syntax-rule is a parser-function.
;; Returns a list of the results.
;; Comments: on entry: yes; on exit: no;
(do* ((list-of-items (clist (look-for-comment)) (append list-of-items (clist item (look-for-comment))))
(item (funcall syntax-rule scanner auto-inst-p)
(funcall syntax-rule scanner auto-inst-p) ))
((not-equal (token-type (funcall scanner 'last-token))
'comma)
(append list-of-items (clist item (look-for-comment))) )
(accept-token-type 'comma) ))
(defun parse-clause (scanner)
;; clause ::= head ( "."
;; \| ":-" [body] "."
;; \| ":#" [body] "."
;;HB; \| ... [body] "."
;; )
;; Comments: on entry: no; on exit: yes.
(let* ((head (parse-head scanner))
(tok-type (funcall scanner 'last-token-type)) )
(funcall scanner) ; accept the symbol between head and body
(case tok-type
;; (point (clist 'hn head )) ; horn-fact
(point (if (atom head)
(clist 'md head) ; include file
(clist 'hn head )) ) ; horn-fact
;HB; (implies (if (eq 'point (funcall scanner 'last-token-type)) ;rule
(cut (prog1 (clist 'hn head (look-for-comment) '\!) ; 'hn-cutfact'
(accept-token-type 'point))) ; has just cut and then point
((implies impliesepsilon) ; as neck, impliesepsilon acts like implies
; with something (cut ampersand) as stacked-tokens
(if (eq 'point (funcall scanner 'last-token-type)) ;rule
;; empty body:
(prog1 (clist 'hn head (look-for-comment)) ; comment after head
(accept-token-type 'point) )
(prog1 (parse-the-body scanner head)
(accept-token-type 'point) )))
(implies-uc (prog1 (parse-the-body scanner head 'uc)
(accept-token-type 'point) ))
(t (signal-error scanner
"Unknown symbol between head and body: "
tok-type )))))
(defun parse-head (scanner)
;; head ::= term [round-list-with-terms]
;; Comments: on entry: no; on exit: yes.
;; (cons (parse-term scanner t) ; t means auto-inst-p
;; (parse-round-list-with-terms scanner) )
(let ((the-term (parse-term scanner t)))
(if (and (eq 'point (funcall scanner 'last-token-type))
(atom the-term) )
;; symbol-fact:
the-term
(cons the-term (parse-round-list-with-terms scanner)) )))
(defun parse-the-body (scanner head &optional (type-of-clause 'hn))
;; On entry: body is non-empty.
;; body ::= [expr+] ( "!" [expr+] ["&" expr]
;; \| "&" expr ["!"]
;; \| "&" "!" expr
;; )
;; Returns a horn- or a footed-rule.
;; Note: PARSE-BODY is a reserved symbol in LUCID-Lisp.
(let (;;(type-of-clause 'hn)
(comment-after-head (look-for-comment))
(body nil) )
(case (funcall scanner 'last-token-type)
;HB; ((cut ampersand))
((cut ampersand impliesepsilon))
(t (setq body (parse-expr+ scanner))) ) ; parse-expr+ accepts comments
(case (funcall scanner 'last-token-type)
(cut (accept-token-type 'cut)
(setq body (append body (clist '\! (look-for-comment))))
(case (funcall scanner 'last-token-type)
((ampersand point))
(t (setq body (append body (parse-expr+ scanner)))) )
(when (eq 'ampersand (funcall scanner 'last-token-type))
(accept-token-type 'ampersand)
(setq body (append body (clist (look-for-comment) (parse-expr scanner) (look-for-comment)))
type-of-clause 'ft )))
(ampersand (accept-token-type 'ampersand)
(setq type-of-clause 'ft )
(if (eq 'cut (funcall scanner 'last-token-type))
(progn ; "&" "!" : ; newsyn
(accept-token-type 'cut)
(setq body (append body
'(\!)
(clist (look-for-comment) (parse-expr scanner) (look-for-comment)))))
(progn
;; "&" :
(setq body (append body
(clist (look-for-comment) (parse-expr scanner) (look-for-comment))))
(when (eq 'cut (funcall scanner 'last-token-type))
(accept-token-type 'cut)
(setq body (append body (clist (look-for-comment) '\! (look-for-comment)))) ))))
(impliesepsilon (accept-token-type 'impliesepsilon) ; as body part,
; impliesepsilon acts like an empty string but accepts
; immediately following stacked-tokens (cut ampersand)
(accept-token-type 'cut)
(accept-token-type 'ampersand)
(setq type-of-clause 'ft )
(setq body (append body
(clist '\! (look-for-comment) (parse-expr scanner) (look-for-comment))))
)
(t) )
(cons type-of-clause
(append (clist head comment-after-head) body) )))
(defun parse-expr (scanner &optional auto-inst-p)
;; expr ::= ( term [ {round-list}
;; | IS expr
;; ]
;; | builtin
;; )
;; Comments: on entry: no; on exit: yes.
;;(declare (ignore auto-inst-p))
(let ((r (parse-term scanner)))
(case (funcall scanner 'last-token-type)
(is (accept-token-type 'is)
(clist 'is (remove-non-var-inst r) (look-for-comment) (parse-expr scanner) (look-for-comment)))
(round-left (let-assoc (parser-fct r *extra-parsing-fcts*)
(parser-fct scanner auto-inst-p)
(construct-application
r
(parse-general-loop scanner
#'parse-round-list
'round-left ))))
(t r) )))
(defun parse-term (scanner &optional auto-inst-p)
;; term ::= ( CONSTANT
;; | VARIABLE
;; | TYPE
;; | sqr-list
;; )
;; [ (":" term) | {sqr-list} ]
;; If auto-inst-p is t then no explicit inst-tag is generated.
;; Needed for terms in the head of a rule and for nested lists.
;; Comments: on entry: no; on exit: yes.
(let ((tok (funcall scanner 'last-token))
(l nil)
(sqr-left-p nil)
)
(case (token-type tok)
(constant (setq l (token-value tok))
(accept-token-type 'constant))
(variable (setq l (parse-variable scanner)))
(type (setq l (token-value tok))
(accept-token-type 'type))
(sqr-left (setq l (cons 'tup (parse-sqr-list scanner t))
sqr-left-p t ))
(t (signal-error scanner
(format nil
(stringcat
"Constant, variable or [ expected."
"~%Found: " ))
tok )))
(case (funcall scanner 'last-token-type)
(sqr-left (setq l (construct-application
l ; comment after sqr-left:
(parse-general-loop scanner
#'parse-sqr-list
'sqr-left
t ) )
sqr-left-p t ))
(colon
(accept-token-type 'colon)
(setq l (mk-typed-expr l (parse-term scanner)))) )
(if (and (not auto-inst-p)
sqr-left-p )
(setq l (list 'inst l)) )
l ))
(defun parse-round-list (scanner &optional auto-inst-p)
;; round-list ::= "(" [expr+] ["|" expr] ")"
;; Comments: on entry: yes; on exit: yes;
(accept-token-type 'round-left)
(let ((l (clist (look-for-comment)))) ; no comment ==> l is empty
(case (funcall scanner 'last-token-type)
(bar )
(round-right )
(t (setq l (append l (parse-expr+ scanner)))) )
(when (eq 'bar (funcall scanner 'last-token-type))
(setq l
(append l
(clist (look-for-comment)
'\|
(progn (accept-token-type 'bar) (look-for-comment))
(parse-expr scanner auto-inst-p)) )))
(accept-token-type 'round-right)
l ))
(defun parse-sqr-list (scanner &optional auto-inst-p)
;; sqr-list ::= "[" [term+] ["|" term] "]"
;; Comments: on entry: yes; on exit: yes;
(accept-token-type 'sqr-left)
(let ((l (clist (look-for-comment)))) ; no comment ==> l is empty
(case (funcall scanner 'last-token-type)
(bar )
(sqr-right )
(t (setq l (append l (parse-term+ scanner auto-inst-p)))) )
(when (eq 'bar (funcall scanner 'last-token-type))
(setq l
(append l
(clist (look-for-comment)
'\|
(progn (accept-token-type 'bar) (look-for-comment))
(parse-term scanner auto-inst-p) ))))
(accept-token-type 'sqr-right)
l ))
(defun parse-round-list-with-terms (scanner)
;; round-list-with-terms ::= "(" [term+] ["|" VARIABLE] ")"
;; Comments: on entry: yes; on exit: yes;
(accept-token-type 'round-left)
(let ((l (clist (look-for-comment)))) ; no comment ==> l is empty
(case (funcall scanner 'last-token-type)
(bar )
(round-right )
(t (setq l (append l (parse-term+ scanner t)))) )
(when (eq 'bar (funcall scanner 'last-token-type))
(setq l (append l
(clist (look-for-comment)
'\|
(progn (accept-token-type 'bar) (look-for-comment))
(parse-variable scanner)) )))
(accept-token-type 'round-right)
l ))
(defun parse-expr+ (scanner &optional auto-inst-p)
;; expr+ ::= expr {"," expr}
;; Returns a list of results.
;; auto-inst-p is not used in this function.
;; Comments: on entry: yes; on exit: no;
(declare (ignore auto-inst-p))
(parse-general-enumeration scanner #'parse-expr) )
(defun parse-term+ (scanner &optional auto-inst-p)
;; term+ ::= term {"," term}
; Returns a list of results.
;; Comments: on entry: yes; on exit: no;
(parse-general-enumeration scanner #'parse-term auto-inst-p) )
(defun parse-variable (scanner)
;; Comments: on entry: yes; on exit: no;
(let ((var-name (funcall scanner 'last-token-value)))
(accept-token-type 'variable)
(if (eq '_ var-name)
'id ; anonymous variable
(list 'vari var-name) )))
(defun parse-goal (scanner)
" goal ::= ('!' expr+ ['&' expr])
| (expr+ ['!' expr*]['&' expr])
Problem on syntax [to do]:
Imagine a batch file containing this two lines:
f(X) !
g(Y)
One goal or two goals ?
The current implementation constructs two goals.
To change this modify the var cont-chars in the function
pro-complete-cmd-p (rfi.lisp).
"
(if (eq 'cut (funcall scanner 'last-token-type))
(progn (accept-token-type 'cut)
(cons '\! (parse-expr+ scanner)) )
(let ((result (parse-expr+ scanner)))
(when (eq 'cut (funcall scanner 'last-token-type))
(setq result (append result '(\!)))
(accept-token-type 'cut)
(unless (eq 'empty (funcall scanner 'last-token-type))
(setq result (append result (parse-expr+ scanner))) ))
(if (eq 'ampersand (funcall scanner 'last-token-type)) ; MP: valued-conjunction top-level
(progn
(accept-token-type 'ampersand)
(setq result (append result (list (parse-expr scanner)))))
(if (> (length result) 1) (setq result (append result '(true)))))
result )))
(defun construct-application (functor list-of-arg-lists)
; Example: functor = f
; list-of-args-lists = ((a b) (x y))
; Result: ((f a b) x y)
(if (null list-of-arg-lists)
functor
(construct-application (cons functor
(car list-of-arg-lists) )
(cdr list-of-arg-lists) )))
(defun remove-non-var-inst (term)
;; removes a possibly existing INST-tag if is not followed by a variable.
(if (and (consp term)
(inst-t term)
(not (vari-t (second term))) )
(second term) ; remove inst
term ))
(defun clist (&rest args)
;; Like list but takes care of comments:
;; If one of the args is a com-cell with empty string-fields
;; then it will be not included in the list.
(let (the-list)
(dolist (element args (reverse the-list))
(unless (and (com-cell-p element)
(or (null *comment-style*)
(and (string= "" (com-cell-pre-txt element))
(string= "" (com-cell-post-txt element))
)))
;; don't ignore the element:
(setq the-list (cons element the-list)) ))))
(defun pro-parse-head (head-as-string)
(with-input-from-string (the-input-stream head-as-string)
(catch :pro-read-error-tag
(parse-head (gen-scanner the-input-stream)) )))
;; -------------------- Exported functions: ---------------------------------
(defun pro-parse-head-functor (head-functor-as-string)
(with-input-from-string (the-input-stream head-functor-as-string)
(catch :pro-read-error-tag
(parse-term (gen-scanner the-input-stream) t) )))
(defun pro-read-data-base (filename)
(with-open-file (the-input-stream filename :direction :input)
(pro-read-data-base-from-stream the-input-stream) ))
(defun pro-read-data-base-from-stream (the-input-stream)
(catch :pro-read-error-tag
(let ((scanner (gen-scanner the-input-stream))
(clause nil)
(data-base nil)
(error-p nil) )
(loop
(if (eq 'empty (funcall scanner 'last-token-type))
(if error-p
(return nil)
(return (reverse data-base)) ))
(setq clause
(catch :pro-read-error-tag
(parse-clause scanner) ))
(cond ((null clause)
;; Error
(setq error-p t)
(rf-terpri)
(rf-princ-like-lisp "Continue reading to find more")
(rf-princ-like-lisp " errors in line ")
(funcall scanner 'continue-after-error)
(rf-princ-like-lisp (token-y-pos (funcall scanner
'last-token )))
(rf-princ-like-lisp ".")
(rf-terpri) )
(error-p
;; Error in previous clause --> don't construct database
)
(t
;; No error
(setq data-base (cons clause data-base)) ))))))
(defun pro-read-clause (str)
(catch :pro-read-error-tag
(with-input-from-string
(the-input-stream str)
(let* ((scanner (gen-scanner the-input-stream))
(clause (parse-clause scanner)) )
(cond ((null clause)
;; Error
nil )
((eq 'empty (funcall scanner 'last-token-type))
;; all of the input is o.k.
(if *comment-style*
(hide-comments clause)
clause ))
(t
(pro-print-error
scanner
(list "Only the first part of the input is correct."))
nil ))))))
(defun pro-read-goal (str)
"Reads a goal from the toplevel. See also function parse-goal. "
(catch :pro-read-error-tag
(with-input-from-string
(the-input-stream str)
(let* ((scanner (gen-scanner the-input-stream))
(goal (parse-goal scanner)) )
(cond ((null goal)
;; Error
nil )
((eq 'empty (funcall scanner 'last-token-type))
;; all of the input is o.k.
(if *comment-style*
(hide-comments goal)
goal ))
((eq 'point (funcall scanner 'last-token-type))
(pro-print-error
scanner
(list "Illegal point at the end of a goal.") )
nil )
(t
(pro-print-error
scanner
(list "Only the first part of the input is correct.") )
nil ))))))
#|
(defun pro-read-goal (str)
; goal ::= expr {"," expr}
(catch :pro-read-error-tag
(with-input-from-string
(the-input-stream str)
(let* ((scanner (gen-scanner the-input-stream))
(goal (parse-general-enumeration scanner #'parse-expr)) )
(cond ((null goal)
;; Error
nil )
((eq 'empty (funcall scanner 'last-token-type))
;; all of the input is o.k.
(if *comment-style*
(hide-comments goal)
goal ))
((eq 'point (funcall scanner 'last-token-type))
(pro-print-error
scanner
(list "Illegal point at the end of a goal.") )
nil )
(t
(pro-print-error
scanner
(list "Only the first part of the input is correct.") )
nil ))))))
|#
(defun pro-split-input (input-line)
; Examples:
;
; (pro-split-input "consult my-base.rfp")
; ==> (consult . "my-base.rfp")
;
; (pro-split-input "[a,b,c]")
; ==> (nil . "")
;
(catch :pro-read-error-tag
(with-input-from-string
(the-input-stream input-line)
(let* ((scanner (gen-scanner the-input-stream))
(tok (funcall scanner 'last-token)) )
(if (and (eq 'constant
(token-type tok) )
(symbolp (token-value tok)) )
(cons (token-value tok)
(subseq input-line
(funcall scanner
'pos-of-first-non-white) ))
(cons nil "") )))))
(defun pro-parse-trace-cmd (str)
;; trace-cmd = { head-functor
;; { :INCL int-list | :EXCL int-list | :STATUS
;; | :PRINT-P symbol | :PRINT-F symbol
;; }}
(catch :pro-read-error-tag
(with-input-from-string
(the-input-stream str)
(do* ((scanner (gen-scanner the-input-stream))
(one-proc nil (pro-parse-trace-cmd-1 scanner))
(result nil (append result one-proc))
)
((eq 'empty (funcall scanner 'last-token-type))
result )
))))
(defun pro-parse-trace-cmd-1 (scanner)
"Parses the description of one procedure to trace."
(do* ((head-functor (parse-term scanner t))
(result (list head-functor) (append result item))
(item)
(abort-p)
(tok-value (funcall scanner 'last-token-value)
(funcall scanner 'last-token-value) )
)
(abort-p result)
(case tok-value
((:incl :excl)
(accept-token-type 'constant)
(setq item (cons tok-value
(pro-parse-trace-cmd-2 scanner) )))
((:print-p :print-f)
(accept-token-type 'constant)
(setq item (list tok-value (parse-term scanner t))) )
(:status
(accept-token-type 'constant)
(setq item (list :status)) )
(t
(setq abort-p t
item nil ) ))))
(defun pro-parse-trace-cmd-2 (scanner)
"parses: ALL | integer {integer}"
(if (eq 'all (funcall scanner 'last-token-value))
(progn (accept-token-type 'constant)
(list 'all) )
(do* ((integers nil (cons tok-value integers))
(tok-value (funcall scanner 'last-token-value)
(funcall scanner 'last-token-value) ))
((not (numberp tok-value))
(reverse integers) )
(accept-token-type 'constant) )))
(pushnew :rf-pro2lisp *features*)
;; ----------------------------------------------------------------------------
;; End of module Pro2Lisp.Lsp
;; ----------------------------------------------------------------------------
;;;### eof