;;; Copyright (c) 2003--2004
;;; John Carroll, Ann Copestake, Robert Malouf, Stephan Oepen;
;;; see `LICENSE' for conditions.
(in-package :mrs)
(defparameter *valid-hook-elements* '("INDEX" "LABEL" "ANCHOR"))
;;; Reading
(defun read-rmrs-grammar (file-name)
;;;
(setf *rule-instructions* nil)
(setf *algebra-rule-instructions* nil)
(clear-rule-record)
(with-open-file (istream file-name :direction :input)
(let* ((*package* (find-package :mrs))
(rules (parse-xml-removing-junk istream)))
(unless (equal (car rules) '|gram|)
(error "~A is not a valid rules file" file-name))
(loop for rule in (cdr rules)
do
(unless (xml-whitespace-string-p rule)
(let ((next-rule (read-rmrs-rule rule)))
(when next-rule
(add-rmrs-rule next-rule)))))))
(setf *rule-instructions*
(nreverse *rule-instructions*))
nil)
(defun add-rmrs-rule (rule)
(let* ((rule-name (rmrs-rule-name rule))
(rule-set (find rule-name *rule-instructions*
:test #'equal :key #'rmrs-rule-set-name)))
(if rule-set
(setf (rmrs-rule-set-alternatives rule-set)
(append (rmrs-rule-set-alternatives rule-set) (list rule)))
(push (make-rmrs-rule-set :name rule-name
:alternatives (list rule))
*rule-instructions*))))
(defun read-rmrs-rule (rule)
;;;
(let ((tag (car rule)))
(if (eq tag '|rule|)
(let ((name nil) (condition nil)
(dtrs nil) (head nil) (head-pos nil)
(semstruct nil) (eqs nil) (inherit nil))
(loop for next-el in (cdr rule)
do
(unless (or (xml-whitespace-string-p next-el)
(not (listp next-el)))
;;; empty comments appear as atoms - just
;;; ignore this
(let*
((next-tag (car next-el))
(tag-content (cdr next-el)))
(ecase next-tag
(|condition| (setf condition (car tag-content)))
(|comment| tag-content)
(|name| (setf name (car tag-content)))
(|dtrs| (setf dtrs (read-rmrs-rule-dtrs tag-content)))
(|inherit| (setf inherit (car tag-content)))
;;; dtrs must appears before equalitiess
(|head| (setf head (car tag-content)))
(|semstruct| (setf semstruct (read-rmrs-semstruct tag-content)))
(|equalities| (push (read-rmrs-rule-eq tag-content dtrs) eqs)))
(when head
(if (string-equal head "RULE")
(setf head-pos -1)
(progn
(setf head-pos (position head dtrs :test #'string-equal))
(unless head-pos
(error
"~%Head ~S is not a member of dtrs ~S in ~A"
head dtrs name))))))))
(if inherit
(make-inherited-rule name dtrs inherit)
(make-rmrs-rule :name name
:condition condition
:dtrs dtrs
:arity (length dtrs)
:head head-pos
:semstruct semstruct
:eqs (nreverse eqs)))))))
(defun make-inherited-rule (name dtrs inherit)
;;; for now, inheritance is from a previously specified rule
;;; this would be trivial, except that we want to allow for
;;; optional daughters, which may involve remapping the pointers
;;; e.g. 1 a rule with D1 OPT D2 D3 inherits from
;;; a rule with OPT D1 D2 OPT OPT D3
;;; then the desired mapping is 0->? 1->0 2->2 3->? 4->? 5->3
;;; e.g., 2 a rule with OPT D1 D2 OPT OPT D3 inherits from
;;; a rule with D1 OPT D2 D3 then
;;; 0->1 1->? 2->2 3->5
(let* ((inherit-struct-set (find inherit *rule-instructions*
:test #'equal :key #'rmrs-rule-set-name))
(inherit-struct (if inherit-struct-set
(car (rmrs-rule-set-alternatives
inherit-struct-set)))))
(unless inherit-struct (error "Undefined rule ~A" inherit))
(let ((inherited-dtrs (rmrs-rule-dtrs inherit-struct))
(number-map nil)
(next-dtr-pos 0)
(next-dtrs dtrs)
(new-eqs nil))
(dotimes (n (length inherited-dtrs))
(push (cons n n) number-map))
(setf number-map (nreverse number-map))
(loop for inherited-dtr in inherited-dtrs and
;;; e.g., OPT D1 D2 OPT OPT D3
mapping in number-map
do
(setf (cdr mapping)
(if (string-equal inherited-dtr "OPT")
nil
(loop (unless next-dtrs (return nil))
(if (string-equal (car next-dtrs) "OPT")
(progn (incf next-dtr-pos)
(pop next-dtrs))
(let ((pos next-dtr-pos))
(progn (incf next-dtr-pos)
(pop next-dtrs)
(return pos))))))))
(setf new-eqs
(loop for eq in (rmrs-rule-eqs inherit-struct)
collect
(make-equality
:eq-els
(loop for eq-el in (equality-eq-els eq)
collect
(if (pointer-p eq-el)
(let ((new-dtr (cdr (assoc (pointer-dtrnum eq-el)
number-map))))
(unless new-dtr
(error "Optional daughter not optional"))
(make-pointer :dtrnum new-dtr
:hook-el (pointer-hook-el eq-el)))
eq-el)))))
(make-rmrs-rule :name name
:dtrs dtrs
:arity (length dtrs)
:head (if (eql (rmrs-rule-head inherit-struct) -1)
-1
(cdr (assoc (rmrs-rule-head inherit-struct)
number-map)))
:semstruct (rmrs-rule-semstruct inherit-struct)
:eqs new-eqs))))
(defun read-rmrs-rule-dtrs (content)
(loop for dtr in content
when (eql (car dtr) '|dtr|)
; unless (string-equal (cadr dtr) "OPT")
collect (cadr dtr)))
(defun read-rmrs-semstruct (content)
;;;
(let ((hook nil) (features nil) (slots nil) (eps nil) (rargs nil) (ings nil)
(h-cons nil))
(loop for next-el in content
do
(unless (xml-whitespace-string-p next-el)
(let*
((next-tag (car next-el))
(tag-content (cdr next-el)))
(if (and (listp next-tag)
(eql (car next-tag) '|hcons|)
(string-equal (third next-tag) "qeq"))
(push (read-rmrs-semstruct-qeq tag-content)
h-cons)
(ecase next-tag
(|features| (setf features (list (car tag-content))))
(|hook| (setf hook (read-rmrs-semstruct-hook tag-content)))
(|slots| (setf slots (read-rmrs-semstruct-slots tag-content)))
(|ep| (push (read-rmrs-semstruct-ep tag-content)
eps))
(|rarg| (push (read-rmrs-semstruct-rarg tag-content)
rargs))
(|ing| (push (read-rmrs-semstruct-in-g tag-content)
ings)))))))
(make-semstruct :hook (or hook (make-default-hook))
:features features
:slots slots
:liszt (nreverse eps)
;; binding-list is constructed when the
;; real semstruct is created
:h-cons (nreverse h-cons)
:rmrs-args (nreverse rargs)
:in-groups (nreverse ings)
:bindings nil)))
(defun read-rmrs-semstruct-hook (content)
;;;
(let ((index nil) (label nil))
(setf index (construct-grammar-var
(read-rmrs-grammar-var (car content))))
(setf label (construct-grammar-var
(read-rmrs-simple '|label| (cadr content))))
(make-indices
:label label
:index index)))
(defun read-rmrs-semstruct-slots (slots)
;;;
(let* ((content (first slots))
(tag (first content))
(body (second content)))
(ecase tag
(|noanchor| :none)
(|anchor| (construct-grammar-var body)))))
(defun read-rmrs-semstruct-ep (content)
;;;
(let ((pred nil) (label nil))
(setf pred (read-rmrs-pred (car content)))
(setf label (read-rmrs-simple '|label| (cadr content)))
(multiple-value-bind (arg extras)
(read-rmrs-grammar-var (caddr content))
(make-rel :pred pred
:handel (construct-grammar-var label)
:flist (list (construct-grammar-var arg extras))))))
(defun read-rmrs-grammar-var (content)
;;;
;;;
(let ((tag (car content))
(body (cdr content)))
(unless (or (eql tag '|var|)
(and (listp tag) (eql (first tag) '|var|))
(eql tag '|index|)
(and (listp tag) (eql (first tag) '|index|)))
(error "Malformed variable ~A" content))
(values (first body)
(if (listp tag)
(construct-rmrs-var-extras (rest tag))))))
;;; read-rmrs-pred is in input.lisp
;;; construct-rmrs-extras is in input.lisp
(defun read-rmrs-semstruct-rarg (content)
;;;
(let ((name nil) (label nil) (arg nil))
(setf name (read-rmrs-simple '|rargname| (car content)))
(setf label (read-rmrs-simple '|label| (cadr content)))
(let* ((argval (caddr content))
(argvaltag (car argval)))
(setf arg
(ecase argvaltag
(|lemma| (make-dummy-constant))
(|constant|
(read-rmrs-simple '|constant| argval))
(|var|
(multiple-value-bind (arg extras)
(read-rmrs-grammar-var argval)
(construct-grammar-var arg extras))))))
(make-rmrs-arg :arg-type name :label (construct-grammar-var label)
:val arg)))
(defun read-rmrs-semstruct-in-g (content)
;;;
(let ((ing-a nil) (ing-b nil))
(setf ing-a (read-rmrs-grammar-var (second (car content))))
(setf ing-b (read-rmrs-grammar-var (second (cadr content))))
(make-in-group :label-a (construct-grammar-var ing-a)
:label-b (construct-grammar-var ing-b))))
(defun read-rmrs-semstruct-qeq (content)
;;;
(let ((hi nil) (lo nil))
(setf hi (read-rmrs-grammar-var (second (car content))))
(setf lo (read-rmrs-grammar-var (second (cadr content))))
(make-hcons :relation "qeq"
:scarg (construct-grammar-var hi)
:outscpd (construct-grammar-var lo))))
(defun read-rmrs-rule-eq (content dtrs)
;;;
;;;
(let ((processed-eqs
(loop for next-el in content
unless (xml-whitespace-string-p next-el)
collect
(let*
((next-tag (car next-el))
(tag-content (cdr next-el)))
(ecase next-tag
(|rv| (construct-grammar-var (car tag-content)))
(|dh| (read-daughter-hook tag-content dtrs)))))))
;; want to end up with a list of either integer+index (representing
;; daughter hooks) or rule variables
(make-equality :eq-els processed-eqs)))
(defun read-daughter-hook (dh dtrs)
;;;
;;; given a dh containing a dtr and a hook-element
;;; and a list of dtrs containing dtr
;;; this returns (integer hook-element) where integer is the position of
;;; dtr on the dtrs
(let*
((dtr (read-rmrs-simple '|dtr| (car dh)))
(hook-element (read-rmrs-simple '|he| (cadr dh)))
(dtr-number (position dtr dtrs :test #'string-equal)))
(if (and dtr-number
(member hook-element *valid-hook-elements* :test #'string-equal))
(make-pointer :dtrnum dtr-number
:hook-el hook-element)
(error "~%Invalid args to read-dtr-hook ~S ~S"
dh dtrs))))
;;; Outputting rules
(defun write-rmrs-rules (filename)
(with-open-file (ostream filename :direction :output
:if-exists :supersede)
(format ostream "~%")
(loop for rule-set in *rule-instructions*
do
(loop for rule in (rmrs-rule-set-alternatives rule-set)
do
(output-rmrs-rule rule ostream)))
(format ostream "~%~%")))
(defun output-rmrs-rule (rule ostream)
(let ((dtrs (rmrs-rule-dtrs rule))
(semstruct (rmrs-rule-semstruct rule))
(head (rmrs-rule-head rule)))
(format ostream "~%")
(format ostream "~%~A" (rmrs-rule-name rule))
(format ostream "~%~{~A~}" dtrs)
(when head
(if (eql head -1)
(format ostream "~%RULE")
(format ostream "~%~A" (elt dtrs head))))
(when semstruct
(output-rmrs-semstruct semstruct ostream))
(loop for eq in (rmrs-rule-eqs rule)
do
(output-rmrs-eq eq dtrs ostream))
(format ostream "~%~%")))
(defun output-rmrs-semstruct (semstruct ostream)
;;;
(format ostream "~%")
(internal-output-rmrs semstruct 'gramxml ostream)
(format ostream "~%"))
(defun output-rmrs-eq (eq dtrs ostream)
;;;
;;;
;;;
;;;
(format ostream "~%")
(loop for eq-el in (equality-eq-els eq)
do
(if (pointer-p eq-el)
(format ostream "~A~A"
(elt dtrs (pointer-dtrnum eq-el))
(pointer-hook-el eq-el))
(format ostream "~A" (var-id eq-el))))
(format ostream ""))