Skip to content

Commit 7f305b5

Browse files
committed
Fix environment restoration and implement compound functions - Add RESTORE_ENV instruction to properly restore environment after function calls - Fix function parameter binding in CSE machine - Compound functions now work correctly (functions using other functions) - Linear recursion and iteration working properly
1 parent b9cb59a commit 7f305b5

File tree

5 files changed

+149
-7
lines changed

5 files changed

+149
-7
lines changed

src/CSE-machine/astToControl.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
} from "../transpiler/types/nodes/scheme-node-types";
77

88
export function astToControl(expr: Expression): ControlItem[] {
9-
// Hỗ trợ cả node Atomic và Extended
9+
1010
if (
1111
expr instanceof Atomic.NumericLiteral ||
1212
expr instanceof Atomic.BooleanLiteral ||

src/CSE-machine/instrCreator.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
Extended,
66
} from "../transpiler/types/nodes/scheme-node-types";
77
import { Location, Position } from "../transpiler/types/location";
8+
import { Environment } from "./environment";
89
import {
910
Instr,
1011
InstrType,
@@ -22,6 +23,7 @@ import {
2223
CarInstr,
2324
CdrInstr,
2425
ConsInstr,
26+
RestoreEnvInstr,
2527
AppInstr,
2628
BranchInstr,
2729
} from "./types";
@@ -187,6 +189,14 @@ export function createBranchInstr(
187189
alternate,
188190
};
189191
}
192+
193+
export function createRestoreEnvInstr(env: Environment): RestoreEnvInstr {
194+
return {
195+
instrType: InstrType.RESTORE_ENV,
196+
srcNode: { type: "StatementSequence", body: [], location: { start: { line: 0, column: 0 }, end: { line: 0, column: 0 } } },
197+
env,
198+
};
199+
}
190200
import { LiteralInstr, VariableInstr, LambdaInstr, IfInstr } from "./types";
191201

192202
// Literal instruction

src/CSE-machine/interpreter.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
CarInstr,
3232
CdrInstr,
3333
ConsInstr,
34+
RestoreEnvInstr,
3435
AppInstr,
3536
BranchInstr,
3637
StatementSequence,
@@ -197,12 +198,10 @@ function evaluateExpression(
197198
}
198199
} else if (expr instanceof Atomic.Conditional) {
199200
console.log("DEBUG: Evaluating Conditional expression");
200-
// Push branch instruction AFTER test evaluation
201-
// This ensures test is evaluated and pushed to stash before branch runs
201+
// Push test expression first, then branch instruction
202+
// The branch instruction will decide which branch to take based on test result
202203
control.push(instr.createBranchInstr(expr.consequent, expr.alternate));
203204
control.push(expr.test);
204-
control.push(expr.consequent);
205-
control.push(expr.alternate);
206205
} else if (expr instanceof Atomic.Lambda) {
207206
// Create closure
208207
const closure: Value = {
@@ -314,12 +313,22 @@ function evaluateInstruction(
314313
console.log("DEBUG: Arguments:", args);
315314

316315
if (operator.type === "closure") {
317-
// Apply closure
316+
// Apply closure - save current environment and create new one
317+
const currentEnv = context.environment;
318318
const newEnv = createBlockEnvironment(operator.env);
319+
320+
// Bind parameters to the new environment
319321
for (let i = 0; i < operator.params.length; i++) {
320322
newEnv.define(operator.params[i], args[i] || { type: "nil" });
321323
}
324+
325+
// Set the new environment for function execution
322326
context.environment = newEnv;
327+
328+
// Push a marker to restore environment after function execution
329+
control.push(instr.createRestoreEnvInstr(currentEnv));
330+
331+
// Push function body for execution
323332
control.push(...operator.body);
324333
} else if (operator.type === "primitive") {
325334
// Apply primitive function
@@ -349,7 +358,9 @@ function evaluateInstruction(
349358
console.log("DEBUG: Test value:", test);
350359
const branchInstr = instruction as BranchInstr;
351360

352-
if (test.type === "boolean" && test.value) {
361+
// Check if test is truthy (not false and not nil)
362+
const isTruthy = test.type !== "boolean" || test.value !== false;
363+
if (isTruthy && test.type !== "nil") {
353364
console.log("DEBUG: Taking consequent branch");
354365
control.push(branchInstr.consequent);
355366
} else if (branchInstr.alternate) {
@@ -454,6 +465,12 @@ function evaluateInstruction(
454465
break;
455466
}
456467

468+
case InstrType.RESTORE_ENV: {
469+
const restoreInstr = instruction as RestoreEnvInstr;
470+
context.environment = restoreInstr.env;
471+
break;
472+
}
473+
457474
default:
458475
throw new Error(`Unsupported instruction type: ${instruction.instrType}`);
459476
}

src/CSE-machine/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export enum InstrType {
6060
CAR = "Car",
6161
CDR = "Cdr",
6262
CONS = "Cons",
63+
RESTORE_ENV = "RestoreEnv",
6364
}
6465

6566
interface BaseInstr {
@@ -175,6 +176,10 @@ export interface ConsInstr extends BaseInstr {
175176
cdr: Expression;
176177
}
177178

179+
export interface RestoreEnvInstr extends BaseInstr {
180+
env: Environment;
181+
}
182+
178183
// ...existing code...
179184

180185
export interface LiteralInstr extends BaseInstr {
@@ -219,6 +224,7 @@ export type Instr =
219224
| CarInstr
220225
| CdrInstr
221226
| ConsInstr
227+
| RestoreEnvInstr
222228
| UnOpInstr
223229
| BinOpInstr
224230
| LiteralInstr

src/test/12-test-chapter1-features.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,112 @@ function testChapter1Features() {
220220
expected: { type: "number", value: 3 },
221221
description: "Length of variable list",
222222
},
223+
224+
// ===== COMPOUND FUNCTIONS =====
225+
{
226+
code: "(define (square x) (* x x))",
227+
expected: { type: "void" },
228+
description: "Define square function",
229+
},
230+
{
231+
code: "(define (cube x) (* x (square x)))",
232+
expected: { type: "void" },
233+
description: "Define cube using square (compound function)",
234+
},
235+
{
236+
code: "(cube 3)",
237+
expected: { type: "number", value: 27 },
238+
description: "Test compound function",
239+
},
240+
{
241+
code: "(define (average x y) (/ (+ x y) 2))",
242+
expected: { type: "void" },
243+
description: "Define average function",
244+
},
245+
{
246+
code: "(define (square-average x y) (square (average x y)))",
247+
expected: { type: "void" },
248+
description: "Compound function using average",
249+
},
250+
{
251+
code: "(square-average 4 6)",
252+
expected: { type: "number", value: 25 },
253+
description: "Test nested compound function",
254+
},
255+
256+
// ===== LINEAR RECURSION =====
257+
{
258+
code: "(define (factorial n) (if (= n 0) 1 (* n (factorial (- n 1)))))",
259+
expected: { type: "void" },
260+
description: "Define factorial with linear recursion",
261+
},
262+
{
263+
code: "(factorial 5)",
264+
expected: { type: "number", value: 120 },
265+
description: "Test factorial recursion",
266+
},
267+
{
268+
code: "(factorial 0)",
269+
expected: { type: "number", value: 1 },
270+
description: "Test factorial base case",
271+
},
272+
{
273+
code: "(define (sum-list lst) (if (null? lst) 0 (+ (car lst) (sum-list (cdr lst)))))",
274+
expected: { type: "void" },
275+
description: "Define sum-list with linear recursion",
276+
},
277+
{
278+
code: "(sum-list (list 1 2 3 4 5))",
279+
expected: { type: "number", value: 15 },
280+
description: "Test sum-list recursion",
281+
},
282+
{
283+
code: "(sum-list ())",
284+
expected: { type: "number", value: 0 },
285+
description: "Test sum-list base case",
286+
},
287+
{
288+
code: "(define (count-elements lst) (if (null? lst) 0 (+ 1 (count-elements (cdr lst)))))",
289+
expected: { type: "void" },
290+
description: "Define count-elements with linear recursion",
291+
},
292+
{
293+
code: "(count-elements (list 'a 'b 'c))",
294+
expected: { type: "number", value: 3 },
295+
description: "Test count-elements recursion",
296+
},
297+
298+
// ===== ITERATION (Tail Recursion) =====
299+
{
300+
code: "(define (factorial-iter n) (define (iter n acc) (if (= n 0) acc (iter (- n 1) (* n acc)))) (iter n 1))",
301+
expected: { type: "void" },
302+
description: "Define factorial with iteration (tail recursion)",
303+
},
304+
{
305+
code: "(factorial-iter 5)",
306+
expected: { type: "number", value: 120 },
307+
description: "Test factorial iteration",
308+
},
309+
{
310+
code: "(define (sum-list-iter lst) (define (iter lst acc) (if (null? lst) acc (iter (cdr lst) (+ (car lst) acc)))) (iter lst 0))",
311+
expected: { type: "void" },
312+
description: "Define sum-list with iteration",
313+
},
314+
{
315+
code: "(sum-list-iter (list 1 2 3 4 5))",
316+
expected: { type: "number", value: 15 },
317+
description: "Test sum-list iteration",
318+
},
319+
{
320+
code: "(define (reverse-list lst) (define (iter lst result) (if (null? lst) result (iter (cdr lst) (cons (car lst) result)))) (iter lst ()))",
321+
expected: { type: "void" },
322+
description: "Define reverse-list with iteration",
323+
},
324+
{
325+
code: "(reverse-list (list 1 2 3))",
326+
expected: { type: "list", elements: [{ type: "number", value: 3 }, { type: "number", value: 2 }, { type: "number", value: 1 }] },
327+
description: "Test reverse-list iteration",
328+
},
223329
];
224330

225331
let passed = 0;
@@ -287,6 +393,9 @@ function testChapter1Features() {
287393
console.log("✅ Built-in functions with variables");
288394
console.log("✅ Boolean operations with variables");
289395
console.log("✅ Function composition");
396+
console.log("✅ Compound functions (functions using other functions)");
397+
console.log("✅ Linear recursion (factorial, sum-list, count-elements)");
398+
console.log("✅ Iteration via tail recursion (factorial-iter, sum-list-iter, reverse-list)");
290399
}
291400

292401
testChapter1Features();

0 commit comments

Comments
 (0)