Implement the following plan:
Plan: Get Course Module Example Programs to Compile and Run
Context
The occam2go transpiler can transpile the full KRoC course module to Go code that passes go vet, but the output has no main() function and string escape sequences are not converted. This means no program that uses the course module can actually compile and run.
Two changes are needed:
1. String escape sequences: Occam uses *c, *n, *t, *s, **, *", *' in string literals. These are currently passed through literally, so "Hello World*c*n" becomes "Hello World*c*n" in Go instead of "Hello World\r\n".
2. Main harness generation: Occam programs define a PROC with the standard entry point signature (CHAN BYTE keyboard?, screen!, error!). The transpiler needs to detect this and generate a func main() that wires stdin/stdout/stderr to channels and calls the entry PROC.
Changes
1. Convert occam string escapes in the parser (parser/parser.go)
Add a convertOccamStringEscapes(raw string) string function (analogous to the existing parseByteLiteralValue) that converts occam escape sequences in string literals to their actual byte values:
- *n → \n (newline, byte 10)
- *c → \r (carriage return, byte 13)
- *t → \t (tab, byte 9)
- *s → (space, byte 32)
- ** → * (literal asterisk)
- *" → " (literal double quote)
- *' → ' (literal single quote)
Apply this conversion at line 2611 where StringLiteral nodes are created:
case lexer.STRING:
left = &ast.StringLiteral{Token: p.curToken, Value: p.convertOccamStringEscapes(p.curToken.Literal)}
This way the AST stores the actual string content (with real escape bytes), and the existing %q formatting in codegen will produce correct Go string literals.
2. Generate main() harness for entry point PROCs (codegen/codegen.go)
In the Generate() function, after separating statements (around line 258), when mainStatements is empty but there are proc declarations:
-
Detect the entry point: Find the last top-level PROC whose parameters match the pattern: exactly 3 params, all IsChan with ChanElemType == "BYTE", first has ChanDir == "?", second and third have ChanDir == "!".
-
Generate the harness: Emit a func main() that:
- Creates 3 buffered
chan byte channels (keyboard, screen, error)
- Starts a screen writer goroutine: reads from screen channel, writes to
os.Stdout via bufio.Writer, flushes on byte 255 (the FLUSH constant from course module)
- Starts an error writer goroutine: same pattern but writes to
os.Stderr
- Starts a keyboard reader goroutine: reads from
os.Stdin via bufio.Reader, sends bytes to keyboard channel
- Calls the entry PROC with the three channels
- Closes screen and error channels after the proc returns
-
Waits for writer goroutines to finish draining via sync.WaitGroup
-
Imports: When generating the harness, ensure os, sync, and bufio are imported. Add a needBufio flag to the Generator struct.
3. Add tests
- Parser test (
parser/parser_test.go): Test convertOccamStringEscapes with various escape sequences.
- Codegen unit test (
codegen/codegen_test.go): Test that a string literal with *n produces a Go string with \n.
- E2E test (
codegen/e2e_test.go or new file): Test hello_world.occ transpile → compile → run, verifying output is Hello World\r\n.
Files to Modify
parser/parser.go — Add convertOccamStringEscapes(), use it at line 2611
codegen/codegen.go — Add entry point detection, main harness generation, needBufio flag, bufio import
parser/parser_test.go — Add string escape tests
codegen/codegen_test.go — Add string escape codegen test
codegen/e2e_test.go (or new e2e file) — Add hello_world course module e2e test
Verification
# Unit tests
go test ./parser -run TestStringEscape
go test ./codegen -run TestStringEscape
# E2E: hello_world with course module
./occam2go -I kroc/modules/course/libsrc -D TARGET.BITS.PER.WORD=32 -o /tmp/hello_world.go kroc/modules/course/examples/hello_world.occ
go build -o /tmp/hello_world /tmp/hello_world.go
/tmp/hello_world # Should print "Hello World" and exit
# Ensure existing tests still pass
go test ./...
# Course module still passes go vet
./occam2go -I kroc/modules/course/libsrc -D TARGET.BITS.PER.WORD=32 -o /tmp/course_out.go kroc/modules/course/libsrc/course.module
go vet /tmp/course_out.go
If you need specific details from before exiting plan mode (like exact code snippets, error messages, or content you generated), read the full transcript at: /home/david/.claude/projects/-home-david-projects-code-associates-occam2go/d3a0ce94-fdf8-473f-b3d7-7da06efe9ce6.jsonl