#!/usr/bin/newlisp
# - syntax.cgi - this utility formats a newLISP source file as an HTML
# file with syntax highlighting
#
# comment following line out when not using as CGI utilty
# for CGI files to convert which are local must end with .txt
# v.1.7 - switch for using as commandline or cgi utility
# v.1.9 - fixed highlighting problem with escaped quotes
# v.2.0 - fixed \r\n translation
# v.2.1 - more compatible CGI
# v.2.3 - changed syntax for write-line
# v.2.4 - added handling of < ? ... > XML tag
# v.2.5 - generate more compliant HTML 4.01 transitional
# v.2.6 - handle number scientific format with e
# v.2.7 - improved keyword regex
# v.2.8 - scientific format with E
# v.2.9 - support for newLISPdoc tag color
# v.3.0 - correctly highlight & and ^ keywords
# v.3.1 - change additional 'nil' arg in 'starts-with' to 1
# v.3.2 - replace 'name' with 'term' and make compatible with older versions
# v.3.3 - added utf-8 meta tag in header
# v.3.4 - fontsize 150% for iPad or iPhone
#
# formats newLISP source files with syntax highlighting in HTML
#
# use on the command line or as cgi file together with a webserver
#
# EXAMPLE command line (set 'cgi-use nil):
#
# ./syntaxi.cgi mysource.lsp > mysource.lsp.html
#
# formats mysorce.lsp and redirects output to a new file mysource.lsp.html
#
# EXAMPLE webserver CGI (tested on Apache) local files must end in txt for security
#
# http://www.mysite.com/syntax.cgi?mysource.lsp.txt
#
# returns mysorce.lsp HTML formatted to the requesting client (browser)
#
# EXAMPLE webserver CGI with other site URL
#
# http://www.mysite.com/syntax.cgi?http://othersite/afile.lsp
#
# displays afile.lsp formateed from other site
#
# the following situations are not handled correctly:
# - nested curly braces for strings like {abd{def}ghi}
# - multiline quoted strings, use [text] [/text] instead
# - multiline braced strings, use [text] [/text] instead
# - comments starting with # but not starting at beginning of line
# use ; as comment starter when comment appears after code
(define cgi-use true)
; make compatible with versions prior to 10.1.11
(when (< (sys-info -2) 10111)
(constant (global 'term) name))
(when cgi-use
(print "Content-Type: text/html\r\n\r\n")
(set 'iPad (find "iPad" (env "HTTP_USER_AGENT") 1))
(set 'iPhone (find "iPhone" (env "HTTP_USER_AGENT") 1))
)
(define keyword-color "#0000AA") ; newLISP keywords
(define tag-color "#308080") ; newLISPdoc tags
(define string-color "#008800") ; single line quoted and braced strings
(define long-string-color "#008800") ; multiline for [text], [/text] tags
(define paren-color "#AA0000") ; parenthesis
(define comment-color "#555555") ; comments
(define number-color "#665500") ; numbers
(define function-name-color "#000088") ; not implemented yet for func in (define (func x y z) ...)
(set 'keywords (map term (filter (fn (x) (primitive? (eval x))) (sort (symbols) > ))))
(push "nil" keywords)
(push "true" keywords)
(set 'keyword-regex (join keywords "|"))
(replace "&" keyword-regex "&&")
(replace "?" keyword-regex "\\?")
(replace "^" keyword-regex "\\^")
(replace "$" keyword-regex "\\$")
(replace "!" keyword-regex "\\!")
(replace "+" keyword-regex "\\+")
(replace "*" keyword-regex "\\*")
(replace "||" keyword-regex "|\\|")
(set 'keyword-regex (append {(\G|\s+|\(|\))(} keyword-regex {)(\s+|\(|\))}))
(set 'file (if cgi-use
(or (read-line) (env "QUERY_STRING"))
(main-args 2)))
(set 'title file)
(when cgi-use
(when (and (not (starts-with file "http://" 1)) (not (ends-with file ".txt")))
(println "<h3>File not allowed for viewing: " file "</h3>")
(exit))
)
(if (starts-with file "http://" 1)
(set 'file (get-url file 10000))
(set 'file (read-file file )))
(unless file
(println "<h3>Cannot find file</h3>")
(exit))
(define (clean-comment str)
(replace {<font color='#......'>} str "" 0)
(replace {</font>} str "")
(replace {[text]} str "[&text]")
(replace {[/text]} str "[&/text]")
)
(define (format-quoted-string str)
(replace {<font color='#......'>} str "" 0)
(replace {</font>} str "")
(replace ";" str ";&")
(replace "{" str "{&")
(replace "}" str "}&")
(replace {\} str "\&")
(replace {[text]} str "[&text]")
(replace {[/text]} str "[&/text]")
(format {<font color='%s'>%s</font>} string-color str)
)
(define (format-braced-string str)
(replace {<font color='#......'>} str "" 0)
(replace {</font>} str "")
(replace ";" str ";&")
(replace {"} str ""&")
(replace {[text]} str "[&text]")
(replace {[/text]} str "[&/text]")
(format {<font color='%s'>%s</font>} string-color str)
)
(define (format-tagged-string str)
(replace {<font color='#......'>} str "" 0)
(replace {</font>} str "")
(replace ";" str ";&")
(format {<font color='%s'>%s</font>} string-color str)
)
; replace special HTML characters
(replace "\r\n" file "\n")
(replace "&" file "&&")
(replace "<(\\w)" file (append "<&" $1) 0)
(replace "(\\w)>" file (append $1 ">&") 0)
(replace "/>" file "/>&" 0)
(replace "</" file "<&/" 0)
(replace "<!" file "<&!" 0)
(replace "<\\?" file "<&?" 0)
; replace escaped quotes
(replace "\092\034" file "\&"&")
; color keywords
(replace keyword-regex file (format {%s<font color='%s'>%s</font>%s} $1 keyword-color $2 $3) 0)
;(replace keyword-regex file (println "->" $0 "<-<br>") 0)
;(exit)
; color numbers
(replace
; <-- lead --><---- hex ---->|<- oct ->|<------- decimal ----- and ----- scientific -->
{(\s+|\(|\))(0x[0-9a-fA-F]+|[+-]?0\d+|([+-]?(0|[1-9]\d*)(\.\d*)?|\.\d+)([eE][+-]?\d+)?)} file
(format {%s<font color='%s'>%s</font>} $1 number-color $2) 0)
; color parens
(replace "(" file (format {<font color='%s'>(</font>} paren-color))
(replace ")" file (format {<font color='%s'>)</font>} paren-color))
; color braced strings
(replace "{.*?}" file (format-braced-string $0) 0) ; no multiline string
; color quoted strings
(replace {".*?"} file (format-quoted-string $0) 0) ; no multiline strings
; color ; comments
(replace ";.*" file (clean-comment $0) 0)
(replace ";.*" file (format {<font color='%s'>%s</font>} comment-color $0) 0)
(replace "(;;.*)(@.*)(\\s.*\n)" file (append $1 (format {<font color='%s'>%s</font>} tag-color $2) $3) 512)
; color # comments
(set 'buff "")
(dolist (lne (parse file "\n"))
(replace "^\\s*#.*" lne (clean-comment $0) 0)
(replace "^\\s*#.*" lne (format {<font color='%s'>%s</font>} comment-color $0) 0)
(if (< (sys-info -2) 9909)
(write-line lne buff)
(write-line buff lne)))
(set 'file buff)
; color tagged strings
(replace {\[text\].*?\[/text\]} file (format-tagged-string $0) 4) ; handles multiline strings
; xlate back special characters
(replace "&&" file "&") ; ampersand
(replace "<&" file "<") ; less
(replace ">&" file ">") ; greater
(replace {"&} file """) ; quotes
(replace {;&} file ";") ; semicolon
(replace {{&} file "{") ; left curly brace
(replace {}&} file "}") ; right curly brace
(replace {[&} file "[") ; left bracket
(replace {\&} file "\") ; back slash
; add pre and post tags
(println (append
{<!DOCTYPE HTML PUBLIC "4.01 Transitional">}
"<html><head><title>" title "</title>\n"
{<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />}
{<style type="text/css">}
(if (or iPad iPhone)
{pre {font-size: 150%}}
{pre {font-size: 100%}} )
"</style></head><body>\n<pre>\n"
file "\n</pre>"
))
(println {<center><font face='Arial' size='-2' color='#444444'>}
{syntax highlighting with <a href="http://newlisp.org">newLISP</a> }
{and <a href="http://newlisp.org/syntax.cgi?code/syntax-cgi.txt">syntax.cgi</a>}
{</font></center>})
(println {</body></html>})
(exit)
;; eof
syntax highlighting with newLISP and syntax.cgi