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