Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
18d8da4
integrate Conductor
kjw142857 Jun 10, 2026
d637754
.
kjw142857 Jun 10, 2026
810656f
jvm changes
kjw142857 Jun 15, 2026
6d73dab
Potential fix for pull request finding 'CodeQL / Workflow does not co…
kjw142857 Jun 15, 2026
ef3a3f2
remove residue exception handling code
kjw142857 Jun 15, 2026
ed2b8f6
implement in-memory class loading
kjw142857 Jun 15, 2026
08b9345
Use conductor@sourceacademy types
kjw142857 Jun 16, 2026
1b1ec53
remove package_lock.json
kjw142857 Jun 16, 2026
1ddea74
change conductor version
kjw142857 Jun 16, 2026
ffedd16
revert yarn.lock file
kjw142857 Jun 16, 2026
e1f8876
revert yarn.lock file
kjw142857 Jun 16, 2026
b1593a3
include try/catch/finally support in code generator
kjw142857 Jun 16, 2026
12926df
fix try statement logic
kjw142857 Jun 24, 2026
b7a4b18
Merge branch 'main' into exception-handling
martin-henz Jun 24, 2026
8f7d8b4
add parser and type checker integration
kjw142857 Jul 1, 2026
79a4e6b
fix finally bug
kjw142857 Jul 1, 2026
2daa965
add tests and fix syntax error
kjw142857 Jul 8, 2026
8e933b6
Patch grammar logic for throws keyword
kjw142857 Jul 16, 2026
181ae61
Revert "Patch grammar logic for throws keyword"
kjw142857 Jul 16, 2026
ee94927
Patch grammar logic for throws keyword
kjw142857 Jul 16, 2026
63f0301
Add fix for execption table finally logic
kjw142857 Jul 21, 2026
df4fede
Add more tests
kjw142857 Jul 21, 2026
e35730a
Add java.lang.Throwable subclasses to compiler/type checker imports
kjw142857 Jul 21, 2026
e8985da
remove some hardcoding for typechecker
kjw142857 Jul 28, 2026
e941932
tighten selector criteria
kjw142857 Aug 10, 2026
89ccf30
add enum support
kjw142857 Aug 10, 2026
4e0b4d5
WIP: Add enum grammar rules and compiler tests
kjw142857 Aug 26, 2026
0625138
Add enum parsing and compiler support (partial)
kjw142857 Aug 26, 2026
954c172
Register enum synthetic methods in symbol table
kjw142857 Aug 26, 2026
b9a196c
Implement exception handling in java-slang (#96)
kjw142857 Aug 26, 2026
f2a823e
Add feature details in README (#88)
kjw142857 Aug 27, 2026
86fc930
Merge branch 'main' into switch-statements
kjw142857 Sep 2, 2026
4cfcdf1
fix exception table merge conflict
kjw142857 Sep 2, 2026
21c3f1f
integrate enum types into switch statements
kjw142857 Sep 2, 2026
d9d7665
add enum ordinals
kjw142857 Sep 2, 2026
9d3e723
correct enum grammar
kjw142857 Sep 2, 2026
af3648d
Merge branch 'main' into switch-statements
kjw142857 Sep 2, 2026
52781ca
remove excessively long logging statement
kjw142857 Sep 2, 2026
0ceb317
remove redundant color test
kjw142857 Sep 2, 2026
1c102bd
Merge branch 'main' into std-imports
kjw142857 Sep 7, 2026
f0f6980
update compiler to add importing of std classes
kjw142857 Sep 7, 2026
c827574
ensure imported classes for compiler are handled by jvm as well
kjw142857 Sep 7, 2026
dde0622
add enum support
kjw142857 Sep 7, 2026
6cd7a78
implement library loading and primitive unboxing
kjw142857 Sep 7, 2026
aa8a837
Merge branch 'std-imports' into switch-statements
kjw142857 Sep 7, 2026
99e7920
Return compiled classes in declaration order, not compilation order
kjw142857 Sep 7, 2026
ae58cef
Make generated enums genuinely extend java.lang.Enum
kjw142857 Sep 7, 2026
d05bc85
change compiler/jvm reference to Java 8
kjw142857 Sep 7, 2026
e17c26d
merge java 8 integration from std-imports
kjw142857 Sep 7, 2026
9c2fc3a
Merge branch 'main' into switch-statements
kjw142857 Sep 7, 2026
317ece6
fix dropping of initialiser and add helper for class collation
kjw142857 Sep 7, 2026
de7cd15
Refactor enum switch label error handling
kjw142857 Sep 9, 2026
55754df
Simplify error check for duplicate classes
kjw142857 Sep 9, 2026
1702d4d
Merge branch 'main' into switch-statements
kjw142857 Sep 9, 2026
8f4b3e8
fix bug with updating of fields
kjw142857 Sep 23, 2026
5f7b9ea
Merge remote-tracking branch 'origin/main' into inner-classes
kjw142857 Sep 23, 2026
9436019
merge from main
kjw142857 Sep 23, 2026
bc0045a
Merge remote-tracking branch 'origin/main' into conductor
kjw142857 Sep 23, 2026
0daa7a2
Add dependencies and conductor bundling
kjw142857 Sep 30, 2026
04f493f
resolve missing components
kjw142857 Oct 6, 2026
81aa6be
Merge branch 'conductor' into inner-classes
kjw142857 Oct 7, 2026
56903d9
Integrate Conductor changes from branch
kjw142857 Oct 7, 2026
c5e47ea
Add two-pass verification for scoping reference checks
kjw142857 Oct 7, 2026
2855d52
Remove buffer usage
kjw142857 Oct 8, 2026
db20791
Add implementation dependencies
kjw142857 Oct 8, 2026
9633658
fix processing of Java evaluator chunks
kjw142857 Oct 23, 2026
d9d3432
add autocomplete suggestions
kjw142857 Oct 23, 2026
593b23f
Merge remote-tracking branch 'origin/main' into conductor
kjw142857 Oct 23, 2026
d0cbff1
fix formatting
kjw142857 Oct 23, 2026
9d126a8
Merge remote-tracking branch 'origin/main' into inner-classes
kjw142857 Oct 23, 2026
6165099
preserve autocomplete and streamline result path
kjw142857 Oct 27, 2026
6ddb58c
Merge branch 'conductor' into inner-classes
kjw142857 Oct 27, 2026
df975cc
fix duplicate inner class name bug, extends writing and merge from co…
kjw142857 Oct 27, 2026
cb374d2
Merge branch 'main' into inner-classes
martin-henz Oct 27, 2026
ecb8a2f
fix declaration and qualified nesting
kjw142857 Oct 27, 2026
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
36 changes: 36 additions & 0 deletions src/ast/__tests__/class-extractor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,39 @@ describe("extract multiple NormalClassDeclaration correctly", () => {
expect(ast).toEqual(expectedAst);
});
});

describe("extract nested NormalClassDeclaration correctly", () => {
it("extract a static nested class inside a class body correctly", () => {
const programStr = `
class Outer {
static class Inner {}
}
`;

const expectedAst: AST = {
kind: "CompilationUnit",
importDeclarations: [],
topLevelClassOrInterfaceDeclarations: [
{
kind: "NormalClassDeclaration",
classModifier: [],
typeIdentifier: "Outer",
classBody: [
{
kind: "NormalClassDeclaration",
classModifier: ["static"],
typeIdentifier: "Inner",
classBody: [],
location: expect.anything(),
},
],
location: expect.anything(),
},
],
location: expect.anything(),
};

const ast = parse(programStr);
expect(ast).toEqual(expectedAst);
});
});
7 changes: 7 additions & 0 deletions src/ast/astExtractor/class-extractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,5 +91,12 @@ export class ClassExtractor extends BaseJavaCstVisitorWithDefaults {
this.body.push(methodNode);
})
}
if (ctx.classDeclaration) {
ctx.classDeclaration.forEach(x => {
const classExtractor = new ClassExtractor();
const classNode = classExtractor.extract(x);
this.body.push(classNode);
})
}
}
}
2 changes: 1 addition & 1 deletion src/ast/types/classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export type ClassModifier =
| "strictfp"
| "enum";

export type ClassBodyDeclaration = ClassMemberDeclaration | ConstructorDeclaration | EnumDeclaration;
export type ClassBodyDeclaration = ClassMemberDeclaration | ConstructorDeclaration | EnumDeclaration | NormalClassDeclaration;
export type ClassMemberDeclaration = MethodDeclaration | FieldDeclaration;

export interface ConstructorDeclaration extends BaseNode {
Expand Down
6 changes: 4 additions & 2 deletions src/compiler/__tests__/classOrdering.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('compiled class ordering', () => {
}
`
const classes = compileFromSource(program)
expect(classes.map(c => c.className)).toEqual(['Main', 'Day'])
expect(classes.map(c => c.className)).toEqual(['Main', 'Main$Day'])
})

it('keeps top-level declaration order, with member enums appended', () => {
Expand All @@ -24,7 +24,9 @@ describe('compiled class ordering', () => {
`
const classes = compileFromSource(program)
expect(classes[0].className).toBe('Main')
expect(new Set(classes.map(c => c.className))).toEqual(new Set(['Main', 'A', 'B']))
expect(new Set(classes.map(c => c.className))).toEqual(
new Set(['Main', 'Main$A', 'Main$B'])
)
})

it('is unchanged when there is no enum', () => {
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/__tests__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { importTest } from "./tests/import.test";
import { arrayTest } from "./tests/array.test";
import { classTest } from "./tests/class.test";
import { enumTest } from "./tests/enum.test";
import { nestedClassesTest } from "./tests/nestedClasses.test";
import { typeConversionTest } from "./tests/typeConversion.test";

describe("compiler tests", () => {
Expand All @@ -25,5 +26,6 @@ describe("compiler tests", () => {
arrayTest();
classTest();
enumTest();
nestedClassesTest();
typeConversionTest();
})
13 changes: 13 additions & 0 deletions src/compiler/__tests__/tests/class.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,19 @@ const testCases: testCase[] = [
`,
expectedLines: ["in f"],
},
{
comment: "instance field with an inline initializer",
program: `
public class Main {
public int one = 1;
public static void main(String[] args) {
Main m = new Main();
System.out.println(m.one);
}
}
`,
expectedLines: ["1"],
},
{
comment: "instance field",
program: `
Expand Down
265 changes: 265 additions & 0 deletions src/compiler/__tests__/tests/nestedClasses.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
import {
runTest,
testCase,
} from "../__utils__/test-utils";
import { compileFromSource } from "../../index";

const testCases: testCase[] = [
{
comment: "basic static nested class instantiation and usage",
program: `
public class Main {
public static class Inner {
public int value;
public Inner(int v) {
this.value = v;
}
public int getValue() {
return this.value;
}
}

public static void main(String[] args) {
Inner inner = new Inner(42);
System.out.println(inner.getValue());
}
}
`,
expectedLines: ["42"],
},
{
comment: "one nested class field typed as a sibling nested class",
program: `
public class Main {
public static class Box {
public int value;
public Box(int v) {
this.value = v;
}
}

public static class Holder {
public Box box;
public Holder(Box b) {
this.box = b;
}
}

public static void main(String[] args) {
Box box = new Box(9);
Holder holder = new Holder(box);
System.out.println(holder.box.value);
}
}
`,
expectedLines: ["9"],
},
{
comment: "nested class instance field with an inline initializer",
program: `
public class Main {
static class Inner1 {
public int one = 1;
}
public static void main(String[] args) {
Inner1 inner1 = new Inner1();
int n = inner1.one;
System.out.println(n);
}
}
`,
expectedLines: ["1"],
},
{
comment: "nested class forward-references a sibling class declared after it",
program: `
public class Main {
static class A {
int get() { return B.code; }
}
static class B {
static int code = 9;
}
public static void main(String[] args) {
A a = new A();
System.out.println(a.get());
}
}
`,
expectedLines: ["9"],
},
{
comment: "nested class forward-references a sibling enum declared after it",
program: `
public class Main {
static class Box {
Color get() { return Color.RED; }
}
enum Color { RED, BLUE }
public static void main(String[] args) {
Box box = new Box();
Color c = box.get();
System.out.println(c.name());
}
}
`,
expectedLines: ["RED"],
},
{
comment: "two levels of static nested class",
program: `
public class Main {
public static class Middle {
public static class Inner {
public String greet() {
return "hello";
}
}
}

public static void main(String[] args) {
Inner inner = new Inner();
System.out.println(inner.greet());
}
}
`,
expectedLines: ["hello"],
},
{
comment: "two unrelated top-level classes each have a nested class with the same simple name",
program: `
class A {
static class Item {}
}
class B {
static class Item {}
}
public class Main {
public static void main(String[] args) {
System.out.println(0);
}
}
`,
expectedLines: ["0"],
},
{
comment: "nested class extends a sibling nested class",
program: `
public class Main {
static class Base {}
static class Child extends Base {
public int get() { return 1; }
}

public static void main(String[] args) {
Child child = new Child();
System.out.println(child.get());
}
}
`,
expectedLines: ["1"],
},
{
comment: "two levels deep: nested class extends a sibling nested class",
program: `
public class Main {
static class Middle {
static class Base {}
static class Child extends Base {
public int get() { return 1; }
}
}

public static void main(String[] args) {
Child child = new Child();
System.out.println(child.get());
}
}
`,
expectedLines: ["1"],
},
{
comment: "nested class extends its own (nested) enclosing class",
program: `
public class Main {
static class Middle {
static class Child extends Middle {
public int get() { return 1; }
}
}

public static void main(String[] args) {
Child child = new Child();
System.out.println(child.get());
}
}
`,
expectedLines: ["1"],
},
{
comment: "unqualified declaration, qualified instantiation (new Outer.Inner())",
program: `
public class Main {
static class Inner {
public int get() { return 1; }
}
public static void main(String[] args) {
Inner inner = new Main.Inner();
System.out.println(inner.get());
}
}
`,
expectedLines: ["1"],
},
{
comment: "qualified declaration and instantiation (Outer.Inner on both sides)",
program: `
public class Main {
static class Inner {
public int get() { return 1; }
}
public static void main(String[] args) {
Main.Inner inner = new Main.Inner();
System.out.println(inner.get());
}
}
`,
expectedLines: ["1"],
},
{
comment: "two levels deep, fully qualified (Outer.Middle.Inner)",
program: `
public class Main {
static class Middle {
static class Inner {
public int get() { return 1; }
}
}
public static void main(String[] args) {
Main.Middle.Inner inner = new Main.Middle.Inner();
System.out.println(inner.get());
}
}
`,
expectedLines: ["1"],
},
];

export const nestedClassesTest = () => describe("static nested classes", () => {
for (let testCase of testCases) {
const { comment: comment, program: program, expectedLines: expectedLines } = testCase;
it(comment, () => runTest(program, expectedLines));
}

it("still rejects two top-level classes sharing the same name", () => {
expect(() =>
compileFromSource(`
class Item {}
class Item {}
public class Main {
public static void main(String[] args) {}
}
`)
).toThrow('"Item" has already been declared')
})
});
Loading
Loading