public
Description: Arc Lisp to C compiler
Homepage:
Clone URL: git://github.com/sacado/arc2c.git
arc2c / make-eval.arc
100644 125 lines (120 sloc) 4.186 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
; make-eval.arc
; by AmkG
; An evaller for macros
 
; notes:
; 1. the created 'eval simulates a global environment
; based on the current Arc environment, but attempts
; to prevent the current Arc environment from being
; mutated by interpreted code. Basically any globals
; read will be copied to a table shadowing the globals
; - if a global is referenced by 'evaled code, it is
; first searched in the shadow table
; 2. we *create* a new eval for each run. Since this
; eval actually keeps a table which shadows globals,
; this prevents previous compilation runs from
; impacting the current run.
; The above are necessary to prevent macro code from
; overriding the compiler code; we can't directly use
; 'eval ^^
 
(def make-eval ()
  " Creates an 'eval-like function for macros. "
  (withs (global-env (table)
          deep-copy nil
          symeval
            (rfn symeval (e (o env global-env))
              (unless (isa e 'sym)
                (err:tostring:pr "make-eval/symeval: not a symbol - " e))
              (if env
                (aif (env e)
                     it
                     (symeval e (env "parent")))
                ; protect the global: make a copy of
                ; it, don't let the target mutate
                ; it directly.
                (= global-env.e (deep-copy:eval e))))
          macro-expand
            (fn (macname . args)
              (let macfn (symeval macname)
                (apply (rep macfn) args))))
    (= deep-copy
       (fn (o)
         (if
           ; annotated object
           (isnt (rep o) o)
             (annotate (deep-copy:type o) (deep-copy:rep o))
           ; list
           (acons o)
             (cons (deep-copy:car o) (deep-copy:cdr o))
           ; string
           (isa o 'string)
             (copy o)
           ; table
           (isa o 'table)
             (let rv (table)
               (ontable k v o
                 (= rv.k (deep-copy v)))
               rv)
           ; thread-local
           (isa o 'thread-local)
             (let rv (thread-local)
               (= (rv) (o))
               rv)
           ; anything else
             o)))
    ; overload 'type to convert 'make-eval-interpreted-fn to 'fn
    (= global-env!type
       (fn (o)
         (let typ (type o)
           (if (is typ 'make-eval-interpreted-fn)
               'fn
               typ))))
    ; the eval function
    (afn (e (o env global-env))
      (zap remove-ssyntaxes e)
      (if
        (isa e 'sym)
          (symeval e env)
        (isa e 'cons)
          (if (and (isa (car e) 'sym) (isa (symeval:car e) 'mac))
              ; macro expand
              (self:macro-expand e)
              ; check the expression
              (case (car e)
                ; TODO: special handling for:
                ; quote quasiquote if set lset
                ; (arc does *not* special-case anything else)
                fn
                  (annotate 'make-eval-interpreted-fn
                            (table 'params (cadr e)
                                   'code (cddr e)
                                   'closure env
                                   'evaller self))
                ; *everything* else is a function apply
                  (apply (self:car e) (map self e))))
          ; everything else evals to itself
          e))))
 
; to allow "real" functions to call 'make-eval-interpreted-fn
(defcall make-eval-interpreted-fn (f . args)
  (with (f (rep f)
         new-env (table)
         rv nil
         destructure
           (afn () ()))
    ; assign arguments to parameter variables
    ((afn (params args)
       (if
         (no params)
           (if args (err "make-eval fn object: Too many arguments"))
         (acons params)
           ; TODO: destructure function
           (if (acons:car params)
               (destructure new-env params args)
               (do (= (new-env:car params) (car args))
                   (self (cdr params) (cdr args))))
         ; a "rest" argument
           (= new-env.params args)))
     f!params args)
    (= (new-env "parent") f!closure)
    (each e f!code
      (= rv (f!evaller e new-env)))
    rv))