[Openmcl-devel] FScript contribution

Joerg Garbers jg at cs.tu-berlin.de
Tue Feb 4 17:20:03 UTC 2003

- Writing scripts for Cocoa classes is very easily done in FScript (see 
- I find the reader macros for cocoa very useful, but still a little 
difficult to read.
- FScript comes as dynamic library (FScript.framework) with a lot of 
goodies and documentation.
- So why not connecting OpenMCL and FScript to call FScript from 
OpenMCL and vice versa?
This is done in the file attachment.


1. Whoever wrote the cocoa examples, thanks a lot!
2. Does anybody know, if the commercial MCL supports cocoa classes 
(windows etc.) too?

fscript.lisp (to be put into the ccl/examples folder):

;;;-*-Mode: LISP; Package: CCL -*-
;;;   file: fscript.lisp
;;;   Copyright (C) 2003 Joerg Garbers
;;;   You may use this code under the  terms of the Lisp Lesser GNU 
;;;   License.
;;;   Purpose: provide helper functions for connecting OpenMCL and 
;;;            enabling to call FScript from OpenMCL and vice versa
;;;   Background:
;;;            FScript is an OpenSource scripting language for Cocoa.
;;;            See http://www.fscript.org (Philippe Mougin).
;;;            FScript.framework must be installed in 
;;;            or change the open-shared-library line.
;;;   Reason:
;;;            FScript allows to write interpreted Cocoa code more
;;;            readable and fault tolerant.
;;;            (See examples at the end of this file.)
;;;            This bridge might also be very interesting to FScript 
;;;            because OpenMCL allows to access standard c functions, 
;;;            are not accessible in FScript.
;;; Most interesting exported functions:
;;; (fs-eval "script") -> lisp string
;;; (fs-result "script") -> c pointer (id)
;;; -[System lisp:(NSString *)]

(in-package "CCL")

(require "cocoa")
;or, if AppKit is not needed: (require "cocoa-window") 

(eval-when (:compile-toplevel :execute)
   (setq *readtable* *objc-readtable*))

;;; load FScript.framework
(open-shared-library "/Library/Frameworks/FScript.framework/FScript")
;;; or load FScript Anywhere by hand (must be launched after require 

;;;; Accessing FScript from LISP ;;;;

;;; create an FScript interpreter
;; without own user interface
(defun new-fs-interpreter ()
   [(@class "FSInterpreter") "setup" :void]
   [[(@class "FSInterpreter") "alloc"] "init"])
;; using FSAController (when FScript Anywhere was applied to 
(defun new-fsa-interpreter ()
   (let* ((fsacontroller [[(@class "FSAController") "alloc"] "init"]))
      [[[fsacontroller "interpreterView"] "interpreter"] "retain"]))

;;; global FScript interpreter reference
(defvar *fsinterpreter* nil)
(setf *fsinterpreter* (new-fs-interpreter))

;;; evaluates script (a lisp string) returning an FSInterpreterResult 
(defun fs-result (script &optional (fs-interpreter *fsinterpreter*))
   (let ((result [fs-interpreter "execute:" :id (%make-nsstring 
     (if (= [result "isOk" :<BOOL>] #$YES)
       [result "result"]
       (error (concatenate 'string "fs-result: FSInterpreter error 
message:" (%get-cstring [[result "errorMessage"] "cString" :address]) " 
for sent Script: " script)))))

;;; evaluates script (a lisp string) returning a lisp string
(defun fs-eval (script &optional (fs-interpreter *fsinterpreter*))
   (let ((result-id (fs-result script fs-interpreter)))
      (if (%null-ptr-p result-id) "nil"
         (let ((c-string [[result-id "description"] "cString" :address]))
            (if (%null-ptr-p c-string) (error "Cocoa error: object did 
not return proper description")
              (%get-cstring c-string))))))

;;;; Accessing LISP from FScript ;;;;

;; there is an instance of class System called "sys" in every FScript 
interpreter environment
(def-objc-class "System" "NSObject"

(define-objc-method ("lisp:" "System")
     (:id string :id)
   (let* ((c-string [string "cString" :address])
          (lisp-string (%get-cstring c-string))
;         (reply-string (concatenate 'string lisp-string "done"))
          (reply-string (princ-to-string (eval (read-from-string 
          (reply-nsstring (%make-nsstring reply-string)))

;;;; Testing                     ;;;;
(require "fscript")
(in-package "CCL")
(setq *readtable* *objc-readtable*)

;; FScript from Lisp
(fs-eval "w:=5+2")
(fs-eval "w")
(fs-eval "[w := NSWindow alloc initWithContentRect:(100<>100 
extent:300<>300) styleMask:NSTitledWindowMask+NSClosableWindowMask 
backing:NSBackingStoreBuffered defer:false.w orderFront:nil] value")

;; This graphic animation must be evaluated in the listener window
;; (not in the terminal window!)
;; The FScript Code is stolen from the FScript tutorial.
(fs-eval "
keyWindow := NSApplication sharedApplication keyWindow.
NSBezierPath setDefaultLineWidth:20.
keyWindow contentView lockFocus.
1 to:550 by:4 do:
path := NSBezierPath bezierPathWithOvalInRect:(x<>130 extent:200 
(NSColor colorWithDeviceRed:x/570 green:0.1 blue:1 -(x/570) alpha:1) 
path stroke.
keyWindow flushWindow.
NSColor whiteColor set.
path setLineWidth:path lineWidth + 2.
path stroke.
keyWindow contentView unlockFocus.
keyWindow display.

;; embed a Lisp call into a FScript call
(fs-eval "'a' ++ (sys lisp:'\"b\"')")

;;; recursive calling test
;; define FScript function fac in FScript environment.
;; it calls Lisp recursively
(fs-eval "fac:=[:n | (n isEqual:0) ifTrue:[1] ifFalse:[n*((sys 
lisp:('(fac ' ++ (n-1) ++ ')')) intValue)]].")
;; define Lisp function fac
;; it calls FScript
(defun fac (n)
   (if (= n 0) 1 (* n (read-from-string (fs-eval (concatenate 'string 
"fac value:" (princ-to-string (- n 1))))))))

;; note, that FScript uses 32 bit integers, so the result is not 
correct for large numbers.
(fac 20)

;;;; Performance                 ;;;;

;; for performance tests: a comparable lisp function, which includes
;; an equal amount of actions:
;; interpreter actions: (read, eval)
;; string manipulations: construct call for n-1,
;;    call (other) interpreter
;;    decode result string
(defun fac-lisp (n)
   (if (= n 0) 1 (* n (read-from-string (princ-to-string (eval 
(read-from-string (concatenate 'string "(fac-lisp " (princ-to-string (- 
n 1)) ")"))))))))
(defun fac-lisp-plain (n)
   (if (= n 0) 1 (* n (fac-lisp-plain (- n 1)))))

(setq n 100)
(time (fac n))
(time (fac-lisp-plain n))
(time (fac-lisp n))
(time (fac n))
;; the durations are aproximately equal.
;; note that lisp does long integer arithmetic which takes also some 
;; and that without string operations fac-lisp-plain is much! faster
;; but of cause, we do not need Cocoa or Lisp to do the basic 
arithmetic functions, do we?

; these numbers are feasable on a 866 MHz G4 (between 0.1 and 1 seconds)
(time (dotimes (x 100000000) (list "42")))
(time (dotimes (x 1000000) (eval "42")))
(time (dotimes (x 100000) (eval (read-from-string "42"))))
(time (dotimes (x 1000) (fs-eval "42")))
(time (dotimes (x 1000) (fs-eval "[:x |'42'] value:1. nil")))
(time (fs-eval "[:x |'42'] value:@(10000 iota). nil"))
(let ((block (fs-result "[:x |'42']"))
       (arg (fs-result "1.0")))
   (time (dotimes (x 10000) [block "value:" :id arg])))

Openmcl-devel mailing list
Openmcl-devel at clozure.com

More information about the Openmcl-devel mailing list