From 74b6d06711afd3efbbc47436f1491900994313f3 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Sat, 17 Oct 2026 06:54:32 +0000 Subject: feat(classes): allocate objects with NEW and rebind named class variables Co-Authored-By: Claude Opus 5.5 --- tests/i/class-heap-new/case.json | 1 + tests/i/class-heap-new/expected.txt | 16 ++++++ tests/i/class-heap-new/notes.md | 5 ++ tests/i/class-heap-new/program.cps | 100 +++++++++++++++++++++++++++++++++ tests/iii/class-new-errors/case.json | 32 +++++++++++ tests/iii/class-new-errors/program.cps | 26 +++++++++ 6 files changed, 180 insertions(+) create mode 100644 tests/i/class-heap-new/case.json create mode 100644 tests/i/class-heap-new/expected.txt create mode 100644 tests/i/class-heap-new/notes.md create mode 100644 tests/i/class-heap-new/program.cps create mode 100644 tests/iii/class-new-errors/case.json create mode 100644 tests/iii/class-new-errors/program.cps (limited to 'tests') diff --git a/tests/i/class-heap-new/case.json b/tests/i/class-heap-new/case.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/i/class-heap-new/case.json @@ -0,0 +1 @@ +{} diff --git a/tests/i/class-heap-new/expected.txt b/tests/i/class-heap-new/expected.txt new file mode 100644 index 0000000..37e731a --- /dev/null +++ b/tests/i/class-heap-new/expected.txt @@ -0,0 +1,16 @@ +rebind:7,9 +copy:7,4,4 +byval:4,20 +caller:4 +byref:1,1 +this:1,1 +reset:1 +array:1,1 +field:1 +alias:1 +deref:1 +args:42 +declare caught +new caught +loop:1 +loop:1 diff --git a/tests/i/class-heap-new/notes.md b/tests/i/class-heap-new/notes.md new file mode 100644 index 0000000..3a6435c --- /dev/null +++ b/tests/i/class-heap-new/notes.md @@ -0,0 +1,5 @@ +# Heap NEW and object storage + +Human review: pending. Expected output was independently traced by AI. + +Project semantics extend guide section 10.2. Each Box constructor sets Value to 1. Rebinding A preserves the old stack object's 7 while the new object becomes 9. Ordinary A <- B copies 4 into the current heap object, leaving B and the old stack object independent. BYVAL copies 4, then independently rebinds and changes its local object to 20. BYREF NEW copies 1 into A's current object, observed through CurrentA. Replacing this likewise copies into that same object; explicit Reset calls the ordinary constructor method. Array-element, field, bare field alias and pointer-dereference NEW assignments preserve their destination addresses and copy 1. Named's argument is 42. Failing throws in both DECLARE and heap construction, reaching the surrounding catches. Loop DECLARE restores its original stack object and constructs it on every iteration, so the previous heap object's 99 is never printed. diff --git a/tests/i/class-heap-new/program.cps b/tests/i/class-heap-new/program.cps new file mode 100644 index 0000000..7b8363f --- /dev/null +++ b/tests/i/class-heap-new/program.cps @@ -0,0 +1,100 @@ +CLASS Box + Value : INTEGER + PROCEDURE NEW() + Value <- 1 + ENDPROCEDURE + PROCEDURE Reset() + this.NEW() + ENDPROCEDURE + PROCEDURE ReplaceSelf() + this <- NEW Box() + ENDPROCEDURE +ENDCLASS +CLASS Named + Value : INTEGER + PROCEDURE NEW(Number : INTEGER) + Value <- Number + ENDPROCEDURE +ENDCLASS +CLASS Holder + Item : Box + PROCEDURE ReplaceField() + Item <- NEW Box + ENDPROCEDURE +ENDCLASS +CLASS Failing + PROCEDURE NEW() + THROW + ENDPROCEDURE +ENDCLASS +TYPE BoxPointer = ^Box +PROCEDURE ReplaceRef(BYREF Item : Box) + Item <- NEW Box +ENDPROCEDURE +PROCEDURE ReplaceVal(Item : Box) + DECLARE Old : BoxPointer + Old <- ^Item + Item <- NEW Box() + Item.Value <- 20 + OUTPUT "byval:", Old^.Value, ",", Item.Value +ENDPROCEDURE +DECLARE A : Box +DECLARE B : Box +DECLARE OldA : BoxPointer +DECLARE CurrentA : BoxPointer +OldA <- ^A +A.Value <- 7 +A <- NEW Box +A.Value <- 9 +CurrentA <- ^A +OUTPUT "rebind:", OldA^.Value, ",", A.Value +B.Value <- 4 +A <- B +OUTPUT "copy:", OldA^.Value, ",", CurrentA^.Value, ",", B.Value +CALL ReplaceVal(A) +OUTPUT "caller:", A.Value +CALL ReplaceRef(A) +OUTPUT "byref:", CurrentA^.Value, ",", A.Value +A.Value <- 11 +A.ReplaceSelf() +OUTPUT "this:", CurrentA^.Value, ",", A.Value +A.Value <- 12 +A.Reset() +OUTPUT "reset:", A.Value +DECLARE Items : ARRAY[1:2] OF Box +DECLARE Element : BoxPointer +Element <- ^Items[1] +Items[1].Value <- 30 +Items[1] <- NEW Box() +OUTPUT "array:", Element^.Value, ",", Items[2].Value +DECLARE H : Holder +DECLARE Field : BoxPointer +Field <- ^H.Item +H.Item.Value <- 40 +H.Item <- NEW Box +OUTPUT "field:", Field^.Value +H.Item.Value <- 41 +H.ReplaceField() +OUTPUT "alias:", Field^.Value +CurrentA^ <- NEW Box +OUTPUT "deref:", A.Value +DECLARE N : Named(0) +N <- NEW Named(42) +OUTPUT "args:", N.Value +TRY + DECLARE Broken : Failing +CATCH + OUTPUT "declare caught" +ENDTRY +TRY + Broken <- NEW Failing +CATCH + OUTPUT "new caught" +ENDTRY +DECLARE Index : INTEGER +FOR Index <- 1 TO 2 + DECLARE Local : Box + OUTPUT "loop:", Local.Value + Local <- NEW Box + Local.Value <- 99 +NEXT Index diff --git a/tests/iii/class-new-errors/case.json b/tests/iii/class-new-errors/case.json new file mode 100644 index 0000000..8ce52ed --- /dev/null +++ b/tests/iii/class-new-errors/case.json @@ -0,0 +1,32 @@ +{ + "errors": [ + { + "reason": "NEW target must have type Other", + "line": 20 + }, + { + "reason": "NEW requires a class type", + "line": 21 + }, + { + "reason": "NEW requires a class type", + "line": 22 + }, + { + "reason": "has no constructor NEW", + "line": 23 + }, + { + "reason": "Incorrect # arguments passed to Required.NEW", + "line": 24 + }, + { + "reason": "Incorrect # arguments passed to Required.NEW", + "line": 25 + }, + { + "reason": "NEW target must have type Hidden", + "line": 26 + } + ] +} diff --git a/tests/iii/class-new-errors/program.cps b/tests/iii/class-new-errors/program.cps new file mode 100644 index 0000000..3a9e9c2 --- /dev/null +++ b/tests/iii/class-new-errors/program.cps @@ -0,0 +1,26 @@ +CLASS Box + Value : INTEGER +ENDCLASS +CLASS Other + Value : INTEGER +ENDCLASS +CLASS Required + PROCEDURE NEW(Number : INTEGER) + ENDPROCEDURE +ENDCLASS +CLASS Hidden + PRIVATE PROCEDURE NEW() + ENDPROCEDURE +ENDCLASS +TYPE RecordValue + DECLARE Value : INTEGER +ENDTYPE +DECLARE B : Box +DECLARE R : Required(1) +B <- NEW Other +B <- NEW INTEGER +B <- NEW RecordValue +B <- NEW Box(1) +R <- NEW Required +R <- NEW Required() +B <- NEW Hidden -- cgit v1.3.1-4-g156e