diff options
| author | Richard Brooksby | 2012-09-19 23:02:51 +0100 |
|---|---|---|
| committer | Richard Brooksby | 2012-09-19 23:02:51 +0100 |
| commit | e819bf596f8bef3f03655a387206e884289dfffa (patch) | |
| tree | 658f7ccab1a9d5eaab11e42f912bc15c7450072f | |
| parent | 3c6dc5d7afd67bf6e4f798f020536dc8e914a532 (diff) | |
| download | emacs-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.c | 195 |
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 */ | |||
| 284 | static obj_t obj_true; /* #t, boolean true */ | 283 | static obj_t obj_true; /* #t, boolean true */ |
| 285 | static obj_t obj_false; /* #f, boolean false */ | 284 | static obj_t obj_false; /* #f, boolean false */ |
| 286 | static obj_t obj_undefined; /* undefined result indicator */ | 285 | static obj_t obj_undefined; /* undefined result indicator */ |
| 286 | static 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 | ||
| 1111 | static obj_t eval(obj_t env, obj_t op_env, obj_t exp) | 1111 | static 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 | |||
| 1264 | static 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 | |||
| 1280 | static 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 | ||
| 1405 | static obj_t entry_and(obj_t env, obj_t op_env, obj_t operator, obj_t operands) | 1449 | static 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 | ||
| 1421 | static obj_t entry_or(obj_t env, obj_t op_env, obj_t operator, obj_t operands) | 1468 | static 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 | ||
| 1438 | static obj_t entry_let(obj_t env, obj_t op_env, obj_t operator, obj_t operands) | 1488 | static 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 | ||
| 1472 | static obj_t entry_let_star(obj_t env, obj_t op_env, obj_t operator, obj_t operands) | 1515 | static 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 | ||
| 1506 | static obj_t entry_letrec(obj_t env, obj_t op_env, obj_t operator, obj_t operands) | 1542 | static 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 | ||
| 1700 | static obj_t entry_begin(obj_t env, obj_t op_env, obj_t operator, obj_t operands) | 1729 | static 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 | ||