aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Brooksby2012-09-19 23:02:51 +0100
committerRichard Brooksby2012-09-19 23:02:51 +0100
commite819bf596f8bef3f03655a387206e884289dfffa (patch)
tree658f7ccab1a9d5eaab11e42f912bc15c7450072f
parent3c6dc5d7afd67bf6e4f798f020536dc8e914a532 (diff)
downloademacs-e819bf596f8bef3f03655a387206e884289dfffa.tar.gz
emacs-e819bf596f8bef3f03655a387206e884289dfffa.zip
Implemented tail recursion.
Copied from Perforce Change: 179572 ServerID: perforce.ravenbrook.com
-rw-r--r--mps/example/scheme/scheme.c195
1 files changed, 109 insertions, 86 deletions
diff --git a/mps/example/scheme/scheme.c b/mps/example/scheme/scheme.c
index 190cc19bd21..fd2473e6e50 100644
--- a/mps/example/scheme/scheme.c
+++ b/mps/example/scheme/scheme.c
@@ -41,7 +41,6 @@
41 * - Quasiquote implementation is messy. 41 * - Quasiquote implementation is messy.
42 * - Lots of library. 42 * - Lots of library.
43 * - \#foo unsatisfactory in read and print 43 * - \#foo unsatisfactory in read and print
44 * - tail recursion (pass current function to eval)
45 */ 44 */
46 45
47#include <stdio.h> 46#include <stdio.h>
@@ -284,6 +283,7 @@ static obj_t obj_error; /* error indicator */
284static obj_t obj_true; /* #t, boolean true */ 283static obj_t obj_true; /* #t, boolean true */
285static obj_t obj_false; /* #f, boolean false */ 284static obj_t obj_false; /* #f, boolean false */
286static obj_t obj_undefined; /* undefined result indicator */ 285static obj_t obj_undefined; /* undefined result indicator */
286static obj_t obj_tail; /* tail recursion indicator */
287 287
288 288
289/* predefined symbols 289/* predefined symbols
@@ -1110,40 +1110,53 @@ static obj_t eval(obj_t env, obj_t op_env, obj_t exp);
1110 1110
1111static obj_t eval(obj_t env, obj_t op_env, obj_t exp) 1111static obj_t eval(obj_t env, obj_t op_env, obj_t exp)
1112{ 1112{
1113 /* self-evaluating */ 1113 for(;;) {
1114 if(TYPE(exp) == TYPE_INTEGER ||
1115 (TYPE(exp) == TYPE_SPECIAL && exp != obj_empty) ||
1116 TYPE(exp) == TYPE_STRING ||
1117 TYPE(exp) == TYPE_CHARACTER)
1118 return exp;
1119
1120 /* symbol lookup */
1121 if(TYPE(exp) == TYPE_SYMBOL) {
1122 obj_t binding = lookup(env, exp);
1123 if(binding == obj_undefined)
1124 error("eval: unbound symbol \"%s\"", exp->symbol.string);
1125 return CDR(binding);
1126 }
1127
1128 /* apply operator or function */
1129 if(TYPE(exp) == TYPE_PAIR) {
1130 obj_t operator; 1114 obj_t operator;
1115 obj_t result;
1116
1117 /* self-evaluating */
1118 if(TYPE(exp) == TYPE_INTEGER ||
1119 (TYPE(exp) == TYPE_SPECIAL && exp != obj_empty) ||
1120 TYPE(exp) == TYPE_STRING ||
1121 TYPE(exp) == TYPE_CHARACTER)
1122 return exp;
1123
1124 /* symbol lookup */
1125 if(TYPE(exp) == TYPE_SYMBOL) {
1126 obj_t binding = lookup(env, exp);
1127 if(binding == obj_undefined)
1128 error("eval: unbound symbol \"%s\"", exp->symbol.string);
1129 return CDR(binding);
1130 }
1131
1132 if(TYPE(exp) != TYPE_PAIR) {
1133 error("eval: unknown syntax");
1134 return obj_error;
1135 }
1136
1137 /* apply operator or function */
1131 if(TYPE(CAR(exp)) == TYPE_SYMBOL) { 1138 if(TYPE(CAR(exp)) == TYPE_SYMBOL) {
1132 obj_t binding = lookup(op_env, CAR(exp)); 1139 obj_t binding = lookup(op_env, CAR(exp));
1133 if(binding != obj_undefined) { 1140 if(binding != obj_undefined) {
1134 operator = CDR(binding); 1141 operator = CDR(binding);
1135 assert(TYPE(operator) == TYPE_OPERATOR); 1142 assert(TYPE(operator) == TYPE_OPERATOR);
1136 return (*operator->operator.entry)(env, op_env, operator, CDR(exp)); 1143 result = (*operator->operator.entry)(env, op_env, operator, CDR(exp));
1144 goto found;
1137 } 1145 }
1138 } 1146 }
1139 operator = eval(env, op_env, CAR(exp)); 1147 operator = eval(env, op_env, CAR(exp));
1140 unless(TYPE(operator) == TYPE_OPERATOR) 1148 unless(TYPE(operator) == TYPE_OPERATOR)
1141 error("eval: application of non-function"); 1149 error("eval: application of non-function");
1142 return (*operator->operator.entry)(env, op_env, operator, CDR(exp)); 1150 result = (*operator->operator.entry)(env, op_env, operator, CDR(exp));
1151
1152 found:
1153 if (!(TYPE(result) == TYPE_PAIR && CAR(result) == obj_tail))
1154 return result;
1155
1156 env = CADR(result);
1157 op_env = CADDR(result);
1158 exp = CAR(CDDDR(result));
1143 } 1159 }
1144
1145 error("eval: unknown syntax");
1146 return obj_error;
1147} 1160}
1148 1161
1149 1162
@@ -1241,6 +1254,42 @@ static void eval_args_rest(char *name, obj_t env, obj_t op_env,
1241} 1254}
1242 1255
1243 1256
1257/* eval_tail -- return an object that will cause eval to loop
1258 *
1259 * Rather than calling `eval` an operator can return a special object that
1260 * causes a calling `eval` to loop, avoiding using up a C stack frame.
1261 * This implements tail recursion (in a simple way).
1262 */
1263
1264static obj_t eval_tail(obj_t env, obj_t op_env, obj_t exp)
1265{
1266 return make_pair(obj_tail,
1267 make_pair(env,
1268 make_pair(op_env,
1269 make_pair(exp,
1270 obj_empty))));
1271}
1272
1273
1274/* eval_body -- evaluate a list of expressions, returning last result
1275 *
1276 * This is used for the bodies of forms such as let, begin, etc. where
1277 * a list of expressions is allowed.
1278 */
1279
1280static obj_t eval_body(obj_t env, obj_t op_env, obj_t operator, obj_t body)
1281{
1282 for (;;) {
1283 if (TYPE(body) != TYPE_PAIR)
1284 error("%s: illegal expression list", operator->operator.name);
1285 if (CDR(body) == obj_empty)
1286 return eval_tail(env, op_env, CAR(body));
1287 (void)eval(env, op_env, CAR(body));
1288 body = CDR(body);
1289 }
1290}
1291
1292
1244/* BUILT-IN OPERATORS */ 1293/* BUILT-IN OPERATORS */
1245 1294
1246 1295
@@ -1287,7 +1336,7 @@ static obj_t entry_interpret(obj_t env, obj_t op_env, obj_t operator, obj_t oper
1287 if(arguments != obj_empty) 1336 if(arguments != obj_empty)
1288 error("eval: function applied to too few arguments"); 1337 error("eval: function applied to too few arguments");
1289 1338
1290 return eval(fun_env, fun_op_env, operator->operator.body); 1339 return eval_tail(fun_env, fun_op_env, operator->operator.body);
1291} 1340}
1292 1341
1293 1342
@@ -1356,9 +1405,9 @@ static obj_t entry_if(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1356 test = eval(env, op_env, CAR(operands)); 1405 test = eval(env, op_env, CAR(operands));
1357 /* Anything which is not #f counts as true [R4RS 6.1]. */ 1406 /* Anything which is not #f counts as true [R4RS 6.1]. */
1358 if(test != obj_false) 1407 if(test != obj_false)
1359 return eval(env, op_env, CADR(operands)); 1408 return eval_tail(env, op_env, CADR(operands));
1360 if(TYPE(CDDR(operands)) == TYPE_PAIR) 1409 if(TYPE(CDDR(operands)) == TYPE_PAIR)
1361 return eval(env, op_env, CADDR(operands)); 1410 return eval_tail(env, op_env, CADDR(operands));
1362 return obj_undefined; 1411 return obj_undefined;
1363} 1412}
1364 1413
@@ -1385,14 +1434,9 @@ static obj_t entry_cond(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1385 } else 1434 } else
1386 result = eval(env, op_env, CAR(clause)); 1435 result = eval(env, op_env, CAR(clause));
1387 if(result != obj_false) { 1436 if(result != obj_false) {
1388 for(;;) { 1437 if (CDR(clause) == obj_empty)
1389 clause = CDR(clause); 1438 return result;
1390 if(TYPE(clause) != TYPE_PAIR) break; 1439 return eval_body(env, op_env, operator, CDR(clause));
1391 result = eval(env, op_env, CAR(clause));
1392 }
1393 if(clause != obj_empty)
1394 error("%s: illegal clause syntax", operator->operator.name);
1395 return result;
1396 } 1440 }
1397 operands = CDR(operands); 1441 operands = CDR(operands);
1398 } 1442 }
@@ -1404,15 +1448,18 @@ static obj_t entry_cond(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1404 1448
1405static obj_t entry_and(obj_t env, obj_t op_env, obj_t operator, obj_t operands) 1449static obj_t entry_and(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1406{ 1450{
1407 while(TYPE(operands) == TYPE_PAIR) { 1451 obj_t test;
1408 obj_t test = eval(env, op_env, CAR(operands)); 1452 if (operands == obj_empty)
1409 if(test == obj_false) 1453 return obj_true;
1410 return obj_false; 1454 do {
1455 if (TYPE(operands) != TYPE_PAIR)
1456 error("%s: illegal syntax", operator->operator.name);
1457 if (CDR(operands) == obj_empty)
1458 return eval_tail(env, op_env, CAR(operands));
1459 test = eval(env, op_env, CAR(operands));
1411 operands = CDR(operands); 1460 operands = CDR(operands);
1412 } 1461 } while (test != obj_false);
1413 if(operands != obj_empty) 1462 return test;
1414 error("%s: illegal syntax", operator->operator.name);
1415 return obj_true;
1416} 1463}
1417 1464
1418 1465
@@ -1420,15 +1467,18 @@ static obj_t entry_and(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1420 1467
1421static obj_t entry_or(obj_t env, obj_t op_env, obj_t operator, obj_t operands) 1468static obj_t entry_or(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1422{ 1469{
1423 while(TYPE(operands) == TYPE_PAIR) { 1470 obj_t test;
1424 obj_t test = eval(env, op_env, CAR(operands)); 1471 if (operands == obj_empty)
1425 if(test != obj_false) 1472 return obj_false;
1426 return obj_true; 1473 do {
1474 if (TYPE(operands) != TYPE_PAIR)
1475 error("%s: illegal syntax", operator->operator.name);
1476 if (CDR(operands) == obj_empty)
1477 return eval_tail(env, op_env, CAR(operands));
1478 test = eval(env, op_env, CAR(operands));
1427 operands = CDR(operands); 1479 operands = CDR(operands);
1428 } 1480 } while (test == obj_false);
1429 if(operands != obj_empty) 1481 return test;
1430 error("%s: illegal syntax", operator->operator.name);
1431 return obj_false;
1432} 1482}
1433 1483
1434 1484
@@ -1437,7 +1487,7 @@ static obj_t entry_or(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1437 1487
1438static obj_t entry_let(obj_t env, obj_t op_env, obj_t operator, obj_t operands) 1488static obj_t entry_let(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1439{ 1489{
1440 obj_t inner_env, bindings, result; 1490 obj_t inner_env, bindings;
1441 unless(TYPE(operands) == TYPE_PAIR && 1491 unless(TYPE(operands) == TYPE_PAIR &&
1442 TYPE(CDR(operands)) == TYPE_PAIR) 1492 TYPE(CDR(operands)) == TYPE_PAIR)
1443 error("%s: illegal syntax", operator->operator.name); 1493 error("%s: illegal syntax", operator->operator.name);
@@ -1455,14 +1505,7 @@ static obj_t entry_let(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1455 } 1505 }
1456 if(bindings != obj_empty) 1506 if(bindings != obj_empty)
1457 error("%s: illegal bindings list", operator->operator.name); 1507 error("%s: illegal bindings list", operator->operator.name);
1458 operands = CDR(operands); 1508 return eval_body(inner_env, op_env, operator, CDR(operands));
1459 while(TYPE(operands) == TYPE_PAIR) {
1460 result = eval(inner_env, op_env, CAR(operands));
1461 operands = CDR(operands);
1462 }
1463 if(operands != obj_empty)
1464 error("%s: illegal expression list", operator->operator.name);
1465 return result;
1466} 1509}
1467 1510
1468 1511
@@ -1471,7 +1514,7 @@ static obj_t entry_let(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1471 1514
1472static obj_t entry_let_star(obj_t env, obj_t op_env, obj_t operator, obj_t operands) 1515static obj_t entry_let_star(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1473{ 1516{
1474 obj_t inner_env, bindings, result; 1517 obj_t inner_env, bindings;
1475 unless(TYPE(operands) == TYPE_PAIR && 1518 unless(TYPE(operands) == TYPE_PAIR &&
1476 TYPE(CDR(operands)) == TYPE_PAIR) 1519 TYPE(CDR(operands)) == TYPE_PAIR)
1477 error("%s: illegal syntax", operator->operator.name); 1520 error("%s: illegal syntax", operator->operator.name);
@@ -1489,14 +1532,7 @@ static obj_t entry_let_star(obj_t env, obj_t op_env, obj_t operator, obj_t opera
1489 } 1532 }
1490 if(bindings != obj_empty) 1533 if(bindings != obj_empty)
1491 error("%s: illegal bindings list", operator->operator.name); 1534 error("%s: illegal bindings list", operator->operator.name);
1492 operands = CDR(operands); 1535 return eval_body(inner_env, op_env, operator, CDR(operands));
1493 while(TYPE(operands) == TYPE_PAIR) {
1494 result = eval(inner_env, op_env, CAR(operands));
1495 operands = CDR(operands);
1496 }
1497 if(operands != obj_empty)
1498 error("%s: illegal expression list", operator->operator.name);
1499 return result;
1500} 1536}
1501 1537
1502 1538
@@ -1505,7 +1541,7 @@ static obj_t entry_let_star(obj_t env, obj_t op_env, obj_t operator, obj_t opera
1505 1541
1506static obj_t entry_letrec(obj_t env, obj_t op_env, obj_t operator, obj_t operands) 1542static obj_t entry_letrec(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1507{ 1543{
1508 obj_t inner_env, bindings, result; 1544 obj_t inner_env, bindings;
1509 unless(TYPE(operands) == TYPE_PAIR && 1545 unless(TYPE(operands) == TYPE_PAIR &&
1510 TYPE(CDR(operands)) == TYPE_PAIR) 1546 TYPE(CDR(operands)) == TYPE_PAIR)
1511 error("%s: illegal syntax", operator->operator.name); 1547 error("%s: illegal syntax", operator->operator.name);
@@ -1529,14 +1565,7 @@ static obj_t entry_letrec(obj_t env, obj_t op_env, obj_t operator, obj_t operand
1529 define(inner_env, CAR(binding), eval(inner_env, op_env, CADR(binding))); 1565 define(inner_env, CAR(binding), eval(inner_env, op_env, CADR(binding)));
1530 bindings = CDR(bindings); 1566 bindings = CDR(bindings);
1531 } 1567 }
1532 operands = CDR(operands); 1568 return eval_body(inner_env, op_env, operator, CDR(operands));
1533 while(TYPE(operands) == TYPE_PAIR) {
1534 result = eval(inner_env, op_env, CAR(operands));
1535 operands = CDR(operands);
1536 }
1537 if(operands != obj_empty)
1538 error("%s: illegal expression list", operator->operator.name);
1539 return result;
1540} 1569}
1541 1570
1542 1571
@@ -1699,14 +1728,7 @@ static obj_t entry_lambda(obj_t env, obj_t op_env, obj_t operator, obj_t operand
1699 1728
1700static obj_t entry_begin(obj_t env, obj_t op_env, obj_t operator, obj_t operands) 1729static obj_t entry_begin(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1701{ 1730{
1702 obj_t result; 1731 return eval_body(env, op_env, operator, operands);
1703 do {
1704 unless(TYPE(operands) == TYPE_PAIR)
1705 error("%s: illegal syntax", operator->operator.name);
1706 result = eval(env, op_env, CAR(operands));
1707 operands = CDR(operands);
1708 } while(operands != obj_empty);
1709 return result;
1710} 1732}
1711 1733
1712 1734
@@ -2335,7 +2357,8 @@ static struct {char *name; obj_t *varp;} sptab[] = {
2335 {"#[error]", &obj_error}, 2357 {"#[error]", &obj_error},
2336 {"#t", &obj_true}, 2358 {"#t", &obj_true},
2337 {"#f", &obj_false}, 2359 {"#f", &obj_false},
2338 {"#[undefined]", &obj_undefined} 2360 {"#[undefined]", &obj_undefined},
2361 {"#[tail]", &obj_tail}
2339}; 2362};
2340 2363
2341 2364