XSLT and newLisp

Started by noah, April 22, 2006, 04:04:36 PM

Previous topic - Next topic

noah

Hi.



I'm new to lisp and newLisp. It looks like a lot of fun.



I use XPATH, and XSLT to transform XML.  I'd like to get into semantic web technologies. Processors for the xml-ai language(s) are/will be written in  languages other than xml. Of course. But newLisp's namespacing ability plus the easy conversion to and from s-expressions and xml, make me think that newLisp can be used for writing xml processors that represent xml with equivalent semantics, functionality, and importantly, macro/function names, but all in newLisp.



Visual similarity doesn't tell you whether each newLisp expression can represent and process xml data in the same way as the xml it looks like. But can you write newLisp SXML that looks like and works like XML?



Suppose I have a data-file:



<?xml version="1.0" encoding="UTF-8"?>
<persons>
  <person>
    <first-name>John</first-name>
    <last-name>Smith</last-name>
    <phone>111.222.3333</phone>
  </person>
  <person>
    <first-name>Bob</first-name>
    <last-name>Hope</last-name>
    <phone>555.111.2222</phone>
  </person>
</persons>


Can I apply a simplified stylesheet like:



(html (@ (xmlns:xsl "http://www.w3.org/1999/XSL/Transform")(xsl:version "1.0"))
  (body
    (table
      (xsl:for-each (@ (select "/persons/person"))
        (tr
          (td (xsl:value-of (@ (select "first-name")))
          (td (xsl:value-of (@ (select "last-name")))
          (td (xsl:value-of (@ (select "phone"))))))))


to get



<html>
  <body>
    <table>
      <tr>
        <td>John</td>
        <td>Smith</td>
        <td>111.222.3333</td>
      </tr>
      <tr>
        <td>Bob</td>
        <td>Hope</td>
        <td>555.111.2222</td></tr></table></body></html>



?



If so, then my questions include:



  • *Can uses of newLisp expand to XSLT push-processing of XML files in a form that looks like XSLT but uses multiple templates?



    *Is it feasible to port the Scheme SXPATH, SXSLT, libraries to newLisp, or would whoever did this have to start from scratch?



    *what potential is there for representing XPATH 2.0, XSLT 2.0, OWL, RDF, RIF, and future W3  AI-like specs in newLisp?



I'm happy just to play around with newLisp as a CGI processor and shell scripting language. But my guess is that discussions about similarities between newLisp and XML have gone before, and some people out there have explored questions like mine. If there's a strong potential for working XML and XSLT emulation in newLisp, and its fairly easy to create, then I'd like to know how. If someone else has produced what I'm talking about, then I'd be happy to use it, if it's offered.



Sorry to seem so "gimme-gimme". I'll also follow someone's guidance to create an SXSLT "context" that precisely emulates XSLT 1.0 and XPATH 1.0, if I can do it and learn (new)Lisp at the same time. Anyone have that kind of patience?



How about just a few hints, if you're not that patient?



-Noah

cormullion

#1
Hi Noah. They're all pretty patient here - at least they have been with me. If anyone knows anything useful, you'll see it posted! (if not necessarily on a Sunday :-))

Lutz

#2
I don't have an immedeate answer to this, but I understand the problem, and I am shure it can be done.



newLISP has many list manipulation functions which can deal with nested lists, i.e: you could use 'ref' to find the position of certain tag in the nested list representaion of the SXML expression. You could than use 'pop' to extract it and 'push' it back as HTML tags. Almost all list functions in newLISP can handle/index into nested lists.



I am not familiar with XSLT, but I see it has constructs like 'for-each', 'select' etc..



Perhaps what we are dealiing with here is translation from one language: SXLT to an other: HTML supplying data from SXML translated from your XML data. One Idea I have in my head is, to define a function for each tag/op in the SXML and then execute the thing as a program, which outputs HTML. Here is a toy example:

(set 'XML "<person>John Doe</person>")

(xml-type-tags nil nil  nil nil) ; setup SXMLprocessing

(set 'SXML (xml-parse XML 31)) => ((person "John Doe"))

; define a tag procedure
(define (person p) (println "<td>" p "</td>))

(dolist (i SXML) (eval i)) => <td>John Doe</td>


This would also work with several 'person's defined. The tag procedure enclosing the database of persons would set up the table.



Another solution would be to recursevely walk through the SXML and branch with 'cond' or 'if' on the different tags and do the work of outputting the HTML as a side effect. This is pretty much how a LISP expression evaluator works, when processing the LISP language. A similar pattern would work processing SXML.



The link here: http://newlisp.org/index.cgi?page=S-expressions_to_XML">http://newlisp.org/index.cgi?page=S-expressions_to_XML uses the same technique to translate SXML back to XML (in Noah's case HTML).



I believe LISP and specially newLISP, because of it's nested list facilities is well suited for this problem. Perhaps analyzing the SCHEME code will give you some ideas, allthough I believe that newLISP in the end will handle it with much less code than SCHEME or another LISP because of newLISP's higher level faclilities.



Similar questions have been put her before but I would not know of any other specific solution.



Lutz

noah

#3
Hello, Lutz and cormullion.



Cormullion, thank you for your reply. I'm very excited about what newLisp has to offer. In fact, my interest in it over the last weekend caused me more than a few problems with other parts of my schedule. I'll post more specifics about my interests in newLisp and xml, and please feel free to respond with ideas and advice. Thank you.





Lutz, thank you for your reply. You write that you're sure it can be done. OK, I'll wait until I know more. What I can do in the meantime is concentrate on how to roundtrip XML so that I can use your language for shell scripting, CGI, AND writing my XSLT. Saxon can process my XSLT while I write it with newLisp. That will give me a chance to create alternates of my shell scripts in newLisp, and to try out a few other new things.



After looking through the FAQ, and browsing the documentation, I didn't see any mention of how you'd like users to report bugs and log suggestions. I don't have either right now, but may sometime in the future. Do you have a formal procedure for users to follow? Do you request a particular format of information for bugs or feature requests?



I noticed that some newLisper's like to give newLisp functions that produce their e-mail, or perform a parsing task for retrieving web content. Do you have a newLisp format for bug reports or feature requests that you'd like me to use?



-Noah

Lutz

#4
Just report problems here on the board and tell us about the version and OS platform you are using and give a description how to reproduce the problem.



For suggestions of enhancements its always good to give a usage example.



Lutz

rickyboy

#5
Quote from: "noah"Lutz, thank you for your reply. You write that you're sure it can be done.


Of course it can be done -- Lutz is always right.  :-)



Given a stylesheet 'person-style.xsl':
<html xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xsl:version="1.0">
  <body>
    <table>
      <xsl:for-each select="/persons/person">
        <tr>
          <td><xsl:value-of select="first-name"/></td>
          <td><xsl:value-of select="last-name"/></td>
          <td><xsl:value-of select="phone"/></td>
        </tr>
      </xsl:for-each>
    </table>
  </body>
</html>

and a database 'person-db.xml':
<?xml version="1.0" encoding="UTF-8"?>
<persons>
  <person>
    <first-name>John</first-name>
    <last-name>Smith</last-name>
    <phone>111.222.3333</phone>
  </person>
  <person>
    <first-name>Bob</first-name>
    <last-name>Hope</last-name>
    <phone>555.111.2222</phone>
  </person>
</persons>

and some handy functions:
(context 'xsl)

(define *db* '())
(define *local-db* '())

;; The evaluator 'eval-sxsl' will evaluate any list with a head which
;; is a symbol contained in '*xsl-procs*'.
(define *xsl-procs* '(xsl:value-of xsl:for-each))

(define-macro (@ proc) (eval (cons (symbol (proc 0) xsl) (1 proc))))

(define (value-of text-element)
  (and text-element
       (let ((text-content (text-element 1)))
         (and (string? text-content) text-content))))

(define (xsl:select xpath-str)
  (xsl:select* (parse xpath-str "/")
               (if (empty? *local-db*) *db* *local-db*)))

(define (xsl:select* xpath db)
  (let ((x0 (xpath 0)))
    (cond ((or (empty? xpath) (empty? db)) nil)
          ((= x0 "") (xsl:select* (1 xpath) db))
          ((= (symbol x0 MAIN) (db 0 0))
           (if (= (length xpath) 1)
               (db 0)
             (xsl:select* (1 xpath) (1 (db 0)))))
          (true (xsl:select* xpath (1 db))))))

(define (xsl-proc-symbol? maybe-symbol)
  (and (symbol? maybe-symbol)
       (member maybe-symbol xsl:*xsl-procs*)))

(define (eval-sxsl sxsl)
  (cond ((atom? sxsl) sxsl)
        ((list? sxsl)
         (if (xsl-proc-symbol? (first sxsl))
             (eval sxsl)
           (map eval-sxsl sxsl)))
        ;; I think the following branch is impossible,
        ;; but I am not sure.
        (true 'IMPROBABLE-CASE-REACHED)))

(define-macro (for-each selector)
  (letn ((xpath (parse (selector 1 1) "/"))
         (last-xpath-symbol (symbol (pop xpath -1) MAIN))
         (xpath-str (join xpath "/"))
         (new-selector (list (selector 0)
                             (list (selector 1 0)
                                   xpath-str)))
         (sxml-data (filter (fn (element)
                              (and (list? element)
                                   (= (element 0) last-xpath-symbol)))
                            (eval new-selector)))
         (sxsl-body (args)))
    (apply append
      (map (fn (*local-db*)
             (map xsl:eval-sxsl sxsl-body))
           sxml-data))))

(context MAIN)

(define (contextualize-symbols expr)
  (cond ((symbol? expr)
         (letn ((contextual-symbol (symbol expr))
                (parsed-symbol (parse (name contextual-symbol) ":")))
               (if (> (length parsed-symbol) 1)
                   (symbol (parsed-symbol -1)
                           (symbol (parsed-symbol 0)))
                 contextual-symbol)))
        ((list? expr) (map contextualize-symbols expr))
        (true expr)))

and some driver code:
(xml-type-tags nil nil nil nil) ; setup SXMLprocessing

;; Read in XML (database) file.
(set 'xsl:*db* (xml-parse (read-file "person-db.xml") 31))

;; Read in XSL (stylesheet) file.
(set 'SXSL (contextualize-symbols
            (xml-parse (read-file "person-style.xsl") 31)))

just crank up some music and let'er rip.  Yeeehaaawww!!
> xsl:*db*
((persons (person (first-name "John") (last-name "Smith") (phone
    "111.222.3333"))
  (person (first-name "Bob") (last-name "Hope") (phone "555.111.2222"))))

> SXSL
((html (@ (xmlns:xsl "http://www.w3.org/1999/XSL/Transform") (xsl:version
    "1.0"))
  (body (table (xsl:for-each (@ (select "/persons/person")) (tr (
       td
       (xsl:value-of (@ (select "first-name"))))
      (td (xsl:value-of (@ (select "last-name"))))
      (td (xsl:value-of (@ (select "phone"))))))))))

> (xsl:eval-sxsl SXSL)
((html (@ (xmlns:xsl "http://www.w3.org/1999/XSL/Transform") (xsl:version
    "1.0"))
  (body (table ((tr (td "John") (td "Smith") (td "111.222.3333"))
     (tr (td "Bob") (td "Hope") (td "555.111.2222")))))))

Now, use Lutz's 'expr2xml' function to recover XHTML from the SXML.  (Actually, you'll have to alter it a bit to handle the element attributes in the '@'.)



Obviously, the supporting functions I wrote into the 'xsl' context (above) don't entirely comply with the standard of XPath and XSLT (ha, ha, ha) -- that's OK 'cause I don't know XPath, etc. anyway!  :-)  However, the point is clear that it's very possible to "roll your own" XSLT-like processing in newLISP.  And it's doesn't take a lot of code.



(By the way, does anyone know why the function 'contextualize-symbols' is needed?  There is a very subtle thing going on with how 'xml-parse' creates symbols from element tag names in the XML source, when the tag names have namespace qualifiers.  Unfortunately when 'xml-parse' reads a tag like '<xsl:for-each ...>' it will not create or reference a symbol like 'for-each' in the 'xsl' context, as expected.  Instead, a symbol 'xsl:for-each' is referenced out of 'MAIN'.  'contextualize-symbols' is a post-processing kludge which rectifies the situation -- it will read an expression and fix the dorked-up symbols it finds in it.  I'm going to sleep now.)
(λx. x x) (λx. x x)

schilling.klaus

#6
Is it possible to  interface newlisp with libxslt, libxml2, and xsltproc instead?

Lutz

#7
newLISP interfaces well to C-based libraries. Look into the simple and extended syntax of the import function. Both need some knowledge of C-programming to use. There are several applications of the simple import syntax in some of the standard modules here:  http://www.newlisp.org/code/modules/">http://www.newlisp.org/code/modules/ specifically: crypto.lsp,  gmp.lsp, mysql.lsp, postgres.lsp, sqlite3.lsp and zlisp.lsp.



For application of the extended import interface see gsl.lsp in the standard modules and http://www.newlisp.org/downloads/OpenGL/opengl-demo-ffi-lsp.txt">http://www.newlisp.org/downloads/OpenGL ... fi-lsp.txt">http://www.newlisp.org/downloads/OpenGL/opengl-demo-ffi-lsp.txt



Without having looked into libxslt, libxml2, and xsltproc, I suspect that those can be served well with the simple syntax of the import function: http://www.newlisp.org/downloads/newlisp_manual.html#import">http://www.newlisp.org/downloads/newlis ... tml#import">http://www.newlisp.org/downloads/newlisp_manual.html#import

Lutz

#8
... and also look for more details on the simple import function and differences to the extended interface here: http://www.newlisp.org/downloads/CodePatterns.html#toc-23">http://www.newlisp.org/downloads/CodePa ... tml#toc-23">http://www.newlisp.org/downloads/CodePatterns.html#toc-23