Home

Awesome

as_lisp

as_lisp is a Lisp dialect implemented in Actionscript.

It's a bytecode compiled language, and comes with its own compiler and a small bytecode interpreter, both written in Actionscript. The language includes the typical Lisp-dialect features you'd expect, like proper closures, tail-call optimization, and macros.

Language implementation should be pretty readable and easy to extend (which also means: it's not particularly optimized). Compiler and bytecode design are heavily influenced by (ie. cribbed from) Quinnec's "Lisp in Small Pieces" and Norvig's "Principles of Artificial Intelligence Programming" . Standing on the shoulders on giants. :)

as_lisp is intended to be used as a library, embedded in another host program, and not a standalone executable. The compiler, bytecode interpreter, and runtime environment, are all easy to access and manipulate from host programs. A simple REPL console host app is included as an example. Since as_lisp can run inside the Adobe Flash runtime, it's compatible with a wide range of environments, including web browsers, and standalone AIR apps (iOS, Android, PC/Mac).

This is very much a work in progress, so please pardon the dust, use at your own risk, and so on. :)

USAGE

var ctx :Context = new Context(null, true); // make a new vm + compiler
ctx.execute("(+ 1 2)");                     // => [ 3 ]

LANGUAGE DETAILS

Value types:

Small set of reserved keywords - everything else is a valid symbol

Tail calls get optimized during compilation, without any language hints

  (define (rec x) (if (= x 0) 0 (rec (- x 1))))
  (rec 1000000) ;; look ma, no stack overflow!

Quotes, quasiquotes and unquotes are supported in the Lisp fashion:

  'x                 ;; => 'x
  `x                 ;; => 'x
  `,x                ;; => x
  `(1 ,(list 2 3))   ;; => '(1 (2 3))
  `(1 ,@(list 2 3))  ;; => '(1 2 3)

Closures

  (set! fn (let ((sum 0)) (lambda (delta) (set! sum (+ sum delta)) sum))) 
  (fn 0)    ;; => 0
  (fn 100)  ;; => 100
  (fn 0)    ;; => 100

Macros are more like Lisp than Scheme.

  ;; (let ((x 1) (y 2)) (+ x 1)) => 
  ;;   ((lambda (x y) (+ x y)) 1 2)
  (defmacro let (bindings . body) 
    `((lambda ,(map car bindings) ,@body) 
      ,@(map cadr bindings)))

Macroexpansion - single-step and full

  (and 1 (or 2 3))         ;; => 2
  (mx1 '(and 1 (or 2 3)))  ;; => (if 1 (core:or 2 3) #f)
  (mx '(and 1 (or 2 3)))   ;; => (if 1 (if* 2 3) #f)

Built-in primitives live in the "core" package and can be redefined

  (+ 1 2)               ;; => 3
  (set! core:+ core:*)  ;; => [Closure]
  (+ 1 2)               ;; => 2

Packages

  (package-set "math")       ;; => "math"
  (package-get)              ;; => "math"
  (package-import ("core"))  ;; => null
  (package-export '(sin cos))

Built-in primitives are very bare bones (for now):

TODOS
KNOWN BUGS
COMPILATION EXAMPLES
inputs:  (+ 1 2)
parsed:  (core:+ 1 2)
  ARGS  0
  CONST 1
  CONST 2
  GVAR  core:+
  CALLJ 2

inputs:  (begin (+ (+ 1 2) 3) 4)
parsed:  (begin (core:+ (core:+ 1 2) 3) 4)
  ARGS  0
  SAVE  "K0"  11
  SAVE  "K1"  7
  CONST 1
  CONST 2
  GVAR  core:+
  CALLJ 2
LABEL "K1"
  CONST 3
  GVAR  core:+
  CALLJ 2
LABEL "K0"
  POP
  CONST 4
  RETURN

inputs:  ((lambda (a) a) 5)
parsed:  ((lambda (a) a) 5)
  ARGS  0
  CONST 5
  FN  [Closure] ; (a)
    ARGS  1
    LVAR  0 0 ; a
    RETURN
  CALLJ 1

inputs:  (begin (set! incf (lambda (x) (+ x 1))) (incf (incf 5)))
parsed:  (begin (set! foo:incf (lambda (foo:x) (core:+ foo:x 1))) (foo:incf (foo:incf 5)))
  ARGS  0
  FN  [Closure] ; ((core:+ foo:x 1))
    ARGS  1
    LVAR  0 0 ; foo:x
    CONST 1
    GVAR  core:+
    CALLJ 2
  GSET  foo:incf
  POP
  SAVE  "K0"  8
  CONST 5
  GVAR  foo:incf
  CALLJ 1
LABEL "K0"
  GVAR  foo:incf
  CALLJ 1