Added :semantic-comment and :vector-construct.

This commit is contained in:
waldemar%netscape.com 2001-02-07 05:07:05 +00:00
Родитель ddf1704526
Коммит 17b22162c1
4 изменённых файлов: 7 добавлений и 1490 удалений

Просмотреть файл

@ -1,678 +0,0 @@
;;; The contents of this file are subject to the Mozilla Public
;;; License Version 1.1 (the "License"); you may not use this file
;;; except in compliance with the License. You may obtain a copy of
;;; the License at http://www.mozilla.org/MPL/
;;;
;;; Software distributed under the License is distributed on an "AS
;;; IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
;;; implied. See the License for the specific language governing
;;; rights and limitations under the License.
;;;
;;; The Original Code is the Language Design and Prototyping Environment.
;;;
;;; The Initial Developer of the Original Code is Netscape Communications
;;; Corporation. Portions created by Netscape Communications Corporation are
;;; Copyright (C) 1999 Netscape Communications Corporation. All
;;; Rights Reserved.
;;;
;;; Contributor(s): Waldemar Horwat <waldemar@acm.org>
;;;
;;; HTML output generator
;;;
;;; Waldemar Horwat (waldemar@acm.org)
;;;
;;; ------------------------------------------------------------------------------------------------------
;;; ELEMENTS
(defstruct (html-element (:constructor make-html-element (name self-closing indent
newlines-before newlines-begin newlines-end newlines-after))
(:predicate html-element?))
(name nil :type symbol :read-only t) ;Name of the tag
(self-closing nil :type bool :read-only t) ;True if the closing tag should be omitted
(indent nil :type integer :read-only t) ;Number of spaces by which to indent this tag's contents in HTML source
(newlines-before nil :type integer :read-only t) ;Number of HTML source newlines preceding the opening tag
(newlines-begin nil :type integer :read-only t) ;Number of HTML source newlines immediately following the opening tag
(newlines-end nil :type integer :read-only t) ;Number of HTML source newlines immediately preceding the closing tag
(newlines-after nil :type integer :read-only t)) ;Number of HTML source newlines following the closing tag
; Define symbol to refer to the given html-element.
(defun define-html (symbol newlines-before newlines-begin newlines-end newlines-after &key self-closing (indent 0))
(setf (get symbol 'html-element) (make-html-element symbol self-closing indent
newlines-before newlines-begin newlines-end newlines-after)))
;;; ------------------------------------------------------------------------------------------------------
;;; ELEMENT DEFINITIONS
(define-html 'a 0 0 0 0)
(define-html 'b 0 0 0 0)
(define-html 'blockquote 1 0 0 1 :indent 2)
(define-html 'body 1 1 1 1)
(define-html 'br 0 0 0 1 :self-closing t)
(define-html 'code 0 0 0 0)
(define-html 'dd 1 0 0 1 :indent 2)
(define-html 'del 0 0 0 0)
(define-html 'div 1 0 0 1 :indent 2)
(define-html 'dl 1 0 0 2 :indent 2)
(define-html 'dt 1 0 0 1 :indent 2)
(define-html 'em 0 0 0 0)
(define-html 'h1 2 0 0 2 :indent 2)
(define-html 'h2 2 0 0 2 :indent 2)
(define-html 'h3 2 0 0 2 :indent 2)
(define-html 'h4 1 0 0 2 :indent 2)
(define-html 'h5 1 0 0 2 :indent 2)
(define-html 'h6 1 0 0 2 :indent 2)
(define-html 'head 1 1 1 2)
(define-html 'hr 1 0 0 1 :self-closing t)
(define-html 'html 0 1 1 1)
(define-html 'i 0 0 0 0)
(define-html 'li 1 0 0 1 :indent 2)
(define-html 'link 1 0 0 1 :self-closing t)
(define-html 'ol 1 1 1 2 :indent 2)
(define-html 'p 1 0 0 2)
(define-html 'script 0 0 0 0)
(define-html 'span 0 0 0 0)
(define-html 'strong 0 0 0 0)
(define-html 'sub 0 0 0 0)
(define-html 'sup 0 0 0 0)
(define-html 'table 1 1 1 2)
(define-html 'td 1 0 0 1 :indent 2)
(define-html 'th 1 0 0 1 :indent 2)
(define-html 'title 1 0 0 1)
(define-html 'tr 1 0 0 1 :indent 2)
(define-html 'u 0 0 0 0)
(define-html 'ul 1 1 1 2 :indent 2)
(define-html 'var 0 0 0 0)
;;; ------------------------------------------------------------------------------------------------------
;;; ATTRIBUTES
;;; The following element attributes require their values to always be in quotes.
(dolist (attribute '(alt href name))
(setf (get attribute 'quoted-attribute) t))
;;; ------------------------------------------------------------------------------------------------------
;;; ENTITIES
(defvar *html-entities-list*
'((#\& . "amp")
(#\" . "quot")
(#\< . "lt")
(#\> . "gt")
(nbsp . "nbsp")))
(defvar *html-entities-hash* (make-hash-table))
(dolist (entity-binding *html-entities-list*)
(setf (gethash (first entity-binding) *html-entities-hash*) (rest entity-binding)))
; Return a freshly consed list of <html-source> that represent the characters in the string except that
; '&', '<', and '>' are replaced by their entities and spaces are replaced by the entity
; given by the space parameter (which should be either 'space or 'nbsp).
(defun escape-html-characters (string space)
(let ((html-sources nil))
(labels
((escape-remainder (start)
(let ((i (position-if #'(lambda (char) (member char '(#\& #\< #\> #\space))) string :start start)))
(if i
(let ((char (char string i)))
(unless (= i start)
(push (subseq string start i) html-sources))
(push (if (eql char #\space) space char) html-sources)
(escape-remainder (1+ i)))
(push (if (zerop start) string (subseq string start)) html-sources)))))
(unless (zerop (length string))
(escape-remainder 0))
(nreverse html-sources))))
; Escape all content strings in the html-source, while interpreting :nowrap, :wrap, and :none pseudo-tags.
; Return a freshly consed list of html-sources.
(defun escape-html-source (html-source space)
(cond
((stringp html-source)
(escape-html-characters html-source space))
((or (characterp html-source) (symbolp html-source) (integerp html-source))
(list html-source))
((consp html-source)
(let ((tag (first html-source))
(contents (rest html-source)))
(case tag
(:none (mapcan #'(lambda (html-source) (escape-html-source html-source space)) contents))
(:nowrap (mapcan #'(lambda (html-source) (escape-html-source html-source 'nbsp)) contents))
(:wrap (mapcan #'(lambda (html-source) (escape-html-source html-source 'space)) contents))
(t (list (cons tag
(mapcan #'(lambda (html-source) (escape-html-source html-source space)) contents)))))))
(t (error "Bad html-source: ~S" html-source))))
; Escape all content strings in the html-source, while interpreting :nowrap, :wrap, and :none pseudo-tags.
(defun escape-html (html-source)
(let ((results (escape-html-source html-source 'space)))
(assert-true (= (length results) 1))
(first results)))
;;; ------------------------------------------------------------------------------------------------------
;;; HTML WRITER
;; <html-source> has one of the following formats:
;; <string> ;String to be printed literally
;; <symbol> ;Named entity
;; <integer> ;Numbered entity
;; space ;Space or newline
;; (<tag> <html-source> ... <html-source>) ;Tag and its contents
;; ((:nest <tag> ... <tag>) <html-source> ... <html-source>) ;Equivalent to (<tag> (... (<tag> <html-source> ... <html-source>)))
;;
;; <tag> has one of the following formats:
;; <symbol> ;Tag with no attributes
;; (<symbol> <attribute> ... <attribute>) ;Tag with attributes
;; :nowrap ;Pseudo-tag indicating that spaces in contents should be non-breaking
;; :wrap ;Pseudo-tag indicating that spaces in contents should be breaking
;; :none ;Pseudo-tag indicating no tag -- the contents should be inlined
;;
;; <attribute> has one of the following formats:
;; (<symbol> <string>) ;Attribute name and value
;; (<symbol>) ;Attribute name with omitted value
(defparameter *html-right-margin* 100)
(defparameter *allow-line-breaks-in-tags* nil) ;Allow line breaks in tags between attributes?
(defvar *current-html-pos*) ;Number of characters written to the current line of the stream; nil if *current-html-newlines* is nonzero
(defvar *current-html-pending*) ;String following a space or newline pending to be printed on the current line or nil if none
(defvar *current-html-indent*) ;Indent to use for emit-html-newlines-and-indent calls
(defvar *current-html-newlines*) ;Number of consecutive newlines just written to the stream; zero if last character wasn't a newline
; Flush *current-html-pending* onto the stream.
(defun flush-current-html-pending (stream)
(when *current-html-pending*
(unless (zerop (length *current-html-pending*))
(write-char #\space stream)
(write-string *current-html-pending* stream)
(incf *current-html-pos* (1+ (length *current-html-pending*))))
(setq *current-html-pending* nil)))
; Emit n-newlines onto the stream and indent the next line by *current-html-indent* spaces.
(defun emit-html-newlines-and-indent (stream n-newlines)
(decf n-newlines *current-html-newlines*)
(when (plusp n-newlines)
(flush-current-html-pending stream)
(dotimes (i n-newlines)
(write-char #\newline stream))
(incf *current-html-newlines* n-newlines)
(setq *current-html-pos* nil)))
; Write the string to the stream, observing *current-html-pending* and *current-html-pos*.
(defun write-html-string (stream html-string)
(unless (zerop (length html-string))
(unless *current-html-pos*
(setq *current-html-newlines* 0)
(write-string (make-string *current-html-indent* :initial-element #\space) stream)
(setq *current-html-pos* *current-html-indent*))
(if *current-html-pending*
(progn
(setq *current-html-pending* (if (zerop (length *current-html-pending*))
html-string
(concatenate 'string *current-html-pending* html-string)))
(when (>= (+ *current-html-pos* (length *current-html-pending*)) *html-right-margin*)
(write-char #\newline stream)
(write-string *current-html-pending* stream)
(setq *current-html-pos* (length *current-html-pending*))
(setq *current-html-pending* nil)))
(progn
(write-string html-string stream)
(incf *current-html-pos* (length html-string))))))
; Return true if the value string contains a character that would require an attribute to be quoted.
; For convenience, this returns true if value contains a period, even though strictly speaking periods do
; not force quoting.
(defun attribute-value-needs-quotes (value)
(dotimes (i (length value) nil)
(let ((ch (char value i)))
(unless (or (char<= #\0 ch #\9) (char<= #\A ch #\Z) (char<= #\a ch #\z) (char= ch #\-))
(return t)))))
; Emit the html tag with the given tag-symbol (name), attributes, and contents.
(defun write-html-tag (stream tag-symbol attributes contents)
(let ((element (assert-non-null (get tag-symbol 'html-element))))
(emit-html-newlines-and-indent stream (html-element-newlines-before element))
(write-html-string stream (format nil "<~A" (html-element-name element)))
(let ((*current-html-indent* (+ *current-html-indent* (html-element-indent element))))
(dolist (attribute attributes)
(let ((name (first attribute))
(value (second attribute)))
(write-html-source stream (if *allow-line-breaks-in-tags* 'space #\space))
(write-html-string stream (string-downcase (symbol-name name)))
(when value
(write-html-string
stream
(format nil
(if (or (attribute-value-needs-quotes value)
(get name 'quoted-attribute))
"=\"~A\""
"=~A")
value)))))
(write-html-string stream ">")
(emit-html-newlines-and-indent stream (html-element-newlines-begin element))
(dolist (html-source contents)
(write-html-source stream html-source)))
(unless (html-element-self-closing element)
(emit-html-newlines-and-indent stream (html-element-newlines-end element))
(write-html-string stream (format nil "</~A>" (html-element-name element))))
(emit-html-newlines-and-indent stream (html-element-newlines-after element))))
; Write html-source to the character stream.
(defun write-html-source (stream html-source)
(cond
((stringp html-source)
(write-html-string stream html-source))
((eq html-source 'space)
(when (zerop *current-html-newlines*)
(flush-current-html-pending stream)
(setq *current-html-pending* "")))
((or (characterp html-source) (symbolp html-source))
(let ((entity-name (gethash html-source *html-entities-hash*)))
(cond
(entity-name
(write-html-string stream (format nil "&~A;" entity-name)))
((characterp html-source)
(write-html-string stream (string html-source)))
(t (error "Bad html-source ~S" html-source)))))
((integerp html-source)
(assert-true (and (>= html-source 0) (< html-source 65536)))
(write-html-string stream (format nil "&#~D;" html-source)))
((consp html-source)
(let ((tag (first html-source))
(contents (rest html-source)))
(if (consp tag)
(write-html-tag stream (first tag) (rest tag) contents)
(write-html-tag stream tag nil contents))))
(t (error "Bad html-source: ~S" html-source))))
; Write the top-level html-source to the character stream.
(defun write-html (html-source &optional (stream t))
(with-standard-io-syntax
(let ((*print-readably* nil)
(*print-escape* nil)
(*print-case* :upcase)
(*current-html-pos* nil)
(*current-html-pending* nil)
(*current-html-indent* 0)
(*current-html-newlines* 9999))
(write-string "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">" stream)
(write-char #\newline stream)
(write-html-source stream (escape-html html-source)))))
; Write html to the text file with the given name (relative to the
; local directory).
(defun write-html-to-local-file (filename html)
(with-open-file (stream (filename-to-semantic-engine-pathname filename)
:direction :output
:if-exists :supersede
#+mcl :mac-file-creator #+mcl "MOSS")
(write-html html stream)))
; Expand the :nest constructs inside html-source.
(defun unnest-html-source (html-source)
(labels
((unnest-tags (tags contents)
(assert-true tags)
(cons (first tags)
(if (endp (rest tags))
contents
(list (unnest-tags (rest tags) contents))))))
(if (consp html-source)
(let ((tag (first html-source))
(contents (rest html-source)))
(if (and (consp tag) (eq (first tag) ':nest))
(unnest-html-source (unnest-tags (rest tag) contents))
(cons tag (mapcar #'unnest-html-source contents))))
html-source)))
; Coalesce an A element immediately containing or contained in a SPAN element into one if their attributes
; are disjoint. Also coalesce SUB and SUP elements immediately containing SPAN elements into one.
(defun coalesce-elements (html-source)
(if (consp html-source)
(let ((tag (first html-source))
(contents (mapcar #'coalesce-elements (rest html-source))))
(cond
((and (consp tag)
(member (first tag) '(a span))
contents
(null (cdr contents))
(consp (car contents))
(let ((tag2 (caar contents)))
(and (consp tag2)
(member (first tag2) '(a span))
(not (eq tag tag2))
(null (intersection (rest tag) (rest tag2) :key #'car)))))
(cons
(cons 'a
(if (eq (first tag) 'a)
(append (rest tag) (rest (caar contents)))
(append (rest (caar contents)) (rest tag))))
(cdar contents)))
((and (member tag '(sub sup))
contents
(null (cdr contents))
(consp (car contents))
(consp (caar contents))
(eq (caaar contents) 'span))
(cons
(cons tag (rest (caar contents)))
(cdar contents)))
(t (cons tag contents))))
html-source))
;;; ------------------------------------------------------------------------------------------------------
;;; HTML MAPPINGS
(defparameter *html-definitions*
'(((:new-line t) (br))
;Misc.
(:spc nbsp)
(:tab2 nbsp nbsp)
(:tab3 nbsp nbsp nbsp)
(:nbhy "-") ;Non-breaking hyphen
;Symbols (-10 suffix means 10-point, etc.)
((:bullet 1) (:script "document.write(U_bull)")) ;#x2022
((:minus 1) "-")
((:not-equal 1) (:script "document.write(U_ne)")) ;#x2260
((:less-or-equal 1) (:script "document.write(U_le)")) ;#x2264
((:greater-or-equal 1) (:script "document.write(U_ge)")) ;#x2265
((:infinity 1) (:script "document.write(U_infin)")) ;#x221E
((:left-single-quote 1) #x2018)
((:right-single-quote 1) #x2019)
((:left-double-quote 1) #x201C)
((:right-double-quote 1) #x201D)
((:left-angle-quote 1) #x00AB)
((:right-angle-quote 1) #x00BB)
((:bottom-10 1) (:script "document.write(U_perp)")) ;#x22A5
((:vector-assign-10 1) (:script "document.write(U_larr)")) ;#x2190
((:up-arrow-10 1) (:script "document.write(U_uarr)")) ;#x2191
((:function-arrow-10 2) (:script "document.write(U_rarr)")) ;#x2192
((:cartesian-product-10 2) (:script "document.write(U_times)")) ;#x00D7
((:identical-10 2) (:script "document.write(U_equiv)")) ;#x2261
((:circle-plus-10 2) (:script "document.write(U_oplus)")) ;#x2295
((:empty-10 2) (:script "document.write(U_empty)")) ;#x2205
((:intersection-10 1) (:script "document.write(U_cap)")) ;#x2229
((:union-10 1) (:script "document.write(U_cup)")) ;#x222A
((:member-10 2) (:script "document.write(U_isin)")) ;#x2208
((:not-member-10 2) (:script "document.write(U_notin)")) ;#x2209
((:derives-10 2) (:script "document.write(U_rArr)")) ;#x21D2
((:left-triangle-bracket-10 1) (:script "document.write(U_lang)")) ;#x2329
((:right-triangle-bracket-10 1) (:script "document.write(U_rang)")) ;#x232A
((:alpha 1) (:script "document.write(U_alpha)"))
((:beta 1) (:script "document.write(U_beta)"))
((:chi 1) (:script "document.write(U_chi)"))
((:delta 1) (:script "document.write(U_delta)"))
((:epsilon 1) (:script "document.write(U_epsilon)"))
((:phi 1) (:script "document.write(U_phi)"))
((:gamma 1) (:script "document.write(U_gamma)"))
((:eta 1) (:script "document.write(U_eta)"))
((:iota 1) (:script "document.write(U_iota)"))
((:kappa 1) (:script "document.write(U_kappa)"))
((:lambda 1) (:script "document.write(U_lambda)"))
((:mu 1) (:script "document.write(U_mu)"))
((:nu 1) (:script "document.write(U_nu)"))
((:omicron 1) (:script "document.write(U_omicron)"))
((:pi 1) (:script "document.write(U_pi)"))
((:theta 1) (:script "document.write(U_theta)"))
((:rho 1) (:script "document.write(U_rho)"))
((:sigma 1) (:script "document.write(U_sigma)"))
((:tau 1) (:script "document.write(U_tau)"))
((:upsilon 1) (:script "document.write(U_upsilon)"))
((:omega 1) (:script "document.write(U_omega)"))
((:xi 1) (:script "document.write(U_xi)"))
((:psi 1) (:script "document.write(U_psi)"))
((:zeta 1) (:script "document.write(U_zeta)"))
;Block Styles
(:js2 (div (class "js2")))
(:es4 (div (class "es4")))
(:body-text p)
(:section-heading h2)
(:subsection-heading h3)
(:grammar-header h4)
(:grammar-rule (:nest :nowrap (div (class "grammar-rule"))))
(:grammar-lhs (:nest :nowrap (div (class "grammar-lhs"))))
(:grammar-lhs-last :grammar-lhs)
(:grammar-rhs (:nest :nowrap (div (class "grammar-rhs"))))
(:grammar-rhs-last :grammar-rhs)
(:grammar-argument (:nest :nowrap (div (class "grammar-argument"))))
(:semantics (:nest :nowrap (p (class "semantics"))))
(:semantics-next (:nest :nowrap (p (class "semantics-next"))))
;Inline Styles
(:script (script (type "text/javascript")))
(:symbol (span (class "symbol")))
(:character-literal code)
(:character-literal-control (span (class "control")))
(:terminal (span (class "terminal")))
(:terminal-keyword (code (class "terminal-keyword")))
(:nonterminal (span (class "nonterminal")))
(:nonterminal-attribute (span (class "nonterminal-attribute")))
(:nonterminal-argument (span (class "nonterminal-argument")))
(:semantic-keyword (span (class "semantic-keyword")))
(:type-expression (span (class "type-expression")))
(:type-name (span (class "type-name")))
(:field-name (span (class "field-name")))
(:global-variable (span (class "global-variable")))
(:local-variable (span (class "local-variable")))
(:action-name (span (class "action-name")))
(:text :wrap)
;Specials
(:invisible del)
((:but-not 6) (b "except"))
((:begin-negative-lookahead 13) "[lookahead" :not-member-10 "{")
((:end-negative-lookahead 2) "}]")
((:line-break 12) "[line" nbsp "break]")
((:no-line-break 15) "[no" nbsp "line" nbsp "break]")
(:subscript sub)
(:superscript sup)
(:plain-subscript :subscript)
((:action-begin 1) "[")
((:action-end 1) "]")
((:vector-begin 1) (b "["))
((:vector-end 1) (b "]"))
((:empty-vector 2) (b "[]"))
((:vector-append 2) :circle-plus-10)
((:tuple-begin 1) (b :left-triangle-bracket-10))
((:tuple-end 1) (b :right-triangle-bracket-10))
((:true 4) (:global-variable "true"))
((:false 5) (:global-variable "false"))
((:unique 6) (:semantic-keyword "unique"))
))
;;; ------------------------------------------------------------------------------------------------------
;;; HTML STREAMS
(defstruct (html-stream (:include markup-stream)
(:constructor allocate-html-stream (env head tail level logical-position enclosing-styles anchors))
(:copier nil)
(:predicate html-stream?))
(enclosing-styles nil :type list :read-only t) ;A list of enclosing styles
(anchors nil :type list :read-only t)) ;A mutable cons cell for accumulating anchors at the beginning of a paragraph
; ;or nil if not inside a paragraph.
(defmethod print-object ((html-stream html-stream) stream)
(print-unreadable-object (html-stream stream :identity t)
(write-string "html-stream" stream)))
; Make a new, empty, open html-stream with the given definitions for its markup-env.
(defun make-html-stream (markup-env level logical-position enclosing-styles anchors)
(let ((head (list nil)))
(allocate-html-stream markup-env head head level logical-position enclosing-styles anchors)))
; Make a new, empty, open, top-level html-stream with the given definitions
; for its markup-env. If links is true, allow links.
(defun make-top-level-html-stream (html-definitions links)
(let ((head (list nil))
(markup-env (make-markup-env links)))
(markup-env-define-alist markup-env html-definitions)
(allocate-html-stream markup-env head head *markup-stream-top-level* nil nil nil)))
; Return the approximate width of the html item; return t if it is a line break.
; Also allow html tags as long as they do not contain line breaks.
(defmethod markup-group-width ((html-stream html-stream) item)
(if (consp item)
(reduce #'+ (rest item) :key #'(lambda (subitem) (markup-group-width html-stream subitem)))
(markup-width html-stream item)))
; Create a top-level html-stream and call emitter to emit its contents.
; emitter takes one argument -- an html-stream to which it should emit paragraphs.
; Return the top-level html-stream. If links is true, allow links.
(defun depict-html-top-level (title links emitter)
(let ((html-stream (make-top-level-html-stream *html-definitions* links)))
(markup-stream-append1 html-stream 'html)
(depict-block-style (html-stream 'head)
(depict-block-style (html-stream 'title)
(markup-stream-append1 html-stream title))
(markup-stream-append1 html-stream '((link (rel "stylesheet") (href "../styles.css"))))
(markup-stream-append1 html-stream '((script (type "text/javascript") (language "JavaScript1.2") (src "../unicodeCompatibility.js")))))
(depict-block-style (html-stream 'body)
(funcall emitter html-stream))
(let ((links (markup-env-links (html-stream-env html-stream))))
(warn-missing-links links))
html-stream))
; Create a top-level html-stream and call emitter to emit its contents.
; emitter takes one argument -- an html-stream to which it should emit paragraphs.
; Write the resulting html to the text file with the given name (relative to the
; local directory).
; If links is true, allow links. If external-link-base is also provided, emit links for
; predefined items and assume that they are located on the page specified by the
; external-link-base string.
(defun depict-html-to-local-file (filename title links emitter &key external-link-base)
(let* ((*external-link-base* external-link-base)
(top-html-stream (depict-html-top-level title links emitter)))
(write-html-to-local-file filename (markup-stream-output top-html-stream)))
filename)
; Return the markup accumulated in the markup-stream after expanding all of its macros.
; The markup-stream is closed after this function is called.
(defmethod markup-stream-output ((html-stream html-stream))
(coalesce-elements
(unnest-html-source
(markup-env-expand (markup-stream-env html-stream) (markup-stream-unexpanded-output html-stream) '(:none :nowrap :wrap :nest)))))
(defmethod depict-block-style-f ((html-stream html-stream) block-style flatten emitter)
(assert-true (<= (markup-stream-level html-stream) *markup-stream-paragraph-level*))
(assert-true (symbolp block-style))
(if (or (null block-style)
(and flatten (member block-style (html-stream-enclosing-styles html-stream))))
(funcall emitter html-stream)
(let ((inner-html-stream (make-html-stream (markup-stream-env html-stream)
*markup-stream-paragraph-level*
nil
(cons block-style (html-stream-enclosing-styles html-stream))
nil)))
(markup-stream-append1 inner-html-stream block-style)
(prog1
(funcall emitter inner-html-stream)
(let ((inner-output (markup-stream-unexpanded-output inner-html-stream)))
(when (or (not flatten) (cdr inner-output))
(markup-stream-append1 html-stream inner-output)))))))
(defmethod depict-paragraph-f ((html-stream html-stream) paragraph-style emitter)
(assert-true (= (markup-stream-level html-stream) *markup-stream-paragraph-level*))
(assert-true (and paragraph-style (symbolp paragraph-style)))
(let* ((anchors (list 'anchors))
(inner-html-stream (make-html-stream (markup-stream-env html-stream)
*markup-stream-content-level*
(make-logical-position)
(cons paragraph-style (html-stream-enclosing-styles html-stream))
anchors)))
(prog1
(funcall emitter inner-html-stream)
(markup-stream-append1 html-stream (cons paragraph-style
(nreconc (cdr anchors)
(markup-stream-unexpanded-output inner-html-stream)))))))
(defmethod depict-char-style-f ((html-stream html-stream) char-style emitter)
(assert-true (>= (markup-stream-level html-stream) *markup-stream-content-level*))
(assert-true (and char-style (symbolp char-style)))
(let ((inner-html-stream (make-html-stream (markup-stream-env html-stream)
*markup-stream-content-level*
(markup-stream-logical-position html-stream)
(cons char-style (html-stream-enclosing-styles html-stream))
(html-stream-anchors html-stream))))
(markup-stream-append1 inner-html-stream char-style)
(prog1
(funcall emitter inner-html-stream)
(markup-stream-append1 html-stream (markup-stream-unexpanded-output inner-html-stream)))))
(defmethod ensure-no-enclosing-style ((html-stream html-stream) style)
(when (member style (html-stream-enclosing-styles html-stream))
(cerror "Ignore" "Style ~S should not be in effect" style)))
(defmethod depict-anchor ((html-stream html-stream) link-prefix link-name duplicate)
(assert-true (= (markup-stream-level html-stream) *markup-stream-content-level*))
(let* ((links (markup-env-links (html-stream-env html-stream)))
(name (record-link-definition links link-prefix link-name duplicate)))
(when name
(push (list (list 'a (list 'name name))) (cdr (html-stream-anchors html-stream))))))
(defmethod depict-link-reference-f ((html-stream html-stream) link-prefix link-name external emitter)
(assert-true (= (markup-stream-level html-stream) *markup-stream-content-level*))
(let* ((links (markup-env-links (html-stream-env html-stream)))
(href (record-link-reference links link-prefix link-name external)))
(if href
(let ((inner-html-stream (make-html-stream (markup-stream-env html-stream)
*markup-stream-content-level*
(markup-stream-logical-position html-stream)
(html-stream-enclosing-styles html-stream)
(html-stream-anchors html-stream))))
(markup-stream-append1 inner-html-stream (list 'a (list 'href href)))
(prog1
(funcall emitter inner-html-stream)
(markup-stream-append1 html-stream (markup-stream-unexpanded-output inner-html-stream))))
(funcall emitter html-stream))))
#|
(write-html
'(html
(head
(:nowrap (title "This is my title!<>")))
((body (atr1 "abc") (beta) (qq))
"My page this is " (br) (p))))
|#

Просмотреть файл

@ -1,812 +0,0 @@
;;; The contents of this file are subject to the Mozilla Public
;;; License Version 1.1 (the "License"); you may not use this file
;;; except in compliance with the License. You may obtain a copy of
;;; the License at http://www.mozilla.org/MPL/
;;;
;;; Software distributed under the License is distributed on an "AS
;;; IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
;;; implied. See the License for the specific language governing
;;; rights and limitations under the License.
;;;
;;; The Original Code is the Language Design and Prototyping Environment.
;;;
;;; The Initial Developer of the Original Code is Netscape Communications
;;; Corporation. Portions created by Netscape Communications Corporation are
;;; Copyright (C) 1999 Netscape Communications Corporation. All
;;; Rights Reserved.
;;;
;;; Contributor(s): Waldemar Horwat <waldemar@acm.org>
;;;
;;; RTF reader and writer
;;;
;;; Waldemar Horwat (waldemar@acm.org)
;;;
(defvar *rtf-author* "Waldemar Horwat")
(defvar *rtf-company* "Netscape")
;;; 1440 twips/inch
;;; 20 twips/pt
(defparameter *rtf-definitions*
'((:rtf-intro rtf 1 mac ansicpg 10000 uc 1 deff 0 deflang 2057 deflangfe 2057)
;Fonts
((+ :rtf-intro) :fonttbl)
(:fonttbl (fonttbl :fonts))
(:times f 0)
((+ :fonts) (:times froman fcharset 256 fprq 2 (* panose "02020603050405020304") "Times New Roman;"))
(:symbol f 3)
((+ :fonts) (:symbol ftech fcharset 2 fprq 2 "Symbol;"))
(:helvetica f 4)
((+ :fonts) (:helvetica fnil fcharset 256 fprq 2 "Helvetica;"))
(:courier f 5)
((+ :fonts) (:courier fmodern fcharset 256 fprq 2 "Courier New;"))
(:palatino f 6)
((+ :fonts) (:palatino fnil fcharset 256 fprq 2 "Palatino;"))
(:zapf-chancery f 7)
((+ :fonts) (:zapf-chancery fscript fcharset 256 fprq 2 "Zapf Chancery;"))
(:zapf-dingbats f 8)
((+ :fonts) (:zapf-dingbats ftech fcharset 2 fprq 2 "Zapf Dingbats;"))
;Color table
((+ :rtf-intro) :colortbl)
(:colortbl (colortbl ";" ;0
red 0 green 0 blue 0 ";" ;1
red 0 green 0 blue 255 ";" ;2
red 0 green 255 blue 255 ";" ;3
red 0 green 255 blue 0 ";" ;4
red 255 green 0 blue 255 ";" ;5
red 255 green 0 blue 0 ";" ;6
red 255 green 255 blue 0 ";" ;7
red 255 green 255 blue 255 ";" ;8
red 0 green 0 blue 128 ";" ;9
red 0 green 128 blue 128 ";" ;10
red 0 green 128 blue 0 ";" ;11
red 128 green 0 blue 128 ";" ;12
red 128 green 0 blue 0 ";" ;13
red 128 green 128 blue 0 ";" ;14
red 128 green 128 blue 128 ";" ;15
red 192 green 192 blue 192 ";")) ;16
(:black cf 1)
(:blue cf 2)
(:turquoise cf 3)
(:bright-green cf 4)
(:pink cf 5)
(:red cf 6)
(:yellow cf 7)
(:white cf 8)
(:dark-blue cf 9)
(:teal cf 10)
(:green cf 11)
(:violet cf 12)
(:dark-red cf 13)
(:dark-yellow cf 14)
(:gray-50 cf 15)
(:gray-25 cf 16)
;Misc.
(:spc " ")
(:tab2 tab)
(:tab3 tab)
(:nbhy _) ;Non-breaking hyphen
(:8-pt fs 16)
(:9-pt fs 18)
(:10-pt fs 20)
(:12-pt fs 24)
(:no-language lang 1024)
(:english-us lang 1033)
(:english-uk lang 2057)
(:english :english-us)
(:reset-section sectd)
(:new-section sect)
(:reset-paragraph pard plain)
((:new-paragraph t) par)
((:new-line t) line)
;Symbols (-10 suffix means 10-point, etc.)
((:bullet 1) bullet)
((:minus 1) endash)
((:not-equal 1) u 8800 \' 173)
((:less-or-equal 1) u 8804 \' 178)
((:greater-or-equal 1) u 8805 \' 179)
((:infinity 1) u 8734 \' 176)
((:left-single-quote 1) lquote)
((:right-single-quote 1) rquote)
((:left-double-quote 1) ldblquote)
((:right-double-quote 1) rdblquote)
((:left-angle-quote 1) u 171 \' 199)
((:right-angle-quote 1) u 187 \' 200)
((:bottom-10 1) (field (* fldinst "SYMBOL 94 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:vector-assign-10 2) (field (* fldinst "SYMBOL 172 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:up-arrow-10 1) (field (* fldinst "SYMBOL 173 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:function-arrow-10 2) (field (* fldinst "SYMBOL 174 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:cartesian-product-10 2) (field (* fldinst "SYMBOL 180 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:identical-10 2) (field (* fldinst "SYMBOL 186 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:circle-plus-10 2) (field (* fldinst "SYMBOL 197 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:empty-10 2) (field (* fldinst "SYMBOL 198 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:intersection-10 1) (field (* fldinst "SYMBOL 199 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:union-10 1) (field (* fldinst "SYMBOL 200 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:member-10 2) (field (* fldinst "SYMBOL 206 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:not-member-10 2) (field (* fldinst "SYMBOL 207 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:derives-10 2) (field (* fldinst "SYMBOL 222 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:left-triangle-bracket-10 1) (field (* fldinst "SYMBOL 225 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:right-triangle-bracket-10 1) (field (* fldinst "SYMBOL 241 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:big-plus-10 2) (field (* fldinst "SYMBOL 58 \\f \"Zapf Dingbats\" \\s 10") (fldrslt :zapf-dingbats :10-pt)))
((:alpha 1) (field (* fldinst "SYMBOL 97 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:beta 1) (field (* fldinst "SYMBOL 98 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:chi 1) (field (* fldinst "SYMBOL 99 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:delta 1) (field (* fldinst "SYMBOL 100 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:epsilon 1) (field (* fldinst "SYMBOL 101 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:phi 1) (field (* fldinst "SYMBOL 102 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:gamma 1) (field (* fldinst "SYMBOL 103 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:eta 1) (field (* fldinst "SYMBOL 104 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:iota 1) (field (* fldinst "SYMBOL 105 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:kappa 1) (field (* fldinst "SYMBOL 107 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:lambda 1) (field (* fldinst "SYMBOL 108 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:mu 1) (field (* fldinst "SYMBOL 109 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:nu 1) (field (* fldinst "SYMBOL 110 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:omicron 1) (field (* fldinst "SYMBOL 111 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:pi 1) (field (* fldinst "SYMBOL 112 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:theta 1) (field (* fldinst "SYMBOL 113 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:rho 1) (field (* fldinst "SYMBOL 114 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:sigma 1) (field (* fldinst "SYMBOL 115 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:tau 1) (field (* fldinst "SYMBOL 116 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:upsilon 1) (field (* fldinst "SYMBOL 117 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:omega 1) (field (* fldinst "SYMBOL 119 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:xi 1) (field (* fldinst "SYMBOL 120 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:psi 1) (field (* fldinst "SYMBOL 121 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
((:zeta 1) (field (* fldinst "SYMBOL 122 \\f \"Symbol\" \\s 10") (fldrslt :symbol :10-pt)))
;Styles
((+ :rtf-intro) :stylesheet)
(:stylesheet (stylesheet :styles))
(:normal-num 0)
(:normal s :normal-num)
((+ :styles) (widctlpar :10-pt :english snext :normal-num "Normal;"))
(:body-text-num 1)
(:body-text s :body-text-num qj sa 120 widctlpar :10-pt :english)
((+ :styles) (:body-text sbasedon :normal-num snext :body-text-num "Body Text;"))
(:header-num 2)
(:header s :header-num nowidctlpar tqr tx 8640 :10-pt :english)
((+ :styles) (:header sbasedon :normal-num snext :header-num "header;"))
(:footer-num 3)
(:footer s :footer-num nowidctlpar tqc tx 4320 :10-pt :english)
((+ :styles) (:footer sbasedon :normal-num snext :footer-num "footer;"))
(:section-heading-num 4)
(:section-heading s :section-heading-num sa 60 keep keepn nowidctlpar hyphpar 0 level 3 b :12-pt :english)
((+ :styles) (:section-heading sbasedon :subsection-heading-num snext :body-text-num "heading 3;"))
(:subsection-heading-num 5)
(:subsection-heading s :subsection-heading-num sa 30 keep keepn nowidctlpar hyphpar 0 level 4 b :10-pt :english)
((+ :styles) (:subsection-heading sbasedon :normal-num snext :body-text-num "heading 4;"))
(:grammar-num 10)
(:grammar s :grammar-num nowidctlpar hyphpar 0 :10-pt :no-language)
((+ :styles) (:grammar sbasedon :normal-num snext :grammar-num "Grammar;"))
(:grammar-header-num 11)
(:grammar-header s :grammar-header-num sb 60 keep keepn nowidctlpar hyphpar 0 b :10-pt :english)
((+ :styles) (:grammar-header sbasedon :normal-num snext :grammar-lhs-num "Grammar Header;"))
(:grammar-lhs-num 12)
(:grammar-lhs s :grammar-lhs-num fi -1440 li 1800 sb 120 keep keepn nowidctlpar hyphpar 0 outlinelevel 4 :10-pt :no-language)
((+ :styles) (:grammar-lhs sbasedon :grammar-num snext :grammar-rhs-num "Grammar LHS;"))
(:grammar-lhs-last-num 13)
(:grammar-lhs-last s :grammar-lhs-last-num fi -1440 li 1800 sb 120 sa 120 keep nowidctlpar hyphpar 0 outlinelevel 4 :10-pt :no-language)
((+ :styles) (:grammar-lhs-last sbasedon :grammar-num snext :grammar-lhs-num "Grammar LHS Last;"))
(:grammar-rhs-num 14)
(:grammar-rhs s :grammar-rhs-num fi -1260 li 1800 keep keepn nowidctlpar tx 720 hyphpar 0 :10-pt :no-language)
((+ :styles) (:grammar-rhs sbasedon :grammar-num snext :grammar-rhs-num "Grammar RHS;"))
(:grammar-rhs-last-num 15)
(:grammar-rhs-last s :grammar-rhs-last-num fi -1260 li 1800 sa 120 keep nowidctlpar tx 720 hyphpar 0 :10-pt :no-language)
((+ :styles) (:grammar-rhs-last sbasedon :grammar-rhs-num snext :grammar-lhs-num "Grammar RHS Last;"))
(:grammar-argument-num 16)
(:grammar-argument s :grammar-argument-num fi -1440 li 1800 sb 120 sa 120 keep nowidctlpar hyphpar 0 outlinelevel 4 :10-pt :no-language)
((+ :styles) (:grammar-argument sbasedon :grammar-num snext :grammar-lhs-num "Grammar Argument;"))
(:semantics-num 20)
(:semantics s :semantics-num li 180 sb 60 sa 60 keep nowidctlpar hyphpar 0 :10-pt :no-language)
((+ :styles) (:semantics sbasedon :normal-num snext :semantics-num "Semantics;"))
(:semantics-next-num 21)
(:semantics-next s :semantics-next-num li 540 sa 60 keep nowidctlpar hyphpar 0 :10-pt :no-language)
((+ :styles) (:semantics-next sbasedon :semantics-num snext :semantics-next-num "Semantics Next;"))
(:default-paragraph-font-num 30)
(:default-paragraph-font cs :default-paragraph-font-num)
((+ :styles) (* :default-paragraph-font additive "Default Paragraph Font;"))
(:page-number-num 31)
(:page-number cs :page-number-num)
((+ :styles) (* :page-number additive sbasedon :default-paragraph-font-num "page number;"))
(:character-literal-num 32)
(:character-literal cs :character-literal-num b :courier :blue :no-language)
((+ :styles) (* :character-literal additive sbasedon :default-paragraph-font-num "Character Literal;"))
(:character-literal-control-num 33)
(:character-literal-control cs :character-literal-control-num b 0 :times :dark-blue)
((+ :styles) (* :character-literal-control additive sbasedon :default-paragraph-font-num "Character Literal Control;"))
(:terminal-num 34)
(:terminal cs :terminal-num b :palatino :teal :no-language)
((+ :styles) (* :terminal additive sbasedon :default-paragraph-font-num "Terminal;"))
(:terminal-keyword-num 35)
(:terminal-keyword cs :terminal-keyword-num b :courier :blue :no-language)
((+ :styles) (* :terminal-keyword additive sbasedon :terminal-num "Terminal Keyword;"))
(:nonterminal-num 36)
(:nonterminal cs :nonterminal-num i :palatino :dark-red :no-language)
((+ :styles) (* :nonterminal additive sbasedon :default-paragraph-font-num "Nonterminal;"))
(:nonterminal-attribute-num 37)
(:nonterminal-attribute cs :nonterminal-attribute-num i 0)
((+ :styles) (* :nonterminal-attribute additive sbasedon :default-paragraph-font-num "Nonterminal Attribute;"))
(:nonterminal-argument-num 38)
(:nonterminal-argument cs :nonterminal-argument-num)
((+ :styles) (* :nonterminal-argument additive sbasedon :default-paragraph-font-num "Nonterminal Argument;"))
(:semantic-keyword-num 40)
(:semantic-keyword cs :semantic-keyword-num b :times)
((+ :styles) (* :semantic-keyword additive sbasedon :default-paragraph-font-num "Semantic Keyword;"))
(:type-expression-num 41)
(:type-expression cs :type-expression-num :times :red :no-language)
((+ :styles) (* :type-expression additive sbasedon :default-paragraph-font-num "Type Expression;"))
(:type-name-num 42)
(:type-name cs :type-name-num scaps :times :red :no-language)
((+ :styles) (* :type-name additive sbasedon :type-expression-num "Type Name;"))
(:field-name-num 43)
(:field-name cs :field-name-num :helvetica :red :no-language)
((+ :styles) (* :field-name additive sbasedon :type-expression-num "Field Name;"))
(:global-variable-num 44)
(:global-variable cs :global-variable-num i :times :green :no-language)
((+ :styles) (* :global-variable additive sbasedon :default-paragraph-font-num "Global Variable;"))
(:local-variable-num 45)
(:local-variable cs :local-variable-num i :times :bright-green :no-language)
((+ :styles) (* :local-variable additive sbasedon :default-paragraph-font-num "Local Variable;"))
(:action-name-num 46)
(:action-name cs :action-name-num :zapf-chancery :violet :no-language)
((+ :styles) (* :action-name additive sbasedon :default-paragraph-font-num "Action Name;"))
;Headers and Footers
(:header-group header :reset-paragraph :header)
(:footer-group (footer :reset-paragraph :footer tab (field (* fldinst (:page-number " PAGE ")) (fldrslt (:page-number :no-language "1")))))
;Document Formatting
(:docfmt widowctrl
ftnbj ;footnotes at bottom of page
aenddoc ;endnotes at end of document
fet 0 ;footnotes only -- no endnotes
formshade ;shade form fields
viewkind 4 ;normal view mode
viewscale 125 ;125% view
pgbrdrhead ;page border surrounds header
pgbrdrfoot) ;page border surrounds footer
;Section Formatting
;Specials
(:text :english)
(:invisible v)
((:but-not 6) (b "except"))
((:begin-negative-lookahead 13) "[lookahead" :not-member-10 "{")
((:end-negative-lookahead 2) "}]")
((:line-break 12) "[line" ~ "break]")
((:no-line-break 15) "[no" ~ "line" ~ "break]")
(:subscript sub)
(:superscript super)
(:plain-subscript b 0 i 0 :subscript)
((:action-begin 1) "[")
((:action-end 1) "]")
((:vector-begin 1) (b "["))
((:vector-end 1) (b "]"))
((:empty-vector 2) (b "[]"))
((:vector-append 2) :circle-plus-10)
((:tuple-begin 1) (b :left-triangle-bracket-10))
((:tuple-end 1) (b :right-triangle-bracket-10))
((:true 4) (:global-variable "true"))
((:false 5) (:global-variable "false"))
((:unique 6) (:semantic-keyword "unique"))
))
;;; ------------------------------------------------------------------------------------------------------
;;; SIMPLE LINE BREAKER
(defparameter *limited-line-right-margin* 100)
; Housekeeping dynamic variables
(defvar *current-limited-lines*) ;Items written so far via break-line to the innermost write-limited-lines
(defvar *current-limited-lines-non-empty*) ;True if something was written to *current-limited-lines*
(defvar *current-limited-position*) ;Number of characters written since the last newline to *current-limited-lines*
; Capture the text written by the emitter function to its single parameter
; (an output stream), dividing the text as specified by dynamically scoped calls
; to break-line. Return the text as a base-string.
(defun write-limited-lines (emitter)
(let ((limited-stream (make-string-output-stream :element-type #-mcl 'character #+mcl 'base-character))
(*current-limited-lines* (make-string-output-stream :element-type #-mcl 'character #+mcl 'base-character))
(*current-limited-lines-non-empty* nil)
(*current-limited-position* 0))
(funcall emitter limited-stream)
(break-line limited-stream)
(get-output-stream-string *current-limited-lines*)))
; Capture the text written by the emitter body to stream-var,
; dividing the text as specified by dynamically scoped calls
; to break-line. Write the result to the stream-var stream.
(defmacro write-limited-block (stream-var &body emitter)
`(progn
(write-string
(write-limited-lines #'(lambda (,stream-var) ,@emitter))
,stream-var)
nil))
; Indicate that this is a potential place for a line break in the stream provided
; by write-limited-lines. If subdivide is true, also indicate that line breaks can
; be inserted anywhere between the previous such point indicated by break-line
; (or the beginning of write-limited-lines if this is the first call to break-line)
; and this point.
(defun break-line (limited-stream &optional subdivide)
(let* ((new-chars (get-output-stream-string limited-stream))
(length (length new-chars)))
(unless (zerop length)
(labels
((subdivide-new-chars (start)
(let ((length-remaining (- length start))
(room-on-line (- *limited-line-right-margin* *current-limited-position*)))
(if (>= room-on-line length-remaining)
(progn
(write-string new-chars *current-limited-lines* :start start)
(incf *current-limited-position* length-remaining))
(let ((end (+ start room-on-line)))
(write-string new-chars *current-limited-lines* :start start :end end)
(write-char #\newline *current-limited-lines*)
(setq *current-limited-position* 0)
(subdivide-new-chars end))))))
(let ((position (+ *current-limited-position* length))
(has-newlines (find #\newline new-chars)))
(cond
((or has-newlines
(and (> position *limited-line-right-margin*) (not subdivide)))
(when *current-limited-lines-non-empty*
(write-char #\newline *current-limited-lines*))
(write-string new-chars *current-limited-lines*)
;Force a line break if break-line is called again and the current
;new-chars contained a line break.
(setq *current-limited-position*
(if has-newlines
(1+ *limited-line-right-margin*)
length)))
((<= position *limited-line-right-margin*)
(write-string new-chars *current-limited-lines*)
(setq *current-limited-position* position))
((>= *current-limited-position* *limited-line-right-margin*)
(write-char #\newline *current-limited-lines*)
(setq *current-limited-position* 0)
(subdivide-new-chars 0))
(t (subdivide-new-chars 0)))
(setq *current-limited-lines-non-empty* t))))))
;;; ------------------------------------------------------------------------------------------------------
;;; RTF READER
; Return true if char can be a part of an RTF control word.
(defun rtf-control-word-char? (char)
(and (char>= char #\a) (char<= char #\z)))
; Read RTF from the character stream and return it in list form.
; Each { ... } group is a sublist.
; Each RTF control symbol or word is represented by a lisp symbol.
; If an RTF control has a numeric argument, then its lisp symbol is followed
; by an integer equal to the argument's value.
; Newlines not escaped by backslashes are ignored.
(defun read-rtf (stream)
(labels
((read (&optional (eof-error-p t))
(read-char stream eof-error-p nil))
(read-group (nested)
(let ((char (read nested)))
(case char
((nil) nil)
(#\} (if nested
nil
(error "Mismatched }")))
(#\{ (cons
(read-group t)
(read-group nested)))
(#\\ (append
(read-control)
(read-group nested)))
(#\newline (read-group nested))
(t (read-text nested (list char))))))
(read-text (nested chars)
(let ((char (read nested)))
(case char
((nil)
(list (coerce (nreverse chars) 'string)))
((#\{ #\} #\\)
(cons (coerce (nreverse chars) 'string)
(progn
(unread-char char stream)
(read-group nested))))
(#\newline (read-text nested chars))
(t (read-text nested (cons char chars))))))
(read-integer (value need-digit)
(let* ((char (read))
(digit (digit-char-p char)))
(cond
(digit (read-integer (+ (* value 10) digit) nil))
(need-digit (error "Empty number"))
((eql char #\space) value)
(t (unread-char char stream)
value))))
(read-hex (n-digits)
(let ((value 0))
(dotimes (n n-digits)
(let ((digit (digit-char-p (read) 16)))
(unless digit
(error "Bad hex digit"))
(setq value (+ (* value 16) digit))))
value))
(read-control ()
(let ((char (read)))
(if (rtf-control-word-char? char)
(let* ((control-string (read-control-word (list char)))
(control-symbol (intern (string-upcase control-string)))
(char (read)))
(case char
(#\space (list control-symbol))
(#\- (list control-symbol (- (read-integer 0 t))))
((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
(unread-char char stream)
(list control-symbol (read-integer 0 t)))
(t (unread-char char stream)
(list control-symbol))))
(let* ((control-string (string char))
(control-symbol (intern (string-upcase control-string))))
(if (eq control-symbol '\')
(list control-symbol (read-hex 2))
(list control-symbol))))))
(read-control-word (chars)
(let ((char (read)))
(if (rtf-control-word-char? char)
(read-control-word (cons char chars))
(progn
(unread-char char stream)
(coerce (nreverse chars) 'string))))))
(read-group nil)))
; Read RTF from the text file with the given name (relative to the
; local directory) and return it in list form.
(defun read-rtf-from-local-file (filename)
(with-open-file (stream (filename-to-semantic-engine-pathname filename)
:direction :input)
(read-rtf stream)))
;;; ------------------------------------------------------------------------------------------------------
;;; RTF WRITER
(defconstant *rtf-special* '(#\\ #\{ #\}))
; Return the string with characters in *rtf-special* preceded by backslashes.
; If there are no such characters, the returned string may be eq to the input string.
(defun escape-rtf (string)
(let ((i (position-if #'(lambda (char) (member char *rtf-special*)) string)))
(if i
(let* ((string-length (length string))
(result-string (make-array string-length :element-type #-mcl 'character #+mcl 'base-character :adjustable t :fill-pointer i)))
(replace result-string string)
(do ((i i (1+ i)))
((= i string-length))
(let ((char (char string i)))
(when (member char *rtf-special*)
(vector-push-extend #\\ result-string))
(vector-push-extend char result-string)))
result-string)
string)))
; Write the string with characters in *rtf-special* preceded by backslashes and not allowing
; linebreaks between a backslash and the character it is escaping. Start with the character
; at offset start in the given string.
(defun write-escaped-rtf (stream string start)
(let ((end (position-if #'(lambda (char) (member char *rtf-special*)) string :start start)))
(if end
(progn
(unless (= start end)
(write-string string stream :start start :end end)
(break-line stream t))
(write-char #\\ stream)
(write-char (char string end) stream)
(break-line stream)
(write-escaped-rtf stream string (1+ end)))
(unless (= start (length string))
(write-string string stream :start start)
(break-line stream t)))))
; Write RTF to the character stream. See read-rtf for a description
; of the layout of the rtf list.
(defun write-rtf (rtf &optional (stream t))
(labels
((write-group-contents (rtf stream)
(let ((first-rtf (first rtf))
(rest-rtf (rest rtf)))
(cond
((listp first-rtf)
(write-group first-rtf stream t))
((stringp first-rtf)
(write-escaped-rtf stream first-rtf 0))
((symbolp first-rtf)
(write-char #\\ stream)
(write first-rtf :stream stream)
(cond
((alpha-char-p (char (symbol-name first-rtf) 0))
(when (integerp (first rest-rtf))
(write (first rest-rtf) :stream stream)
(setq rest-rtf (rest rest-rtf)))
(let ((first-rest (first rest-rtf)))
(when (and (stringp first-rest)
(or (zerop (length first-rest))
(let ((ch (char first-rest 0)))
(or (alphanumericp ch)
(eql ch #\space)
(eql ch #\-)
(eql ch #\+)))))
(write-char #\space stream))))
((eq first-rtf '\')
(unless (integerp (first rest-rtf))
(error "Bad rtf: ~S" rtf))
(format stream "~2,'0x" (first rest-rtf))
(setq rest-rtf (rest rest-rtf)))))
(t (error "Bad rtf: ~S" rtf)))
(when rest-rtf
(break-line stream)
(write-group-contents rest-rtf stream))))
(write-group (rtf stream nested)
(write-limited-block stream
(when nested
(write-char #\{ stream))
(when rtf
(write-group-contents rtf stream))
(when nested
(write-char #\} stream)))))
(with-standard-io-syntax
(let ((*print-readably* nil)
(*print-escape* nil)
(*print-case* :downcase))
(write-group rtf stream nil)))))
; Write RTF to the text file with the given name (relative to the
; local directory).
(defun write-rtf-to-local-file (filename rtf)
(with-open-file (stream (filename-to-semantic-engine-pathname filename)
:direction :output
:if-exists :supersede
#+mcl :external-format #+mcl "RTF "
#+mcl :mac-file-creator #+mcl "MSWD")
(write-rtf rtf stream)))
;;; ------------------------------------------------------------------------------------------------------
;;; RTF STREAMS
(defstruct (rtf-stream (:include markup-stream)
(:constructor allocate-rtf-stream (env head tail level logical-position))
(:copier nil)
(:predicate rtf-stream?))
(style nil :type symbol)) ;Current section or paragraph style or nil if none or emitting paragraph contents
(defmethod print-object ((rtf-stream rtf-stream) stream)
(print-unreadable-object (rtf-stream stream :identity t)
(write-string "rtf-stream" stream)))
; Make a new, empty, open rtf-stream with the given definitions for its markup-env.
(defun make-rtf-stream (markup-env level &optional logical-position)
(let ((head (list nil)))
(allocate-rtf-stream markup-env head head level logical-position)))
; Make a new, empty, open, top-level rtf-stream with the given definitions
; for its markup-env.
(defun make-top-level-rtf-stream (rtf-definitions)
(let ((head (list nil))
(markup-env (make-markup-env nil)))
(markup-env-define-alist markup-env rtf-definitions)
(allocate-rtf-stream markup-env head head *markup-stream-top-level* nil)))
; Append a block to the end of the rtf-stream. The block may be inlined
; if nothing else follows it in the rtf-stream.
(defun rtf-stream-append-or-inline-block (rtf-stream block)
(assert-type block list)
(when block
(let ((pretail (markup-stream-tail rtf-stream)))
(markup-stream-append1 rtf-stream block)
(setf (markup-stream-pretail rtf-stream) pretail))))
; Return the approximate width of the rtf item; return t if it is a line break.
; Also allow rtf groups as long as they do not contain line breaks.
(defmethod markup-group-width ((rtf-stream rtf-stream) item)
(if (consp item)
(reduce #'+ item :key #'(lambda (subitem) (markup-group-width rtf-stream subitem)))
(markup-width rtf-stream item)))
; Return the information group or nil if none is needed.
; Any of the inputs can be nil, in which case the corresponding info entry is omitted.
(defun generate-document-info (title author company time)
(and (or title author company time)
(cons 'info
(nconc
(and title (list (list 'title (assert-type title string))))
(and author (list (list 'author (assert-type author string))
(list 'operator author)))
(and time (multiple-value-bind (second minute hour day month year) (decode-universal-time time)
(let ((rtf-time (list 'yr year 'mo month 'dy day 'hr hour 'min minute 'sec second)))
(list (cons 'creatim rtf-time)
(cons 'revtim rtf-time)
(list 'edmins 0)))))
(and company (list (list '* 'company (assert-type company string))))))))
(defun time-to-string (time)
(multiple-value-bind (second minute hour day month year weekday) (decode-universal-time time)
(declare (ignore second minute hour))
(format nil "~A, ~A ~D, ~D"
(nth weekday '("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday"))
(nth (1- month) '("January" "February" "March" "April" "May" "June" "July" "August" "September" "October" "November" "December"))
day
year)))
; Return the header group.
(defun generate-header-group (title time)
(list :header-group (assert-type title string) 'tab (time-to-string time)))
; Create a top-level rtf-stream and call emitter to emit its contents.
; emitter takes one argument -- an rtf-stream to which it should emit paragraphs.
; Return the top-level rtf-stream.
(defun depict-rtf-top-level (title emitter)
(let* ((top-rtf-stream (make-top-level-rtf-stream *rtf-definitions*))
(rtf-stream (make-rtf-stream (markup-stream-env top-rtf-stream) *markup-stream-paragraph-level*))
(time (get-universal-time)))
(markup-stream-append1 rtf-stream ':rtf-intro)
(let ((info (generate-document-info title *rtf-author* *rtf-company* time)))
(when info
(markup-stream-append1 rtf-stream info)))
(markup-stream-append1 rtf-stream ':docfmt)
(markup-stream-append1 rtf-stream ':reset-section)
(markup-stream-append1 rtf-stream (generate-header-group title time))
(markup-stream-append1 rtf-stream ':footer-group)
(funcall emitter rtf-stream)
(markup-stream-append1 top-rtf-stream (markup-stream-unexpanded-output rtf-stream))
top-rtf-stream))
; Create a top-level rtf-stream and call emitter to emit its contents.
; emitter takes one argument -- an rtf-stream to which it should emit paragraphs.
; Write the resulting RTF to the text file with the given name (relative to the
; local directory).
(defun depict-rtf-to-local-file (filename title emitter)
(let ((top-rtf-stream (depict-rtf-top-level title emitter)))
(write-rtf-to-local-file filename (markup-stream-output top-rtf-stream)))
filename)
; Return the markup accumulated in the markup-stream after expanding all of its macros.
; The markup-stream is closed after this function is called.
(defmethod markup-stream-output ((rtf-stream rtf-stream))
(markup-env-expand (markup-stream-env rtf-stream) (markup-stream-unexpanded-output rtf-stream) nil))
(defmethod depict-block-style-f ((rtf-stream rtf-stream) block-style flatten emitter)
(declare (ignore block-style flatten))
(assert-true (= (markup-stream-level rtf-stream) *markup-stream-paragraph-level*))
(funcall emitter rtf-stream))
(defmethod depict-paragraph-f ((rtf-stream rtf-stream) paragraph-style emitter)
(assert-true (= (markup-stream-level rtf-stream) *markup-stream-paragraph-level*))
(assert-true (and paragraph-style (symbolp paragraph-style)))
(unless (eq paragraph-style (rtf-stream-style rtf-stream))
(markup-stream-append1 rtf-stream ':reset-paragraph)
(markup-stream-append1 rtf-stream paragraph-style))
(setf (rtf-stream-style rtf-stream) nil)
(setf (markup-stream-level rtf-stream) *markup-stream-content-level*)
(setf (markup-stream-logical-position rtf-stream) (make-logical-position))
(prog1
(funcall emitter rtf-stream)
(setf (markup-stream-level rtf-stream) *markup-stream-paragraph-level*)
(setf (rtf-stream-style rtf-stream) paragraph-style)
(setf (markup-stream-logical-position rtf-stream) nil)
(markup-stream-append1 rtf-stream ':new-paragraph)))
(defmethod depict-char-style-f ((rtf-stream rtf-stream) char-style emitter)
(assert-true (>= (markup-stream-level rtf-stream) *markup-stream-content-level*))
(assert-true (and char-style (symbolp char-style)))
(let ((inner-rtf-stream (make-rtf-stream (markup-stream-env rtf-stream) *markup-stream-content-level* (markup-stream-logical-position rtf-stream))))
(markup-stream-append1 inner-rtf-stream char-style)
(prog1
(funcall emitter inner-rtf-stream)
(rtf-stream-append-or-inline-block rtf-stream (markup-stream-unexpanded-output inner-rtf-stream)))))
(defmethod ensure-no-enclosing-style ((rtf-stream rtf-stream) style)
(declare (ignore style)))
(defmethod depict-anchor ((rtf-stream rtf-stream) link-prefix link-name duplicate)
(declare (ignore link-prefix link-name duplicate))
(assert-true (= (markup-stream-level rtf-stream) *markup-stream-content-level*)))
(defmethod depict-link-reference-f ((rtf-stream rtf-stream) link-prefix link-name external emitter)
(declare (ignore link-prefix link-name external))
(assert-true (= (markup-stream-level rtf-stream) *markup-stream-content-level*))
(funcall emitter rtf-stream))
#|
(setq r (read-rtf-from-local-file "SampleStyles.rtf"))
(write-rtf-to-local-file "Y.rtf" r)
|#

Просмотреть файл

@ -464,6 +464,7 @@
(:grammar-argument (:nest :nowrap (div (class "grammar-argument")))) (:grammar-argument (:nest :nowrap (div (class "grammar-argument"))))
(:semantics (:nest :nowrap (p (class "semantics")))) (:semantics (:nest :nowrap (p (class "semantics"))))
(:semantics-next (:nest :nowrap (p (class "semantics-next")))) (:semantics-next (:nest :nowrap (p (class "semantics-next"))))
(:semantic-comment (:nest :nowrap (p (class "semantic-comment"))))
;Inline Styles ;Inline Styles
(:script (script (type "text/javascript"))) (:script (script (type "text/javascript")))
@ -499,6 +500,7 @@
((:vector-begin 1) (b "[")) ((:vector-begin 1) (b "["))
((:vector-end 1) (b "]")) ((:vector-end 1) (b "]"))
((:empty-vector 2) (b "[]")) ((:empty-vector 2) (b "[]"))
((:vector-construct 1) (b "|"))
((:vector-append 2) :circle-plus-10) ((:vector-append 2) :circle-plus-10)
((:tuple-begin 1) (b :left-triangle-bracket-10)) ((:tuple-begin 1) (b :left-triangle-bracket-10))
((:tuple-end 1) (b :right-triangle-bracket-10)) ((:tuple-end 1) (b :right-triangle-bracket-10))

Просмотреть файл

@ -232,6 +232,10 @@
(:semantics-next s :semantics-next-num li 540 sa 60 keep nowidctlpar hyphpar 0 :10-pt :no-language) (:semantics-next s :semantics-next-num li 540 sa 60 keep nowidctlpar hyphpar 0 :10-pt :no-language)
((+ :styles) (:semantics-next sbasedon :semantics-num snext :semantics-next-num "Semantics Next;")) ((+ :styles) (:semantics-next sbasedon :semantics-num snext :semantics-next-num "Semantics Next;"))
(:semantic-comment-num 22)
(:semantic-comment s :semantic-comment-num qj li 180 sb 120 sa 0 widctlpar :10-pt :english)
((+ :styles) (:semantic-comment sbasedon :normal-num snext :semantics-num "Semantic Comment;"))
(:default-paragraph-font-num 30) (:default-paragraph-font-num 30)
(:default-paragraph-font cs :default-paragraph-font-num) (:default-paragraph-font cs :default-paragraph-font-num)
((+ :styles) (* :default-paragraph-font additive "Default Paragraph Font;")) ((+ :styles) (* :default-paragraph-font additive "Default Paragraph Font;"))
@ -333,6 +337,7 @@
((:vector-begin 1) (b "[")) ((:vector-begin 1) (b "["))
((:vector-end 1) (b "]")) ((:vector-end 1) (b "]"))
((:empty-vector 2) (b "[]")) ((:empty-vector 2) (b "[]"))
((:vector-construct 1) (b "|"))
((:vector-append 2) :circle-plus-10) ((:vector-append 2) :circle-plus-10)
((:tuple-begin 1) (b :left-triangle-bracket-10)) ((:tuple-begin 1) (b :left-triangle-bracket-10))
((:tuple-end 1) (b :right-triangle-bracket-10)) ((:tuple-end 1) (b :right-triangle-bracket-10))