xe: added support for symeval in expression converter

; xe = expand expression     xe :: (expr,cte) -> ast

(def xe (e cte)
  (if
    (const-expr? e)
      (xe-const-expr e cte)
    (ident-expr? e)
      (xe-ident-expr e cte)
    (form-expr? e)
      (xe-form-expr e cte)
      (err "syntax-error" e)))

(def const-expr? (e)
  (or (in e t nil) (in (type e) 'int 'num 'char 'string)))

(def ident-expr? (e)
  (isa e 'sym))

(def form-expr? (e)
  (and e (alist e))) ; a non-empty list

(def xe-const-expr (e cte)
  (make-lit '() e))

(def xe-ident-expr (e cte)
  (let b (xe-lookup e cte)
    (if (avar b)
      (make-ref '() b)
      (err "can't reference a nonvariable" e))))

(def xe-form-expr (e cte)
  (let h (car e)
    (let b (and (ident-expr? h) (xe-lookup h cte))
      (if (amacro b)
        (b!expander e cte)
        (make-app (xe-exprs e cte))))))

(def xe-exprs (le cte)
  (map [xe _ cte] le))

(= initial-ctes* '())

(mac add-macro (name nbargs)
  (w/uniq prim-name
    `(let ,prim-name (coerce (+ "%" (coerce ',name 'string)) 'sym)
      (push (make-macro ',name
        (fn (e cte)
          (if (is (len (cdr e)) ,nbargs)
            (make-prim (xe-exprs (cdr e) cte) ,prim-name)
            (err:string ',name " : expects " ,nbargs "arg(s)")))) initial-ctes*))))

(add-macro < 2)
(add-macro > 2)
(add-macro <= 2)
(add-macro >= 2)
(add-macro + 2)
(add-macro - 2)
(add-macro * 2)
(add-macro / 2)
(add-macro mod 2)

(push (make-macro 'do
  (fn (e cte)
    (if
      (is (len (cdr e)) 0)
        (xe nil cte)
      (is (len (cdr e)) 1)
        (xe (cadr e) cte)
        (make-seq (xe-exprs (cdr e) cte))))) initial-ctes*)

(push (make-macro 'let
  (fn (e cte)
    (if (>= (len (cdr e)) 1)
      (xe (list (+ (list 'fn (list e.1)) (cut e 3)) e.2) cte)
      (err "let expects a binding")))) initial-ctes*)

(push (make-macro 'or
  (fn (e cte)
    (if
      (is (len (cdr e)) 0)
        (xe nil cte)
      (is (len (cdr e)) 1)
        (xe (cadr e) cte)
        (xe `((lambda (t1 t2) (if t1 t1 (t2))) ,(cadr e) (lambda () (or ,@(cddr e)))) cte)))) initial-ctes*)

(push (make-macro 'and
  (fn (e cte)
    (if
      (is (len (cdr e)) 0)
        (xe t cte)
      (is (len (cdr e)) 1)
        (xe (cadr e) cte)
        (xe `((lambda (t1 t2) (if t1 (t2) t1)) ,(cadr e) (lambda () (and ,@(cddr e)))) cte)))) initial-ctes*)

(push (make-macro '=
  (fn (e cte)
    (xe (cons 'set (cdr e)) cte))) initial-ctes*)

(push (make-macro 'def
  (fn (e cte)
    (xe `(set ,(cadr e) (fn ,(car:cddr e) ,@(cdr:cddr e))) cte))) initial-ctes*)

;-----------------------------------------------------------------------------
; all the macros above this line should eventually be ported
; into lib-ac.scm.arc or in our version of arc.arc

(push (make-macro 'quote
  (fn (e cte)
    (if (is (len (cdr e)) 1)
      (make-quote '() (cadr e))
      (err "quote expects 1 arg")))) initial-ctes*)

(push (make-macro 'set
  (fn (e cte)
    (if (is (len (cdr e)) 2)
      (let b (xe-lookup (cadr e) cte)
        (if (avar b)
          (make-set (xe-exprs (cddr e) cte) b)
          (err "can't set a nonvariable" e)))
      (err "set expects 2 args")))) initial-ctes*)

(push (make-macro 'if
  (fn (e cte)
    (if
      (is (len (cdr e)) 3)
        (make-cnd (xe-exprs (cdr e) cte))
      (is (len (cdr e)) 2)
        (xe `(if ,(cadr e) ,(car:cddr e) nil) cte)
        (err "if expects 2 or 3 args")))) initial-ctes*)

(push (make-macro 'fn
  (fn (e cte)
    (let (_ exp-params . body) e
      (if (>= (len (cdr e)) 1)
        (withs
          ; support proper and improper lists
          ; as argument list: (arg arg) or (arg . arg)
          (params (map-improper new-var exp-params)
           proper-params (if (alist params) (makeproper params) (cons params nil))
           new-cte (extend proper-params cte))
          (make-lam (list:xe (cons 'do body) new-cte) params))
        (err "fn expects a parameter list"))))) initial-ctes*)

(push (make-macro 'symeval
  (fn (e cte)
    (if (isnt 2 (len e)) (err "symeval expects 1 arg"))
    (let (_ form) e
      (if (caris form 'quote)
          (make-ref '() (new-global (cadr form)))
          (make-prim (list:xe form cte) '%symeval))))) initial-ctes*)

(def make-initial-cte ()
  initial-ctes*)

(def xe-lookup (id cte)
  (or
    (lookup id cte)
    (lookup id xe-global-cte*)
    (let v (new-global id)
      (push v xe-global-cte*)
      v)))

(= xe-global-cte* (make-initial-cte))

(def parse-file (filename)
  (= xe-global-cte* (make-initial-cte))
  (xe
    (w/infile f filename
      (cons 'do (readall f
                         (list 'used-to-detect-eof))))
    '()))

; xe = expand expression     xe :: (expr,cte) -> ast

(def xe (e cte)
  (if
    (const-expr? e)
      (xe-const-expr e cte)
    (ident-expr? e)
      (xe-ident-expr e cte)
    (form-expr? e)
      (xe-form-expr e cte)
      (err "syntax-error" e)))

(def const-expr? (e)
  (or (in e t nil) (in (type e) 'int 'num 'char 'string)))

(def ident-expr? (e)
  (isa e 'sym))

(def form-expr? (e)
  (and e (alist e))) ; a non-empty list

(def xe-const-expr (e cte)
  (make-lit '() e))

(def xe-ident-expr (e cte)
  (let b (xe-lookup e cte)
    (if (avar b)
      (make-ref '() b)
      (err "can't reference a nonvariable" e))))

(def xe-form-expr (e cte)
  (let h (car e)
    (let b (and (ident-expr? h) (xe-lookup h cte))
      (if (amacro b)
        (b!expander e cte)
        (make-app (xe-exprs e cte))))))

(def xe-exprs (le cte)
  (map [xe _ cte] le))

(= initial-ctes* '())

(mac add-macro (name nbargs)
  (w/uniq prim-name
    `(let ,prim-name (coerce (+ "%" (coerce ',name 'string)) 'sym)
      (push (make-macro ',name
        (fn (e cte)
          (if (is (len (cdr e)) ,nbargs)
            (make-prim (xe-exprs (cdr e) cte) ,prim-name)
            (err:string ',name " : expects " ,nbargs "arg(s)")))) initial-ctes*))))

(add-macro < 2)
(add-macro > 2)
(add-macro <= 2)
(add-macro >= 2)
(add-macro + 2)
(add-macro - 2)
(add-macro * 2)
(add-macro / 2)
(add-macro mod 2)

(push (make-macro 'do
  (fn (e cte)
    (if
      (is (len (cdr e)) 0)
        (xe nil cte)
      (is (len (cdr e)) 1)
        (xe (cadr e) cte)
        (make-seq (xe-exprs (cdr e) cte))))) initial-ctes*)

(push (make-macro 'let
  (fn (e cte)
    (if (>= (len (cdr e)) 1)
      (xe (list (+ (list 'fn (list e.1)) (cut e 3)) e.2) cte)
      (err "let expects a binding")))) initial-ctes*)

(push (make-macro 'or
  (fn (e cte)
    (if
      (is (len (cdr e)) 0)
        (xe nil cte)
      (is (len (cdr e)) 1)
        (xe (cadr e) cte)
        (xe `((lambda (t1 t2) (if t1 t1 (t2))) ,(cadr e) (lambda () (or ,@(cddr e)))) cte)))) initial-ctes*)

(push (make-macro 'and
  (fn (e cte)
    (if
      (is (len (cdr e)) 0)
        (xe t cte)
      (is (len (cdr e)) 1)
        (xe (cadr e) cte)
        (xe `((lambda (t1 t2) (if t1 (t2) t1)) ,(cadr e) (lambda () (and ,@(cddr e)))) cte)))) initial-ctes*)

(push (make-macro '=
  (fn (e cte)
    (xe (cons 'set (cdr e)) cte))) initial-ctes*)

(push (make-macro 'def
  (fn (e cte)
    (xe `(set ,(cadr e) (fn ,(car:cddr e) ,@(cdr:cddr e))) cte))) initial-ctes*)

;-----------------------------------------------------------------------------
; all the macros above this line should eventually be ported
; into lib-ac.scm.arc or in our version of arc.arc

(push (make-macro 'quote
  (fn (e cte)
    (if (is (len (cdr e)) 1)
      (make-quote '() (cadr e))
      (err "quote expects 1 arg")))) initial-ctes*)

(push (make-macro 'set
  (fn (e cte)
    (if (is (len (cdr e)) 2)
      (let b (xe-lookup (cadr e) cte)
        (if (avar b)
          (make-set (xe-exprs (cddr e) cte) b)
          (err "can't set a nonvariable" e)))
      (err "set expects 2 args")))) initial-ctes*)

(push (make-macro 'if
  (fn (e cte)
    (if
      (is (len (cdr e)) 3)
        (make-cnd (xe-exprs (cdr e) cte))
      (is (len (cdr e)) 2)
        (xe `(if ,(cadr e) ,(car:cddr e) nil) cte)
        (err "if expects 2 or 3 args")))) initial-ctes*)

(push (make-macro 'fn
  (fn (e cte)
    (let (_ exp-params . body) e
      (if (>= (len (cdr e)) 1)
        (withs
          ; support proper and improper lists
          ; as argument list: (arg arg) or (arg . arg)
          (params (map-improper new-var exp-params)
           proper-params (if (alist params) (makeproper params) (cons params nil))
           new-cte (extend proper-params cte))
          (make-lam (list:xe (cons 'do body) new-cte) params))
        (err "fn expects a parameter list"))))) initial-ctes*)

(def make-initial-cte ()
  initial-ctes*)

(def xe-lookup (id cte)
  (or
    (lookup id cte)
    (lookup id xe-global-cte*)
    (let v (new-global id)
      (push v xe-global-cte*)
      v)))

(= xe-global-cte* (make-initial-cte))

(def parse-file (filename)
  (= xe-global-cte* (make-initial-cte))
  (xe
    (w/infile f filename
      (cons 'do (readall f
                         (list 'used-to-detect-eof))))
    '()))