« Next Steps…? It’s not Easter, but I’m back from the dead! »

Ouija: A Scheme Interpreter in Flash

Today I’m releasing to you, the world, my Scheme interpreter in Flash. This is in no way a polished thing, so don’t expect a ton, but it is pretty cool in my opinion. In the past few months I’ve created a few flash prototypes with it because it lets me develop significantly faster than with just plain ActionScript.

Please note while reading this that the company that I was once employed at is no longer, and I am currently looking for employment. So, if you are reading this and saying to yourself, “Wow, I wish I could hire someone that could make me a Scheme interpreter in Flash”, you are in luck!

Anyway, here’s a REPL for you to play with, the top portion is interactive while the bottom is persistent, allowing you to put code there which remains when you refresh or come back later. The REPL does have it’s problems, but it’s good enough to let you play with it. The SWF file that you’re hitting is loading REPL.oui when it starts, but only because the SWF name is REPL.swf. If you download REPL.swf and rename it Test.swf, it will attempt to load Test.oui at runtime.

The differences between this and the Lisp Interpreter I made a while back are as follows:

  • Scheme syntax instead of Common Lisp
  • Closures work
  • Continuations
  • FULL ACCESS TO THE WHOLE FLASH8 RUNTIME, this allows you to do graphics, sound, animation and anything else you can do with Flash normally
  • In AS2 as opposed to AS3; I did this due to the flexibility for AS2 over AS3, and my hope is to port it very shortly.

So if you know Scheme already you can start messing around with things like

(define a '(1 2 3 4))
(apply + a)

The next step is doing things with the Flash side.

(define pic (create-clip))
(set! (_x pic) 200)
(load-image pic "ouija.jpg")
(set! (_scale pic) 50)

So now you have a movieClip on the stage with an image loaded into it and sized to 50%. Normally in Flash we’d have to call a load command and set up an event to fire when it was loaded so that we could start doing things with it, but because we’re using Scheme, we actually can halt execution of code until the image is loaded (this is the default behavior, but we can still utilize the event-driven method by using threads). We can do the same thing with tweening.


(transition 24
(pic
('_x :to 300)
('_rotation :to 360)))

When you’re done messing with the image you can remove it like this


((=> pic 'remove-movie-clip))

This is the basic method of interacting with the Flash API. In Flash a movieclip has a method on it called removeMovieClip. By calling (=> pic ‘remove-movie-clip) we are getting that method, and by wrapping it in an extra set of parenthesis we are calling it (note that the dashes from remove-movie-clip are removed and the letter immediately following the dash is capitalized when the method is looked up. This is because Ouija is case-insensitive where as ActionScript is case-sensitive).

Now, audio is another case where I don’t have any “nice” ouija methods written. So in Flash you’d have to write.


var song=new Sound();
song.loadSound("song.mp3")
//yes there is an mp3 called song.mp3 on my server
//and I'm pretty sure the writer isn't going to sue me
//for putting it up there.
song.onLoad=function() { song.start(); }

So translated directly into Ouija it would look like this

(define song (make-instance *sound))
;the * at the beginning is to make the first letter caps
((=> song 'load-sound) "song.mp3")
(set! (=> song 'on-load) (lambda () ((=> song 'start))))

And then to stop it


((=> song 'stop))

In all, there are quite a lot of things you can do and ideas to play with. I’m sure there are a ton of broken things, but I figure if I don’t release this thing now, I never will.

Also if you’re interested you can check out this site I created in Ouija (here is the source code, also note that the version of the SWF contains some extras not in the REPL). After I finished this version I hand-compiled it so that it would run more quickly on the pre-Intel Mac versions of the Flash Player and then we updated some things. That version is here. After that, the company shutdown.

Alright, so here’s a list of functions and constants in Ouija.

Constants

  • t or #t : the true boolean
  • nil or #f : unlike Scheme, Ouija uses the Common Lisp nil as both false and the end of a list, #f is also defined, but it is a reference to the NIL object
  • _root : this is a reference to the root MovieClip of the running flash
  • pi, 2pi, pi/2, pi/3, pi/4, pi/6, pi/12 : pi and common fractions of pi

List Functions

  • (cons a b) : creates a cons cell
  • (car a) (cdr a) (caar a) (cadr a) (cdar a) (cddr a) : these can also be used with set!, (set! (cadr a) 5)
  • (list 1 2 3 4 5) : creates a list
  • (list* 1 2 3 4 5) : creates a list with the last cell being a dotted pair, in this case (1 2 3 4 . 5)
  • (append list1 list2 …) : returns new list with list1 appended to the beginning of list2
  • (append! list1 list2 …) : destructive version of above
  • (reverse lst) : reverses list
  • (reverse! lst) : destructive version of above
  • (map fn lst …) : return new list by applying fn to each element of lst, can take multiple lists
  • (for-each fn lst) : like map, but only for side-effects, doesn’t return a list
  • (map-append fn lst) : like map only it appends instead of consing
  • (filter fn lst) : returns new list filled with elements that didn’t return NIL when passed to fn
  • (foldl fn init lst) : left fold
  • (foldr fn init lst) : right fold
  • (compose fn1 fn2 …) : returns a function composed of the arguments
  • (nth index lst) : returns the nth element in list, also works with set!
  • (nthcdr index lst) : like about, but returns whole cons cell
  • first, second ….. tenth : also works for set!
  • (copy-list lst) : return copy of list
  • (last lst) : returns the last cons cell in the list
  • (butlast lst) : returns a copy of the list without it’s last element
  • (length lst) : returns the length of the list, also works for arrays
  • (getarg prop-list key) : returns the value of the associated value in a prop-list
  • (filter-out-keys prop-list remove-list) : returns new prop-list with specified keys removed
  • (member value list) : finds value in list, returns the rest of the list

Math Stuff

  • = + - * / < > <= >=: all take multiple parameters
  • max min
  • sqrt cos sin tan acos asin atan atan2 floor ceiling round expt modulo truncate quotient remainder

String Stuff

  • (->string arg) : converts argument to string
  • (mkstr arg1 arg2 …) : converts each arg to a string and concatenates them all
  • (string-append arg1 arg2 …) : like mkstr, but it assumes all args are strings already
  • (format ctrl-string arg1 arg2 …) : only supports ~a ~n ~x and ~~.

Object Stuff

  • (make-instance class arg1 arg2 …) : creates a new instance of a class
  • (=> obj ’slot) : returns property of an object, works with set!
  • (create . prop-list) : creates object ex: (create :name “Chuck Loads” :age 30 :sex ‘male)
  • (array arg1 arg2 …) : creates array
  • (with obj . body) : allows you to execute code in the scope of the given object

Normal Scheme Stuff

  • if lambda eq? equal? quote and or when unless push! pop! inc! dec! not let (normal and named) fluid-let call/cc let/cc dynamic-wind display newline
  • cond (use t instead of else)
  • define : can be used to define curried functions as well (define ((add a) b) (+ a b))

Predicates

  • boolean? number? atom? pair? list? symbol? keyword? procedure? null? zero? negative? positive? odd? even?

Flash Stuff

  • (create-clip) : create movieclip, optionally takes parent clip
  • (create-textfield) : creates textfield, optionally takes parent clip
  • ((=> clip ‘line-style) 1 0 100) : need to do this or something like it to draw on the clip
  • (draw-rect clip x y width height)
  • (draw-line clip x y width height)
  • (draw-rounded-rect clip x y width height radius)
  • (draw-circle clip x y r)
  • (transition frames . data) : see the example
  • _visible _alpha _x _y _xscale _yscale _scale _rotation _width _height : function accessors for movieclip properties
  • (return-click) : waits for user to click on something and then returns a reference to what was clicked.
  • (get-timer) : return ticks since swf started
  • (update-stage) : updates stage during execution of a function
  • (display-layout parent . children) : creates a movieclip and textfield hierarchy, example below


(display-layout image-content
(movie-clip (:id mc :_visible #f)
(text-field (:id title
:font "ITC Franklin Gothic Demi" :size 26
:auto-size "left" :text-color 0×5f2c1f
:embed-fonts #t :selectable #f
:_x 26 :_y 20
:text (=> content-data 'title)))
(text-field (:id body
:font "ITC Franklin Gothic Book" :size 14
:auto-size "left" :text-color 0×333333
:multiline #t :word-wrap #t :embed-fonts #t
:_x 280 :_y 60 :_width 260
:html-text (=> content-data 'text)))
(movie-clip (:_x 30 :_y 65
:src "test.jpg")))))

Other stuff

  • (eval val) : evaluates symbol, value or s-expression
  • (load file) : loads and executes Ouija code from a file
  • (read) : only works in repl, gets value from user
  • (read-from-string str) : parses string into symbol, number or s-expression
  • (gensym) : create symbol
  • (defmacro (name . args) . body) : Common Lisp style macro; Ouija does not do Scheme macros
  • (thunk . body) : same as (lambda () . body)
  • (amb arg1 arg2 …) : you should know what this does; if not, look it up
  • (amb-assert test)
  • (amb-collect . body)
  • (random (&optional n)) : returns random number
  • (random-char) : return random character
  • (random-string (&optional length)) : returns random string
  • (thread fn) : calls fn in a separate thread
  • (sleep seconds) : waits for a certain amount of time before execution is resumed
  • (dotimes (i times) . body) : standard CL dotimes
  • (dolist (i lst) . body) : standard CL dolist
  • (in-parallel fn1 fn2 …) : executes each function in a “thread”, then returns a list of the results
  • (map-in-parallel fn lst) : like map, only it does each in a “thread” instead of sequentially

Comments

  1. brett | February 27th, 2008 | 3:50 pm

    Could you explain why this is useful to Flash developers? Why abstract Actionscript to a language that is more cumbersome to use? I mean, kudos to you, this is a cool achievement, but is it pragmatic?

  2. admin | February 27th, 2008 | 4:05 pm

    @Brett: I have found that ActionScript is way more painful to use. I’ve been developing in AS since the very beginning and only found Lisp/Scheme a few years ago and have been able to be way more productive in Scheme than I ever had in Flash. I believe this has to do with High-level features like Macros and Continuations. This project’s main purpose was to allow me to have that level of productivity in Flash.

  3. Wynand Winterbach | February 27th, 2008 | 6:19 pm

    This is awesome man. Kudos!

    If ever I should have to do Flash work, this is where I’d look first.

  4. James Urquhart | February 28th, 2008 | 6:36 am

    Nice work!

    I find it quite interesting how capable the flash runtime is nowadays when implementing interpreters.

    Only a few months ago, i had a go at implementing a SCUMM interpreter ( http://www.cuppadev.co.uk/2007/11/22/flash-plays-scumm-take-two/ ), and i was able to implement a surprising amount of functionality. Keep in mind though that i only had to run bytecode, rather than implement a fully featured parser so in many ways my little project is inferior to yours. :)

    In any case, keep up the good work.

  5. Norman | February 28th, 2008 | 10:12 am

    This is really nice !! Bravo !!

  6. Joel | February 28th, 2008 | 11:24 am

    Have you found an effective way to get AS3 to do the ‘eval()’ stuff like AS2 does?

  7. enrico | February 28th, 2008 | 12:09 pm

    Very cool!! With a broad enough adoption, this is real game changer. Looking forward to the AS3 port.

  8. itistoday | February 29th, 2008 | 12:22 am

    mmmyeshhh but is it therefore slower than AS3?

  9. admin | February 29th, 2008 | 12:38 pm

    @itistoday:
    Well, as2 is slower than as3, and an interpreter is slower than compiled code. My hope is to build a compiler once I’m done with the AS3 interpreter that would run the compiled code faster than normal AS3 could.

  10. itistoday | March 1st, 2008 | 1:52 pm

    @admin: Neat! Good luck! :-)

  11. Brett Morgan | March 4th, 2008 | 12:20 am

    I’d love to see the source to this one as well (I’m currently digging through your Lisp/AS3 source base).

  12. Tbone | March 7th, 2008 | 4:13 pm

    Are you going to release the source code so we can play with this at some point?

  13. admin | March 8th, 2008 | 10:51 pm

    @TBag:
    I’m planning on it, just been busy

  14. daniel | April 25th, 2008 | 2:20 am

    hey, this is awesome! a very portable scheme interpreter with excellent multimedia support.

    any chance for the source code at some point? or any suggestions for how to move beyond the abilities shown off in the examples?

    thanks a lot!

  15. admin | April 28th, 2008 | 10:47 pm

    @Daniel:

    I’m sure I’ll release the source code eventually, but for the mean time I would suggest learning the Flash 8/AS2 API, once you know that combined with the list of all the commands you should be in good shape.

  16. Daniel | April 29th, 2008 | 1:52 am

    Thanks!

  17. Raoul Duke | May 1st, 2008 | 10:31 pm

    that rocks. any chance of a command to import external swfs by url? or some such thing?

  18. admin | May 4th, 2008 | 10:54 pm

    @Mr. Duke
    you should be able to load swfs just like how I loaded images in the example. If it doesn’t work it’s the fault of the crazy cross domain security settings in the flash Player.

  19. Solve et Coagula » Blog Archive » Scheme in Flash questions | September 8th, 2008 | 11:22 pm

    […] I was excited because there was someone out there having the same vision that I had when creating Ouija. Like I mentioned in my last post I’m currently working on a version of Ouija that targets […]

  20. Raoul Duke | October 6th, 2008 | 9:39 pm

    publish the source some day? :-)

  21. Curran | February 9th, 2009 | 12:46 am

    I’m guessing you will never ever release the source code. It’s a shame, this project would be a great thing to use, and I’m sure the open source community would embrace it.

    I for one would really appreciate it if you released the source code. Anyway, congrats on getting it working!

  22. JamesD | June 11th, 2009 | 9:18 am

    Thanks for the useful info. It’s so interesting

Post a comment