Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions docs/lib/mce.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* returns the parse tree that results from parsing
* the string <CODE>str</CODE> as a Python program. The format
* of the parse tree is described in chapter 4 of
* the textbook
* in <a href="https://sourceacademy.org/sicpjs/">Structure and
* Interpretation of Computer Programs, JavaScript Adaptation</a> (SICP).
* @param {str} x - given program as a string
* @returns {value} parse tree
*/
function parse(str) {}

/**
* returns the list of tokens that results from lexing the string <CODE>s</CODE>
* @param {str} s - given program as a string
* @returns {linked_list} linked list of tokens
*/
function tokenize(str) {}

/**
* calls the function <CODE>f</CODE>
* with arguments given in linked list <CODE>xs</CODE>. For example: <PRE><CODE>def times(x, y):
* return x * y
*
* apply_in_underlying_python(times, list(2, 3)); # returns 6</CODE></PRE>
* @param {function} f - function to be applied
* @param {linked_list} xs - arguments given in list
* @returns {value} whatever <CODE>f</CODE> returns
*/
function apply_in_underlying_python(f, xs) {}
6 changes: 3 additions & 3 deletions docs/md/README_3.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ these groups:
<a href="../LINKED LISTS/index.html">LINKED LISTS</a>: Support for linked lists
</li>
<li>
<a href="../PAIR MUTATOR/index.html">PAIR MUTATOR</a>: Mutating pairs
<a href="../PAIR MUTATORS/index.html">PAIR MUTATORS</a>: Mutating pairs
</li>
<li>
<a href="../LIST/index.html">LIST</a>: Support for lists
<a href="../LISTS/index.html">LISTS</a>: Support for lists
</li>
<li>
<a href="../STREAM/index.html">STREAM</a>: Support for streams
<a href="../STREAMS/index.html">STREAMS</a>: Support for streams
</li>
</ul>

Expand Down
6 changes: 3 additions & 3 deletions docs/md/README_4.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ these groups:
<a href="../LINKED LISTS/index.html">LINKED LISTS</a>: Support for linked lists
</li>
<li>
<a href="../PAIR MUTATOR/index.html">PAIR MUTATOR</a>: Mutating pairs
<a href="../PAIR MUTATORS/index.html">PAIR MUTATORS</a>: Mutating pairs
</li>
<li>
<a href="../LIST/index.html">LIST</a>: Support for lists
<a href="../LISTS/index.html">LISTS</a>: Support for lists
</li>
<li>
<a href="../STREAM/index.html">STREAM</a>: Support for streams
<a href="../STREAMS/index.html">STREAM</a>: Support for streams
</li>
<li>
<a href="../MCE/index.html">MCE</a>: Support for the meta-circular evaluator
Expand Down
14 changes: 7 additions & 7 deletions docs/specs/python_interpreter.tex
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ \section*{Interpreter Support}
return x * y
apply_in_underlying_python(times, linked_list(2, 3)) # returns 6
\end{lstlisting}
\item \lstinline{tokenize(x)}: \textit{primitive}, returns the literal list of tokens that results from tokenizing
the string \lstinline{x} as a Source program. Each token is a string that contains the characters of
\item \lstinline{tokenize(x)}: \textit{primitive}, returns the literal linked list of tokens that results from tokenizing
the string \lstinline{x} as a Python program. Each token is a string that contains the characters of
the token as they appear in the program. Comments are ignored.
\item \lstinline{parse(x)}: \textit{primitive}, returns the parse tree that results from parsing
the string \lstinline{x} as a Source program. The following two pages describe the shape of the parse tree.
The tree is represented by the tagged lists on the right; the angle brackets denote recursive application
of the transformation rules. Implementations are allowed to support more of JavaScript than listed.
the string \lstinline{x} as a Python program. The following two pages describe the shape of the parse tree.
The tree is represented by the tagged linked lists on the right; the angle brackets denote recursive application
of the transformation rules. Implementations are allowed to support more of Python than listed.
\end{itemize}

In addition, the Source Academy frontend predeclares the name \lstinline{__PROGRAM__} in all Source languages to
In addition, the Source Academy frontend predeclares the name \lstinline{__program__} in all Python languages to
refer to the string representation of the entrypoint file of the program in the editor that is being run using ``Run''.
The entrypoint file is the file containing the code that acts as the entrypoint of the program being run.
If \lstinline{__PROGRAM__} is used in the REPL, it refers to the string representation of the editor content
If \lstinline{__program__} is used in the REPL, it refers to the string representation of the editor content
at the time when ``Run'' was last pressed.

\newpage
Expand Down
6 changes: 0 additions & 6 deletions nodemon.json

This file was deleted.

8 changes: 8 additions & 0 deletions scripts/jsdoc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,22 @@ run() {
# STREAMS
run_jsdoc "STREAMS" "README_STREAMS.md" "STREAMS" "$LIB/stream.js" &

# MCE
run_jsdoc "MCE" "README_MCE.md" "MCE" "$LIB/mce.js" &


# Python §1
run_jsdoc "Python §1" "README_1.md" "python_1" "$LIB/misc.js" "$LIB/math.js" &

# Python §2
run_jsdoc "Python §2" "README_2.md" "python_2" "$LIB/misc.js" "$LIB/math.js" "$LIB/linked_list.js" &

# Python §3
run_jsdoc "Python §3" "README_3.md" "python_3" "$LIB/misc.js" "$LIB/math.js" "$LIB/linked_list.js" "$LIB/pairmutator.js" "$LIB/list.js" "$LIB/stream.js" &

# Python §4
run_jsdoc "Python §4" "README_4.md" "python_4" "$LIB/misc.js" "$LIB/math.js" "$LIB/linked_list.js" "$LIB/pairmutator.js" "$LIB/list.js" "$LIB/stream.js" "$LIB/mce.js" &

wait
}

Expand Down
1 change: 0 additions & 1 deletion src/engines/cse/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export class Context {
public errors: RuntimeSourceError[] = [];
public moduleContexts: { [name: string]: ModuleContext };
public prelude: string | null = null;

runtime: {
break: boolean;
debuggerOn: boolean;
Expand Down
17 changes: 15 additions & 2 deletions src/engines/cse/control.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { StmtNS } from "../../ast-types";
import { Stack } from "./stack";
import { Instr, Node } from "./types";
import { isEnvDependent } from "./utils";
import { Instr, InstrType, Node } from "./types";
import { isEnvDependent, isInstr } from "./utils";

export type ControlItem = Node | Instr;

Expand All @@ -10,9 +10,12 @@ export type ControlItem = Node | Instr;
*/
export class Control extends Stack<ControlItem> {
private numEnvDependentItems: number;
private numFunctionResets: number;

public constructor(program?: StmtNS.Stmt) {
super();
this.numEnvDependentItems = 0;
this.numFunctionResets = 0;
// Load program into control stack
if (program) this.push(program);
}
Expand All @@ -26,11 +29,18 @@ export class Control extends Stack<ControlItem> {
return this.numEnvDependentItems;
}

public getNumFunctionResets(): number {
return this.numFunctionResets;
}

public pop(): ControlItem | undefined {
const item = super.pop();
if (item !== undefined && isEnvDependent(item)) {
this.numEnvDependentItems--;
}
if (item !== undefined && isInstr(item) && item.instrType === InstrType.RESET) {
this.numFunctionResets--;
}
return item;
}

Expand All @@ -39,6 +49,9 @@ export class Control extends Stack<ControlItem> {
if (isEnvDependent(item)) {
this.numEnvDependentItems++;
}
if (isInstr(item) && item.instrType === InstrType.RESET) {
this.numFunctionResets++;
}
});
super.push(...items);
}
Expand Down
4 changes: 3 additions & 1 deletion src/engines/cse/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,9 @@ export const createProgramEnvironment = (context: Context, isPrelude: boolean):
return createSimpleEnvironment(
context,
isPrelude ? "prelude" : "programEnvironment",
isPrelude ? null : getPreludeEnvironment(context),
isPrelude
? getGlobalEnvironment(context)
: getPreludeEnvironment(context) || getGlobalEnvironment(context),
);
};

Expand Down
34 changes: 32 additions & 2 deletions src/engines/cse/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
createEnvironment,
createProgramEnvironment,
currentEnvironment,
getGlobalEnvironment,
popEnvironment,
pushEnvironment,
} from "./environment";
Expand Down Expand Up @@ -50,10 +51,12 @@ import {
WhileInstr,
} from "./types";
import {
checkStackOverFlow,
envChanging,
evaluateForIterator,
evaluateListAssignment,
generateForIncrement,
isInstr,
isNode,
pyDefineVariable,
pyGetVariable,
Expand All @@ -66,6 +69,7 @@ export interface IOptions {
envSteps: number;
stepLimit: number;
variant: number;
recursionLimit: number;
}

type CmdEvaluator<T extends ControlItem> = (
Expand Down Expand Up @@ -119,6 +123,7 @@ export async function evaluate(
groups: [],
envSteps: 100000,
stepLimit: -1,
recursionLimit: 1024,
variant: 4,
...(options as Partial<IOptions>),
};
Expand All @@ -145,6 +150,7 @@ export async function evaluate(
context.stash,
opts.envSteps,
opts.stepLimit,
opts.recursionLimit,
opts.variant,
opts.isPrelude,
);
Expand Down Expand Up @@ -203,6 +209,7 @@ export async function evaluate(
* @param stash Points to the current Stash.
* @param envSteps Number of environment steps to run.
* @param stepLimit Maximum number of steps to execute.
* @param recursionLimit Maximum depth of recursion allowed.
* @param variant The language variant being executed.
* @param isPrelude Whether the program is the prelude.
* @returns The top value of the stash after execution.
Expand All @@ -214,6 +221,7 @@ export async function runCSEMachine(
stash: Stash,
envSteps: number,
stepLimit: number,
recursionLimit: number,
variant: number,
isPrelude: boolean = false,
): Promise<Value> {
Expand All @@ -224,6 +232,7 @@ export async function runCSEMachine(
stash,
envSteps,
stepLimit,
recursionLimit,
variant,
isPrelude,
);
Expand All @@ -246,6 +255,7 @@ export async function runCSEMachine(
* @param stash The stash storage.
* @param _envSteps Number of environment steps to run.
* @param stepLimit Maximum number of steps to execute.
* @param recursionLimit Maximum depth of recursion allowed.
* @param variant The language variant being executed.
* @param isPrelude Whether the program is the prelude.
* @yields The current state of the stash, control stack, and step count.
Expand All @@ -257,6 +267,7 @@ export async function* generateCSEMachineStateStream(
stash: Stash,
_envSteps: number,
stepLimit: number,
recursionLimit: number,
variant: number,
isPrelude: boolean = false,
) {
Expand All @@ -271,6 +282,11 @@ export async function* generateCSEMachineStateStream(
context.runtime.nodes.unshift(command);
}

// Define the program
const globalEnvironment = getGlobalEnvironment(context);
if (globalEnvironment) {
pyDefineVariable(context, "__program__", { type: "string", value: code }, globalEnvironment);
}
while (command) {
// Return to capture a snapshot of the control and stash after the target step count is reached
// if (!isPrelude && steps === envSteps) {
Expand Down Expand Up @@ -333,6 +349,7 @@ export async function* generateCSEMachineStateStream(
isPrelude,
variant,
);
checkStackOverFlow(code, command, context, control, recursionLimit);
}

command = control.peek();
Expand Down Expand Up @@ -640,7 +657,7 @@ const cmdEvaluators: CmdEvaluators = {
let head;
while (true) {
head = control.pop();
if (!head || ("instrType" in head && head.instrType === InstrType.RESET)) {
if (!head || (isInstr(head) && head.instrType === InstrType.RESET)) {
break;
}
}
Expand Down Expand Up @@ -1057,6 +1074,17 @@ const cmdEvaluators: CmdEvaluators = {
stash: Stash,
_isPrelude: boolean,
) {
// Tail-Call Optimisation
const topElement = control.peek();
if (
topElement !== undefined &&
isInstr(topElement) &&
topElement.instrType === InstrType.RESET
) {
control.pop();
popEnvironment(context);
}

const numOfArgs = instr.numOfArgs;

const rawArgs: Value[] = [];
Expand Down Expand Up @@ -1112,7 +1140,9 @@ const cmdEvaluators: CmdEvaluators = {
}
} else if (callable?.type === "builtin") {
const result = await callable.func(args, code, instr.srcNode, context);
stash.push(result);
if (result !== undefined) {
stash.push(result);
}
} else {
handleRuntimeError(
context,
Expand Down
9 changes: 7 additions & 2 deletions src/engines/cse/stash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,13 @@ export interface BuiltinValue {
type: "builtin";
name: string;
func:
| ((args: Value[], code: string, command: ExprNS.Call, context: Context) => Value)
| ((args: Value[], code: string, command: ExprNS.Call, context: Context) => Promise<Value>);
| ((args: Value[], code: string, command: ExprNS.Call, context: Context) => Value | undefined)
| ((
args: Value[],
code: string,
command: ExprNS.Call,
context: Context,
) => Promise<Value | undefined>);
minArgs: number;
}

Expand Down
Loading
Loading