1 /*===========================================================================
2  *  Filename : env.c
3  *  About    : A Scheme environemnt implementation
4  *
5  *  Copyright (C) 2005      Kazuki Ohta <mover AT hct.zaq.ne.jp>
6  *  Copyright (C) 2005-2006 Jun Inoue <jun.lambda AT gmail.com>
7  *  Copyright (C) 2005-2006 YAMAMOTO Kengo <yamaken AT bp.iij4u.or.jp>
8  *  Copyright (c) 2007-2008 SigScheme Project <uim-en AT googlegroups.com>
9  *
10  *  All rights reserved.
11  *
12  *  Redistribution and use in source and binary forms, with or without
13  *  modification, are permitted provided that the following conditions
14  *  are met:
15  *
16  *  1. Redistributions of source code must retain the above copyright
17  *     notice, this list of conditions and the following disclaimer.
18  *  2. Redistributions in binary form must reproduce the above copyright
19  *     notice, this list of conditions and the following disclaimer in the
20  *     documentation and/or other materials provided with the distribution.
21  *  3. Neither the name of authors nor the names of its contributors
22  *     may be used to endorse or promote products derived from this software
23  *     without specific prior written permission.
24  *
25  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
26  *  IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
27  *  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28  *  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
29  *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30  *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31  *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
32  *  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
33  *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34  *  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35  *  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 ===========================================================================*/
37 
38 /*
39  *   SigScheme's environment object is a list formed as below.
40  *
41  *     frame = (cons (var1 var2 var3 ...)
42  *                   (val1 val2 val3 ...))
43  *     env   = (frame1 frame2 frame3 ...)
44  *
45  *   Other 2 forms are also used to handle dotted args.
46  *
47  *     frame = (cons (var1 var2 var3 . rest1)
48  *                   (val1 val2 val3 var4 var5 ...))
49  *
50  *     frame = (cons rest2
51  *                   (val1 val2 val3 var4 var5 ...))
52  *
53  *   In this case, rest1 is bound to (var4 var5 ...) and rest2 is bound to
54  *   (val1 val2 val3 var4 var5 ...).
55  *
56  *   The environment object should not be manipulated manually, to allow
57  *   replacing with another implementation. Use the function interfaces.
58  *
59  *   To ensure valid use of the environment objects is environment
60  *   constructor's responsibility. i.e. Any lookup operations assume that the
61  *   environment object is valid. To keep the assumption true, any environemnt
62  *   object modification and injection from user code must be
63  *   validated. Although the validation for the injection may cost high,
64  *   ordinary code only use (interaction-environment) and other R5RS
65  *   environment specifiers. Since these 'trusted' specifiers can cheaply be
66  *   identified, the validation cost is also. The validation can reject any
67  *   handmade invalid environment objects.
68  */
69 
70 #include <config.h>
71 
72 #include "sigscheme.h"
73 #include "sigschemeinternal.h"
74 
75 /*=======================================
76   File Local Macro Definitions
77 =======================================*/
78 #define TRUSTED_ENVP(env) (EQ(env, SCM_INTERACTION_ENV)                      \
79                            || EQ(env, SCM_R5RS_ENV)                          \
80                            || EQ(env, SCM_NULL_ENV))
81 
82 /*=======================================
83   File Local Type Definitions
84 =======================================*/
85 
86 /*=======================================
87   Variable Definitions
88 =======================================*/
89 
90 /*=======================================
91   File Local Function Declarations
92 =======================================*/
93 static scm_bool valid_framep(ScmObj frame);
94 
95 /*=======================================
96   Function Definitions
97 =======================================*/
98 SCM_EXPORT scm_bool
scm_toplevel_environmentp(ScmObj env)99 scm_toplevel_environmentp(ScmObj env)
100 {
101     return NULLP(env);
102 }
103 
104 #if SCM_USE_HYGIENIC_MACRO
105 
106 /* ScmPackedEnv is scm_int_t. */
107 
108 SCM_EXPORT ScmPackedEnv
scm_pack_env(ScmObj env)109 scm_pack_env(ScmObj env)
110 {
111     scm_int_t depth;
112     DECLARE_INTERNAL_FUNCTION("scm_env_depth");
113 
114     depth = scm_length(env);
115     SCM_ASSERT(SCM_LISTLEN_PROPERP(depth));
116     return depth;
117 }
118 
119 /* Not used. */
120 SCM_EXPORT ScmObj
scm_unpack_env(ScmPackedEnv packed,ScmObj context)121 scm_unpack_env(ScmPackedEnv packed, ScmObj context)
122 {
123     scm_int_t depth;
124 
125     depth = scm_length(context);
126     while (depth-- > packed)
127         context = CDR(context);
128     return context;
129 }
130 
131 
132 static ScmRef
lookup_n_frames(ScmObj id,scm_int_t n,ScmObj env)133 lookup_n_frames(ScmObj id, scm_int_t n, ScmObj env)
134 {
135     ScmRef ref;
136 
137     while (n--) {
138         SCM_ASSERT(ENVP(env));
139         ref = scm_lookup_frame(id, CAR(env));
140         if (ref != SCM_INVALID_REF)
141             return ref;
142         env = CDR(env);
143     }
144     return SCM_INVALID_REF;
145 }
146 
147 
148 /**
149  * Resolves X in scm_unpack_env(XPENV, ENV), Y in ENV and tests
150  * whether they are bound to the same location.  The parameters x and
151  * y are thus UNINTERCHANGEABLE.
152  *
153  * The pattern matcher must compare scm_wrap_identifier(x, xpenv) with
154  * y, but for performance we'd like to do that without actually
155  * allocating the wrapper.  In the absence of syntax-case or
156  * comparable mechanisms allowing for unhygienic transforms, the
157  * binding frame of X and Y are always both contained in ENV, so we
158  * might as well require that ENV be the environment in which one of
159  * the operands (namely, Y) is to be looked up.
160  *
161  * But this is definitely an ugly interface, and also inconvenient
162  * because the function needs a different signature when unhygienic
163  * transforms are enabled.  So, FIXME: is there a better way?
164  *
165  * Moving this to macro.c can be an option, but keep in mind some
166  * aspects are inherently tightly coupled with the lookup functions.
167  */
168 SCM_EXPORT scm_bool
scm_identifierequalp(ScmObj x,ScmPackedEnv xpenv,ScmObj y,ScmPackedEnv penv,ScmObj env)169 scm_identifierequalp(ScmObj x, ScmPackedEnv xpenv,
170                      ScmObj y, ScmPackedEnv penv, ScmObj env)
171 {
172     ScmRef yloc;
173 
174     SCM_ASSERT(xpenv <= penv);
175     SCM_ASSERT(SCM_PENV_EQ(scm_pack_env(env), penv));
176 
177     while (penv-- > xpenv) {
178         if (scm_lookup_frame(y, CAR(env)) != SCM_INVALID_REF)
179             return scm_false;
180         env = CDR(env);
181     }
182     if (EQ(x, y))
183         return scm_true;
184     yloc = scm_lookup_environment(y, env);
185     if (yloc != SCM_INVALID_REF)
186         return (scm_lookup_environment(x, env) == yloc);
187     if (scm_lookup_environment(x, env) != SCM_INVALID_REF)
188         return scm_false;
189     return EQ(SCM_UNWRAP_KEYWORD(x), SCM_UNWRAP_KEYWORD(y));
190 }
191 
192 /**
193  * Returns an identifier that is bound to the same location as ID
194  * within ENV (whose packed representation is DEPTH), but is not eq?
195  * with ID.
196  */
197 SCM_EXPORT ScmObj
scm_wrap_identifier(ScmObj id,ScmPackedEnv depth,ScmObj env)198 scm_wrap_identifier(ScmObj id, ScmPackedEnv depth, ScmObj env)
199 {
200     scm_int_t id_depth;
201 
202     SCM_ASSERT(IDENTIFIERP(id));
203     SCM_ASSERT(depth == scm_pack_env(env));
204 
205     if (FARSYMBOLP(id)) {
206         /* Try to reduce lookup overhead. */
207         id_depth = SCM_FARSYMBOL_ENV(id);
208         SCM_ASSERT(id_depth <= depth);
209         if (lookup_n_frames(id, depth - id_depth, env) == SCM_INVALID_REF) {
210             /* ID hasn't been bound since it was captured. */
211             return MAKE_FARSYMBOL(SCM_FARSYMBOL_SYM(id), id_depth);
212         }
213     }
214     return MAKE_FARSYMBOL(id, depth);
215 }
216 #endif /* SCM_USE_HYGIENIC_MACRO */
217 
218 /**
219  * Construct a new frame on an env
220  *
221  * @a formals and @a actuals must be valid.
222  *
223  * @param formals Symbol list as variable names of new frame. It accepts dotted
224  *                list to handle function arguments directly.
225  * @param actuals Arbitrary Scheme object list as values of new frame.
226  *
227  * @see scm_eval()
228  */
229 SCM_EXPORT ScmObj
scm_extend_environment(ScmObj formals,ScmObj actuals,ScmObj env)230 scm_extend_environment(ScmObj formals, ScmObj actuals, ScmObj env)
231 {
232     ScmObj frame;
233     DECLARE_INTERNAL_FUNCTION("scm_extend_environment");
234 
235     SCM_ASSERT(scm_valid_environment_extensionp(formals, actuals));
236     SCM_ASSERT(VALID_ENVP(env));
237 
238     frame = CONS(formals, actuals);
239     return CONS(frame, env);
240 }
241 
242 /**
243  * Replace entire content of recentmost frame of an env
244  *
245  * The environment must be replaced with returned one in caller side even if
246  * this implementation returns identical to the one passed. This rule is
247  * required to be compatible with future alternative implementations.
248  */
249 SCM_EXPORT ScmObj
scm_replace_environment(ScmObj formals,ScmObj actuals,ScmObj env)250 scm_replace_environment(ScmObj formals, ScmObj actuals, ScmObj env)
251 {
252     ScmObj frame;
253     DECLARE_INTERNAL_FUNCTION("scm_replace_environment");
254 
255     SCM_ASSERT(scm_valid_environment_extensionp(formals, actuals));
256     SCM_ASSERT(VALID_ENVP(env));
257     SCM_ASSERT(CONSP(env));
258 
259     frame = CAR(env);
260     SET_CAR(frame, formals);
261     SET_CDR(frame, actuals);
262 
263     return env;
264 }
265 
266 /**
267  * Replace all actuals of recentmost frame of an env
268  *
269  * The environment must be replaced with returned one in caller side even if
270  * this implementation returns identical to the one passed. This rule is
271  * required to be compatible with future alternative implementations.
272  */
273 SCM_EXPORT ScmObj
scm_update_environment(ScmObj actuals,ScmObj env)274 scm_update_environment(ScmObj actuals, ScmObj env)
275 {
276     ScmObj frame;
277     DECLARE_INTERNAL_FUNCTION("scm_update_environment");
278 
279     SCM_ASSERT(VALID_ENVP(env));
280     SCM_ASSERT(CONSP(env));
281 
282     frame = CAR(env);
283     SCM_ASSERT(scm_valid_environment_extensionp(CAR(frame), actuals));
284     SET_CDR(frame, actuals);
285 
286     return env;
287 }
288 
289 /** Add a binding to recentmost frame of an env */
290 SCM_EXPORT ScmObj
scm_add_environment(ScmObj var,ScmObj val,ScmObj env)291 scm_add_environment(ScmObj var, ScmObj val, ScmObj env)
292 {
293     ScmObj frame, formals, actuals;
294     DECLARE_INTERNAL_FUNCTION("scm_add_environment");
295 
296     SCM_ASSERT(IDENTIFIERP(var));
297     SCM_ASSERT(VALID_ENVP(env));
298 
299     /* add (var, val) pair to recentmost frame of the env */
300     if (NULLP(env)) {
301         frame = CONS(LIST_1(var), LIST_1(val));
302         env = LIST_1(frame);
303     } else if (CONSP(env)) {
304         frame = CAR(env);
305         formals = CONS(var, CAR(frame));
306         actuals = CONS(val, CDR(frame));
307         SET_CAR(frame, formals);
308         SET_CDR(frame, actuals);
309     } else {
310         SCM_NOTREACHED;
311     }
312     return env;
313 }
314 
315 /**
316  * Lookup a variable of an env
317  *
318  * @return Reference to the variable. SCM_INVALID_REF if not found.
319  */
320 SCM_EXPORT ScmRef
scm_lookup_environment(ScmObj var,ScmObj env)321 scm_lookup_environment(ScmObj var, ScmObj env)
322 {
323     ScmObj frame;
324     ScmRef ref;
325 #if SCM_USE_HYGIENIC_MACRO
326     scm_int_t depth, id_depth;
327     ScmObj env_save;
328 #endif /* SCM_USE_HYGIENIC_MACRO */
329     DECLARE_INTERNAL_FUNCTION("scm_lookup_environment");
330 
331     SCM_ASSERT(IDENTIFIERP(var));
332     SCM_ASSERT(VALID_ENVP(env));
333 
334     /* lookup in frames */
335 #if SCM_USE_HYGIENIC_MACRO
336     env_save = env;
337     depth = 0;
338 #endif
339     for (; !NULLP(env); env = CDR(env)) {
340         frame = CAR(env);
341         ref = scm_lookup_frame(var, frame);
342         if (ref != SCM_INVALID_REF)
343             return ref;
344 #if SCM_USE_HYGIENIC_MACRO
345         ++depth;
346 #endif
347     }
348     SCM_ASSERT(NULLP(env));
349 
350 #if SCM_USE_HYGIENIC_MACRO
351     if (FARSYMBOLP(var)) {
352         scm_int_t i;
353         id_depth = SCM_FARSYMBOL_ENV(var);
354         if (id_depth > depth)
355             scm_macro_bad_scope(var);
356         for (i = depth - id_depth; i--; )
357             env_save = CDR(env_save);
358         ref = lookup_n_frames(SCM_FARSYMBOL_SYM(var),
359                               id_depth, env_save);
360         SCM_ASSERT(ref != SCM_INVALID_REF || SYMBOLP(SCM_FARSYMBOL_SYM(var)));
361         return ref;
362     }
363 #endif
364 
365     return SCM_INVALID_REF;
366 }
367 
368 /** Lookup a variable in a frame */
369 SCM_EXPORT ScmRef
scm_lookup_frame(ScmObj var,ScmObj frame)370 scm_lookup_frame(ScmObj var, ScmObj frame)
371 {
372     ScmObj formals;
373     ScmRef actuals;
374     DECLARE_INTERNAL_FUNCTION("scm_lookup_frame");
375 
376     SCM_ASSERT(IDENTIFIERP(var));
377     SCM_ASSERT(valid_framep(frame));
378 
379     for (formals = CAR(frame), actuals = REF_CDR(frame);
380          CONSP(formals);
381          formals = CDR(formals), actuals = REF_CDR(DEREF(actuals)))
382     {
383         if (EQ(var, CAR(formals)))
384             return REF_CAR(DEREF(actuals));
385     }
386     /* dotted list */
387     if (EQ(var, formals))
388         return actuals;
389 
390     return SCM_INVALID_REF;
391 }
392 
393 ScmObj
scm_symbol_value(ScmObj var,ScmObj env)394 scm_symbol_value(ScmObj var, ScmObj env)
395 {
396     ScmRef ref;
397     ScmObj val;
398     DECLARE_INTERNAL_FUNCTION("scm_symbol_value");
399 
400     SCM_ASSERT(IDENTIFIERP(var));
401 
402     ref = scm_lookup_environment(var, env);
403     if (ref != SCM_INVALID_REF) {
404         /* Found in the environment. Since scm_s_body() may produce unbound
405          * variables as internal definitions, subsequent error check is
406          * required. */
407         val = DEREF(ref);
408     } else {
409         /* Fallback to top-level binding. */
410 #if SCM_USE_HYGIENIC_MACRO
411         if (FARSYMBOLP(var))
412             var = SCM_FARSYMBOL_SYM(var);
413         SCM_ASSERT(SYMBOLP(var));
414 #endif
415         val = SCM_SYMBOL_VCELL(var);
416     }
417 
418     if (EQ(val, SCM_UNBOUND))
419         ERR_OBJ("unbound variable", var);
420 
421     return val;
422 }
423 
424 /*
425  * Validators
426  */
427 SCM_EXPORT scm_bool
scm_valid_environmentp(ScmObj env)428 scm_valid_environmentp(ScmObj env)
429 {
430     ScmObj frame, rest;
431     DECLARE_INTERNAL_FUNCTION("scm_valid_environmentp");
432 
433     if (TRUSTED_ENVP(env))
434         return scm_true;
435 
436     /*
437      * The env is extended and untrusted. Since this case rarely occurs in
438      * ordinary codes, the expensive validation cost is acceptable.
439      */
440 
441     if (!PROPER_LISTP(env))
442         return scm_false;
443     for (rest = env; !NULLP(rest); rest = CDR(rest)) {
444         frame = CAR(rest);
445         if (!valid_framep(frame))
446             return scm_false;
447     }
448 
449     return scm_true;
450 }
451 
452 static scm_bool
valid_framep(ScmObj frame)453 valid_framep(ScmObj frame)
454 {
455     ScmObj formals, actuals;
456     DECLARE_INTERNAL_FUNCTION("valid_framep");
457 
458     if (CONSP(frame)) {
459         formals = CAR(frame);
460         actuals = CDR(frame);
461         if (scm_valid_environment_extensionp(formals, actuals))
462             return scm_true;
463     }
464     return scm_false;
465 }
466 
467 SCM_EXPORT scm_bool
scm_valid_environment_extensionp(ScmObj formals,ScmObj actuals)468 scm_valid_environment_extensionp(ScmObj formals, ScmObj actuals)
469 {
470     scm_int_t formals_len, actuals_len;
471 
472     formals_len = scm_validate_formals(formals);
473     actuals_len = scm_validate_actuals(actuals);
474     return scm_valid_environment_extension_lengthp(formals_len, actuals_len);
475 }
476 
477 /* formals_len must be validated by scm_validate_formals() prior to here */
478 SCM_EXPORT scm_bool
scm_valid_environment_extension_lengthp(scm_int_t formals_len,scm_int_t actuals_len)479 scm_valid_environment_extension_lengthp(scm_int_t formals_len,
480                                         scm_int_t actuals_len)
481 {
482     if (SCM_LISTLEN_ERRORP(formals_len))
483         return scm_false;
484     if (SCM_LISTLEN_DOTTEDP(formals_len)) {
485         formals_len = SCM_LISTLEN_DOTTED(formals_len);
486         if (SCM_LISTLEN_PROPERP(actuals_len))
487             return (formals_len <= actuals_len);
488 
489         /* (lambda args (set-cdr! args #t) args) */
490         if (SCM_LISTLEN_DOTTEDP(actuals_len))
491             return (formals_len <= SCM_LISTLEN_DOTTED(actuals_len));
492 
493         /* (lambda args (set-cdr! args args) args) */
494         if (SCM_LISTLEN_CIRCULARP(actuals_len))  /* always true */
495             return scm_true;
496     }
497     return (formals_len == actuals_len);
498 }
499 
500 SCM_EXPORT scm_int_t
scm_validate_formals(ScmObj formals)501 scm_validate_formals(ScmObj formals)
502 {
503 #if SCM_STRICT_ARGCHECK
504     scm_int_t len;
505     DECLARE_INTERNAL_FUNCTION("scm_validate_formals");
506 
507     /*
508      * SigScheme does not perform the check for duplicate variable name in
509      * formals. It is an user's responsibility.
510      *
511      * R5RS: 4.1.4 Procedures
512      * It is an error for a <variable> to appear more than once in <formals>.
513      */
514 
515     /* This loop goes infinite if the formals is circular. SigSchme expects
516      * that user codes are sane here. */
517     for (len = 0; CONSP(formals); formals = CDR(formals), len++) {
518         if (!IDENTIFIERP(CAR(formals)))
519             return SCM_LISTLEN_ENCODE_ERROR(len);
520     }
521     if (NULLP(formals))
522         return len;
523     /* dotted list allowed */
524     if (IDENTIFIERP(formals))
525         return SCM_LISTLEN_ENCODE_DOTTED(len);
526     return SCM_LISTLEN_ENCODE_ERROR(len);
527 #else
528     /* Crashless loose validation:
529      * Regard any non-list object as symbol. Since the lookup operation search
530      * for a variable by EQ, this is safe although loosely allows
531      * R5RS-incompatible code. */
532     return scm_finite_length(formals);
533 #endif
534 }
535 
536 SCM_EXPORT scm_int_t
scm_validate_actuals(ScmObj actuals)537 scm_validate_actuals(ScmObj actuals)
538 {
539     scm_int_t len;
540 
541 #if SCM_STRICT_ARGCHECK
542     len = scm_length(actuals);
543 #else
544     /* Crashless loose validation:
545      * This loop goes infinite if the formals is circular. SigSchme expects
546      * that user codes are sane here. */
547     len = scm_finite_length(actuals);
548 #endif
549     if (SCM_LISTLEN_DOTTEDP(len))
550         return SCM_LISTLEN_ENCODE_ERROR(len);
551     return len;
552 }
553