[Ecls-list] Simple Hello word! function

Pascal J. Bourguignon pjb at informatimago.com
Mon Jul 5 20:17:02 UTC 2010


Louis Höfler <louis.hoefler at gmx.de> writes:
> Von: Juan Jose Garcia-Ripoll [mailto:juanjose.garciaripoll at googlemail.com]
> Gesendet: Sonntag, 4. Juli 2010 19:33
> An: Pascal J. Bourguignon
> Cc: ecls-list at lists.sourceforge.net
> Betreff: Re: [Ecls-list] Simple Hello word! function
>
> The other option, which is not that bad, is to create a REPL for that string.
>
> cl_object string, stream, object, end;
>
> string = make_simple_base_string(".....");
> stream = cl_make_string_input_stream(1,string);
> eof = string; /* Marker for EOF */
> do {
>   object = cl_read(3, stream, Cnil, eof);
>   if (object == eof)
>     break;
>   ...
>  si_eval...(object);
> } while (1);
>
> Thank you for your help.
>
> I used your code and implemented it.
>
> cl_object string, stream, object, eof, evObj;
> string = make_simple_base_string(srccontent);
> stream = cl_make_string_input_stream(1,string);
> eof = string; /* Marker for EOF */
> do {
>  object = cl_read(3, stream, Cnil, eof);
>  if(object == eof)
>   break;
>  evObj = si_safe_eval(2, object, Cnil);
>  cl_object princed = cl_princ_to_string(evObj); //Get output
>  char* output = (char*)princed->base_string.self; //Convert the last result to text
>  ap_rputs(output, r); //Print out the last result to the client
>  ap_rputs("\n", r);
> } while (1);

I was afraid of that...

Are you a masochist or what?  Why do you write it in C?


> If I run this lisp code:
> (defun hello-world-two()
>  (print "Hello world!")
>  (print "How are you today"))
> (hello-world-two)
>
> It just prints out:
>
> How are you today

This is unlikely.  (print "How are you today") will print a new line,
a double-quote, some words, and a double-quote.



> While ECL itself prints:
>
> "Hello world!"
> "How are you today"
> "How are you today"
>
> Do I have to run the evaluated object
> through a loop to get all output?


I didn't consider stream outputs, because you didn't say much about it
(Ok, I could have read your sources more, but I've my own things to do
too).

First notice that there's not only *STANDARD-OUTPUT*, but also, if we
consider only the standard streams,

   *STANDARD-OUTPUT*
   *ERROR-OUTPUT*
   *TRACE-OUTPUT*
   *TERMINAL-IO*
   *QUERY-IO*
   *DEBUG-IO*

and of course, you may also want to do something for *STANDARD-INPUT*
and the input part of the IO streams above.  Often all these streams
are actually set up as synonym streams to *TERMINAL-IO*.

For the input, we could have none (but then we must be careful with
*debug-io* since if we don't handle all the errors, the debugger will
be called trying to read from it, and if it's hooked to an empty
stream, it will detect an end-of-file error it seems that in this
case, ecl exists.  You probably don't what that.

But you asked for output.  You have basically two choices:

- set up the file descriptors so that the lisp standard stream write
  to the same file descriptors where you're writing the HTML code to
  be presented, if such a file descriptor exists.  Or:

- collect the output and do with it whatever you need to do to have it
  appended to the HTML code to be presented.


One problem with the first option is that you won't control a lot what
will be generated,  as Juan Jose mentionned in writing:

   "It might need some additional code to silence it."

Any library may suddenly write to some output stream and break your
HTML.




So I'll stay with my original advice, adding a collection of the
output for each forms, to let your code decide what and how to do with
it.


(defun eval-from-string (string)
  "
Reads expressions from the strings and evaluates them in sequence,
until an error occurs, or no expression remains.  Returns a list of
lists, each sublists containing either:
    (T   start-pos end-pos expression-read-and-evaluated T   result-values
         standard-output error-output trace-output terminal-io)
    (T   start-pos end-pos expression-read-and-evaluated NIL error-condition
         standard-output error-output trace-output terminal-io)
    (NIL start-pos end-pos unreadable-substring          NIL error-condition)
The first sublist with NIL in its fifth position will be also the last of the
result.
"
  (with-output-to-string (*standard-output*)
    (with-output-to-string (*error-output*)
      (with-output-to-string (*trace-output*)
        (with-output-to-string (other-output)
          (with-input-from-string (empty-input "")
            (let* ((*terminal-io* (make-two-way-stream empty-input other-output))
                   (*query-io*    (make-synonym-stream '*terminal-io*))
                   (*debug-io*    (make-synonym-stream '*terminal-io*)))
              (flet ((result (readp start-pos end-pos expression evalp results)
                       (dolist (stream (list *standard-output* *error-output* *trace-output* *terminal-io*))
                         (finish-output stream))
                       (list readp start-pos end-pos expression evalp results
                             (get-output-stream-string *standard-output*)
                             (get-output-stream-string *error-output*)
                             (get-output-stream-string *trace-output*)
                             (get-output-stream-string other-output))))
                (loop
                   :with results = '()
                   :with slength = (length string)
                   :with start = 0
                   :for current = start
                   :while (< start slength)
                   :do (multiple-value-bind (form end)
                           (handler-case
                               (read-from-string string t nil :start current)
                             (end-of-file (err)
                               (when (find-if-not  (lambda (ch) (find ch #(#\space #\newline #\tab #\return #\vt)))
                                                   (subseq string current))
                                 (push (result nil current slength (subseq string current) nil err) results))
                               (return-from eval-from-string (nreverse results)))
                             (t (err)
                               (push (result nil current slength (subseq string current) nil err) results)
                               (return-from eval-from-string (nreverse results))))
                         (setf start end)
                         (handler-case
                             (push (result t current end form t (multiple-value-list (eval form))) results)
                           (t (err)
                             (push (result t current end form nil err) results)
                             (return-from eval-from-string (nreverse results)))))
                   :finally (return-from eval-from-string (nreverse results)))))))))))



(eval-from-string "(#.(progn (print \"I'm reading the operator!\" *trace-output*) 
                             'print)
                    42)

                   (defun logg (level message)
                      (case level
                        ((:error) (princ message *error-output*) (terpri *error-output*))
                        ((:info)  (princ message *trace-output*) (terpri *trace-output*))))

                   (logg :info \"Starting\")

                   (+ 1 2) ; notice, no printing.

                   (progn
                      (princ \"out\" *standard-output*)
                      (princ \"err\" *error-output*)
                      (princ \"trace\" *trace-output*)
                      (princ \"terminal \" *terminal-io*)
                      (princ \"query \" *query-io*)
                      (princ \"debug \" *debug-io*)
                      (values 1 2 3))

                   (prin1 (1+ 2)) (terpri)
                   (logg :error \"Oops, I wanted to print (+ 1 2)\")

                 ")
-->

((T 0 123 (PRINT 42) T (42) "
42 "
  "" "
\"I'm reading the operator!\" "
  "")
 (T 123 387
  (DEFUN LOGG (LEVEL MESSAGE)
    (CASE LEVEL
      ((:ERROR) (PRINC MESSAGE *ERROR-OUTPUT*) (TERPRI *ERROR-OUTPUT*))
      ((:INFO) (PRINC MESSAGE *TRACE-OUTPUT*) (TERPRI *TRACE-OUTPUT*))))
  T (LOGG) "" "" "" "")
 (T 387 431 (LOGG :INFO "Starting") T (NIL) "" "" "Starting
"
  "")
 (T 431 459 (+ 1 2) T (3) "" "" "" "")
 (T 459 861
  (PROGN
   (PRINC "out" *STANDARD-OUTPUT*)
   (PRINC "err" *ERROR-OUTPUT*)
   (PRINC "trace" *TRACE-OUTPUT*)
   (PRINC "terminal " *TERMINAL-IO*)
   (PRINC "query " *QUERY-IO*)
   (PRINC "debug " *DEBUG-IO*)
   (VALUES 1 2 3))
  T (1 2 3) "out" "err" "trace" "terminal query debug ")
 (T 861 896 (PRIN1 (1+ 2)) T (3) "3" "" "" "")
 (T 896 905 (TERPRI) T (NIL) "
"
  "" "" "")
 (T 905 972 (LOGG :ERROR "Oops, I wanted to print (+ 1 2)") T (NIL) ""
  "Oops, I wanted to print (+ 1 2)
"
  "" ""))




And of course, do not write it in C, it's much easier in Lisp!  Just
call it from C, and process the output list, dispatching the data
where it belongs to:

    - generated HTML,
    - an error dialog,
    - an error log,
    - whatever the browser needs the various parts to be sent to.



-- 
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
----------> http://www.netmeister.org/news/learn2quote.html <-----------
---> http://homepage.ntlworld.com/g.mccaughan/g/remarks/uquote.html <---

__Pascal Bourguignon__                     http://www.informatimago.com/





More information about the ecl-devel mailing list