Tweak port update from Claude.ai

gettimothy via Squeak-dev <[email protected]>
Newsgroups gmane.comp.lang.smalltalk.squeak.general
Message-ID <[email protected]>
# Subject: Tweak/OpenCobalt port to Squeak 6.1 — CCompiler Facade milestone



**To:** mailto:[email protected]  

**Date:** Monday, May 25, 2026



---



Hello Squeakers,



I wanted to share a meaningful milestone in the ongoing effort to port

Tweak/OpenCobalt to 64-bit Squeak 6.1 alpha.



---



## Background



Tweak is the constraint-based, player/costume/script UI framework that

grew out of the Squeak Etoys lineage and formed the foundation of

OpenCobalt. The codebase dates from the early 2000s and was designed

around a parallel compiler pipeline — CCompiler, CParser, CEncoder,

and a full set of C-prefixed AST node classes (CMessageNode,

CBlockNode, etc.) rooted at CParseNode. The "C" stands for Croquet,

not C-the-language.



The challenge: none of that parallel pipeline is compatible with

modern 64-bit Squeak's bytecode format (SistaV1), slot layout, or

compiler architecture.



---



## The Architectural Pivot



Eliot Miranda suggested the right approach: rather than porting the

parallel C-node pipeline forward, make **CCompiler a Facade over the

standard Squeak compiler**. CCompiler becomes a thin subclass of

Compiler. CParser is wired to produce standard Squeak AST nodes

(MethodNode, BlockNode, MessageNode, etc.) that the standard

EncoderForSistaV1 can handle. The C-node class hierarchy remains in

the image as (effectively) dead code — no need to delete it, no need

to port it.



This is architecturally clean and means all future maintenance rides

on the standard Squeak compiler infrastructure rather than a parallel

system nobody remembers how to maintain.



---



## What We Fixed This Session



Getting `CCompiler superclass: Compiler` to actually work required

untangling a chain of issues:



1. **`CCompiler class >> initialize`** — was `self recompileAll`.

   Neutralized before changing superclass (would have been catastrophic).



2. **`CParser >> encoderFromCue:`** — was hardwiring `CEncoder new`.

   Now respects `aCompilationCue encoderClass` or falls back to the

   standard encoder.



3. **`CParser >> parse:class:category:noPattern:context:notifying:ifFail:`**

   — was hardcoding `CEncoder new init:`. Now uses `EncoderForSistaV1`

   via a proper `CompilationCue`.



4. **`CCompiler >> setCue:`** — was calling `getClass` on the argument

   without a type check. When called with a raw class object instead of

   a CompilationCue, it raised MNU. Added `isKindOf: CompilationCue` guard.



5. **`CCompiler >> from:class:context:notifying:`** — was setting raw

   instance variables, bypassing the cue system entirely. Rewired to

   call `self setCue: (CompilationCue source:context:class:requestor:)`.



6. **`CCompiler >> evaluate:in:to:notifying:ifFail:logged:`** — was using

   the old DoIt-installation approach (addSelectorSilently: on

   UndefinedObject, which Squeak rightly rejects). Now delegates to

   `evaluateCue:ifFail:logged:` matching the modern Compiler.



7. **`MethodProperties >> propertyKeysAndValuesDo:` and `pragmasDo:`** —

   two methods the compilation pipeline expected but CCompiler's version

   of MethodProperties didn't provide. Added them.



8. **`CCompiler compileAll`** — the final and most important step.

   After `CCompiler superclass: Compiler`, the class gained new inherited

   instance variables. Any method compiled before the superclass change

   had stale bytecode slot indices. `compileAll` recompiled everything

   with the correct indices. Without this, `cue` reads in some methods

   were returning nil from the wrong ivar slot — leading to a confusing

   cascade of `UndefinedObject>>sourceStream` errors.



---



## The Result



```smalltalk

CCompiler evaluate: '3 + 4'   "→ 7"

```



The Facade is working. The standard Squeak compilation pipeline runs

end-to-end through CCompiler and CParser, producing correct bytecode

via EncoderForSistaV1.



---



## A Note on AI-Assisted Porting



This session was a collaboration between myself, Claude claude.ai

(used for diagnosis and architectural reasoning), and Claude Code

(automated execution of fix attempts via an MCP-TCP bridge into the

live Squeak image). The combination works well: claude.ai reasons

about the Squeak class hierarchy and compiler internals; Claude Code

executes targeted fixes in the live image.



The pattern that emerged: claude.ai diagnoses, Claude Code applies. When

Claude Code looped (it made 150+ TCP connections over the session, mostly

re-diagnosing the same issue from scratch), the winning move was to apply

the two-line fix manually in the Workspace: `CCompiler compileAll` followed

by the test expression. Sometimes the Workspace is still the right tool.



---



## What's Next



The immediate target is filing in `Tweak-Core-Object-tty.20`, which was

the package that blocked progress in earlier sessions. After that:

Tweak-Costume, Tweak-Basic, Tweak-Platforms, Tweak-Widgets,

Tweak-Morphic, Tweak-Projects, and Tweak-ToolBuilder.



The image is Squeak 6.1 alpha, 64-bit. The package cache is from the

original OpenCobalt distribution. Happy to share the image or discuss

the approach further.



Many thanks to Eliot Miranda for the Facade suggestion — it was exactly

right.



---



Timothy 

(with Claude Sonnet 4.6)



---



*Related reading:*

- *Squeak Etoys*: original player/costume/script model

- *OpenCobalt*: the Tweak-based collaborative 3D browser this codebase comes from

- *SistaV1*: Squeak 6.x bytecode format, the compilation target for all of the above

Squeak-dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.