Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
6a2e041
feat: Complete CSE machine and Conductor integration
TRK95 Aug 8, 2025
09ed1d1
rm remnant code
TRK95 Aug 8, 2025
1343254
chore: add dist/index.js to repository for CDN and browser use
TRK95 Aug 9, 2025
c5e3a83
Add GitHub Pages deployment workflow
TRK95 Aug 9, 2025
4198709
Add GitHub Pages landing page
TRK95 Aug 9, 2025
7a60363
Fix: Add HelloServiceMessage protocol handshake in RunnerPlugin const…
TRK95 Aug 9, 2025
d94c4af
Fix scm-slang integration: UMD format, conductor initialization, and …
TRK95 Aug 10, 2025
a12f62b
Remove package-lock.json to fix CI/CD - project uses Yarn
TRK95 Aug 10, 2025
b277f97
Fix GitHub Actions: Use yarn instead of npm for deploy-pages workflow
TRK95 Aug 10, 2025
1aaf9a8
Temporarily disable deploy-pages workflow - need to setup GitHub Page…
TRK95 Aug 10, 2025
2efa27b
Remove deploy-pages workflow to follow py-slang pattern - only keep N…
TRK95 Aug 10, 2025
b9cb59a
Add dist/ to .gitignore and remove dist folder - follow py-slang pattern
TRK95 Aug 10, 2025
7f305b5
Fix environment restoration and implement compound functions - Add RE…
TRK95 Aug 10, 2025
7394fea
Fix list operations and add comprehensive list support - Fix parser t…
TRK95 Aug 10, 2025
7ce0ea1
Fix code formatting with Prettier - Ensure all files conform to proje…
TRK95 Aug 10, 2025
f205f30
Bump jest from 30.0.4 to 30.0.5 (#161)
dependabot[bot] Aug 13, 2025
3cd7cbc
Bump ts-jest from 29.4.0 to 29.4.1 (#167)
dependabot[bot] Aug 13, 2025
a4485eb
Add stepper functionality with lambda and user-defined function support
TRK95 Aug 22, 2025
9870318
chore(SRI-NA): pin jest/ts-jest/typescript; relax service handler typ…
TRK95 Sep 8, 2025
6984801
feat: Complete CSE machine and Conductor integration
TRK95 Aug 8, 2025
5604c76
rm remnant code
TRK95 Aug 8, 2025
95d7467
chore: add dist/index.js to repository for CDN and browser use
TRK95 Aug 9, 2025
9e58002
Add GitHub Pages deployment workflow
TRK95 Aug 9, 2025
e2ef903
Add GitHub Pages landing page
TRK95 Aug 9, 2025
c0f3c5b
Fix: Add HelloServiceMessage protocol handshake in RunnerPlugin const…
TRK95 Aug 9, 2025
75bf14f
Fix scm-slang integration: UMD format, conductor initialization, and …
TRK95 Aug 10, 2025
25fbb7a
Remove package-lock.json to fix CI/CD - project uses Yarn
TRK95 Aug 10, 2025
4b8fb47
Fix GitHub Actions: Use yarn instead of npm for deploy-pages workflow
TRK95 Aug 10, 2025
9215ecd
Temporarily disable deploy-pages workflow - need to setup GitHub Page…
TRK95 Aug 10, 2025
4e2a319
Remove deploy-pages workflow to follow py-slang pattern - only keep N…
TRK95 Aug 10, 2025
7ba244c
Add dist/ to .gitignore and remove dist folder - follow py-slang pattern
TRK95 Aug 10, 2025
a9e7991
Fix environment restoration and implement compound functions - Add RE…
TRK95 Aug 10, 2025
79db289
Fix list operations and add comprehensive list support - Fix parser t…
TRK95 Aug 10, 2025
cea33f7
Fix code formatting with Prettier - Ensure all files conform to proje…
TRK95 Aug 10, 2025
d0b70d7
Add stepper functionality with lambda and user-defined function support
TRK95 Aug 22, 2025
8d37b19
chore(SRI-NA): pin jest/ts-jest/typescript; relax service handler typ…
TRK95 Sep 8, 2025
3ad4131
chore(SRI-NA): prepare rebase by committing lockfile+pkg resolution
TRK95 Sep 8, 2025
25be399
chore(SRI-NA): include yarn install state
TRK95 Sep 8, 2025
1aa5abb
chore(SRI-NA): resolve package.json/yarn.lock conflicts; pin jest 29.…
TRK95 Sep 8, 2025
b17e15f
chore: format code with prettier
TRK95 Sep 8, 2025
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ typings/

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
Expand All @@ -91,6 +90,9 @@ dist
# vuepress build output
.vuepress/dist

# Build output
dist/

# Serverless directories
.serverless/

Expand Down
Binary file added .yarn/install-state.gz
Binary file not shown.
1 change: 1 addition & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodeLinker: node-modules
25 changes: 17 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"type": "git",
"url": "git+https://github.com/source-academy/scm-slang.git"
},
"main": "index.js",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"bugs": {
"url": "https://github.com/source-academy/scm-slang/issues"
},
Expand All @@ -25,6 +26,8 @@
"js-base64": "^3.7.7"
},
"scripts": {
"build": "rollup -c --bundleConfigAsCjs",
"start": "npm run build && node dist/index.js",
"build-libs": "npx ts-node ./src/compile-libs.ts",
"test": "jest",
"test-coverage": "jest --coverage",
Expand All @@ -35,22 +38,28 @@
},
"meta": {},
"devDependencies": {
"@babel/preset-env": "^7.28.0",
"@types/jest": "^30.0.0",
"@babel/preset-env": "^7.27.2",
"@rollup/plugin-commonjs": "^28.0.3",
"@rollup/plugin-node-resolve": "^16.0.1",
"@rollup/plugin-typescript": "^12.1.2",
"@types/jest": "^29.5.12",
"@types/node": "^22.15.30",
"@typescript-eslint/eslint-plugin": "^6.4.0",
"babel-jest": "^30.0.5",
Copy link
Member

@s-kybound s-kybound Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a downgrade?

nit - please merge the latest set of packages, unless this is necessary

"babel-jest": "^29.7.0",
"escodegen": "^2.1.0",
"eslint": "^8.57.1",
"eslint-config-standard-with-typescript": "^43.0.1",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-n": "^15.0.0 || ^16.0.0 ",
"eslint-plugin-promise": "^6.6.0",
"husky": "^9.1.7",
"jest": "^30.0.4",
"prettier": "^3.6.2",
"jest": "^29.7.0",
"jest-util": "^30.0.5",
"prettier": "^3.5.3",
"rollup": "^4.38.0",
"source-map": "^0.7.4",
"ts-jest": "^29.4.0",
"typescript": "*"
"ts-jest": "^29.1.2",
"tslib": "^2.8.1",
"typescript": "^5.4.5"
}
}
33 changes: 33 additions & 0 deletions rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "@rollup/plugin-typescript";

/**
* @type {import('rollup').RollupOptions}
*/
const config = {
input: "src/index.ts",
output: {
file: "dist/index.js",
format: "umd",
name: "ScmSlangRunner",
sourcemap: false,
globals: {},
},
plugins: [
resolve({
preferBuiltins: false,
browser: true,
}),
commonjs({
include: "node_modules/**",
transformMixedEsModules: true,
requireReturnsDefault: "auto",
}),
typescript({
tsconfig: "./tsconfig.json",
}),
],
};

export default config;
39 changes: 39 additions & 0 deletions src/CSE-machine/astToControl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { ControlItem } from "./control";
import {
Expression,
Atomic,
Extended,
} from "../transpiler/types/nodes/scheme-node-types";

export function astToControl(expr: Expression): ControlItem[] {
if (
expr instanceof Atomic.NumericLiteral ||
expr instanceof Atomic.BooleanLiteral ||
expr instanceof Atomic.StringLiteral ||
expr instanceof Atomic.Symbol ||
expr instanceof Atomic.Identifier ||
expr instanceof Atomic.Lambda ||
expr instanceof Atomic.Application ||
expr instanceof Atomic.Definition ||
expr instanceof Atomic.Reassignment ||
expr instanceof Atomic.Conditional ||
expr instanceof Atomic.Sequence ||
expr instanceof Atomic.Pair ||
expr instanceof Atomic.Nil ||
expr instanceof Atomic.Import ||
expr instanceof Atomic.Export ||
expr instanceof Atomic.Vector ||
expr instanceof Atomic.DefineSyntax ||
expr instanceof Atomic.SyntaxRules ||
expr instanceof Extended.FunctionDefinition ||
expr instanceof Extended.Let ||
expr instanceof Extended.Cond ||
expr instanceof Extended.List ||
expr instanceof Extended.Begin ||
expr instanceof Extended.Delay
) {
return [expr];
}
console.log("DEBUG expr:", expr);
throw new Error(`Unhandled expr type: ${expr.constructor.name}`);
}
26 changes: 26 additions & 0 deletions src/CSE-machine/closure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// closure.ts
import { Expression } from "../transpiler/types/nodes/scheme-node-types";
import { Environment } from "./environment";

export class Closure {
readonly type = "Closure";
readonly params: string[];
readonly body: Expression[];
readonly env: Environment;
readonly declaredName?: string;
readonly srcNode?: Expression; // AST node for debugging

constructor(
params: string[],
body: Expression[],
env: Environment,
declaredName?: string,
srcNode?: Expression
) {
this.params = params;
this.body = body;
this.env = env;
this.declaredName = declaredName;
this.srcNode = srcNode;
}
}
151 changes: 151 additions & 0 deletions src/CSE-machine/complex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Complex number implementation for Scheme
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm curious as to why complex number logic is separated into its own file. will this affect the scheme numeric tower?

// Based on py-slang PyComplexNumber

export class SchemeComplexNumber {
public real: number;
public imag: number;

constructor(real: number, imag: number) {
this.real = real;
this.imag = imag;
}

public static fromNumber(value: number): SchemeComplexNumber {
return new SchemeComplexNumber(value, 0);
}

public static fromString(str: string): SchemeComplexNumber {
// Handle Scheme complex number syntax: 3+4i, 1-2i, 5i
if (!/[iI]/.test(str)) {
const realVal = Number(str);
if (isNaN(realVal)) {
throw new Error(`Invalid complex string: ${str}`);
}
return new SchemeComplexNumber(realVal, 0);
}

const lower = str.toLowerCase();
if (lower.endsWith("i")) {
const numericPart = str.substring(0, str.length - 1);

// Handle purely imaginary: i, +i, -i
if (numericPart === "" || numericPart === "+") {
return new SchemeComplexNumber(0, 1);
} else if (numericPart === "-") {
return new SchemeComplexNumber(0, -1);
}

// Check if it's purely imaginary: 5i
const imagVal = Number(numericPart);
if (!isNaN(imagVal)) {
return new SchemeComplexNumber(0, imagVal);
}

// Handle complex with both real and imaginary parts: 3+4i, 1-2i
const match = numericPart.match(
/^([\+\-]?\d+(?:\.\d+)?(?:[eE][+\-]?\d+)?)([\+\-]\d+(?:\.\d+)?(?:[eE][+\-]?\d+)?)$/
);
if (match) {
const realPart = Number(match[1]);
const imagPart = Number(match[2]);
return new SchemeComplexNumber(realPart, imagPart);
}
}

throw new Error(`Invalid complex string: ${str}`);
}

public static fromValue(
value: number | string | SchemeComplexNumber
): SchemeComplexNumber {
if (value instanceof SchemeComplexNumber) {
return value;
} else if (typeof value === "number") {
return SchemeComplexNumber.fromNumber(value);
} else if (typeof value === "string") {
return SchemeComplexNumber.fromString(value);
} else {
throw new Error(`Cannot convert ${typeof value} to complex number`);
}
}

// Arithmetic operations
public add(other: SchemeComplexNumber): SchemeComplexNumber {
return new SchemeComplexNumber(
this.real + other.real,
this.imag + other.imag
);
}

public sub(other: SchemeComplexNumber): SchemeComplexNumber {
return new SchemeComplexNumber(
this.real - other.real,
this.imag - other.imag
);
}

public mul(other: SchemeComplexNumber): SchemeComplexNumber {
// (a + bi) * (c + di) = (ac - bd) + (ad + bc)i
const real = this.real * other.real - this.imag * other.imag;
const imag = this.real * other.imag + this.imag * other.real;
return new SchemeComplexNumber(real, imag);
}

public div(other: SchemeComplexNumber): SchemeComplexNumber {
// (a + bi) / (c + di) = ((ac + bd) + (bc - ad)i) / (c² + d²)
const denominator = other.real * other.real + other.imag * other.imag;
if (denominator === 0) {
throw new Error("Division by zero");
}
const real =
(this.real * other.real + this.imag * other.imag) / denominator;
const imag =
(this.imag * other.real - this.real * other.imag) / denominator;
return new SchemeComplexNumber(real, imag);
}

public negate(): SchemeComplexNumber {
return new SchemeComplexNumber(-this.real, -this.imag);
}

// Comparison (only for equality)
public equals(other: SchemeComplexNumber): boolean {
return this.real === other.real && this.imag === other.imag;
}

// Magnitude
public abs(): number {
return Math.sqrt(this.real * this.real + this.imag * this.imag);
}

// String representation
public toString(): string {
if (this.imag === 0) {
return this.real.toString();
} else if (this.real === 0) {
if (this.imag === 1) return "i";
if (this.imag === -1) return "-i";
return `${this.imag}i`;
} else {
const imagPart =
this.imag === 1
? "i"
: this.imag === -1
? "-i"
: this.imag > 0
? `+${this.imag}i`
: `${this.imag}i`;
return `${this.real}${imagPart}`;
}
}

// Convert to JavaScript number (only if purely real)
public toNumber(): number {
if (this.imag !== 0) {
throw new Error(
"Cannot convert complex number with imaginary part to real number"
);
}
return this.real;
}
}
Loading